diff options
author | Ron Yorston <rmy@pobox.com> | 2021-01-14 13:28:49 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2021-01-14 13:28:49 +0000 |
commit | 89963b524d211e1aec12b72b3725be05ee95c8cf (patch) | |
tree | 48590aef62b7ee7686b7898256f29def8d9c50b9 /shell | |
parent | 9aa5a829070392c2ac6494d0c4e674c0c2bc7dab (diff) | |
parent | 2b7c1aa92c68524559a2067609d09309d5c09adc (diff) | |
download | busybox-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.c | 202 | ||||
-rw-r--r-- | shell/ash_test/ash-misc/piped_input.right | 2 | ||||
-rwxr-xr-x | shell/ash_test/ash-misc/piped_input.tests | 3 | ||||
-rw-r--r-- | shell/ash_test/ash-vars/var_bash_repl_empty_var.right | 1 | ||||
-rwxr-xr-x | shell/ash_test/ash-vars/var_bash_repl_empty_var.tests | 2 | ||||
-rw-r--r-- | shell/hush.c | 404 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/piped_input.right | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/piped_input.tests | 3 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/var_bash_repl_empty_var.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/var_bash_repl_empty_var.tests | 2 | ||||
-rw-r--r-- | shell/shell_common.c | 21 |
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 */ | ||
421 | static const char *const optletters_optnames[] = { | 422 | static 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 | */ | ||
719 | struct strlist { | ||
720 | struct strlist *next; | ||
721 | char *text; | ||
722 | }; | ||
723 | |||
724 | struct alias; | ||
725 | |||
726 | struct 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 | */ | ||
746 | struct 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 | |||
764 | static struct parsefile basepf; /* top level input file */ | ||
765 | static struct parsefile *g_parsefile = &basepf; /* current input file */ | ||
766 | #if ENABLE_PLATFORM_POSIX | ||
767 | static char *commandname; /* currently executing command */ | ||
768 | #endif | ||
769 | |||
770 | |||
710 | /* ============ Interrupts / exceptions */ | 771 | /* ============ Interrupts / exceptions */ |
711 | 772 | ||
712 | static void exitshell(void) NORETURN; | 773 | static 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 | */ | ||
1474 | struct strlist { | ||
1475 | struct strlist *next; | ||
1476 | char *text; | ||
1477 | }; | ||
1478 | |||
1479 | struct alias; | ||
1480 | |||
1481 | struct 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 | */ | ||
1501 | struct 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 | |||
1519 | static struct parsefile basepf; /* top level input file */ | ||
1520 | static struct parsefile *g_parsefile = &basepf; /* current input file */ | ||
1521 | #if ENABLE_PLATFORM_POSIX | ||
1522 | static char *commandname; /* currently executing command */ | ||
1523 | #endif | ||
1524 | |||
1525 | |||
1526 | /* ============ Message printing */ | 1530 | /* ============ Message printing */ |
1527 | 1531 | ||
1528 | static void | 1532 | static 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; | |||
5340 | static void | 5344 | static void |
5341 | cmdputs(const char *s) | 5345 | cmdputs(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 | ||
9130 | static const char *const tokname_array[] = { | 9137 | static 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 | |||
12164 | change_epoch(struct var *vepoch, const char *fmt) | 12170 | change_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) | |||
12175 | static void FAST_FUNC | 12181 | static void FAST_FUNC |
12176 | change_seconds(const char *value UNUSED_PARAM) | 12182 | change_seconds(const char *value UNUSED_PARAM) |
12177 | { | 12183 | { |
12178 | change_epoch(&vepochs, "%lu"); | 12184 | change_epoch(&vepochs, "%llu"); |
12179 | } | 12185 | } |
12180 | 12186 | ||
12181 | static void FAST_FUNC | 12187 | static void FAST_FUNC |
12182 | change_realtime(const char *value UNUSED_PARAM) | 12188 | change_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 @@ | |||
1 | TEST | ||
2 | One: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 @@ | |||
1 | exec 2>&1 | ||
2 | echo 'echo TEST; false' | $THIS_SH | ||
3 | echo 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 | ||
2 | w | ||
2 | Ok:0 | 3 | Ok: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 @@ | |||
1 | unset v | ||
2 | echo ${v/*/w} | ||
1 | v='' | 3 | v='' |
2 | echo ${v/*/w} | 4 | echo ${v/*/w} |
3 | echo Ok:$? | 5 | echo 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 */ |
598 | static const struct { | 598 | static 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 | ||
1143 | static const struct built_in_command bltins1[] = { | 1143 | static 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 | */ |
1228 | static const struct built_in_command bltins2[] = { | 1228 | static 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) | |||
2050 | static void hush_exit(int exitcode) | 2070 | static 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? |
2096 | static int check_and_run_traps(void) | 2114 | static 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 | } |
2623 | static int get_user_input(struct in_str *i) | 2641 | static 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 */ |
2707 | static ALWAYS_INLINE int fgetc_interactive(struct in_str *i) | 2735 | static 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 | ||
2715 | static int i_getch(struct in_str *i) | 2743 | static 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 | }; |
3893 | enum { | 3921 | enum { |
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) | |||
9972 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 10023 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
9973 | int hush_main(int argc, char **argv) | 10024 | int 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 |
10459 | static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv)) | 10537 | static 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 @@ | |||
1 | TEST | ||
2 | One: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 @@ | |||
1 | exec 2>&1 | ||
2 | echo 'echo TEST; false' | $THIS_SH | ||
3 | echo 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 | ||
2 | w | ||
2 | Ok:0 | 3 | Ok: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 @@ | |||
1 | unset v | ||
2 | echo ${v/*/w} | ||
1 | v='' | 3 | v='' |
2 | echo ${v/*/w} | 4 | echo ${v/*/w} |
3 | echo Ok:$? | 5 | echo 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 | ||
396 | static const struct limits limits_tbl[] = { | 396 | /* Order of entries matches order in which bash prints "ulimit -a" */ |
397 | static 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 | ||
439 | static const char limits_help[] ALIGN1 = | 446 | static 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 = | |||
479 | static const char limit_chars[] ALIGN1 = | 488 | static 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 = | |||
520 | static const char ulimit_opt_string[] ALIGN1 = "-HSa" | 531 | static 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 | ||