diff options
author | Ron Yorston <rmy@pobox.com> | 2020-11-12 08:27:51 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2020-11-12 08:27:51 +0000 |
commit | ead8b92e3d66ab45235e137f85fb3a529dcc64ef (patch) | |
tree | af268270382dad969218063d4a8120fc91a9e631 /shell | |
parent | 567728c22dddea4ed33b8a69641ba2e0c3f1f600 (diff) | |
parent | 64981b4c8e88812c322bee3832f1d421ff670ed5 (diff) | |
download | busybox-w32-ead8b92e3d66ab45235e137f85fb3a529dcc64ef.tar.gz busybox-w32-ead8b92e3d66ab45235e137f85fb3a529dcc64ef.tar.bz2 busybox-w32-ead8b92e3d66ab45235e137f85fb3a529dcc64ef.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
33 files changed, 343 insertions, 103 deletions
diff --git a/shell/ash.c b/shell/ash.c index 1f57c2d7e..b8f75af05 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -240,17 +240,17 @@ | |||
240 | #define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT | 240 | #define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT |
241 | /* BASH_TEST2: [[ EXPR ]] | 241 | /* BASH_TEST2: [[ EXPR ]] |
242 | * Status of [[ support: | 242 | * Status of [[ support: |
243 | * We replace && and || with -a and -o | 243 | * && and || work as they should |
244 | * = is glob match operator, not equality operator: STR = GLOB | ||
245 | * == same as = | ||
246 | * =~ is regex match operator: STR =~ REGEX | ||
244 | * TODO: | 247 | * TODO: |
245 | * singleword+noglob expansion: | 248 | * singleword+noglob expansion: |
246 | * v='a b'; [[ $v = 'a b' ]]; echo 0:$? | 249 | * v='a b'; [[ $v = 'a b' ]]; echo 0:$? |
247 | * [[ /bin/n* ]]; echo 0:$? | 250 | * [[ /bin/n* ]]; echo 0:$? |
248 | * -a/-o are not AND/OR ops! (they are just strings) | ||
249 | * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) | 251 | * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) |
250 | * = is glob match operator, not equality operator: STR = GLOB | 252 | * ( ) < > should not have special meaning (IOW: should not require quoting) |
251 | * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") | 253 | * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*" |
252 | * == same as = | ||
253 | * add =~ regex match operator: STR =~ REGEX | ||
254 | */ | 254 | */ |
255 | #define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST) | 255 | #define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST) |
256 | #define BASH_SOURCE ENABLE_ASH_BASH_COMPAT | 256 | #define BASH_SOURCE ENABLE_ASH_BASH_COMPAT |
@@ -3173,7 +3173,7 @@ updatepwd(const char *dir) | |||
3173 | lim++; | 3173 | lim++; |
3174 | } | 3174 | } |
3175 | } | 3175 | } |
3176 | p = strtok(cdcomppath, "/"); | 3176 | p = strtok_r(cdcomppath, "/", &cdcomppath); |
3177 | while (p) { | 3177 | while (p) { |
3178 | switch (*p) { | 3178 | switch (*p) { |
3179 | case '.': | 3179 | case '.': |
@@ -3192,7 +3192,7 @@ updatepwd(const char *dir) | |||
3192 | new = stack_putstr(p, new); | 3192 | new = stack_putstr(p, new); |
3193 | USTPUTC('/', new); | 3193 | USTPUTC('/', new); |
3194 | } | 3194 | } |
3195 | p = strtok(NULL, "/"); | 3195 | p = strtok_r(NULL, "/", &cdcomppath); |
3196 | } | 3196 | } |
3197 | if (new > lim) | 3197 | if (new > lim) |
3198 | STUNPUTC(new); | 3198 | STUNPUTC(new); |
@@ -4780,61 +4780,54 @@ waitpid_child(int *status, int wait_flags) | |||
4780 | return pid; | 4780 | return pid; |
4781 | } | 4781 | } |
4782 | #define waitpid(p, s, f) waitpid_child(s, f) | 4782 | #define waitpid(p, s, f) waitpid_child(s, f) |
4783 | #define wait_block_or_sig(s) waitpid_child(s, 0) | 4783 | #endif |
4784 | 4784 | ||
4785 | #else | 4785 | #define DOWAIT_NONBLOCK 0 |
4786 | #define DOWAIT_BLOCK 1 | ||
4787 | #define DOWAIT_BLOCK_OR_SIG 2 | ||
4788 | #if BASH_WAIT_N | ||
4789 | # define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */ | ||
4790 | #endif | ||
4786 | 4791 | ||
4787 | static int | 4792 | static int |
4788 | wait_block_or_sig(int *status) | 4793 | waitproc(int block, int *status) |
4789 | { | 4794 | { |
4790 | int pid; | 4795 | #if !ENABLE_PLATFORM_MINGW32 |
4796 | sigset_t oldmask; | ||
4797 | int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; | ||
4798 | int err; | ||
4791 | 4799 | ||
4792 | do { | 4800 | #if JOBS |
4793 | sigset_t mask; | 4801 | if (doing_jobctl) |
4802 | flags |= WUNTRACED; | ||
4803 | #endif | ||
4794 | 4804 | ||
4795 | /* Poll all children for changes in their state */ | 4805 | do { |
4796 | got_sigchld = 0; | 4806 | got_sigchld = 0; |
4797 | /* if job control is active, accept stopped processes too */ | 4807 | do |
4798 | pid = waitpid(-1, status, doing_jobctl ? (WNOHANG|WUNTRACED) : WNOHANG); | 4808 | err = waitpid(-1, status, flags); |
4799 | if (pid != 0) | 4809 | while (err < 0 && errno == EINTR); |
4800 | break; /* Error (e.g. EINTR, ECHILD) or pid */ | ||
4801 | 4810 | ||
4802 | /* Children exist, but none are ready. Sleep until interesting signal */ | 4811 | if (err || (err = -!block)) |
4803 | #if 1 | 4812 | break; |
4804 | sigfillset(&mask); | 4813 | |
4805 | sigprocmask2(SIG_SETMASK, &mask); /* mask is updated */ | 4814 | sigfillset(&oldmask); |
4806 | while (!got_sigchld && !pending_sig) { | 4815 | sigprocmask2(SIG_SETMASK, &oldmask); /* mask is updated */ |
4807 | sigsuspend(&mask); | ||
4808 | /* ^^^ add "sigdelset(&mask, SIGCHLD);" before sigsuspend | ||
4809 | * to make sure SIGCHLD is not masked off? | ||
4810 | * It was reported that this: | ||
4811 | * fn() { : | return; } | ||
4812 | * shopt -s lastpipe | ||
4813 | * fn | ||
4814 | * exec ash SCRIPT | ||
4815 | * under bash 4.4.23 runs SCRIPT with SIGCHLD masked, | ||
4816 | * making "wait" commands in SCRIPT block forever. | ||
4817 | */ | ||
4818 | } | ||
4819 | sigprocmask(SIG_SETMASK, &mask, NULL); | ||
4820 | #else /* unsafe: a signal can set pending_sig after check, but before pause() */ | ||
4821 | while (!got_sigchld && !pending_sig) | 4816 | while (!got_sigchld && !pending_sig) |
4822 | pause(); | 4817 | sigsuspend(&oldmask); |
4823 | #endif | 4818 | sigprocmask(SIG_SETMASK, &oldmask, NULL); |
4819 | //simpler, but unsafe: a signal can set pending_sig after check, but before pause(): | ||
4820 | //while (!got_sigchld && !pending_sig) | ||
4821 | // pause(); | ||
4824 | 4822 | ||
4825 | /* If it was SIGCHLD, poll children again */ | ||
4826 | } while (got_sigchld); | 4823 | } while (got_sigchld); |
4827 | 4824 | ||
4828 | return pid; | 4825 | return err; |
4829 | } | 4826 | #else |
4830 | #endif | 4827 | int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; |
4831 | 4828 | return waitpid(-1, status, flags); | |
4832 | #define DOWAIT_NONBLOCK 0 | ||
4833 | #define DOWAIT_BLOCK 1 | ||
4834 | #define DOWAIT_BLOCK_OR_SIG 2 | ||
4835 | #if BASH_WAIT_N | ||
4836 | # define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */ | ||
4837 | #endif | 4829 | #endif |
4830 | } | ||
4838 | 4831 | ||
4839 | static int | 4832 | static int |
4840 | waitone(int block, struct job *job) | 4833 | waitone(int block, struct job *job) |
@@ -4842,7 +4835,7 @@ waitone(int block, struct job *job) | |||
4842 | int pid; | 4835 | int pid; |
4843 | int status; | 4836 | int status; |
4844 | struct job *jp; | 4837 | struct job *jp; |
4845 | struct job *thisjob; | 4838 | struct job *thisjob = NULL; |
4846 | #if BASH_WAIT_N | 4839 | #if BASH_WAIT_N |
4847 | bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS); | 4840 | bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS); |
4848 | block = (block & ~DOWAIT_JOBSTATUS); | 4841 | block = (block & ~DOWAIT_JOBSTATUS); |
@@ -4869,21 +4862,8 @@ waitone(int block, struct job *job) | |||
4869 | * SIG_DFL handler does not wake sigsuspend(). | 4862 | * SIG_DFL handler does not wake sigsuspend(). |
4870 | */ | 4863 | */ |
4871 | INT_OFF; | 4864 | INT_OFF; |
4872 | if (block == DOWAIT_BLOCK_OR_SIG) { | 4865 | pid = waitproc(block, &status); |
4873 | pid = wait_block_or_sig(&status); | 4866 | TRACE(("wait returns pid %d, status=%d\n", pid, status)); |
4874 | } else { | ||
4875 | int wait_flags = 0; | ||
4876 | if (block == DOWAIT_NONBLOCK) | ||
4877 | wait_flags = WNOHANG; | ||
4878 | /* if job control is active, accept stopped processes too */ | ||
4879 | if (doing_jobctl) | ||
4880 | wait_flags |= WUNTRACED; | ||
4881 | /* NB: _not_ safe_waitpid, we need to detect EINTR */ | ||
4882 | pid = waitpid(-1, &status, wait_flags); | ||
4883 | } | ||
4884 | TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", | ||
4885 | pid, status, errno, strerror(errno))); | ||
4886 | thisjob = NULL; | ||
4887 | if (pid <= 0) | 4867 | if (pid <= 0) |
4888 | goto out; | 4868 | goto out; |
4889 | 4869 | ||
@@ -4971,21 +4951,35 @@ static int | |||
4971 | dowait(int block, struct job *jp) | 4951 | dowait(int block, struct job *jp) |
4972 | { | 4952 | { |
4973 | #if !ENABLE_PLATFORM_MINGW32 | 4953 | #if !ENABLE_PLATFORM_MINGW32 |
4974 | int pid = block == DOWAIT_NONBLOCK ? got_sigchld : 1; | 4954 | smallint gotchld = *(volatile smallint *)&got_sigchld; |
4955 | int rpid; | ||
4956 | int pid; | ||
4957 | |||
4958 | if (jp && jp->state != JOBRUNNING) | ||
4959 | block = DOWAIT_NONBLOCK; | ||
4960 | |||
4961 | if (block == DOWAIT_NONBLOCK && !gotchld) | ||
4962 | return 1; | ||
4963 | |||
4964 | rpid = 1; | ||
4975 | 4965 | ||
4976 | while (jp ? jp->state == JOBRUNNING : pid > 0) { | 4966 | do { |
4977 | if (!jp) | ||
4978 | got_sigchld = 0; | ||
4979 | pid = waitone(block, jp); | 4967 | pid = waitone(block, jp); |
4980 | } | 4968 | rpid &= !!pid; |
4969 | |||
4970 | if (!pid || (jp && jp->state != JOBRUNNING)) | ||
4971 | block = DOWAIT_NONBLOCK; | ||
4972 | } while (pid >= 0); | ||
4973 | |||
4974 | return rpid; | ||
4981 | #else | 4975 | #else |
4982 | int pid = 1; | 4976 | int pid = 1; |
4983 | 4977 | ||
4984 | while (jp ? jp->state == JOBRUNNING : pid > 0) | 4978 | while (jp ? jp->state == JOBRUNNING : pid > 0) |
4985 | pid = waitone(block, jp); | 4979 | pid = waitone(block, jp); |
4986 | #endif | ||
4987 | 4980 | ||
4988 | return pid; | 4981 | return pid; |
4982 | #endif | ||
4989 | } | 4983 | } |
4990 | 4984 | ||
4991 | #if JOBS | 4985 | #if JOBS |
@@ -5232,7 +5226,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv) | |||
5232 | job = getjob(*argv, 0); | 5226 | job = getjob(*argv, 0); |
5233 | } | 5227 | } |
5234 | /* loop until process terminated or stopped */ | 5228 | /* loop until process terminated or stopped */ |
5235 | dowait(DOWAIT_BLOCK_OR_SIG, NULL); | 5229 | dowait(DOWAIT_BLOCK_OR_SIG, job); |
5236 | if (pending_sig) | 5230 | if (pending_sig) |
5237 | goto sigout; | 5231 | goto sigout; |
5238 | job->waited = 1; | 5232 | job->waited = 1; |
@@ -12622,7 +12616,8 @@ simplecmd(void) | |||
12622 | tokpushback = 1; | 12616 | tokpushback = 1; |
12623 | goto out; | 12617 | goto out; |
12624 | } | 12618 | } |
12625 | wordtext = (char *) (t == TAND ? "-a" : "-o"); | 12619 | /* pass "&&" or "||" to [[ ]] as literal args */ |
12620 | wordtext = (char *) (t == TAND ? "&&" : "||"); | ||
12626 | #endif | 12621 | #endif |
12627 | case TWORD: | 12622 | case TWORD: |
12628 | n = stzalloc(sizeof(struct narg)); | 12623 | n = stzalloc(sizeof(struct narg)); |
@@ -13600,7 +13595,7 @@ parsebackq: { | |||
13600 | goto done; | 13595 | goto done; |
13601 | 13596 | ||
13602 | case '\\': | 13597 | case '\\': |
13603 | pc = pgetc(); /* or pgetc_eatbnl()? why (example)? */ | 13598 | pc = pgetc(); /* not pgetc_eatbnl! */ |
13604 | if (pc != '\\' && pc != '`' && pc != '$' | 13599 | if (pc != '\\' && pc != '`' && pc != '$' |
13605 | && (!synstack->dblquote || pc != '"') | 13600 | && (!synstack->dblquote || pc != '"') |
13606 | ) { | 13601 | ) { |
diff --git a/shell/ash_test/ash-misc/wait7.right b/shell/ash_test/ash-misc/wait7.right new file mode 100644 index 000000000..4b6445841 --- /dev/null +++ b/shell/ash_test/ash-misc/wait7.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Background1 | ||
2 | Ok:0 | ||
diff --git a/shell/ash_test/ash-misc/wait7.tests b/shell/ash_test/ash-misc/wait7.tests new file mode 100755 index 000000000..a54a778e5 --- /dev/null +++ b/shell/ash_test/ash-misc/wait7.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | sleep 1 && echo "Background1" & | ||
2 | pid=$! | ||
3 | sleep 3 && echo "Background2: BUG!" & | ||
4 | # Shouldn't wait for 2nd bkgd: | ||
5 | wait $pid | ||
6 | kill $! | ||
7 | echo Ok:$? | ||
diff --git a/shell/ash_test/ash-signals/usage.right b/shell/ash_test/ash-signals/usage.right new file mode 100644 index 000000000..c0dbd6c3c --- /dev/null +++ b/shell/ash_test/ash-signals/usage.right | |||
@@ -0,0 +1,14 @@ | |||
1 | ___ | ||
2 | ___ | ||
3 | ___ | ||
4 | trap -- 'a' EXIT | ||
5 | trap -- 'a' INT | ||
6 | trap -- 'a' USR1 | ||
7 | trap -- 'a' USR2 | ||
8 | ___ | ||
9 | ___ | ||
10 | trap -- 'a' USR1 | ||
11 | trap -- 'a' USR2 | ||
12 | ___ | ||
13 | ___ | ||
14 | trap -- 'a' USR2 | ||
diff --git a/shell/ash_test/ash-signals/usage.tests b/shell/ash_test/ash-signals/usage.tests new file mode 100755 index 000000000..d29c6e74a --- /dev/null +++ b/shell/ash_test/ash-signals/usage.tests | |||
@@ -0,0 +1,23 @@ | |||
1 | # no output -- default state | ||
2 | echo ___ | ||
3 | trap | ||
4 | |||
5 | # assign some traps | ||
6 | echo ___ | ||
7 | trap "a" EXIT INT USR1 USR2 | ||
8 | |||
9 | # show them all | ||
10 | echo ___ | ||
11 | trap | ||
12 | |||
13 | # clear one | ||
14 | echo ___ | ||
15 | trap 0 INT | ||
16 | echo ___ | ||
17 | trap | ||
18 | |||
19 | # clear another | ||
20 | echo ___ | ||
21 | trap "-" USR1 | ||
22 | echo ___ | ||
23 | trap | ||
diff --git a/shell/hush.c b/shell/hush.c index e9cec1cc9..ab7263381 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -63,7 +63,6 @@ | |||
63 | * reserved words: function select | 63 | * reserved words: function select |
64 | * advanced test: [[ ]] | 64 | * advanced test: [[ ]] |
65 | * process substitution: <(list) and >(list) | 65 | * process substitution: <(list) and >(list) |
66 | * =~: regex operator | ||
67 | * let EXPR [EXPR...] | 66 | * let EXPR [EXPR...] |
68 | * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION) | 67 | * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION) |
69 | * If the last arg evaluates to 0, let returns 1; 0 otherwise. | 68 | * If the last arg evaluates to 0, let returns 1; 0 otherwise. |
@@ -84,13 +83,12 @@ | |||
84 | * [[ args ]] are CMD_SINGLEWORD_NOGLOB: | 83 | * [[ args ]] are CMD_SINGLEWORD_NOGLOB: |
85 | * v='a b'; [[ $v = 'a b' ]]; echo 0:$? | 84 | * v='a b'; [[ $v = 'a b' ]]; echo 0:$? |
86 | * [[ /bin/n* ]]; echo 0:$? | 85 | * [[ /bin/n* ]]; echo 0:$? |
86 | * = is glob match operator, not equality operator: STR = GLOB | ||
87 | * == same as = | ||
88 | * =~ is regex match operator: STR =~ REGEX | ||
87 | * TODO: | 89 | * TODO: |
88 | * &&/|| are AND/OR ops, -a/-o are not | ||
89 | * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) | 90 | * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) |
90 | * = is glob match operator, not equality operator: STR = GLOB | 91 | * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*" |
91 | * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") | ||
92 | * == same as = | ||
93 | * add =~ regex match operator: STR =~ REGEX | ||
94 | */ | 92 | */ |
95 | //config:config HUSH | 93 | //config:config HUSH |
96 | //config: bool "hush (68 kb)" | 94 | //config: bool "hush (68 kb)" |
@@ -651,14 +649,16 @@ struct command { | |||
651 | smallint cmd_type; /* CMD_xxx */ | 649 | smallint cmd_type; /* CMD_xxx */ |
652 | #define CMD_NORMAL 0 | 650 | #define CMD_NORMAL 0 |
653 | #define CMD_SUBSHELL 1 | 651 | #define CMD_SUBSHELL 1 |
654 | #if BASH_TEST2 || ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY | 652 | #if BASH_TEST2 |
655 | /* used for "[[ EXPR ]]", and to prevent word splitting and globbing in | 653 | /* used for "[[ EXPR ]]" */ |
656 | * "export v=t*" | 654 | # define CMD_TEST2_SINGLEWORD_NOGLOB 2 |
657 | */ | 655 | #endif |
658 | # define CMD_SINGLEWORD_NOGLOB 2 | 656 | #if ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY |
657 | /* used to prevent word splitting and globbing in "export v=t*" */ | ||
658 | # define CMD_SINGLEWORD_NOGLOB 3 | ||
659 | #endif | 659 | #endif |
660 | #if ENABLE_HUSH_FUNCTIONS | 660 | #if ENABLE_HUSH_FUNCTIONS |
661 | # define CMD_FUNCDEF 3 | 661 | # define CMD_FUNCDEF 4 |
662 | #endif | 662 | #endif |
663 | 663 | ||
664 | smalluint cmd_exitcode; | 664 | smalluint cmd_exitcode; |
@@ -4112,6 +4112,14 @@ static int done_word(struct parse_context *ctx) | |||
4112 | ctx->ctx_dsemicolon = 0; | 4112 | ctx->ctx_dsemicolon = 0; |
4113 | } else | 4113 | } else |
4114 | # endif | 4114 | # endif |
4115 | # if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | ||
4116 | if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB | ||
4117 | && strcmp(ctx->word.data, "]]") == 0 | ||
4118 | ) { | ||
4119 | /* allow "[[ ]] >file" etc */ | ||
4120 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; | ||
4121 | } else | ||
4122 | # endif | ||
4115 | if (!command->argv /* if it's the first word... */ | 4123 | if (!command->argv /* if it's the first word... */ |
4116 | # if ENABLE_HUSH_LOOPS | 4124 | # if ENABLE_HUSH_LOOPS |
4117 | && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ | 4125 | && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ |
@@ -4146,11 +4154,13 @@ static int done_word(struct parse_context *ctx) | |||
4146 | (ctx->ctx_res_w == RES_SNTX)); | 4154 | (ctx->ctx_res_w == RES_SNTX)); |
4147 | return (ctx->ctx_res_w == RES_SNTX); | 4155 | return (ctx->ctx_res_w == RES_SNTX); |
4148 | } | 4156 | } |
4157 | # if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | ||
4158 | if (strcmp(ctx->word.data, "[[") == 0) { | ||
4159 | command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB; | ||
4160 | } else | ||
4161 | # endif | ||
4149 | # if defined(CMD_SINGLEWORD_NOGLOB) | 4162 | # if defined(CMD_SINGLEWORD_NOGLOB) |
4150 | if (0 | 4163 | if (0 |
4151 | # if BASH_TEST2 | ||
4152 | || strcmp(ctx->word.data, "[[") == 0 | ||
4153 | # endif | ||
4154 | /* In bash, local/export/readonly are special, args | 4164 | /* In bash, local/export/readonly are special, args |
4155 | * are assignments and therefore expansion of them | 4165 | * are assignments and therefore expansion of them |
4156 | * should be "one-word" expansion: | 4166 | * should be "one-word" expansion: |
@@ -4172,7 +4182,8 @@ static int done_word(struct parse_context *ctx) | |||
4172 | ) { | 4182 | ) { |
4173 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; | 4183 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; |
4174 | } | 4184 | } |
4175 | /* fall through */ | 4185 | # else |
4186 | { /* empty block to pair "if ... else" */ } | ||
4176 | # endif | 4187 | # endif |
4177 | } | 4188 | } |
4178 | #endif /* HAS_KEYWORDS */ | 4189 | #endif /* HAS_KEYWORDS */ |
@@ -5354,9 +5365,15 @@ static struct pipe *parse_stream(char **pstring, | |||
5354 | if (ch != '\n') | 5365 | if (ch != '\n') |
5355 | next = i_peek_and_eat_bkslash_nl(input); | 5366 | next = i_peek_and_eat_bkslash_nl(input); |
5356 | 5367 | ||
5357 | is_special = "{}<>;&|()#" /* special outside of "str" */ | 5368 | is_special = "{}<>&|();#" /* special outside of "str" */ |
5358 | "$\"" IF_HUSH_TICK("`") /* always special */ | 5369 | "$\"" IF_HUSH_TICK("`") /* always special */ |
5359 | SPECIAL_VAR_SYMBOL_STR; | 5370 | SPECIAL_VAR_SYMBOL_STR; |
5371 | #if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | ||
5372 | if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) { | ||
5373 | /* In [[ ]], {}<>&|() are not special */ | ||
5374 | is_special += 8; | ||
5375 | } else | ||
5376 | #endif | ||
5360 | /* Are { and } special here? */ | 5377 | /* Are { and } special here? */ |
5361 | if (ctx.command->argv /* word [word]{... - non-special */ | 5378 | if (ctx.command->argv /* word [word]{... - non-special */ |
5362 | || ctx.word.length /* word{... - non-special */ | 5379 | || ctx.word.length /* word{... - non-special */ |
@@ -6953,7 +6970,7 @@ static char **expand_strvec_to_strvec(char **argv) | |||
6953 | return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); | 6970 | return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); |
6954 | } | 6971 | } |
6955 | 6972 | ||
6956 | #if defined(CMD_SINGLEWORD_NOGLOB) | 6973 | #if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB) |
6957 | static char **expand_strvec_to_strvec_singleword_noglob(char **argv) | 6974 | static char **expand_strvec_to_strvec_singleword_noglob(char **argv) |
6958 | { | 6975 | { |
6959 | return expand_variables(argv, EXP_FLAG_SINGLEWORD); | 6976 | return expand_variables(argv, EXP_FLAG_SINGLEWORD); |
@@ -8698,9 +8715,15 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) | |||
8698 | */ | 8715 | */ |
8699 | if (WIFSIGNALED(status)) { | 8716 | if (WIFSIGNALED(status)) { |
8700 | int sig = WTERMSIG(status); | 8717 | int sig = WTERMSIG(status); |
8701 | if (i == fg_pipe->num_cmds-1) | 8718 | if (G.run_list_level == 1 |
8702 | /* TODO: use strsignal() instead for bash compat? but that's bloat... */ | 8719 | /* ^^^^^ Do not print in nested contexts, example: |
8703 | puts(sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig)); | 8720 | * echo `sleep 1; sh -c 'kill -9 $$'` - prints "137", NOT "Killed 137" |
8721 | */ | ||
8722 | && i == fg_pipe->num_cmds-1 | ||
8723 | ) { | ||
8724 | /* strsignal() is for bash compat. ~600 bloat versus bbox's get_signame() */ | ||
8725 | puts(sig == SIGINT || sig == SIGPIPE ? "" : strsignal(sig)); | ||
8726 | } | ||
8704 | /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */ | 8727 | /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */ |
8705 | /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? | 8728 | /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? |
8706 | * Maybe we need to use sig | 128? */ | 8729 | * Maybe we need to use sig | 128? */ |
@@ -9127,6 +9150,11 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
9127 | } | 9150 | } |
9128 | 9151 | ||
9129 | /* Expand the rest into (possibly) many strings each */ | 9152 | /* Expand the rest into (possibly) many strings each */ |
9153 | #if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | ||
9154 | if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) | ||
9155 | argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); | ||
9156 | else | ||
9157 | #endif | ||
9130 | #if defined(CMD_SINGLEWORD_NOGLOB) | 9158 | #if defined(CMD_SINGLEWORD_NOGLOB) |
9131 | if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) | 9159 | if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) |
9132 | argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); | 9160 | argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); |
diff --git a/shell/hush_test/hush-bugs/strops5_bug.right b/shell/hush_test/hush-bugs/strops5_bug.right new file mode 100644 index 000000000..aba7bb429 --- /dev/null +++ b/shell/hush_test/hush-bugs/strops5_bug.right | |||
@@ -0,0 +1,2 @@ | |||
1 | 4:no:1 | ||
2 | 5:YES:0 | ||
diff --git a/shell/hush_test/hush-bugs/strops5_bug.tests b/shell/hush_test/hush-bugs/strops5_bug.tests new file mode 100755 index 000000000..3e24df768 --- /dev/null +++ b/shell/hush_test/hush-bugs/strops5_bug.tests | |||
@@ -0,0 +1,10 @@ | |||
1 | v='*.z' | ||
2 | |||
3 | # Buggy: | ||
4 | # the problem is that expansion rules of LHS and RHS of ~= | ||
5 | # should not be the same: in RHS, "$v" and "*" should escape metas | ||
6 | # (currently "$v" does not), | ||
7 | # but in LHS, they should _not_ do that | ||
8 | # (currently "*" does). Thus these cases fail: | ||
9 | [[ a.z == "$v" ]]; echo 4:no:$? # BUG: "$v" expands to *.z | ||
10 | [[ "*".z == ?.z ]]; echo 5:YES:$? # BUG: "*" expands to \* | ||
diff --git a/shell/hush_test/hush-misc/sig_exitcode.right b/shell/hush_test/hush-misc/sig_exitcode.right index d5f000a08..7cbc1072d 100644 --- a/shell/hush_test/hush-misc/sig_exitcode.right +++ b/shell/hush_test/hush-misc/sig_exitcode.right | |||
@@ -1,5 +1,5 @@ | |||
1 | KILL | 1 | Killed |
2 | 137:137 | 2 | 137:137 |
3 | KILL | 3 | Killed |
4 | 0:0 | 4 | 0:0 |
5 | Done | 5 | Done |
diff --git a/shell/hush_test/hush-misc/wait7.right b/shell/hush_test/hush-misc/wait7.right new file mode 100644 index 000000000..4b6445841 --- /dev/null +++ b/shell/hush_test/hush-misc/wait7.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Background1 | ||
2 | Ok:0 | ||
diff --git a/shell/hush_test/hush-misc/wait7.tests b/shell/hush_test/hush-misc/wait7.tests new file mode 100755 index 000000000..a54a778e5 --- /dev/null +++ b/shell/hush_test/hush-misc/wait7.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | sleep 1 && echo "Background1" & | ||
2 | pid=$! | ||
3 | sleep 3 && echo "Background2: BUG!" & | ||
4 | # Shouldn't wait for 2nd bkgd: | ||
5 | wait $pid | ||
6 | kill $! | ||
7 | echo Ok:$? | ||
diff --git a/shell/hush_test/hush-psubst/tick6.right b/shell/hush_test/hush-psubst/tick6.right new file mode 100644 index 000000000..065fd3e79 --- /dev/null +++ b/shell/hush_test/hush-psubst/tick6.right | |||
@@ -0,0 +1 @@ | |||
137 | |||
diff --git a/shell/hush_test/hush-psubst/tick6.tests b/shell/hush_test/hush-psubst/tick6.tests new file mode 100755 index 000000000..33dd3630d --- /dev/null +++ b/shell/hush_test/hush-psubst/tick6.tests | |||
@@ -0,0 +1 @@ | |||
true; echo `sh -c 'kill -9 $$'` $? | |||
diff --git a/shell/hush_test/hush-signals/catch.right b/shell/hush_test/hush-signals/catch.right index 80a062c4b..68530c6e7 100644 --- a/shell/hush_test/hush-signals/catch.right +++ b/shell/hush_test/hush-signals/catch.right | |||
@@ -2,4 +2,4 @@ sending USR2 | |||
2 | caught | 2 | caught |
3 | sending USR2 | 3 | sending USR2 |
4 | sending USR2 | 4 | sending USR2 |
5 | USR2 | 5 | User defined signal 2 |
diff --git a/shell/hush_test/hush-signals/signal1.right b/shell/hush_test/hush-signals/signal1.right new file mode 100644 index 000000000..cf403ac62 --- /dev/null +++ b/shell/hush_test/hush-signals/signal1.right | |||
@@ -0,0 +1,20 @@ | |||
1 | got signal | ||
2 | trap -- 'echo got signal' USR1 | ||
3 | sent 1 signal | ||
4 | got signal | ||
5 | wait interrupted | ||
6 | trap -- 'echo got signal' USR1 | ||
7 | sent 2 signal | ||
8 | got signal | ||
9 | wait interrupted | ||
10 | trap -- 'echo got signal' USR1 | ||
11 | sent 3 signal | ||
12 | got signal | ||
13 | wait interrupted | ||
14 | trap -- 'echo got signal' USR1 | ||
15 | sent 4 signal | ||
16 | got signal | ||
17 | wait interrupted | ||
18 | trap -- 'echo got signal' USR1 | ||
19 | sent 5 signal | ||
20 | sleep completed | ||
diff --git a/shell/hush_test/hush-signals/signal1.tests b/shell/hush_test/hush-signals/signal1.tests new file mode 100755 index 000000000..61943467a --- /dev/null +++ b/shell/hush_test/hush-signals/signal1.tests | |||
@@ -0,0 +1,28 @@ | |||
1 | trap "echo got signal" USR1 | ||
2 | |||
3 | for try in 1 2 3 4 5; do | ||
4 | kill -USR1 $$ | ||
5 | sleep 0.2 | ||
6 | echo "sent $try signal" | ||
7 | done & | ||
8 | |||
9 | # Ensure "wait" has something to wait for | ||
10 | sleep 2 & | ||
11 | |||
12 | # Ensure we do not execute "trap" below before "kill -USR1" above | ||
13 | # (was getting failure on loaded machine without this) | ||
14 | sleep 0.1 | ||
15 | |||
16 | sleeping=true | ||
17 | while $sleeping; do | ||
18 | trap | ||
19 | if wait %%; then | ||
20 | echo "sleep completed" | ||
21 | sleeping=false | ||
22 | elif [ $? = 127 ]; then | ||
23 | echo "BUG: no processes to wait for?!" | ||
24 | sleeping=false | ||
25 | else | ||
26 | echo "wait interrupted" | ||
27 | fi | ||
28 | done | ||
diff --git a/shell/hush_test/hush-signals/signal_read2.right b/shell/hush_test/hush-signals/signal_read2.right index 71a6bc16d..87d8da304 100644 --- a/shell/hush_test/hush-signals/signal_read2.right +++ b/shell/hush_test/hush-signals/signal_read2.right | |||
@@ -1,2 +1,2 @@ | |||
1 | HUP | 1 | Hangup |
2 | Done:129 | 2 | Done:129 |
diff --git a/shell/hush_test/hush-signals/subshell.right b/shell/hush_test/hush-signals/subshell.right index f865b932b..248fcc41a 100644 --- a/shell/hush_test/hush-signals/subshell.right +++ b/shell/hush_test/hush-signals/subshell.right | |||
@@ -17,5 +17,5 @@ Ok | |||
17 | trap -- '' HUP | 17 | trap -- '' HUP |
18 | trap -- '' QUIT | 18 | trap -- '' QUIT |
19 | trap -- '' SYS | 19 | trap -- '' SYS |
20 | TERM | 20 | Terminated |
21 | Done | 21 | Done |
diff --git a/shell/hush_test/hush-test2/andor1.right b/shell/hush_test/hush-test2/andor1.right new file mode 100644 index 000000000..038c7a681 --- /dev/null +++ b/shell/hush_test/hush-test2/andor1.right | |||
@@ -0,0 +1,6 @@ | |||
1 | 1:YES | ||
2 | 2:no | ||
3 | 3:YES | ||
4 | 4:YES | ||
5 | 5:no | ||
6 | 6:no | ||
diff --git a/shell/hush_test/hush-test2/andor1.tests b/shell/hush_test/hush-test2/andor1.tests new file mode 100755 index 000000000..c449de7e6 --- /dev/null +++ b/shell/hush_test/hush-test2/andor1.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | e='' | ||
2 | [[ a && b ]] && echo 1:YES | ||
3 | [[ a && '' ]] || echo 2:no | ||
4 | [[ a || b ]] && echo 3:YES | ||
5 | [[ '' || b ]] && echo 4:YES | ||
6 | [[ "" || "$e" ]] || echo 5:no | ||
7 | [[ "" || $e ]] || echo 6:no | ||
diff --git a/shell/hush_test/hush-test2/noglob1.right b/shell/hush_test/hush-test2/noglob1.right new file mode 100644 index 000000000..d0c3f1d8e --- /dev/null +++ b/shell/hush_test/hush-test2/noglob1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | 1:YES:0 | ||
2 | 2:YES:0 | ||
diff --git a/shell/hush_test/hush-test2/noglob1.tests b/shell/hush_test/hush-test2/noglob1.tests new file mode 100755 index 000000000..963bacbd3 --- /dev/null +++ b/shell/hush_test/hush-test2/noglob1.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | v='*.tests' | ||
2 | [[ *.tests ]]; echo 1:YES:$? | ||
3 | [[ $v ]]; echo 2:YES:$? | ||
diff --git a/shell/hush_test/hush-test2/strops1.right b/shell/hush_test/hush-test2/strops1.right new file mode 100644 index 000000000..590496301 --- /dev/null +++ b/shell/hush_test/hush-test2/strops1.right | |||
@@ -0,0 +1,8 @@ | |||
1 | 1:YES:0 | ||
2 | 2:YES:0 | ||
3 | 3:YES:0 | ||
4 | 4:YES:0 | ||
5 | 5:YES:0 | ||
6 | 6:YES:0 | ||
7 | 7:YES:0 | ||
8 | 8:no:1 | ||
diff --git a/shell/hush_test/hush-test2/strops1.tests b/shell/hush_test/hush-test2/strops1.tests new file mode 100755 index 000000000..bb24e2a2f --- /dev/null +++ b/shell/hush_test/hush-test2/strops1.tests | |||
@@ -0,0 +1,15 @@ | |||
1 | v='*.z' | ||
2 | [[ a.z = *.z ]]; echo 1:YES:$? | ||
3 | [[ a.z == $v ]]; echo 2:YES:$? | ||
4 | |||
5 | # wildcards can match a slash | ||
6 | [[ a/b = a*b ]]; echo 3:YES:$? | ||
7 | [[ a/b == a?b ]]; echo 4:YES:$? | ||
8 | |||
9 | # wildcards can match a leading dot | ||
10 | [[ a/.b = a/*b ]]; echo 5:YES:$? | ||
11 | [[ a/.b == a/?b ]]; echo 6:YES:$? | ||
12 | |||
13 | # wildcards can be escaped | ||
14 | [[ abc = a*c ]]; echo 7:YES:$? | ||
15 | [[ abc == a\*c ]]; echo 8:no:$? | ||
diff --git a/shell/hush_test/hush-test2/strops2.right b/shell/hush_test/hush-test2/strops2.right new file mode 100644 index 000000000..8ddb4b0f0 --- /dev/null +++ b/shell/hush_test/hush-test2/strops2.right | |||
@@ -0,0 +1,6 @@ | |||
1 | 1:ERR2:2 | ||
2 | 2:YES:0 | ||
3 | 3:YES:0 | ||
4 | 4:YES:0 | ||
5 | 5:no:1 | ||
6 | 6:YES:0 | ||
diff --git a/shell/hush_test/hush-test2/strops2.tests b/shell/hush_test/hush-test2/strops2.tests new file mode 100755 index 000000000..ab325bc9f --- /dev/null +++ b/shell/hush_test/hush-test2/strops2.tests | |||
@@ -0,0 +1,12 @@ | |||
1 | # malformed regex | ||
2 | [[ a =~ * ]]; echo 1:ERR2:$? | ||
3 | |||
4 | [[ a/b =~ a.b ]]; echo 2:YES:$? | ||
5 | [[ a/b =~ /*b ]]; echo 3:YES:$? | ||
6 | |||
7 | v='[]b.-]' | ||
8 | [[ a/.b] =~ $v ]]; echo 4:YES:$? | ||
9 | |||
10 | v=']b.-' | ||
11 | [[ a/.b] =~ $v ]]; echo 5:no:$? | ||
12 | [[ a/.b] =~ [$v] ]]; echo 6:YES:$? | ||
diff --git a/shell/hush_test/hush-test2/strops3.right b/shell/hush_test/hush-test2/strops3.right new file mode 100644 index 000000000..14cc04fdc --- /dev/null +++ b/shell/hush_test/hush-test2/strops3.right | |||
@@ -0,0 +1,7 @@ | |||
1 | 1:YES:0 | ||
2 | 2:YES:0 | ||
3 | 3:no:1 | ||
4 | 4:YES:0 | ||
5 | 2u:YES:0 | ||
6 | 3u:YES:0 | ||
7 | 4u:YES:0 | ||
diff --git a/shell/hush_test/hush-test2/strops3.tests b/shell/hush_test/hush-test2/strops3.tests new file mode 100755 index 000000000..927476671 --- /dev/null +++ b/shell/hush_test/hush-test2/strops3.tests | |||
@@ -0,0 +1,13 @@ | |||
1 | # regex should accept '+' operator | ||
2 | [[ abcdef =~ a[b-z]+ ]]; echo 1:YES:$? | ||
3 | |||
4 | # newline matches by "match any" patterns | ||
5 | v=' | ||
6 | ' | ||
7 | [[ "$v" =~ . ]]; echo 2:YES:$? | ||
8 | [[ "$v" =~ "[$v]" ]]; echo 3:no:$? # hmm bash does return 1... why? | ||
9 | [[ "$v" =~ [^a] ]]; echo 4:YES:$? | ||
10 | # should work even without quotes: | ||
11 | [[ $v =~ . ]]; echo 2u:YES:$? | ||
12 | [[ $v =~ [$v] ]]; echo 3u:YES:$? | ||
13 | [[ $v =~ [^a] ]]; echo 4u:YES:$? | ||
diff --git a/shell/hush_test/hush-test2/strops4.right b/shell/hush_test/hush-test2/strops4.right new file mode 100644 index 000000000..53a176261 --- /dev/null +++ b/shell/hush_test/hush-test2/strops4.right | |||
@@ -0,0 +1,2 @@ | |||
1 | 1:no:1 | ||
2 | 2:YES:0 | ||
diff --git a/shell/hush_test/hush-test2/strops4.tests b/shell/hush_test/hush-test2/strops4.tests new file mode 100755 index 000000000..bb381426d --- /dev/null +++ b/shell/hush_test/hush-test2/strops4.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # < > are not redirect operators | ||
2 | [[ a > b ]]; echo 1:no:$? | ||
3 | [[ a < b ]]; echo 2:YES:$? | ||
diff --git a/shell/hush_test/hush-test2/strops5.right b/shell/hush_test/hush-test2/strops5.right new file mode 100644 index 000000000..9ed4d6569 --- /dev/null +++ b/shell/hush_test/hush-test2/strops5.right | |||
@@ -0,0 +1,3 @@ | |||
1 | 1:YES:0 | ||
2 | 2:no:1 | ||
3 | 3:YES:0 | ||
diff --git a/shell/hush_test/hush-test2/strops5.tests b/shell/hush_test/hush-test2/strops5.tests new file mode 100755 index 000000000..ee01e5c30 --- /dev/null +++ b/shell/hush_test/hush-test2/strops5.tests | |||
@@ -0,0 +1,13 @@ | |||
1 | v='*.z' | ||
2 | [[ a.z = *.z ]]; echo 1:YES:$? | ||
3 | [[ a.z = "*".z ]]; echo 2:no:$? | ||
4 | [[ a.z == $v ]]; echo 3:YES:$? | ||
5 | |||
6 | # Buggy: | ||
7 | # the problem is that expansion rules of LHS and RHS of ~= | ||
8 | # should not be the same: in RHS, "$v" and "*" should escape metas | ||
9 | # (currently "$v" does not), | ||
10 | # but in LHS, they should _not_ do that | ||
11 | # (currently "*" does). Thus these cases fail: | ||
12 | #[[ a.z == "$v" ]]; echo 4:no:$? # BUG: "$v" expands to *.z | ||
13 | #[[ "*".z == ?.z ]]; echo 5:YES:$? # BUG: "*" expands to \* | ||
diff --git a/shell/math.c b/shell/math.c index aac5017d0..2942cdd26 100644 --- a/shell/math.c +++ b/shell/math.c | |||
@@ -251,7 +251,7 @@ typedef struct remembered_name { | |||
251 | } remembered_name; | 251 | } remembered_name; |
252 | 252 | ||
253 | 253 | ||
254 | static arith_t FAST_FUNC | 254 | static arith_t |
255 | evaluate_string(arith_state_t *math_state, const char *expr); | 255 | evaluate_string(arith_state_t *math_state, const char *expr); |
256 | 256 | ||
257 | static const char* | 257 | static const char* |
@@ -582,7 +582,7 @@ static arith_t strto_arith_t(const char *nptr, char **endptr) | |||
582 | # endif | 582 | # endif |
583 | #endif | 583 | #endif |
584 | 584 | ||
585 | static arith_t FAST_FUNC | 585 | static arith_t |
586 | evaluate_string(arith_state_t *math_state, const char *expr) | 586 | evaluate_string(arith_state_t *math_state, const char *expr) |
587 | { | 587 | { |
588 | operator lasttok; | 588 | operator lasttok; |