aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2020-11-12 08:27:51 +0000
committerRon Yorston <rmy@pobox.com>2020-11-12 08:27:51 +0000
commitead8b92e3d66ab45235e137f85fb3a529dcc64ef (patch)
treeaf268270382dad969218063d4a8120fc91a9e631 /shell
parent567728c22dddea4ed33b8a69641ba2e0c3f1f600 (diff)
parent64981b4c8e88812c322bee3832f1d421ff670ed5 (diff)
downloadbusybox-w32-ead8b92e3d66ab45235e137f85fb3a529dcc64ef.tar.gz
busybox-w32-ead8b92e3d66ab45235e137f85fb3a529dcc64ef.tar.bz2
busybox-w32-ead8b92e3d66ab45235e137f85fb3a529dcc64ef.zip
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c145
-rw-r--r--shell/ash_test/ash-misc/wait7.right2
-rwxr-xr-xshell/ash_test/ash-misc/wait7.tests7
-rw-r--r--shell/ash_test/ash-signals/usage.right14
-rwxr-xr-xshell/ash_test/ash-signals/usage.tests23
-rw-r--r--shell/hush.c70
-rw-r--r--shell/hush_test/hush-bugs/strops5_bug.right2
-rwxr-xr-xshell/hush_test/hush-bugs/strops5_bug.tests10
-rw-r--r--shell/hush_test/hush-misc/sig_exitcode.right4
-rw-r--r--shell/hush_test/hush-misc/wait7.right2
-rwxr-xr-xshell/hush_test/hush-misc/wait7.tests7
-rw-r--r--shell/hush_test/hush-psubst/tick6.right1
-rwxr-xr-xshell/hush_test/hush-psubst/tick6.tests1
-rw-r--r--shell/hush_test/hush-signals/catch.right2
-rw-r--r--shell/hush_test/hush-signals/signal1.right20
-rwxr-xr-xshell/hush_test/hush-signals/signal1.tests28
-rw-r--r--shell/hush_test/hush-signals/signal_read2.right2
-rw-r--r--shell/hush_test/hush-signals/subshell.right2
-rw-r--r--shell/hush_test/hush-test2/andor1.right6
-rwxr-xr-xshell/hush_test/hush-test2/andor1.tests7
-rw-r--r--shell/hush_test/hush-test2/noglob1.right2
-rwxr-xr-xshell/hush_test/hush-test2/noglob1.tests3
-rw-r--r--shell/hush_test/hush-test2/strops1.right8
-rwxr-xr-xshell/hush_test/hush-test2/strops1.tests15
-rw-r--r--shell/hush_test/hush-test2/strops2.right6
-rwxr-xr-xshell/hush_test/hush-test2/strops2.tests12
-rw-r--r--shell/hush_test/hush-test2/strops3.right7
-rwxr-xr-xshell/hush_test/hush-test2/strops3.tests13
-rw-r--r--shell/hush_test/hush-test2/strops4.right2
-rwxr-xr-xshell/hush_test/hush-test2/strops4.tests3
-rw-r--r--shell/hush_test/hush-test2/strops5.right3
-rwxr-xr-xshell/hush_test/hush-test2/strops5.tests13
-rw-r--r--shell/math.c4
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
4787static int 4792static int
4788wait_block_or_sig(int *status) 4793waitproc(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
4839static int 4832static int
4840waitone(int block, struct job *job) 4833waitone(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
4971dowait(int block, struct job *jp) 4951dowait(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 @@
1Background1
2Ok: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 @@
1sleep 1 && echo "Background1" &
2pid=$!
3sleep 3 && echo "Background2: BUG!" &
4# Shouldn't wait for 2nd bkgd:
5wait $pid
6kill $!
7echo 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___
4trap -- 'a' EXIT
5trap -- 'a' INT
6trap -- 'a' USR1
7trap -- 'a' USR2
8___
9___
10trap -- 'a' USR1
11trap -- 'a' USR2
12___
13___
14trap -- '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
2echo ___
3trap
4
5# assign some traps
6echo ___
7trap "a" EXIT INT USR1 USR2
8
9# show them all
10echo ___
11trap
12
13# clear one
14echo ___
15trap 0 INT
16echo ___
17trap
18
19# clear another
20echo ___
21trap "-" USR1
22echo ___
23trap
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)
6957static char **expand_strvec_to_strvec_singleword_noglob(char **argv) 6974static 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 @@
14:no:1
25: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 @@
1v='*.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 @@
1KILL 1Killed
2137:137 2137:137
3KILL 3Killed
40:0 40:0
5Done 5Done
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 @@
1Background1
2Ok: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 @@
1sleep 1 && echo "Background1" &
2pid=$!
3sleep 3 && echo "Background2: BUG!" &
4# Shouldn't wait for 2nd bkgd:
5wait $pid
6kill $!
7echo 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
2caught 2caught
3sending USR2 3sending USR2
4sending USR2 4sending USR2
5USR2 5User 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 @@
1got signal
2trap -- 'echo got signal' USR1
3sent 1 signal
4got signal
5wait interrupted
6trap -- 'echo got signal' USR1
7sent 2 signal
8got signal
9wait interrupted
10trap -- 'echo got signal' USR1
11sent 3 signal
12got signal
13wait interrupted
14trap -- 'echo got signal' USR1
15sent 4 signal
16got signal
17wait interrupted
18trap -- 'echo got signal' USR1
19sent 5 signal
20sleep 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 @@
1trap "echo got signal" USR1
2
3for try in 1 2 3 4 5; do
4 kill -USR1 $$
5 sleep 0.2
6 echo "sent $try signal"
7done &
8
9# Ensure "wait" has something to wait for
10sleep 2 &
11
12# Ensure we do not execute "trap" below before "kill -USR1" above
13# (was getting failure on loaded machine without this)
14sleep 0.1
15
16sleeping=true
17while $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
28done
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 @@
1HUP 1Hangup
2Done:129 2Done: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
17trap -- '' HUP 17trap -- '' HUP
18trap -- '' QUIT 18trap -- '' QUIT
19trap -- '' SYS 19trap -- '' SYS
20TERM 20Terminated
21Done 21Done
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 @@
11:YES
22:no
33:YES
44:YES
55:no
66: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 @@
1e=''
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 @@
11:YES:0
22: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 @@
1v='*.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 @@
11:YES:0
22:YES:0
33:YES:0
44:YES:0
55:YES:0
66:YES:0
77:YES:0
88: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 @@
1v='*.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 @@
11:ERR2:2
22:YES:0
33:YES:0
44:YES:0
55:no:1
66: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
7v='[]b.-]'
8[[ a/.b] =~ $v ]]; echo 4:YES:$?
9
10v=']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 @@
11:YES:0
22:YES:0
33:no:1
44:YES:0
52u:YES:0
63u:YES:0
74u: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
5v='
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 @@
11:no:1
22: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 @@
11:YES:0
22:no:1
33: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 @@
1v='*.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
254static arith_t FAST_FUNC 254static arith_t
255evaluate_string(arith_state_t *math_state, const char *expr); 255evaluate_string(arith_state_t *math_state, const char *expr);
256 256
257static const char* 257static 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
585static arith_t FAST_FUNC 585static arith_t
586evaluate_string(arith_state_t *math_state, const char *expr) 586evaluate_string(arith_state_t *math_state, const char *expr)
587{ 587{
588 operator lasttok; 588 operator lasttok;