aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2021-01-14 13:28:49 +0000
committerRon Yorston <rmy@pobox.com>2021-01-14 13:28:49 +0000
commit89963b524d211e1aec12b72b3725be05ee95c8cf (patch)
tree48590aef62b7ee7686b7898256f29def8d9c50b9 /shell
parent9aa5a829070392c2ac6494d0c4e674c0c2bc7dab (diff)
parent2b7c1aa92c68524559a2067609d09309d5c09adc (diff)
downloadbusybox-w32-89963b524d211e1aec12b72b3725be05ee95c8cf.tar.gz
busybox-w32-89963b524d211e1aec12b72b3725be05ee95c8cf.tar.bz2
busybox-w32-89963b524d211e1aec12b72b3725be05ee95c8cf.zip
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c202
-rw-r--r--shell/ash_test/ash-misc/piped_input.right2
-rwxr-xr-xshell/ash_test/ash-misc/piped_input.tests3
-rw-r--r--shell/ash_test/ash-vars/var_bash_repl_empty_var.right1
-rwxr-xr-xshell/ash_test/ash-vars/var_bash_repl_empty_var.tests2
-rw-r--r--shell/hush.c404
-rw-r--r--shell/hush_test/hush-misc/piped_input.right2
-rwxr-xr-xshell/hush_test/hush-misc/piped_input.tests3
-rw-r--r--shell/hush_test/hush-vars/var_bash_repl_empty_var.right1
-rwxr-xr-xshell/hush_test/hush-vars/var_bash_repl_empty_var.tests2
-rw-r--r--shell/shell_common.c21
11 files changed, 380 insertions, 263 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 897548f3c..780900b40 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -418,9 +418,12 @@ static void forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes)
418 418
419/* ============ Shell options */ 419/* ============ Shell options */
420 420
421/* If you add/change options hare, update --help text too */
421static const char *const optletters_optnames[] = { 422static const char *const optletters_optnames[] = {
422 "e" "errexit", 423 "e" "errexit",
423 "f" "noglob", 424 "f" "noglob",
425/* bash has '-o ignoreeof', but no short synonym -I for it */
426/* (in bash, set -I disables invisible variables (what's that?)) */
424 "I" "ignoreeof", 427 "I" "ignoreeof",
425/* The below allowed this invocation: 428/* The below allowed this invocation:
426 * ash -c 'set -i; echo $-; sleep 5; echo $-' 429 * ash -c 'set -i; echo $-; sleep 5; echo $-'
@@ -429,9 +432,10 @@ static const char *const optletters_optnames[] = {
429 * In our code, this is denoted by empty long name: 432 * In our code, this is denoted by empty long name:
430 */ 433 */
431 "i" "", 434 "i" "",
435/* (removing "i" altogether would remove it from "$-", not good) */
432 "m" "monitor", 436 "m" "monitor",
433 "n" "noexec", 437 "n" "noexec",
434/* Ditto: bash has no "set -s" */ 438/* Ditto: bash has no "set -s", "set -c" */
435#if !ENABLE_PLATFORM_MINGW32 439#if !ENABLE_PLATFORM_MINGW32
436 "s" "", 440 "s" "",
437#else 441#else
@@ -707,6 +711,63 @@ var_end(const char *var)
707} 711}
708 712
709 713
714/* ============ Parser data */
715
716/*
717 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
718 */
719struct strlist {
720 struct strlist *next;
721 char *text;
722};
723
724struct alias;
725
726struct strpush {
727 struct strpush *prev; /* preceding string on stack */
728 char *prev_string;
729 int prev_left_in_line;
730#if ENABLE_ASH_ALIAS
731 struct alias *ap; /* if push was associated with an alias */
732#endif
733 char *string; /* remember the string since it may change */
734
735 /* Remember last two characters for pungetc. */
736 int lastc[2];
737
738 /* Number of outstanding calls to pungetc. */
739 int unget;
740};
741
742/*
743 * The parsefile structure pointed to by the global variable parsefile
744 * contains information about the current file being read.
745 */
746struct parsefile {
747 struct parsefile *prev; /* preceding file on stack */
748 int linno; /* current line */
749 int pf_fd; /* file descriptor (or -1 if string) */
750 int left_in_line; /* number of chars left in this line */
751 int left_in_buffer; /* number of chars left in this buffer past the line */
752 char *next_to_pgetc; /* next char in buffer */
753 char *buf; /* input buffer */
754 struct strpush *strpush; /* for pushing strings at this level */
755 struct strpush basestrpush; /* so pushing one is fast */
756
757 /* Remember last two characters for pungetc. */
758 int lastc[2];
759
760 /* Number of outstanding calls to pungetc. */
761 int unget;
762};
763
764static struct parsefile basepf; /* top level input file */
765static struct parsefile *g_parsefile = &basepf; /* current input file */
766#if ENABLE_PLATFORM_POSIX
767static char *commandname; /* currently executing command */
768#endif
769
770
710/* ============ Interrupts / exceptions */ 771/* ============ Interrupts / exceptions */
711 772
712static void exitshell(void) NORETURN; 773static void exitshell(void) NORETURN;
@@ -1466,63 +1527,6 @@ showtree(union node *n)
1466#endif /* DEBUG */ 1527#endif /* DEBUG */
1467 1528
1468 1529
1469/* ============ Parser data */
1470
1471/*
1472 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1473 */
1474struct strlist {
1475 struct strlist *next;
1476 char *text;
1477};
1478
1479struct alias;
1480
1481struct strpush {
1482 struct strpush *prev; /* preceding string on stack */
1483 char *prev_string;
1484 int prev_left_in_line;
1485#if ENABLE_ASH_ALIAS
1486 struct alias *ap; /* if push was associated with an alias */
1487#endif
1488 char *string; /* remember the string since it may change */
1489
1490 /* Remember last two characters for pungetc. */
1491 int lastc[2];
1492
1493 /* Number of outstanding calls to pungetc. */
1494 int unget;
1495};
1496
1497/*
1498 * The parsefile structure pointed to by the global variable parsefile
1499 * contains information about the current file being read.
1500 */
1501struct parsefile {
1502 struct parsefile *prev; /* preceding file on stack */
1503 int linno; /* current line */
1504 int pf_fd; /* file descriptor (or -1 if string) */
1505 int left_in_line; /* number of chars left in this line */
1506 int left_in_buffer; /* number of chars left in this buffer past the line */
1507 char *next_to_pgetc; /* next char in buffer */
1508 char *buf; /* input buffer */
1509 struct strpush *strpush; /* for pushing strings at this level */
1510 struct strpush basestrpush; /* so pushing one is fast */
1511
1512 /* Remember last two characters for pungetc. */
1513 int lastc[2];
1514
1515 /* Number of outstanding calls to pungetc. */
1516 int unget;
1517};
1518
1519static struct parsefile basepf; /* top level input file */
1520static struct parsefile *g_parsefile = &basepf; /* current input file */
1521#if ENABLE_PLATFORM_POSIX
1522static char *commandname; /* currently executing command */
1523#endif
1524
1525
1526/* ============ Message printing */ 1530/* ============ Message printing */
1527 1531
1528static void 1532static void
@@ -2284,7 +2288,7 @@ static const struct {
2284 int flags; 2288 int flags;
2285 const char *var_text; 2289 const char *var_text;
2286 void (*var_func)(const char *) FAST_FUNC; 2290 void (*var_func)(const char *) FAST_FUNC;
2287} varinit_data[] = { 2291} varinit_data[] ALIGN_PTR = {
2288 /* 2292 /*
2289 * Note: VEXPORT would not work correctly here for NOFORK applets: 2293 * Note: VEXPORT would not work correctly here for NOFORK applets:
2290 * some environment strings may be constant. 2294 * some environment strings may be constant.
@@ -5137,7 +5141,7 @@ getstatus(struct job *job)
5137 job->sigint = 1; 5141 job->sigint = 1;
5138#endif 5142#endif
5139 } 5143 }
5140 retval += 128; 5144 retval |= 128;
5141 } 5145 }
5142 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n", 5146 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
5143 jobno(job), job->nprocs, status, retval)); 5147 jobno(job), job->nprocs, status, retval));
@@ -5203,7 +5207,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
5203 if (status != -1 && !WIFSTOPPED(status)) { 5207 if (status != -1 && !WIFSTOPPED(status)) {
5204 retval = WEXITSTATUS(status); 5208 retval = WEXITSTATUS(status);
5205 if (WIFSIGNALED(status)) 5209 if (WIFSIGNALED(status))
5206 retval = WTERMSIG(status) + 128; 5210 retval = 128 | WTERMSIG(status);
5207 goto ret; 5211 goto ret;
5208 } 5212 }
5209 } 5213 }
@@ -5238,7 +5242,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
5238 ret: 5242 ret:
5239 return retval; 5243 return retval;
5240 sigout: 5244 sigout:
5241 retval = 128 + pending_sig; 5245 retval = 128 | pending_sig;
5242 return retval; 5246 return retval;
5243} 5247}
5244 5248
@@ -5340,7 +5344,7 @@ static char *cmdnextc;
5340static void 5344static void
5341cmdputs(const char *s) 5345cmdputs(const char *s)
5342{ 5346{
5343 static const char vstype[VSTYPE + 1][3] = { 5347 static const char vstype[VSTYPE + 1][3] ALIGN1 = {
5344 "", "}", "-", "+", "?", "=", 5348 "", "}", "-", "+", "?", "=",
5345 "%", "%%", "#", "##" 5349 "%", "%%", "#", "##"
5346 IF_BASH_SUBSTR(, ":") 5350 IF_BASH_SUBSTR(, ":")
@@ -7578,7 +7582,8 @@ subevalvar(char *start, char *str, int strloc,
7578 slash_pos = -1; 7582 slash_pos = -1;
7579 if (repl) { 7583 if (repl) {
7580 slash_pos = expdest - ((char *)stackblock() + strloc); 7584 slash_pos = expdest - ((char *)stackblock() + strloc);
7581 STPUTC('/', expdest); 7585 if (!(flag & EXP_DISCARD))
7586 STPUTC('/', expdest);
7582 //bb_error_msg("repl+1:'%s'", repl + 1); 7587 //bb_error_msg("repl+1:'%s'", repl + 1);
7583 p = argstr(repl + 1, (flag & EXP_DISCARD) | EXP_TILDE); /* EXP_TILDE: echo "${v/x/~}" expands ~ ! */ 7588 p = argstr(repl + 1, (flag & EXP_DISCARD) | EXP_TILDE); /* EXP_TILDE: echo "${v/x/~}" expands ~ ! */
7584 *repl = '/'; 7589 *repl = '/';
@@ -7740,7 +7745,7 @@ subevalvar(char *start, char *str, int strloc,
7740 len = 0; 7745 len = 0;
7741 idx = startp; 7746 idx = startp;
7742 end = str - 1; 7747 end = str - 1;
7743 while (idx < end) { 7748 while (idx <= end) {
7744 try_to_match: 7749 try_to_match:
7745 loc = scanright(idx, rmesc, rmescend, str, quotes, 1); 7750 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
7746 //bb_error_msg("scanright('%s'):'%s'", str, loc); 7751 //bb_error_msg("scanright('%s'):'%s'", str, loc);
@@ -7748,6 +7753,8 @@ subevalvar(char *start, char *str, int strloc,
7748 /* No match, advance */ 7753 /* No match, advance */
7749 char *restart_detect = stackblock(); 7754 char *restart_detect = stackblock();
7750 skip_matching: 7755 skip_matching:
7756 if (idx >= end)
7757 break;
7751 STPUTC(*idx, expdest); 7758 STPUTC(*idx, expdest);
7752 if (quotes && (unsigned char)*idx == CTLESC) { 7759 if (quotes && (unsigned char)*idx == CTLESC) {
7753 idx++; 7760 idx++;
@@ -7760,8 +7767,6 @@ subevalvar(char *start, char *str, int strloc,
7760 len++; 7767 len++;
7761 rmesc++; 7768 rmesc++;
7762 /* continue; - prone to quadratic behavior, smarter code: */ 7769 /* continue; - prone to quadratic behavior, smarter code: */
7763 if (idx >= end)
7764 break;
7765 if (str[0] == '*') { 7770 if (str[0] == '*') {
7766 /* Pattern is "*foo". If "*foo" does not match "long_string", 7771 /* Pattern is "*foo". If "*foo" does not match "long_string",
7767 * it would never match "ong_string" etc, no point in trying. 7772 * it would never match "ong_string" etc, no point in trying.
@@ -7844,7 +7849,9 @@ subevalvar(char *start, char *str, int strloc,
7844 out: 7849 out:
7845 amount = loc - expdest; 7850 amount = loc - expdest;
7846 STADJUST(amount, expdest); 7851 STADJUST(amount, expdest);
7852#if BASH_PATTERN_SUBST
7847 out1: 7853 out1:
7854#endif
7848 /* Remove any recorded regions beyond start of variable */ 7855 /* Remove any recorded regions beyond start of variable */
7849 removerecordregions(startloc); 7856 removerecordregions(startloc);
7850 7857
@@ -9127,7 +9134,7 @@ enum {
9127 , /* thus far 29 bits used */ 9134 , /* thus far 29 bits used */
9128}; 9135};
9129 9136
9130static const char *const tokname_array[] = { 9137static const char *const tokname_array[] ALIGN_PTR = {
9131 "end of file", 9138 "end of file",
9132 "newline", 9139 "newline",
9133 "redirection", 9140 "redirection",
@@ -11360,7 +11367,7 @@ preadfd(void)
11360 11367
11361 g_parsefile->next_to_pgetc = buf; 11368 g_parsefile->next_to_pgetc = buf;
11362#if ENABLE_FEATURE_EDITING 11369#if ENABLE_FEATURE_EDITING
11363 retry: 11370 /* retry: */
11364 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) 11371 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
11365 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 11372 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
11366 else { 11373 else {
@@ -11382,17 +11389,16 @@ preadfd(void)
11382 if (nr == 0) { 11389 if (nr == 0) {
11383 /* ^C pressed, "convert" to SIGINT */ 11390 /* ^C pressed, "convert" to SIGINT */
11384 write(STDOUT_FILENO, "^C", 2); 11391 write(STDOUT_FILENO, "^C", 2);
11392# if !ENABLE_PLATFORM_MINGW32
11393 raise(SIGINT);
11394# endif
11385 if (trap[SIGINT]) { 11395 if (trap[SIGINT]) {
11386 buf[0] = '\n'; 11396 buf[0] = '\n';
11387 buf[1] = '\0'; 11397 buf[1] = '\0';
11388# if !ENABLE_PLATFORM_MINGW32
11389 raise(SIGINT);
11390# endif
11391 return 1; 11398 return 1;
11392 } 11399 }
11393 exitstatus = 128 + SIGINT; 11400 exitstatus = 128 + SIGINT;
11394 bb_putchar('\n'); 11401 return -1;
11395 goto retry;
11396 } 11402 }
11397 if (nr < 0) { 11403 if (nr < 0) {
11398 if (errno == 0) { 11404 if (errno == 0) {
@@ -12164,10 +12170,10 @@ static void FAST_FUNC
12164change_epoch(struct var *vepoch, const char *fmt) 12170change_epoch(struct var *vepoch, const char *fmt)
12165{ 12171{
12166 struct timeval tv; 12172 struct timeval tv;
12167 char buffer[sizeof("%lu.nnnnnn") + sizeof(long)*3]; 12173 char buffer[sizeof("%llu.nnnnnn") + sizeof(long long)*3];
12168 12174
12169 gettimeofday(&tv, NULL); 12175 xgettimeofday(&tv);
12170 sprintf(buffer, fmt, (unsigned long)tv.tv_sec, (unsigned)tv.tv_usec); 12176 sprintf(buffer, fmt, (unsigned long long)tv.tv_sec, (unsigned)tv.tv_usec);
12171 setvar(vepoch->var_text, buffer, VNOFUNC); 12177 setvar(vepoch->var_text, buffer, VNOFUNC);
12172 vepoch->flags &= ~VNOFUNC; 12178 vepoch->flags &= ~VNOFUNC;
12173} 12179}
@@ -12175,13 +12181,13 @@ change_epoch(struct var *vepoch, const char *fmt)
12175static void FAST_FUNC 12181static void FAST_FUNC
12176change_seconds(const char *value UNUSED_PARAM) 12182change_seconds(const char *value UNUSED_PARAM)
12177{ 12183{
12178 change_epoch(&vepochs, "%lu"); 12184 change_epoch(&vepochs, "%llu");
12179} 12185}
12180 12186
12181static void FAST_FUNC 12187static void FAST_FUNC
12182change_realtime(const char *value UNUSED_PARAM) 12188change_realtime(const char *value UNUSED_PARAM)
12183{ 12189{
12184 change_epoch(&vepochr, "%lu.%06u"); 12190 change_epoch(&vepochr, "%llu.%06u");
12185} 12191}
12186#endif 12192#endif
12187 12193
@@ -15001,6 +15007,7 @@ reset(void)
15001 /* from input.c: */ 15007 /* from input.c: */
15002 g_parsefile->left_in_buffer = 0; 15008 g_parsefile->left_in_buffer = 0;
15003 g_parsefile->left_in_line = 0; /* clear input buffer */ 15009 g_parsefile->left_in_line = 0; /* clear input buffer */
15010 g_parsefile->unget = 0;
15004 popallfiles(); 15011 popallfiles();
15005 15012
15006 /* from var.c: */ 15013 /* from var.c: */
@@ -15017,8 +15024,7 @@ exitshell(void)
15017 char *p; 15024 char *p;
15018 15025
15019#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 15026#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
15020 if (line_input_state) 15027 save_history(line_input_state); /* may be NULL */
15021 save_history(line_input_state);
15022#endif 15028#endif
15023 savestatus = exitstatus; 15029 savestatus = exitstatus;
15024 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus)); 15030 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
@@ -15197,7 +15203,8 @@ init(void)
15197 15203
15198 15204
15199//usage:#define ash_trivial_usage 15205//usage:#define ash_trivial_usage
15200//usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS] / -s [ARGS]]" 15206//usage: "[-il] [-/+Cabefmnuvx] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS] / -s [ARGS]]"
15207//////// comes from ^^^^^^^^^^optletters
15201//usage:#define ash_full_usage "\n\n" 15208//usage:#define ash_full_usage "\n\n"
15202//usage: "Unix shell interpreter" 15209//usage: "Unix shell interpreter"
15203 15210
@@ -15246,9 +15253,9 @@ procargs(char **argv)
15246 } 15253 }
15247 if (mflag == 2) 15254 if (mflag == 2)
15248 mflag = iflag; 15255 mflag = iflag;
15256 /* Unset options which weren't explicitly set or unset */
15249 for (i = 0; i < NOPTS; i++) 15257 for (i = 0; i < NOPTS; i++)
15250 if (optlist[i] == 2) 15258 optlist[i] &= 1; /* same effect as "if (optlist[i] == 2) optlist[i] = 0;" */
15251 optlist[i] = 0;
15252#if DEBUG == 2 15259#if DEBUG == 2
15253 debug = 1; 15260 debug = 1;
15254#endif 15261#endif
@@ -15277,6 +15284,19 @@ procargs(char **argv)
15277 shellparam.nparam++; 15284 shellparam.nparam++;
15278 xargv++; 15285 xargv++;
15279 } 15286 }
15287
15288 /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry.
15289 * Try:
15290 * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect
15291 * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed
15292 * NB: must do it before setting up signals (in optschanged())
15293 * and reading .profile etc (after we return from here):
15294 */
15295#if !ENABLE_PLATFORM_MINGW32
15296 if (iflag)
15297 signal(SIGHUP, SIG_DFL);
15298#endif
15299
15280 optschanged(); 15300 optschanged();
15281 15301
15282 return login_sh; 15302 return login_sh;
@@ -15493,7 +15513,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
15493 15513
15494 if (sflag || minusc == NULL) { 15514 if (sflag || minusc == NULL) {
15495#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY 15515#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
15496 if (iflag) { 15516 if (line_input_state) {
15497 const char *hp = lookupvar("HISTFILE"); 15517 const char *hp = lookupvar("HISTFILE");
15498 if (!hp) { 15518 if (!hp) {
15499 hp = lookupvar("HOME"); 15519 hp = lookupvar("HOME");
@@ -15507,7 +15527,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
15507 } 15527 }
15508 } 15528 }
15509 if (hp) 15529 if (hp)
15510 line_input_state->hist_file = hp; 15530 line_input_state->hist_file = xstrdup(hp);
15511# if ENABLE_FEATURE_SH_HISTFILESIZE 15531# if ENABLE_FEATURE_SH_HISTFILESIZE
15512 hp = lookupvar("HISTFILESIZE"); 15532 hp = lookupvar("HISTFILESIZE");
15513 line_input_state->max_history = size_from_HISTFILESIZE(hp); 15533 line_input_state->max_history = size_from_HISTFILESIZE(hp);
@@ -15515,16 +15535,6 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
15515 } 15535 }
15516#endif 15536#endif
15517 state4: /* XXX ??? - why isn't this before the "if" statement */ 15537 state4: /* XXX ??? - why isn't this before the "if" statement */
15518
15519 /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry.
15520 * Try:
15521 * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect
15522 * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed
15523 */
15524#if !ENABLE_PLATFORM_MINGW32
15525 signal(SIGHUP, SIG_DFL);
15526#endif
15527
15528 cmdloop(1); 15538 cmdloop(1);
15529 } 15539 }
15530#if PROFILE 15540#if PROFILE
diff --git a/shell/ash_test/ash-misc/piped_input.right b/shell/ash_test/ash-misc/piped_input.right
new file mode 100644
index 000000000..7b8bf6758
--- /dev/null
+++ b/shell/ash_test/ash-misc/piped_input.right
@@ -0,0 +1,2 @@
1TEST
2One:1
diff --git a/shell/ash_test/ash-misc/piped_input.tests b/shell/ash_test/ash-misc/piped_input.tests
new file mode 100755
index 000000000..929fec0db
--- /dev/null
+++ b/shell/ash_test/ash-misc/piped_input.tests
@@ -0,0 +1,3 @@
1exec 2>&1
2echo 'echo TEST; false' | $THIS_SH
3echo One:$?
diff --git a/shell/ash_test/ash-vars/var_bash_repl_empty_var.right b/shell/ash_test/ash-vars/var_bash_repl_empty_var.right
index 892916783..a8d1a3bef 100644
--- a/shell/ash_test/ash-vars/var_bash_repl_empty_var.right
+++ b/shell/ash_test/ash-vars/var_bash_repl_empty_var.right
@@ -1,2 +1,3 @@
1 1
2w
2Ok:0 3Ok:0
diff --git a/shell/ash_test/ash-vars/var_bash_repl_empty_var.tests b/shell/ash_test/ash-vars/var_bash_repl_empty_var.tests
index 73a43d38e..22aaba560 100755
--- a/shell/ash_test/ash-vars/var_bash_repl_empty_var.tests
+++ b/shell/ash_test/ash-vars/var_bash_repl_empty_var.tests
@@ -1,3 +1,5 @@
1unset v
2echo ${v/*/w}
1v='' 3v=''
2echo ${v/*/w} 4echo ${v/*/w}
3echo Ok:$? 5echo Ok:$?
diff --git a/shell/hush.c b/shell/hush.c
index ab7263381..77f90f82f 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -596,10 +596,10 @@ typedef struct in_str {
596/* The descrip member of this structure is only used to make 596/* The descrip member of this structure is only used to make
597 * debugging output pretty */ 597 * debugging output pretty */
598static const struct { 598static const struct {
599 int mode; 599 int32_t mode;
600 signed char default_fd; 600 signed char default_fd;
601 char descrip[3]; 601 char descrip[3];
602} redir_table[] = { 602} redir_table[] ALIGN4 = {
603 { O_RDONLY, 0, "<" }, 603 { O_RDONLY, 0, "<" },
604 { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, 604 { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" },
605 { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" }, 605 { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" },
@@ -653,7 +653,7 @@ struct command {
653/* used for "[[ EXPR ]]" */ 653/* used for "[[ EXPR ]]" */
654# define CMD_TEST2_SINGLEWORD_NOGLOB 2 654# define CMD_TEST2_SINGLEWORD_NOGLOB 2
655#endif 655#endif
656#if ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY 656#if BASH_TEST2 || ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY
657/* used to prevent word splitting and globbing in "export v=t*" */ 657/* used to prevent word splitting and globbing in "export v=t*" */
658# define CMD_SINGLEWORD_NOGLOB 3 658# define CMD_SINGLEWORD_NOGLOB 3
659#endif 659#endif
@@ -1027,7 +1027,7 @@ struct globals {
1027 struct sigaction sa; 1027 struct sigaction sa;
1028 char optstring_buf[sizeof("eixcs")]; 1028 char optstring_buf[sizeof("eixcs")];
1029#if BASH_EPOCH_VARS 1029#if BASH_EPOCH_VARS
1030 char epoch_buf[sizeof("%lu.nnnnnn") + sizeof(long)*3]; 1030 char epoch_buf[sizeof("%llu.nnnnnn") + sizeof(long long)*3];
1031#endif 1031#endif
1032#if ENABLE_FEATURE_EDITING 1032#if ENABLE_FEATURE_EDITING
1033 char user_input_buf[CONFIG_FEATURE_EDITING_MAX_LEN]; 1033 char user_input_buf[CONFIG_FEATURE_EDITING_MAX_LEN];
@@ -1140,7 +1140,7 @@ struct built_in_command {
1140#endif 1140#endif
1141}; 1141};
1142 1142
1143static const struct built_in_command bltins1[] = { 1143static const struct built_in_command bltins1[] ALIGN_PTR = {
1144 BLTIN("." , builtin_source , "Run commands in file"), 1144 BLTIN("." , builtin_source , "Run commands in file"),
1145 BLTIN(":" , builtin_true , NULL), 1145 BLTIN(":" , builtin_true , NULL),
1146#if ENABLE_HUSH_JOB 1146#if ENABLE_HUSH_JOB
@@ -1225,7 +1225,7 @@ static const struct built_in_command bltins1[] = {
1225/* These builtins won't be used if we are on NOMMU and need to re-exec 1225/* These builtins won't be used if we are on NOMMU and need to re-exec
1226 * (it's cheaper to run an external program in this case): 1226 * (it's cheaper to run an external program in this case):
1227 */ 1227 */
1228static const struct built_in_command bltins2[] = { 1228static const struct built_in_command bltins2[] ALIGN_PTR = {
1229#if ENABLE_HUSH_TEST 1229#if ENABLE_HUSH_TEST
1230 BLTIN("[" , builtin_test , NULL), 1230 BLTIN("[" , builtin_test , NULL),
1231#endif 1231#endif
@@ -1649,13 +1649,33 @@ static int refill_HFILE_and_getc(HFILE *fp)
1649 /* Already saw EOF */ 1649 /* Already saw EOF */
1650 return EOF; 1650 return EOF;
1651 } 1651 }
1652#if ENABLE_HUSH_INTERACTIVE && !ENABLE_FEATURE_EDITING
1653 /* If user presses ^C, read() restarts after SIGINT (we use SA_RESTART).
1654 * IOW: ^C will not immediately stop line input.
1655 * But poll() is different: it does NOT restart after signals.
1656 */
1657 if (fp == G.HFILE_stdin) {
1658 struct pollfd pfd[1];
1659 pfd[0].fd = fp->fd;
1660 pfd[0].events = POLLIN;
1661 n = poll(pfd, 1, -1);
1662 if (n < 0
1663 /*&& errno == EINTR - assumed true */
1664 && sigismember(&G.pending_set, SIGINT)
1665 ) {
1666 return '\0';
1667 }
1668 }
1669#else
1670/* if FEATURE_EDITING=y, we do not use this routine for interactive input */
1671#endif
1652 /* Try to buffer more input */ 1672 /* Try to buffer more input */
1653 fp->cur = fp->buf;
1654 n = safe_read(fp->fd, fp->buf, sizeof(fp->buf)); 1673 n = safe_read(fp->fd, fp->buf, sizeof(fp->buf));
1655 if (n < 0) { 1674 if (n < 0) {
1656 bb_simple_perror_msg("read error"); 1675 bb_simple_perror_msg("read error");
1657 n = 0; 1676 n = 0;
1658 } 1677 }
1678 fp->cur = fp->buf;
1659 fp->end = fp->buf + n; 1679 fp->end = fp->buf + n;
1660 if (n == 0) { 1680 if (n == 0) {
1661 /* EOF/error */ 1681 /* EOF/error */
@@ -2050,8 +2070,7 @@ static sighandler_t pick_sighandler(unsigned sig)
2050static void hush_exit(int exitcode) 2070static void hush_exit(int exitcode)
2051{ 2071{
2052#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 2072#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
2053 if (G.line_input_state) 2073 save_history(G.line_input_state); /* may be NULL */
2054 save_history(G.line_input_state);
2055#endif 2074#endif
2056 2075
2057 fflush_all(); 2076 fflush_all();
@@ -2091,7 +2110,6 @@ static void hush_exit(int exitcode)
2091#endif 2110#endif
2092} 2111}
2093 2112
2094
2095//TODO: return a mask of ALL handled sigs? 2113//TODO: return a mask of ALL handled sigs?
2096static int check_and_run_traps(void) 2114static int check_and_run_traps(void)
2097{ 2115{
@@ -2259,13 +2277,13 @@ static const char* FAST_FUNC get_local_var_value(const char *name)
2259 { 2277 {
2260 const char *fmt = NULL; 2278 const char *fmt = NULL;
2261 if (strcmp(name, "EPOCHSECONDS") == 0) 2279 if (strcmp(name, "EPOCHSECONDS") == 0)
2262 fmt = "%lu"; 2280 fmt = "%llu";
2263 else if (strcmp(name, "EPOCHREALTIME") == 0) 2281 else if (strcmp(name, "EPOCHREALTIME") == 0)
2264 fmt = "%lu.%06u"; 2282 fmt = "%llu.%06u";
2265 if (fmt) { 2283 if (fmt) {
2266 struct timeval tv; 2284 struct timeval tv;
2267 gettimeofday(&tv, NULL); 2285 xgettimeofday(&tv);
2268 sprintf(G.epoch_buf, fmt, (unsigned long)tv.tv_sec, 2286 sprintf(G.epoch_buf, fmt, (unsigned long long)tv.tv_sec,
2269 (unsigned)tv.tv_usec); 2287 (unsigned)tv.tv_usec);
2270 return G.epoch_buf; 2288 return G.epoch_buf;
2271 } 2289 }
@@ -2614,7 +2632,7 @@ static const char *setup_prompt_string(void)
2614 /* bash uses $PWD value, even if it is set by user. 2632 /* bash uses $PWD value, even if it is set by user.
2615 * It uses current dir only if PWD is unset. 2633 * It uses current dir only if PWD is unset.
2616 * We always use current dir. */ 2634 * We always use current dir. */
2617 G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#'); 2635 prompt_str = G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#');
2618 } 2636 }
2619# endif 2637# endif
2620 debug_printf("prompt_str '%s'\n", prompt_str); 2638 debug_printf("prompt_str '%s'\n", prompt_str);
@@ -2622,33 +2640,33 @@ static const char *setup_prompt_string(void)
2622} 2640}
2623static int get_user_input(struct in_str *i) 2641static int get_user_input(struct in_str *i)
2624{ 2642{
2643# if ENABLE_FEATURE_EDITING
2644 /* In EDITING case, this function reads next input line,
2645 * saves it in i->p, then returns 1st char of it.
2646 */
2625 int r; 2647 int r;
2626 const char *prompt_str; 2648 const char *prompt_str;
2627 2649
2628 prompt_str = setup_prompt_string(); 2650 prompt_str = setup_prompt_string();
2629# if ENABLE_FEATURE_EDITING
2630 for (;;) { 2651 for (;;) {
2631 reinit_unicode_for_hush(); 2652 reinit_unicode_for_hush();
2632 if (G.flag_SIGINT) { 2653 G.flag_SIGINT = 0;
2633 /* There was ^C'ed, make it look prettier: */
2634 bb_putchar('\n');
2635 G.flag_SIGINT = 0;
2636 }
2637 /* buglet: SIGINT will not make new prompt to appear _at once_, 2654 /* buglet: SIGINT will not make new prompt to appear _at once_,
2638 * only after <Enter>. (^C works immediately) */ 2655 * only after <Enter>. (^C works immediately) */
2639 r = read_line_input(G.line_input_state, prompt_str, 2656 r = read_line_input(G.line_input_state, prompt_str,
2640 G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1 2657 G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1
2641 ); 2658 );
2642 /* read_line_input intercepts ^C, "convert" it to SIGINT */ 2659 /* read_line_input intercepts ^C, "convert" it to SIGINT */
2643 if (r == 0) 2660 if (r == 0) {
2644 raise(SIGINT); 2661 raise(SIGINT);
2662 }
2645 check_and_run_traps(); 2663 check_and_run_traps();
2646 if (r != 0 && !G.flag_SIGINT) 2664 if (r != 0 && !G.flag_SIGINT)
2647 break; 2665 break;
2648 /* ^C or SIGINT: repeat */ 2666 /* ^C or SIGINT: repeat */
2649 /* bash prints ^C even on real SIGINT (non-kbd generated) */ 2667 /* bash prints ^C even on real SIGINT (non-kbd generated) */
2650 write(STDOUT_FILENO, "^C", 2); 2668 write(STDOUT_FILENO, "^C\n", 3);
2651 G.last_exitcode = 128 + SIGINT; 2669 G.last_exitcode = 128 | SIGINT;
2652 } 2670 }
2653 if (r < 0) { 2671 if (r < 0) {
2654 /* EOF/error detected */ 2672 /* EOF/error detected */
@@ -2659,9 +2677,15 @@ static int get_user_input(struct in_str *i)
2659 i->p = G.user_input_buf; 2677 i->p = G.user_input_buf;
2660 return (unsigned char)*i->p++; 2678 return (unsigned char)*i->p++;
2661# else 2679# else
2680 /* In !EDITING case, this function gets called for every char.
2681 * Buffering happens deeper in the call chain, in hfgetc(i->file).
2682 */
2683 int r;
2684
2662 for (;;) { 2685 for (;;) {
2663 G.flag_SIGINT = 0; 2686 G.flag_SIGINT = 0;
2664 if (i->last_char == '\0' || i->last_char == '\n') { 2687 if (i->last_char == '\0' || i->last_char == '\n') {
2688 const char *prompt_str = setup_prompt_string();
2665 /* Why check_and_run_traps here? Try this interactively: 2689 /* Why check_and_run_traps here? Try this interactively:
2666 * $ trap 'echo INT' INT; (sleep 2; kill -INT $$) & 2690 * $ trap 'echo INT' INT; (sleep 2; kill -INT $$) &
2667 * $ <[enter], repeatedly...> 2691 * $ <[enter], repeatedly...>
@@ -2669,19 +2693,23 @@ static int get_user_input(struct in_str *i)
2669 */ 2693 */
2670 check_and_run_traps(); 2694 check_and_run_traps();
2671 fputs(prompt_str, stdout); 2695 fputs(prompt_str, stdout);
2696 fflush_all();
2672 } 2697 }
2673 fflush_all();
2674//FIXME: here ^C or SIGINT will have effect only after <Enter>
2675 r = hfgetc(i->file); 2698 r = hfgetc(i->file);
2676 /* In !ENABLE_FEATURE_EDITING we don't use read_line_input, 2699 /* In !ENABLE_FEATURE_EDITING we don't use read_line_input,
2677 * no ^C masking happens during fgetc, no special code for ^C: 2700 * no ^C masking happens during fgetc, no special code for ^C:
2678 * it generates SIGINT as usual. 2701 * it generates SIGINT as usual.
2679 */ 2702 */
2680 check_and_run_traps(); 2703 check_and_run_traps();
2681 if (G.flag_SIGINT) 2704 if (r != '\0' && !G.flag_SIGINT)
2682 G.last_exitcode = 128 + SIGINT;
2683 if (r != '\0')
2684 break; 2705 break;
2706 if (G.flag_SIGINT) {
2707 /* ^C or SIGINT: repeat */
2708 /* bash prints ^C even on real SIGINT (non-kbd generated) */
2709 /* kernel prints "^C" itself, just print newline: */
2710 write(STDOUT_FILENO, "\n", 1);
2711 G.last_exitcode = 128 | SIGINT;
2712 }
2685 } 2713 }
2686 return r; 2714 return r;
2687# endif 2715# endif
@@ -2703,14 +2731,14 @@ static int fgetc_interactive(struct in_str *i)
2703 } 2731 }
2704 return ch; 2732 return ch;
2705} 2733}
2706#else 2734#else /* !INTERACTIVE */
2707static ALWAYS_INLINE int fgetc_interactive(struct in_str *i) 2735static ALWAYS_INLINE int fgetc_interactive(struct in_str *i)
2708{ 2736{
2709 int ch; 2737 int ch;
2710 do ch = hfgetc(i->file); while (ch == '\0'); 2738 do ch = hfgetc(i->file); while (ch == '\0');
2711 return ch; 2739 return ch;
2712} 2740}
2713#endif /* INTERACTIVE */ 2741#endif /* !INTERACTIVE */
2714 2742
2715static int i_getch(struct in_str *i) 2743static int i_getch(struct in_str *i)
2716{ 2744{
@@ -3888,7 +3916,7 @@ struct reserved_combo {
3888 char literal[6]; 3916 char literal[6];
3889 unsigned char res; 3917 unsigned char res;
3890 unsigned char assignment_flag; 3918 unsigned char assignment_flag;
3891 int flag; 3919 uint32_t flag;
3892}; 3920};
3893enum { 3921enum {
3894 FLAG_END = (1 << RES_NONE ), 3922 FLAG_END = (1 << RES_NONE ),
@@ -3921,7 +3949,7 @@ static const struct reserved_combo* match_reserved_word(o_string *word)
3921 * to turn the compound list into a command. 3949 * to turn the compound list into a command.
3922 * FLAG_START means the word must start a new compound list. 3950 * FLAG_START means the word must start a new compound list.
3923 */ 3951 */
3924 static const struct reserved_combo reserved_list[] = { 3952 static const struct reserved_combo reserved_list[] ALIGN4 = {
3925# if ENABLE_HUSH_IF 3953# if ENABLE_HUSH_IF
3926 { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, 3954 { "!", RES_NONE, NOT_ASSIGNMENT , 0 },
3927 { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START }, 3955 { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START },
@@ -5263,19 +5291,19 @@ static struct pipe *parse_stream(char **pstring,
5263 5291
5264 if (heredoc_cnt) { 5292 if (heredoc_cnt) {
5265 syntax_error_unterm_str("here document"); 5293 syntax_error_unterm_str("here document");
5266 goto parse_error; 5294 goto parse_error_exitcode1;
5267 } 5295 }
5268 if (end_trigger == ')') { 5296 if (end_trigger == ')') {
5269 syntax_error_unterm_ch('('); 5297 syntax_error_unterm_ch('(');
5270 goto parse_error; 5298 goto parse_error_exitcode1;
5271 } 5299 }
5272 if (end_trigger == '}') { 5300 if (end_trigger == '}') {
5273 syntax_error_unterm_ch('{'); 5301 syntax_error_unterm_ch('{');
5274 goto parse_error; 5302 goto parse_error_exitcode1;
5275 } 5303 }
5276 5304
5277 if (done_word(&ctx)) { 5305 if (done_word(&ctx)) {
5278 goto parse_error; 5306 goto parse_error_exitcode1;
5279 } 5307 }
5280 o_free_and_set_NULL(&ctx.word); 5308 o_free_and_set_NULL(&ctx.word);
5281 done_pipe(&ctx, PIPE_SEQ); 5309 done_pipe(&ctx, PIPE_SEQ);
@@ -5345,7 +5373,7 @@ static struct pipe *parse_stream(char **pstring,
5345 while (1) { 5373 while (1) {
5346 if (ch == EOF) { 5374 if (ch == EOF) {
5347 syntax_error_unterm_ch('\''); 5375 syntax_error_unterm_ch('\'');
5348 goto parse_error; 5376 goto parse_error_exitcode1;
5349 } 5377 }
5350 nommu_addchr(&ctx.as_string, ch); 5378 nommu_addchr(&ctx.as_string, ch);
5351 if (ch == '\'') 5379 if (ch == '\'')
@@ -5424,7 +5452,7 @@ static struct pipe *parse_stream(char **pstring,
5424 /* ch == last eaten whitespace char */ 5452 /* ch == last eaten whitespace char */
5425#endif 5453#endif
5426 if (done_word(&ctx)) { 5454 if (done_word(&ctx)) {
5427 goto parse_error; 5455 goto parse_error_exitcode1;
5428 } 5456 }
5429 if (ch == '\n') { 5457 if (ch == '\n') {
5430 /* Is this a case when newline is simply ignored? 5458 /* Is this a case when newline is simply ignored?
@@ -5467,7 +5495,7 @@ static struct pipe *parse_stream(char **pstring,
5467 if (heredoc_cnt) { 5495 if (heredoc_cnt) {
5468 heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input); 5496 heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input);
5469 if (heredoc_cnt != 0) 5497 if (heredoc_cnt != 0)
5470 goto parse_error; 5498 goto parse_error_exitcode1;
5471 } 5499 }
5472 ctx.is_assignment = MAYBE_ASSIGNMENT; 5500 ctx.is_assignment = MAYBE_ASSIGNMENT;
5473 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); 5501 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
@@ -5517,7 +5545,7 @@ static struct pipe *parse_stream(char **pstring,
5517#endif 5545#endif
5518 ) { 5546 ) {
5519 if (done_word(&ctx)) { 5547 if (done_word(&ctx)) {
5520 goto parse_error; 5548 goto parse_error_exitcode1;
5521 } 5549 }
5522 done_pipe(&ctx, PIPE_SEQ); 5550 done_pipe(&ctx, PIPE_SEQ);
5523 ctx.is_assignment = MAYBE_ASSIGNMENT; 5551 ctx.is_assignment = MAYBE_ASSIGNMENT;
@@ -5538,7 +5566,7 @@ static struct pipe *parse_stream(char **pstring,
5538 /* Example: bare "{ }", "()" */ 5566 /* Example: bare "{ }", "()" */
5539 G.last_exitcode = 2; /* bash compat */ 5567 G.last_exitcode = 2; /* bash compat */
5540 syntax_error_unexpected_ch(ch); 5568 syntax_error_unexpected_ch(ch);
5541 goto parse_error2; 5569 goto parse_error;
5542 } 5570 }
5543 if (heredoc_cnt_ptr) 5571 if (heredoc_cnt_ptr)
5544 *heredoc_cnt_ptr = heredoc_cnt; 5572 *heredoc_cnt_ptr = heredoc_cnt;
@@ -5560,7 +5588,7 @@ static struct pipe *parse_stream(char **pstring,
5560 case '>': 5588 case '>':
5561 redir_fd = redirect_opt_num(&ctx.word); 5589 redir_fd = redirect_opt_num(&ctx.word);
5562 if (done_word(&ctx)) { 5590 if (done_word(&ctx)) {
5563 goto parse_error; 5591 goto parse_error_exitcode1;
5564 } 5592 }
5565 redir_style = REDIRECT_OVERWRITE; 5593 redir_style = REDIRECT_OVERWRITE;
5566 if (next == '>') { 5594 if (next == '>') {
@@ -5571,16 +5599,16 @@ static struct pipe *parse_stream(char **pstring,
5571#if 0 5599#if 0
5572 else if (next == '(') { 5600 else if (next == '(') {
5573 syntax_error(">(process) not supported"); 5601 syntax_error(">(process) not supported");
5574 goto parse_error; 5602 goto parse_error_exitcode1;
5575 } 5603 }
5576#endif 5604#endif
5577 if (parse_redirect(&ctx, redir_fd, redir_style, input)) 5605 if (parse_redirect(&ctx, redir_fd, redir_style, input))
5578 goto parse_error; 5606 goto parse_error_exitcode1;
5579 continue; /* get next char */ 5607 continue; /* get next char */
5580 case '<': 5608 case '<':
5581 redir_fd = redirect_opt_num(&ctx.word); 5609 redir_fd = redirect_opt_num(&ctx.word);
5582 if (done_word(&ctx)) { 5610 if (done_word(&ctx)) {
5583 goto parse_error; 5611 goto parse_error_exitcode1;
5584 } 5612 }
5585 redir_style = REDIRECT_INPUT; 5613 redir_style = REDIRECT_INPUT;
5586 if (next == '<') { 5614 if (next == '<') {
@@ -5597,11 +5625,11 @@ static struct pipe *parse_stream(char **pstring,
5597#if 0 5625#if 0
5598 else if (next == '(') { 5626 else if (next == '(') {
5599 syntax_error("<(process) not supported"); 5627 syntax_error("<(process) not supported");
5600 goto parse_error; 5628 goto parse_error_exitcode1;
5601 } 5629 }
5602#endif 5630#endif
5603 if (parse_redirect(&ctx, redir_fd, redir_style, input)) 5631 if (parse_redirect(&ctx, redir_fd, redir_style, input))
5604 goto parse_error; 5632 goto parse_error_exitcode1;
5605 continue; /* get next char */ 5633 continue; /* get next char */
5606 case '#': 5634 case '#':
5607 if (ctx.word.length == 0 && !ctx.word.has_quoted_part) { 5635 if (ctx.word.length == 0 && !ctx.word.has_quoted_part) {
@@ -5655,7 +5683,7 @@ static struct pipe *parse_stream(char **pstring,
5655 if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) { 5683 if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) {
5656 debug_printf_parse("parse_stream parse error: " 5684 debug_printf_parse("parse_stream parse error: "
5657 "parse_dollar returned 0 (error)\n"); 5685 "parse_dollar returned 0 (error)\n");
5658 goto parse_error; 5686 goto parse_error_exitcode1;
5659 } 5687 }
5660 continue; /* get next char */ 5688 continue; /* get next char */
5661 case '"': 5689 case '"':
@@ -5671,7 +5699,7 @@ static struct pipe *parse_stream(char **pstring,
5671 if (ctx.is_assignment == NOT_ASSIGNMENT) 5699 if (ctx.is_assignment == NOT_ASSIGNMENT)
5672 ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; 5700 ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
5673 if (!encode_string(&ctx.as_string, &ctx.word, input, '"')) 5701 if (!encode_string(&ctx.as_string, &ctx.word, input, '"'))
5674 goto parse_error; 5702 goto parse_error_exitcode1;
5675 ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; 5703 ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
5676 continue; /* get next char */ 5704 continue; /* get next char */
5677#if ENABLE_HUSH_TICK 5705#if ENABLE_HUSH_TICK
@@ -5682,7 +5710,7 @@ static struct pipe *parse_stream(char **pstring,
5682 o_addchr(&ctx.word, '`'); 5710 o_addchr(&ctx.word, '`');
5683 USE_FOR_NOMMU(pos = ctx.word.length;) 5711 USE_FOR_NOMMU(pos = ctx.word.length;)
5684 if (!add_till_backquote(&ctx.word, input, /*in_dquote:*/ 0)) 5712 if (!add_till_backquote(&ctx.word, input, /*in_dquote:*/ 0))
5685 goto parse_error; 5713 goto parse_error_exitcode1;
5686# if !BB_MMU 5714# if !BB_MMU
5687 o_addstr(&ctx.as_string, ctx.word.data + pos); 5715 o_addstr(&ctx.as_string, ctx.word.data + pos);
5688 o_addchr(&ctx.as_string, '`'); 5716 o_addchr(&ctx.as_string, '`');
@@ -5697,7 +5725,7 @@ static struct pipe *parse_stream(char **pstring,
5697 case_semi: 5725 case_semi:
5698#endif 5726#endif
5699 if (done_word(&ctx)) { 5727 if (done_word(&ctx)) {
5700 goto parse_error; 5728 goto parse_error_exitcode1;
5701 } 5729 }
5702 done_pipe(&ctx, PIPE_SEQ); 5730 done_pipe(&ctx, PIPE_SEQ);
5703#if ENABLE_HUSH_CASE 5731#if ENABLE_HUSH_CASE
@@ -5724,7 +5752,7 @@ static struct pipe *parse_stream(char **pstring,
5724 continue; /* get next char */ 5752 continue; /* get next char */
5725 case '&': 5753 case '&':
5726 if (done_word(&ctx)) { 5754 if (done_word(&ctx)) {
5727 goto parse_error; 5755 goto parse_error_exitcode1;
5728 } 5756 }
5729 if (next == '&') { 5757 if (next == '&') {
5730 ch = i_getch(input); 5758 ch = i_getch(input);
@@ -5736,7 +5764,7 @@ static struct pipe *parse_stream(char **pstring,
5736 goto new_cmd; 5764 goto new_cmd;
5737 case '|': 5765 case '|':
5738 if (done_word(&ctx)) { 5766 if (done_word(&ctx)) {
5739 goto parse_error; 5767 goto parse_error_exitcode1;
5740 } 5768 }
5741#if ENABLE_HUSH_CASE 5769#if ENABLE_HUSH_CASE
5742 if (ctx.ctx_res_w == RES_MATCH) 5770 if (ctx.ctx_res_w == RES_MATCH)
@@ -5768,7 +5796,7 @@ static struct pipe *parse_stream(char **pstring,
5768 case '{': { 5796 case '{': {
5769 int n = parse_group(&ctx, input, ch); 5797 int n = parse_group(&ctx, input, ch);
5770 if (n < 0) { 5798 if (n < 0) {
5771 goto parse_error; 5799 goto parse_error_exitcode1;
5772 } 5800 }
5773 debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n); 5801 debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n);
5774 heredoc_cnt += n; 5802 heredoc_cnt += n;
@@ -5786,16 +5814,16 @@ static struct pipe *parse_stream(char **pstring,
5786 * and it will match } earlier (not here). */ 5814 * and it will match } earlier (not here). */
5787 G.last_exitcode = 2; 5815 G.last_exitcode = 2;
5788 syntax_error_unexpected_ch(ch); 5816 syntax_error_unexpected_ch(ch);
5789 goto parse_error2; 5817 goto parse_error;
5790 default: 5818 default:
5791 if (HUSH_DEBUG) 5819 if (HUSH_DEBUG)
5792 bb_error_msg_and_die("BUG: unexpected %c", ch); 5820 bb_error_msg_and_die("BUG: unexpected %c", ch);
5793 } 5821 }
5794 } /* while (1) */ 5822 } /* while (1) */
5795 5823
5796 parse_error: 5824 parse_error_exitcode1:
5797 G.last_exitcode = 1; 5825 G.last_exitcode = 1;
5798 parse_error2: 5826 parse_error:
5799 { 5827 {
5800 struct parse_context *pctx; 5828 struct parse_context *pctx;
5801 IF_HAS_KEYWORDS(struct parse_context *p2;) 5829 IF_HAS_KEYWORDS(struct parse_context *p2;)
@@ -6490,9 +6518,11 @@ static NOINLINE int expand_one_var(o_string *output, int n,
6490 * Word is expanded to produce a glob pattern. 6518 * Word is expanded to produce a glob pattern.
6491 * Then var's value is matched to it and matching part removed. 6519 * Then var's value is matched to it and matching part removed.
6492 */ 6520 */
6493//FIXME: ${x#...${...}...} 6521 /* bash compat: if x is "" and no shrinking of it is possible,
6494//should evaluate inner ${...} even if x is "" and no shrinking of it is possible - 6522 * inner ${...} is not evaluated. Example:
6495//inner ${...} may have side effects! 6523 * unset b; : ${a%${b=B}}; echo $b
6524 * assignment b=B only happens if $a is not "".
6525 */
6496 if (val && val[0]) { 6526 if (val && val[0]) {
6497 char *t; 6527 char *t;
6498 char *exp_exp_word; 6528 char *exp_exp_word;
@@ -6531,7 +6561,12 @@ static NOINLINE int expand_one_var(o_string *output, int n,
6531 * and if // is used, it is encoded as \: 6561 * and if // is used, it is encoded as \:
6532 * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL> 6562 * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
6533 */ 6563 */
6534 if (val && val[0]) { 6564 /* bash compat: if var is "", both pattern and repl
6565 * are still evaluated, if it is unset, then not:
6566 * unset b; a=; : ${a/z/${b=3}}; echo $b # b=3
6567 * unset b; unset a; : ${a/z/${b=3}}; echo $b # b not set
6568 */
6569 if (val /*&& val[0]*/) {
6535 /* pattern uses non-standard expansion. 6570 /* pattern uses non-standard expansion.
6536 * repl should be unbackslashed and globbed 6571 * repl should be unbackslashed and globbed
6537 * by the usual expansion rules: 6572 * by the usual expansion rules:
@@ -6567,8 +6602,9 @@ static NOINLINE int expand_one_var(o_string *output, int n,
6567 free(pattern); 6602 free(pattern);
6568 free(repl); 6603 free(repl);
6569 } else { 6604 } else {
6570 /* Empty variable always gives nothing */ 6605 /* Unset variable always gives nothing */
6571 // "v=''; echo ${v/*/w}" prints "", not "w" 6606 // a=; echo ${a/*/w} # "w"
6607 // unset a; echo ${a/*/w} # ""
6572 /* Just skip "replace" part */ 6608 /* Just skip "replace" part */
6573 *p++ = SPECIAL_VAR_SYMBOL; 6609 *p++ = SPECIAL_VAR_SYMBOL;
6574 p = strchr(p, SPECIAL_VAR_SYMBOL); 6610 p = strchr(p, SPECIAL_VAR_SYMBOL);
@@ -6583,40 +6619,48 @@ static NOINLINE int expand_one_var(o_string *output, int n,
6583 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> 6619 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
6584 */ 6620 */
6585 arith_t beg, len; 6621 arith_t beg, len;
6622 unsigned vallen;
6586 const char *errmsg; 6623 const char *errmsg;
6587 6624
6588 beg = expand_and_evaluate_arith(exp_word, &errmsg); 6625 beg = expand_and_evaluate_arith(exp_word, &errmsg);
6589 if (errmsg) 6626 if (errmsg)
6590 goto arith_err; 6627 goto empty_result;
6591 debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); 6628 debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
6592 *p++ = SPECIAL_VAR_SYMBOL; 6629 *p++ = SPECIAL_VAR_SYMBOL;
6593 exp_word = p; 6630 exp_word = p;
6594 p = strchr(p, SPECIAL_VAR_SYMBOL); 6631 p = strchr(p, SPECIAL_VAR_SYMBOL);
6595 *p = '\0'; 6632 *p = '\0';
6596 len = expand_and_evaluate_arith(exp_word, &errmsg); 6633 vallen = val ? strlen(val) : 0;
6597 if (errmsg)
6598 goto arith_err;
6599 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
6600 if (beg < 0) { 6634 if (beg < 0) {
6601 /* negative beg counts from the end */ 6635 /* negative beg counts from the end */
6602 beg = (arith_t)strlen(val) + beg; 6636 beg = (arith_t)vallen + beg;
6603 if (beg < 0) /* ${v: -999999} is "" */ 6637 }
6604 beg = len = 0; 6638 /* If expansion will be empty, do not even evaluate len */
6639 if (!val || beg < 0 || beg > vallen) {
6640 /* Why > vallen, not >=? bash:
6641 * unset b; a=ab; : ${a:2:${b=3}}; echo $b # "", b=3 (!!!)
6642 * unset b; a=a; : ${a:2:${b=3}}; echo $b # "", b not set
6643 */
6644 goto empty_result;
6605 } 6645 }
6646 len = expand_and_evaluate_arith(exp_word, &errmsg);
6647 if (errmsg)
6648 goto empty_result;
6649 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
6606 debug_printf_varexp("from val:'%s'\n", val); 6650 debug_printf_varexp("from val:'%s'\n", val);
6607 if (len < 0) { 6651 if (len < 0) {
6608 /* in bash, len=-n means strlen()-n */ 6652 /* in bash, len=-n means strlen()-n */
6609 len = (arith_t)strlen(val) - beg + len; 6653 len = (arith_t)vallen - beg + len;
6610 if (len < 0) /* bash compat */ 6654 if (len < 0) /* bash compat */
6611 msg_and_die_if_script("%s: substring expression < 0", var); 6655 msg_and_die_if_script("%s: substring expression < 0", var);
6612 } 6656 }
6613 if (len <= 0 || !val || beg >= strlen(val)) { 6657 if (len <= 0 || !val /*|| beg >= vallen*/) {
6614 arith_err: 6658 empty_result:
6615 val = NULL; 6659 val = NULL;
6616 } else { 6660 } else {
6617 /* Paranoia. What if user entered 9999999999999 6661 /* Paranoia. What if user entered 9999999999999
6618 * which fits in arith_t but not int? */ 6662 * which fits in arith_t but not int? */
6619 if (len >= INT_MAX) 6663 if (len > INT_MAX)
6620 len = INT_MAX; 6664 len = INT_MAX;
6621 val = to_be_freed = xstrndup(val + beg, len); 6665 val = to_be_freed = xstrndup(val + beg, len);
6622 } 6666 }
@@ -6650,12 +6694,13 @@ static NOINLINE int expand_one_var(o_string *output, int n,
6650 * 6694 *
6651 * Word-splitting and single quote behavior: 6695 * Word-splitting and single quote behavior:
6652 * 6696 *
6653 * $ f() { for i; do echo "|$i|"; done; }; 6697 * $ f() { for i; do echo "|$i|"; done; }
6654 * 6698 *
6655 * $ x=; f ${x:?'x y' z} 6699 * $ x=; f ${x:?'x y' z}; echo $?
6656 * bash: x: x y z #BUG: does not abort, ${} results in empty expansion 6700 * bash: x: x y z # neither f nor "echo $?" executes
6701 * (if interactive, bash does not exit, but merely aborts to prompt. $? is set to 1)
6657 * $ x=; f "${x:?'x y' z}" 6702 * $ x=; f "${x:?'x y' z}"
6658 * bash: x: x y z # dash prints: dash: x: 'x y' z #BUG: does not abort, ${} results in "" 6703 * bash: x: x y z # dash prints: dash: x: 'x y' z
6659 * 6704 *
6660 * $ x=; f ${x:='x y' z} 6705 * $ x=; f ${x:='x y' z}
6661 * |x| 6706 * |x|
@@ -7743,7 +7788,11 @@ static void restore_redirects(struct squirrel *sq)
7743 free(sq); 7788 free(sq);
7744 } 7789 }
7745 if (G.HFILE_stdin 7790 if (G.HFILE_stdin
7746 && G.HFILE_stdin->fd != STDIN_FILENO 7791 && G.HFILE_stdin->fd > STDIN_FILENO
7792 /* we compare > STDIN, not == STDIN, since hfgetc()
7793 * closes fd and sets ->fd to -1 if EOF is reached.
7794 * Testcase: echo 'pwd' | hush
7795 */
7747 ) { 7796 ) {
7748 /* Testcase: interactive "read r <FILE; echo $r; read r; echo $r". 7797 /* Testcase: interactive "read r <FILE; echo $r; read r; echo $r".
7749 * Redirect moves ->fd to e.g. 10, 7798 * Redirect moves ->fd to e.g. 10,
@@ -8136,7 +8185,6 @@ static void exec_function(char ***to_free,
8136 8185
8137 /* On MMU, funcp->body is always non-NULL */ 8186 /* On MMU, funcp->body is always non-NULL */
8138 n = run_list(funcp->body); 8187 n = run_list(funcp->body);
8139 fflush_all();
8140 _exit(n); 8188 _exit(n);
8141# else 8189# else
8142//? close_saved_fds_and_FILE_fds(); 8190//? close_saved_fds_and_FILE_fds();
@@ -8211,7 +8259,6 @@ static void exec_builtin(char ***to_free,
8211{ 8259{
8212#if BB_MMU 8260#if BB_MMU
8213 int rcode; 8261 int rcode;
8214 fflush_all();
8215//? close_saved_fds_and_FILE_fds(); 8262//? close_saved_fds_and_FILE_fds();
8216 rcode = x->b_function(argv); 8263 rcode = x->b_function(argv);
8217 fflush_all(); 8264 fflush_all();
@@ -8715,6 +8762,7 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
8715 */ 8762 */
8716 if (WIFSIGNALED(status)) { 8763 if (WIFSIGNALED(status)) {
8717 int sig = WTERMSIG(status); 8764 int sig = WTERMSIG(status);
8765#if ENABLE_HUSH_JOB
8718 if (G.run_list_level == 1 8766 if (G.run_list_level == 1
8719 /* ^^^^^ Do not print in nested contexts, example: 8767 /* ^^^^^ Do not print in nested contexts, example:
8720 * echo `sleep 1; sh -c 'kill -9 $$'` - prints "137", NOT "Killed 137" 8768 * echo `sleep 1; sh -c 'kill -9 $$'` - prints "137", NOT "Killed 137"
@@ -8724,10 +8772,12 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
8724 /* strsignal() is for bash compat. ~600 bloat versus bbox's get_signame() */ 8772 /* strsignal() is for bash compat. ~600 bloat versus bbox's get_signame() */
8725 puts(sig == SIGINT || sig == SIGPIPE ? "" : strsignal(sig)); 8773 puts(sig == SIGINT || sig == SIGPIPE ? "" : strsignal(sig));
8726 } 8774 }
8775#endif
8727 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */ 8776 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
8728 /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? 8777 /* MIPS has 128 sigs (1..128), if sig==128,
8729 * Maybe we need to use sig | 128? */ 8778 * 128 + sig would result in exitcode 256 -> 0!
8730 ex = sig + 128; 8779 */
8780 ex = 128 | sig;
8731 } 8781 }
8732 fg_pipe->cmds[i].cmd_exitcode = ex; 8782 fg_pipe->cmds[i].cmd_exitcode = ex;
8733 } else { 8783 } else {
@@ -8774,7 +8824,8 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
8774 /* child exited */ 8824 /* child exited */
8775 int rcode = WEXITSTATUS(status); 8825 int rcode = WEXITSTATUS(status);
8776 if (WIFSIGNALED(status)) 8826 if (WIFSIGNALED(status))
8777 rcode = 128 + WTERMSIG(status); 8827 /* NB: not 128 + sig, MIPS has sig 128 */
8828 rcode = 128 | WTERMSIG(status);
8778 pi->cmds[i].cmd_exitcode = rcode; 8829 pi->cmds[i].cmd_exitcode = rcode;
8779 if (G.last_bg_pid == pi->cmds[i].pid) 8830 if (G.last_bg_pid == pi->cmds[i].pid)
8780 G.last_bg_pid_exitcode = rcode; 8831 G.last_bg_pid_exitcode = rcode;
@@ -8894,10 +8945,10 @@ static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid)
8894 debug_printf_exec("childpid==waitfor_pid:%d status:0x%08x\n", childpid, status); 8945 debug_printf_exec("childpid==waitfor_pid:%d status:0x%08x\n", childpid, status);
8895 rcode = WEXITSTATUS(status); 8946 rcode = WEXITSTATUS(status);
8896 if (WIFSIGNALED(status)) 8947 if (WIFSIGNALED(status))
8897 rcode = 128 + WTERMSIG(status); 8948 rcode = 128 | WTERMSIG(status);
8898 if (WIFSTOPPED(status)) 8949 if (WIFSTOPPED(status))
8899 /* bash: "cmd & wait $!" and cmd stops: $? = 128 + stopsig */ 8950 /* bash: "cmd & wait $!" and cmd stops: $? = 128 | stopsig */
8900 rcode = 128 + WSTOPSIG(status); 8951 rcode = 128 | WSTOPSIG(status);
8901 rcode++; 8952 rcode++;
8902 break; /* "wait PID" called us, give it exitcode+1 */ 8953 break; /* "wait PID" called us, give it exitcode+1 */
8903 } 8954 }
@@ -9287,7 +9338,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
9287 * during builtin/nofork. 9338 * during builtin/nofork.
9288 */ 9339 */
9289 if (sigismember(&G.pending_set, SIGINT)) 9340 if (sigismember(&G.pending_set, SIGINT))
9290 rcode = 128 + SIGINT; 9341 rcode = 128 | SIGINT;
9291 } 9342 }
9292 free(argv_expanded); 9343 free(argv_expanded);
9293 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) 9344 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
@@ -9972,11 +10023,14 @@ static int set_mode(int state, char mode, const char *o_opt)
9972int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 10023int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
9973int hush_main(int argc, char **argv) 10024int hush_main(int argc, char **argv)
9974{ 10025{
10026 pid_t cached_getpid;
9975 enum { 10027 enum {
9976 OPT_login = (1 << 0), 10028 OPT_login = (1 << 0),
9977 }; 10029 };
9978 unsigned flags; 10030 unsigned flags;
9979 unsigned builtin_argc; 10031#if !BB_MMU
10032 unsigned builtin_argc = 0;
10033#endif
9980 char **e; 10034 char **e;
9981 struct variable *cur_var; 10035 struct variable *cur_var;
9982 struct variable *shell_ver; 10036 struct variable *shell_ver;
@@ -9998,6 +10052,10 @@ int hush_main(int argc, char **argv)
9998 G.argv0_for_re_execing = argv[0]; 10052 G.argv0_for_re_execing = argv[0];
9999#endif 10053#endif
10000 10054
10055 cached_getpid = getpid(); /* for tcsetpgrp() during init */
10056 G.root_pid = cached_getpid; /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */
10057 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10058
10001 /* Deal with HUSH_VERSION */ 10059 /* Deal with HUSH_VERSION */
10002 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); 10060 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
10003 unsetenv("HUSH_VERSION"); /* in case it exists in initial env */ 10061 unsetenv("HUSH_VERSION"); /* in case it exists in initial env */
@@ -10080,6 +10138,17 @@ int hush_main(int argc, char **argv)
10080 * PS4='+ ' 10138 * PS4='+ '
10081 */ 10139 */
10082 10140
10141#if NUM_SCRIPTS > 0
10142 if (argc < 0) {
10143 char *script = get_script_content(-argc - 1);
10144 G.global_argv = argv;
10145 G.global_argc = string_array_len(argv);
10146 //install_special_sighandlers(); - needed?
10147 parse_and_run_string(script);
10148 goto final_return;
10149 }
10150#endif
10151
10083 /* Initialize some more globals to non-zero values */ 10152 /* Initialize some more globals to non-zero values */
10084 die_func = restore_ttypgrp_and__exit; 10153 die_func = restore_ttypgrp_and__exit;
10085 10154
@@ -10093,17 +10162,9 @@ int hush_main(int argc, char **argv)
10093 /* Parse options */ 10162 /* Parse options */
10094 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ 10163 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
10095 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; 10164 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
10096 builtin_argc = 0;
10097#if NUM_SCRIPTS > 0
10098 if (argc < 0) {
10099 optarg = get_script_content(-argc - 1);
10100 optind = 0;
10101 argc = string_array_len(argv);
10102 goto run_script;
10103 }
10104#endif
10105 while (1) { 10165 while (1) {
10106 int opt = getopt(argc, argv, "+c:exinsl" 10166 int opt = getopt(argc, argv, "+" /* stop at 1st non-option */
10167 "cexinsl"
10107#if !BB_MMU 10168#if !BB_MMU
10108 "<:$:R:V:" 10169 "<:$:R:V:"
10109# if ENABLE_HUSH_FUNCTIONS 10170# if ENABLE_HUSH_FUNCTIONS
@@ -10115,50 +10176,11 @@ int hush_main(int argc, char **argv)
10115 break; 10176 break;
10116 switch (opt) { 10177 switch (opt) {
10117 case 'c': 10178 case 'c':
10118 /* Possibilities: 10179 /* Note: -c is not an option with param!
10119 * sh ... -c 'script' 10180 * "hush -c -l SCRIPT" is valid. "hush -cSCRIPT" is not.
10120 * sh ... -c 'script' ARG0 [ARG1...]
10121 * On NOMMU, if builtin_argc != 0,
10122 * sh ... -c 'builtin' BARGV... "" ARG0 [ARG1...]
10123 * "" needs to be replaced with NULL
10124 * and BARGV vector fed to builtin function.
10125 * Note: the form without ARG0 never happens:
10126 * sh ... -c 'builtin' BARGV... ""
10127 */ 10181 */
10128#if NUM_SCRIPTS > 0
10129 run_script:
10130#endif
10131 if (!G.root_pid) {
10132 G.root_pid = getpid();
10133 G.root_ppid = getppid();
10134 }
10135 G.global_argv = argv + optind;
10136 G.global_argc = argc - optind;
10137 if (builtin_argc) {
10138 /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
10139 const struct built_in_command *x;
10140
10141 install_special_sighandlers();
10142 x = find_builtin(optarg);
10143 if (x) { /* paranoia */
10144 G.global_argc -= builtin_argc; /* skip [BARGV...] "" */
10145 G.global_argv += builtin_argc;
10146 G.global_argv[-1] = NULL; /* replace "" */
10147 fflush_all();
10148 G.last_exitcode = x->b_function(argv + optind - 1);
10149 }
10150 goto final_return;
10151 }
10152 G.opt_c = 1; 10182 G.opt_c = 1;
10153 if (!G.global_argv[0]) { 10183 break;
10154 /* -c 'script' (no params): prevent empty $0 */
10155 G.global_argv--; /* points to argv[i] of 'script' */
10156 G.global_argv[0] = argv[0];
10157 G.global_argc++;
10158 } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
10159 install_special_sighandlers();
10160 parse_and_run_string(optarg);
10161 goto final_return;
10162 case 'i': 10184 case 'i':
10163 /* Well, we cannot just declare interactiveness, 10185 /* Well, we cannot just declare interactiveness,
10164 * we have to have some stuff (ctty, etc) */ 10186 * we have to have some stuff (ctty, etc) */
@@ -10205,6 +10227,11 @@ int hush_main(int argc, char **argv)
10205 optarg++; 10227 optarg++;
10206 G.depth_of_loop = bb_strtou(optarg, &optarg, 16); 10228 G.depth_of_loop = bb_strtou(optarg, &optarg, 16);
10207# endif 10229# endif
10230 /* Suppress "killed by signal" message, -$ hack is used
10231 * for subshells: echo `sh -c 'kill -9 $$'`
10232 * should be silent.
10233 */
10234 IF_HUSH_JOB(G.run_list_level = 1;)
10208# if ENABLE_HUSH_FUNCTIONS 10235# if ENABLE_HUSH_FUNCTIONS
10209 /* nommu uses re-exec trick for "... | func | ...", 10236 /* nommu uses re-exec trick for "... | func | ...",
10210 * should allow "return". 10237 * should allow "return".
@@ -10229,19 +10256,14 @@ int hush_main(int argc, char **argv)
10229 } 10256 }
10230# endif 10257# endif
10231#endif 10258#endif
10232 case 'n': 10259 /*case '?': invalid option encountered (set_mode('?') will fail) */
10233 case 'x': 10260 /*case 'n':*/
10234 case 'e': 10261 /*case 'x':*/
10262 /*case 'e':*/
10263 default:
10235 if (set_mode(1, opt, NULL) == 0) /* no error */ 10264 if (set_mode(1, opt, NULL) == 0) /* no error */
10236 break; 10265 break;
10237 default:
10238#ifndef BB_VER
10239 fprintf(stderr, "Usage: sh [FILE]...\n"
10240 " or: sh -c command [args]...\n\n");
10241 exit(EXIT_FAILURE);
10242#else
10243 bb_show_usage(); 10266 bb_show_usage();
10244#endif
10245 } 10267 }
10246 } /* option parsing loop */ 10268 } /* option parsing loop */
10247 10269
@@ -10250,16 +10272,14 @@ int hush_main(int argc, char **argv)
10250 G.global_argv = argv + (optind - 1); 10272 G.global_argv = argv + (optind - 1);
10251 G.global_argv[0] = argv[0]; 10273 G.global_argv[0] = argv[0];
10252 10274
10253 if (!G.root_pid) {
10254 G.root_pid = getpid();
10255 G.root_ppid = getppid();
10256 }
10257
10258 /* If we are login shell... */ 10275 /* If we are login shell... */
10259 if (flags & OPT_login) { 10276 if (flags & OPT_login) {
10277 const char *hp = NULL;
10260 HFILE *input; 10278 HFILE *input;
10279
10261 debug_printf("sourcing /etc/profile\n"); 10280 debug_printf("sourcing /etc/profile\n");
10262 input = hfopen("/etc/profile"); 10281 input = hfopen("/etc/profile");
10282 run_profile:
10263 if (input != NULL) { 10283 if (input != NULL) {
10264 install_special_sighandlers(); 10284 install_special_sighandlers();
10265 parse_and_run_file(input); 10285 parse_and_run_file(input);
@@ -10272,6 +10292,64 @@ int hush_main(int argc, char **argv)
10272 * bash also sources ~/.bash_logout on exit. 10292 * bash also sources ~/.bash_logout on exit.
10273 * If called as sh, skips .bash_XXX files. 10293 * If called as sh, skips .bash_XXX files.
10274 */ 10294 */
10295 if (!hp) { /* unless we looped on the "goto" already */
10296 hp = get_local_var_value("HOME");
10297 if (hp && hp[0]) {
10298 debug_printf("sourcing ~/.profile\n");
10299 hp = concat_path_file(hp, ".profile");
10300 input = hfopen(hp);
10301 free((char*)hp);
10302 goto run_profile;
10303 }
10304 }
10305 }
10306
10307 /* -c takes effect *after* -l */
10308 if (G.opt_c) {
10309 /* Possibilities:
10310 * sh ... -c 'script'
10311 * sh ... -c 'script' ARG0 [ARG1...]
10312 * On NOMMU, if builtin_argc != 0,
10313 * sh ... -c 'builtin' BARGV... "" ARG0 [ARG1...]
10314 * "" needs to be replaced with NULL
10315 * and BARGV vector fed to builtin function.
10316 * Note: the form without ARG0 never happens:
10317 * sh ... -c 'builtin' BARGV... ""
10318 */
10319 char *script;
10320
10321 install_special_sighandlers();
10322
10323 G.global_argc--;
10324 G.global_argv++;
10325#if !BB_MMU
10326 if (builtin_argc) {
10327 /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
10328 const struct built_in_command *x;
10329 x = find_builtin(G.global_argv[0]);
10330 if (x) { /* paranoia */
10331 argv = G.global_argv;
10332 G.global_argc -= builtin_argc + 1; /* skip [BARGV...] "" */
10333 G.global_argv += builtin_argc + 1;
10334 G.global_argv[-1] = NULL; /* replace "" */
10335 G.last_exitcode = x->b_function(argv);
10336 }
10337 goto final_return;
10338 }
10339#endif
10340
10341 script = G.global_argv[0];
10342 if (!script)
10343 bb_error_msg_and_die(bb_msg_requires_arg, "-c");
10344 if (!G.global_argv[1]) {
10345 /* -c 'script' (no params): prevent empty $0 */
10346 G.global_argv[0] = argv[0];
10347 } else { /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
10348 G.global_argc--;
10349 G.global_argv++;
10350 }
10351 parse_and_run_string(script);
10352 goto final_return;
10275 } 10353 }
10276 10354
10277 /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */ 10355 /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */
@@ -10363,7 +10441,7 @@ int hush_main(int argc, char **argv)
10363 * (bash, too, does this only if ctty is available) */ 10441 * (bash, too, does this only if ctty is available) */
10364 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ 10442 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10365 /* Grab control of the terminal */ 10443 /* Grab control of the terminal */
10366 tcsetpgrp(G_interactive_fd, getpid()); 10444 tcsetpgrp(G_interactive_fd, cached_getpid);
10367 } 10445 }
10368 enable_restore_tty_pgrp_on_exit(); 10446 enable_restore_tty_pgrp_on_exit();
10369 10447
@@ -10456,7 +10534,7 @@ static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM)
10456} 10534}
10457 10535
10458#if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL 10536#if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL
10459static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv)) 10537static NOINLINE int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv))
10460{ 10538{
10461 int argc = string_array_len(argv); 10539 int argc = string_array_len(argv);
10462 return applet_main_func(argc, argv); 10540 return applet_main_func(argc, argv);
@@ -11662,7 +11740,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid
11662 sig = check_and_run_traps(); 11740 sig = check_and_run_traps();
11663 if (sig /*&& sig != SIGCHLD - always true */) { 11741 if (sig /*&& sig != SIGCHLD - always true */) {
11664 /* Do this for any (non-ignored) signal, not only for ^C */ 11742 /* Do this for any (non-ignored) signal, not only for ^C */
11665 ret = 128 + sig; 11743 ret = 128 | sig;
11666 break; 11744 break;
11667 } 11745 }
11668 /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */ 11746 /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
@@ -11762,7 +11840,7 @@ static int FAST_FUNC builtin_wait(char **argv)
11762 process_wait_result(NULL, pid, status); 11840 process_wait_result(NULL, pid, status);
11763 ret = WEXITSTATUS(status); 11841 ret = WEXITSTATUS(status);
11764 if (WIFSIGNALED(status)) 11842 if (WIFSIGNALED(status))
11765 ret = 128 + WTERMSIG(status); 11843 ret = 128 | WTERMSIG(status);
11766 } 11844 }
11767 } while (*++argv); 11845 } while (*++argv);
11768 11846
diff --git a/shell/hush_test/hush-misc/piped_input.right b/shell/hush_test/hush-misc/piped_input.right
new file mode 100644
index 000000000..7b8bf6758
--- /dev/null
+++ b/shell/hush_test/hush-misc/piped_input.right
@@ -0,0 +1,2 @@
1TEST
2One:1
diff --git a/shell/hush_test/hush-misc/piped_input.tests b/shell/hush_test/hush-misc/piped_input.tests
new file mode 100755
index 000000000..929fec0db
--- /dev/null
+++ b/shell/hush_test/hush-misc/piped_input.tests
@@ -0,0 +1,3 @@
1exec 2>&1
2echo 'echo TEST; false' | $THIS_SH
3echo One:$?
diff --git a/shell/hush_test/hush-vars/var_bash_repl_empty_var.right b/shell/hush_test/hush-vars/var_bash_repl_empty_var.right
index 892916783..a8d1a3bef 100644
--- a/shell/hush_test/hush-vars/var_bash_repl_empty_var.right
+++ b/shell/hush_test/hush-vars/var_bash_repl_empty_var.right
@@ -1,2 +1,3 @@
1 1
2w
2Ok:0 3Ok:0
diff --git a/shell/hush_test/hush-vars/var_bash_repl_empty_var.tests b/shell/hush_test/hush-vars/var_bash_repl_empty_var.tests
index 73a43d38e..22aaba560 100755
--- a/shell/hush_test/hush-vars/var_bash_repl_empty_var.tests
+++ b/shell/hush_test/hush-vars/var_bash_repl_empty_var.tests
@@ -1,3 +1,5 @@
1unset v
2echo ${v/*/w}
1v='' 3v=''
2echo ${v/*/w} 4echo ${v/*/w}
3echo Ok:$? 5echo Ok:$?
diff --git a/shell/shell_common.c b/shell/shell_common.c
index a6ee60851..1897fee3b 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -393,12 +393,18 @@ struct limits {
393 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ 393 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
394}; 394};
395 395
396static const struct limits limits_tbl[] = { 396/* Order of entries matches order in which bash prints "ulimit -a" */
397static const struct limits limits_tbl[] ALIGN2 = {
397 { RLIMIT_CORE, 9, }, // -c 398 { RLIMIT_CORE, 9, }, // -c
398 { RLIMIT_DATA, 10, }, // -d 399 { RLIMIT_DATA, 10, }, // -d
400#ifdef RLIMIT_NICE
399 { RLIMIT_NICE, 0, }, // -e 401 { RLIMIT_NICE, 0, }, // -e
400 { RLIMIT_FSIZE, 9, }, // -f
401#define LIMIT_F_IDX 3 402#define LIMIT_F_IDX 3
403#else
404/* for example, Hurd */
405#define LIMIT_F_IDX 2
406#endif
407 { RLIMIT_FSIZE, 9, }, // -f
402#ifdef RLIMIT_SIGPENDING 408#ifdef RLIMIT_SIGPENDING
403 { RLIMIT_SIGPENDING, 0, }, // -i 409 { RLIMIT_SIGPENDING, 0, }, // -i
404#endif 410#endif
@@ -433,13 +439,16 @@ static const struct limits limits_tbl[] = {
433 { RLIMIT_LOCKS, 0, }, // -x 439 { RLIMIT_LOCKS, 0, }, // -x
434#endif 440#endif
435}; 441};
436// bash also shows: 442// 1) bash also shows:
437//pipe size (512 bytes, -p) 8 443//pipe size (512 bytes, -p) 8
444// 2) RLIMIT_RTTIME ("timeout for RT tasks in us") is not in the table
438 445
439static const char limits_help[] ALIGN1 = 446static const char limits_help[] ALIGN1 =
440 "core file size (blocks)" // -c 447 "core file size (blocks)" // -c
441 "\0""data seg size (kb)" // -d 448 "\0""data seg size (kb)" // -d
449#ifdef RLIMIT_NICE
442 "\0""scheduling priority" // -e 450 "\0""scheduling priority" // -e
451#endif
443 "\0""file size (blocks)" // -f 452 "\0""file size (blocks)" // -f
444#ifdef RLIMIT_SIGPENDING 453#ifdef RLIMIT_SIGPENDING
445 "\0""pending signals" // -i 454 "\0""pending signals" // -i
@@ -479,7 +488,9 @@ static const char limits_help[] ALIGN1 =
479static const char limit_chars[] ALIGN1 = 488static const char limit_chars[] ALIGN1 =
480 "c" 489 "c"
481 "d" 490 "d"
491#ifdef RLIMIT_NICE
482 "e" 492 "e"
493#endif
483 "f" 494 "f"
484#ifdef RLIMIT_SIGPENDING 495#ifdef RLIMIT_SIGPENDING
485 "i" 496 "i"
@@ -520,7 +531,9 @@ static const char limit_chars[] ALIGN1 =
520static const char ulimit_opt_string[] ALIGN1 = "-HSa" 531static const char ulimit_opt_string[] ALIGN1 = "-HSa"
521 "c::" 532 "c::"
522 "d::" 533 "d::"
534#ifdef RLIMIT_NICE
523 "e::" 535 "e::"
536#endif
524 "f::" 537 "f::"
525#ifdef RLIMIT_SIGPENDING 538#ifdef RLIMIT_SIGPENDING
526 "i::" 539 "i::"
@@ -737,7 +750,7 @@ shell_builtin_ulimit(char **argv)
737 750
738 if (opt_cnt == 0) { 751 if (opt_cnt == 0) {
739 /* "bare ulimit": treat it as if it was -f */ 752 /* "bare ulimit": treat it as if it was -f */
740 getrlimit(limits_tbl[LIMIT_F_IDX].cmd, &limit); 753 getrlimit(RLIMIT_FSIZE, &limit);
741 printlim(opts, &limit, &limits_tbl[LIMIT_F_IDX]); 754 printlim(opts, &limit, &limits_tbl[LIMIT_F_IDX]);
742 } 755 }
743 756