aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>2010-09-14 13:08:20 +1000
committerNguyễn Thái Ngọc Duy <pclouds@gmail.com>2010-09-14 13:08:20 +1000
commit6a6efd31038d7afe977e3059508ae863e65cbdf5 (patch)
tree5cd69a751e893b83176751c80fcea7a7afeed1ae /shell
parenta6a2325ecf402054132daae169f71edb0fb849e3 (diff)
parent29082231d0cb1a5b327de5d515b16f332d4dbdaf (diff)
downloadbusybox-w32-6a6efd31038d7afe977e3059508ae863e65cbdf5.tar.gz
busybox-w32-6a6efd31038d7afe977e3059508ae863e65cbdf5.tar.bz2
busybox-w32-6a6efd31038d7afe977e3059508ae863e65cbdf5.zip
Merge branch 'origin/master' (early part)
Diffstat (limited to 'shell')
-rw-r--r--shell/Config.src106
-rw-r--r--shell/Kbuild.src2
-rw-r--r--shell/ash.c48
-rw-r--r--shell/ash_test/ash-quoting/dollar_repl_slash_bash1.right10
-rwxr-xr-xshell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests21
-rw-r--r--shell/ash_test/ash-redir/redir9.right2
-rw-r--r--shell/ash_test/ash-redir/redir9.tests4
-rw-r--r--shell/cttyhack.c3
-rw-r--r--shell/hush.c384
-rw-r--r--shell/hush_test/hush-vars/var_serial.right5
-rwxr-xr-xshell/hush_test/hush-vars/var_serial.tests22
11 files changed, 398 insertions, 209 deletions
diff --git a/shell/Config.src b/shell/Config.src
index 800911966..f415a5fa6 100644
--- a/shell/Config.src
+++ b/shell/Config.src
@@ -110,112 +110,6 @@ config ASH_EXPAND_PRMT
110 This option recreates the prompt string from the environment 110 This option recreates the prompt string from the environment
111 variable each time it is displayed. 111 variable each time it is displayed.
112 112
113config HUSH
114 bool "hush"
115 default y
116 help
117 hush is a small shell (22k). It handles the normal flow control
118 constructs such as if/then/elif/else/fi, for/in/do/done, while loops,
119 case/esac. Redirections, here documents, $((arithmetic))
120 and functions are supported.
121
122 It will compile and work on no-mmu systems.
123
124 It does not handle select, aliases, brace expansion,
125 tilde expansion, &>file and >&file redirection of stdout+stderr.
126
127config HUSH_BASH_COMPAT
128 bool "bash-compatible extensions"
129 default y
130 depends on HUSH
131 help
132 Enable bash-compatible extensions.
133
134config HUSH_HELP
135 bool "help builtin"
136 default y
137 depends on HUSH
138 help
139 Enable help builtin in hush. Code size + ~1 kbyte.
140
141config HUSH_INTERACTIVE
142 bool "Interactive mode"
143 default y
144 depends on HUSH
145 help
146 Enable interactive mode (prompt and command editing).
147 Without this, hush simply reads and executes commands
148 from stdin just like a shell script from a file.
149 No prompt, no PS1/PS2 magic shell variables.
150
151config HUSH_JOB
152 bool "Job control"
153 default y
154 depends on HUSH_INTERACTIVE
155 help
156 Enable job control: Ctrl-Z backgrounds, Ctrl-C interrupts current
157 command (not entire shell), fg/bg builtins work. Without this option,
158 "cmd &" still works by simply spawning a process and immediately
159 prompting for next command (or executing next command in a script),
160 but no separate process group is formed.
161
162config HUSH_TICK
163 bool "Process substitution"
164 default y
165 depends on HUSH
166 help
167 Enable process substitution `command` and $(command) in hush.
168
169config HUSH_IF
170 bool "Support if/then/elif/else/fi"
171 default y
172 depends on HUSH
173 help
174 Enable if/then/elif/else/fi in hush.
175
176config HUSH_LOOPS
177 bool "Support for, while and until loops"
178 default y
179 depends on HUSH
180 help
181 Enable for, while and until loops in hush.
182
183config HUSH_CASE
184 bool "Support case ... esac statement"
185 default y
186 depends on HUSH
187 help
188 Enable case ... esac statement in hush. +400 bytes.
189
190config HUSH_FUNCTIONS
191 bool "Support funcname() { commands; } syntax"
192 default y
193 depends on HUSH
194 help
195 Enable support for shell functions in hush. +800 bytes.
196
197config HUSH_LOCAL
198 bool "Support local builtin"
199 default y
200 depends on HUSH_FUNCTIONS
201 help
202 Enable support for local variables in functions.
203
204config HUSH_EXPORT_N
205 bool "Support export '-n' option"
206 default y
207 depends on HUSH
208 help
209 Enable support for export '-n' option in hush. It is a bash extension.
210
211config HUSH_RANDOM_SUPPORT
212 bool "Pseudorandom generator and $RANDOM variable"
213 default y
214 depends on HUSH
215 help
216 Enable pseudorandom generator and dynamic variable "$RANDOM".
217 Each read of "$RANDOM" will generate a new pseudorandom value.
218
219 113
220choice 114choice
221 prompt "Choose which shell is aliased to 'sh' name" 115 prompt "Choose which shell is aliased to 'sh' name"
diff --git a/shell/Kbuild.src b/shell/Kbuild.src
index d76b35386..c7eb5b61a 100644
--- a/shell/Kbuild.src
+++ b/shell/Kbuild.src
@@ -9,9 +9,7 @@ lib-y:=
9INSERT 9INSERT
10 10
11lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o 11lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
12lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
13lib-$(CONFIG_CTTYHACK) += cttyhack.o 12lib-$(CONFIG_CTTYHACK) += cttyhack.o
14 13
15lib-$(CONFIG_SH_MATH_SUPPORT) += math.o 14lib-$(CONFIG_SH_MATH_SUPPORT) += math.o
16lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o 15lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
17lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
diff --git a/shell/ash.c b/shell/ash.c
index 5ac2a1922..e0b15e343 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -64,9 +64,7 @@
64# define CLEAR_RANDOM_T(rnd) ((void)0) 64# define CLEAR_RANDOM_T(rnd) ((void)0)
65#endif 65#endif
66 66
67#define SKIP_definitions 1 67#include "NUM_APPLETS.h"
68#include "applet_tables.h"
69#undef SKIP_definitions
70#if NUM_APPLETS == 1 68#if NUM_APPLETS == 1
71/* STANDALONE does not make sense, and won't compile */ 69/* STANDALONE does not make sense, and won't compile */
72# undef CONFIG_FEATURE_SH_STANDALONE 70# undef CONFIG_FEATURE_SH_STANDALONE
@@ -5216,7 +5214,7 @@ openredirect(union node *redir)
5216 break; 5214 break;
5217 case NFROMTO: 5215 case NFROMTO:
5218 fname = redir->nfile.expfname; 5216 fname = redir->nfile.expfname;
5219 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666); 5217 f = open(fname, O_RDWR|O_CREAT, 0666);
5220 if (f < 0) 5218 if (f < 0)
5221 goto ecreate; 5219 goto ecreate;
5222 break; 5220 break;
@@ -6507,8 +6505,7 @@ subevalvar(char *p, char *str, int strloc, int subtype,
6507 char *startp; 6505 char *startp;
6508 char *loc; 6506 char *loc;
6509 char *rmesc, *rmescend; 6507 char *rmesc, *rmescend;
6510 IF_ASH_BASH_COMPAT(char *repl = NULL;) 6508 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
6511 IF_ASH_BASH_COMPAT(char null = '\0';)
6512 IF_ASH_BASH_COMPAT(int pos, len, orig_len;) 6509 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
6513 int saveherefd = herefd; 6510 int saveherefd = herefd;
6514 int amount, workloc, resetloc; 6511 int amount, workloc, resetloc;
@@ -6629,7 +6626,7 @@ subevalvar(char *p, char *str, int strloc, int subtype,
6629 if (!repl) { 6626 if (!repl) {
6630 repl = parse_sub_pattern(str, varflags & VSQUOTE); 6627 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6631 if (!repl) 6628 if (!repl)
6632 repl = &null; 6629 repl = nullstr;
6633 } 6630 }
6634 6631
6635 /* If there's no pattern to match, return the expansion unmolested */ 6632 /* If there's no pattern to match, return the expansion unmolested */
@@ -6680,8 +6677,12 @@ subevalvar(char *p, char *str, int strloc, int subtype,
6680 idx = loc; 6677 idx = loc;
6681 } 6678 }
6682 6679
6683 for (loc = repl; *loc; loc++) { 6680 for (loc = (char*)repl; *loc; loc++) {
6684 char *restart_detect = stackblock(); 6681 char *restart_detect = stackblock();
6682 if (quotes && *loc == '\\') {
6683 STPUTC(CTLESC, expdest);
6684 len++;
6685 }
6685 STPUTC(*loc, expdest); 6686 STPUTC(*loc, expdest);
6686 if (stackblock() != restart_detect) 6687 if (stackblock() != restart_detect)
6687 goto restart; 6688 goto restart;
@@ -6691,6 +6692,10 @@ subevalvar(char *p, char *str, int strloc, int subtype,
6691 if (subtype == VSREPLACE) { 6692 if (subtype == VSREPLACE) {
6692 while (*idx) { 6693 while (*idx) {
6693 char *restart_detect = stackblock(); 6694 char *restart_detect = stackblock();
6695 if (quotes && *idx == '\\') {
6696 STPUTC(CTLESC, expdest);
6697 len++;
6698 }
6694 STPUTC(*idx, expdest); 6699 STPUTC(*idx, expdest);
6695 if (stackblock() != restart_detect) 6700 if (stackblock() != restart_detect)
6696 goto restart; 6701 goto restart;
@@ -6704,11 +6709,10 @@ subevalvar(char *p, char *str, int strloc, int subtype,
6704 /* We've put the replaced text into a buffer at workloc, now 6709 /* We've put the replaced text into a buffer at workloc, now
6705 * move it to the right place and adjust the stack. 6710 * move it to the right place and adjust the stack.
6706 */ 6711 */
6707 startp = stackblock() + startloc;
6708 STPUTC('\0', expdest); 6712 STPUTC('\0', expdest);
6709 memmove(startp, stackblock() + workloc, len); 6713 startp = (char *)stackblock() + startloc;
6710 startp[len++] = '\0'; 6714 memmove(startp, (char *)stackblock() + workloc, len + 1);
6711 amount = expdest - ((char *)stackblock() + startloc + len - 1); 6715 amount = expdest - (startp + len);
6712 STADJUST(-amount, expdest); 6716 STADJUST(-amount, expdest);
6713 return startp; 6717 return startp;
6714 } 6718 }
@@ -7008,7 +7012,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7008 */ 7012 */
7009 STPUTC('\0', expdest); 7013 STPUTC('\0', expdest);
7010 patloc = expdest - (char *)stackblock(); 7014 patloc = expdest - (char *)stackblock();
7011 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype, 7015 if (NULL == subevalvar(p, /* str: */ NULL, patloc, subtype,
7012 startloc, varflags, 7016 startloc, varflags,
7013//TODO: | EXP_REDIR too? All other such places do it too 7017//TODO: | EXP_REDIR too? All other such places do it too
7014 /* quotes: */ flags & (EXP_FULL | EXP_CASE), 7018 /* quotes: */ flags & (EXP_FULL | EXP_CASE),
@@ -7171,13 +7175,11 @@ addfname(const char *name)
7171 exparg.lastp = &sp->next; 7175 exparg.lastp = &sp->next;
7172} 7176}
7173 7177
7174static char *expdir;
7175
7176/* 7178/*
7177 * Do metacharacter (i.e. *, ?, [...]) expansion. 7179 * Do metacharacter (i.e. *, ?, [...]) expansion.
7178 */ 7180 */
7179static void 7181static void
7180expmeta(char *enddir, char *name) 7182expmeta(char *expdir, char *enddir, char *name)
7181{ 7183{
7182 char *p; 7184 char *p;
7183 const char *cp; 7185 const char *cp;
@@ -7276,7 +7278,7 @@ expmeta(char *enddir, char *name)
7276 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';) 7278 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7277 continue; 7279 continue;
7278 p[-1] = '/'; 7280 p[-1] = '/';
7279 expmeta(p, endname); 7281 expmeta(expdir, p, endname);
7280 } 7282 }
7281 } 7283 }
7282 } 7284 }
@@ -7358,6 +7360,7 @@ expandmeta(struct strlist *str /*, int flag*/)
7358 /* TODO - EXP_REDIR */ 7360 /* TODO - EXP_REDIR */
7359 7361
7360 while (str) { 7362 while (str) {
7363 char *expdir;
7361 struct strlist **savelastp; 7364 struct strlist **savelastp;
7362 struct strlist *sp; 7365 struct strlist *sp;
7363 char *p; 7366 char *p;
@@ -7374,8 +7377,7 @@ expandmeta(struct strlist *str /*, int flag*/)
7374 int i = strlen(str->text); 7377 int i = strlen(str->text);
7375 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 7378 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7376 } 7379 }
7377 7380 expmeta(expdir, expdir, p);
7378 expmeta(expdir, p);
7379 free(expdir); 7381 free(expdir);
7380 if (p != str->text) 7382 if (p != str->text)
7381 free(p); 7383 free(p);
@@ -9547,19 +9549,15 @@ evalcommand(union node *cmd, int flags)
9547 /* Print the command if xflag is set. */ 9549 /* Print the command if xflag is set. */
9548 if (xflag) { 9550 if (xflag) {
9549 int n; 9551 int n;
9550 const char *p = " %s"; 9552 const char *p = " %s" + 1;
9551 9553
9552 p++;
9553 fdprintf(preverrout_fd, p, expandstr(ps4val())); 9554 fdprintf(preverrout_fd, p, expandstr(ps4val()));
9554
9555 sp = varlist.list; 9555 sp = varlist.list;
9556 for (n = 0; n < 2; n++) { 9556 for (n = 0; n < 2; n++) {
9557 while (sp) { 9557 while (sp) {
9558 fdprintf(preverrout_fd, p, sp->text); 9558 fdprintf(preverrout_fd, p, sp->text);
9559 sp = sp->next; 9559 sp = sp->next;
9560 if (*p == '%') { 9560 p = " %s";
9561 p--;
9562 }
9563 } 9561 }
9564 sp = arglist.list; 9562 sp = arglist.list;
9565 } 9563 }
diff --git a/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.right b/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.right
new file mode 100644
index 000000000..b212c246c
--- /dev/null
+++ b/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.right
@@ -0,0 +1,10 @@
1192\.168\.0\.1
2192\.168\.0\.1[
3192\.168\.0\.1[
4192\\.168\\.0\\.1[
5192\.168\.0\.1[
6192\.168\.0\.1
7192\.168\.0\.1[
8192\.168\.0\.1[
9192\\.168\\.0\\.1[
10192\.168\.0\.1[
diff --git a/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests b/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests
new file mode 100755
index 000000000..3fa2f186d
--- /dev/null
+++ b/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests
@@ -0,0 +1,21 @@
1# The bug here was triggered by:
2# * performin pathname expansion because we see [
3# * replace operator did not escape \ in replace string
4
5IP=192.168.0.1
6
7rm -f '192.168.0.1['
8echo "${IP//./\\.}"
9echo "${IP//./\\.}"'[' # bug was here
10echo "${IP//./\\.}[" # bug was here
11echo "${IP//./\\\\.}[" # bug was here
12echo "192\.168\.0\.1["
13
14echo >'192.168.0.1['
15echo "${IP//./\\.}"
16echo "${IP//./\\.}"'[' # bug was here
17echo "${IP//./\\.}[" # bug was here
18echo "${IP//./\\\\.}[" # bug was here
19echo "192\.168\.0\.1["
20
21rm -f '192.168.0.1['
diff --git a/shell/ash_test/ash-redir/redir9.right b/shell/ash_test/ash-redir/redir9.right
new file mode 100644
index 000000000..34c2512e4
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir9.right
@@ -0,0 +1,2 @@
1Ok
2Done:0
diff --git a/shell/ash_test/ash-redir/redir9.tests b/shell/ash_test/ash-redir/redir9.tests
new file mode 100644
index 000000000..8befa611c
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir9.tests
@@ -0,0 +1,4 @@
1echo Ok >file.tmp
2cat 0<>file.tmp
3echo Done:$?
4rm file.tmp
diff --git a/shell/cttyhack.c b/shell/cttyhack.c
index bde2acdc9..67736ad62 100644
--- a/shell/cttyhack.c
+++ b/shell/cttyhack.c
@@ -81,6 +81,5 @@ int cttyhack_main(int argc UNUSED_PARAM, char **argv)
81 } 81 }
82 } 82 }
83 83
84 BB_EXECVP(argv[0], argv); 84 BB_EXECVP_or_die(argv);
85 bb_perror_msg_and_die("can't execute '%s'", argv[0]);
86} 85}
diff --git a/shell/hush.c b/shell/hush.c
index 4832e2c48..c5a8ea617 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -101,6 +101,137 @@
101# define PIPE_BUF 4096 /* amount of buffering in a pipe */ 101# define PIPE_BUF 4096 /* amount of buffering in a pipe */
102#endif 102#endif
103 103
104//applet:IF_HUSH(APPLET(hush, _BB_DIR_BIN, _BB_SUID_DROP))
105//applet:IF_MSH(APPLET(msh, _BB_DIR_BIN, _BB_SUID_DROP))
106//applet:IF_LASH(APPLET(lash, _BB_DIR_BIN, _BB_SUID_DROP))
107//applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, _BB_DIR_BIN, _BB_SUID_DROP, sh))
108//applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, _BB_DIR_BIN, _BB_SUID_DROP, bash))
109
110//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
111//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
112
113//config:config HUSH
114//config: bool "hush"
115//config: default y
116//config: help
117//config: hush is a small shell (22k). It handles the normal flow control
118//config: constructs such as if/then/elif/else/fi, for/in/do/done, while loops,
119//config: case/esac. Redirections, here documents, $((arithmetic))
120//config: and functions are supported.
121//config:
122//config: It will compile and work on no-mmu systems.
123//config:
124//config: It does not handle select, aliases, brace expansion,
125//config: tilde expansion, &>file and >&file redirection of stdout+stderr.
126//config:
127//config:config HUSH_BASH_COMPAT
128//config: bool "bash-compatible extensions"
129//config: default y
130//config: depends on HUSH
131//config: help
132//config: Enable bash-compatible extensions.
133//config:
134//config:config HUSH_HELP
135//config: bool "help builtin"
136//config: default y
137//config: depends on HUSH
138//config: help
139//config: Enable help builtin in hush. Code size + ~1 kbyte.
140//config:
141//config:config HUSH_INTERACTIVE
142//config: bool "Interactive mode"
143//config: default y
144//config: depends on HUSH
145//config: help
146//config: Enable interactive mode (prompt and command editing).
147//config: Without this, hush simply reads and executes commands
148//config: from stdin just like a shell script from a file.
149//config: No prompt, no PS1/PS2 magic shell variables.
150//config:
151//config:config HUSH_JOB
152//config: bool "Job control"
153//config: default y
154//config: depends on HUSH_INTERACTIVE
155//config: help
156//config: Enable job control: Ctrl-Z backgrounds, Ctrl-C interrupts current
157//config: command (not entire shell), fg/bg builtins work. Without this option,
158//config: "cmd &" still works by simply spawning a process and immediately
159//config: prompting for next command (or executing next command in a script),
160//config: but no separate process group is formed.
161//config:
162//config:config HUSH_TICK
163//config: bool "Process substitution"
164//config: default y
165//config: depends on HUSH
166//config: help
167//config: Enable process substitution `command` and $(command) in hush.
168//config:
169//config:config HUSH_IF
170//config: bool "Support if/then/elif/else/fi"
171//config: default y
172//config: depends on HUSH
173//config: help
174//config: Enable if/then/elif/else/fi in hush.
175//config:
176//config:config HUSH_LOOPS
177//config: bool "Support for, while and until loops"
178//config: default y
179//config: depends on HUSH
180//config: help
181//config: Enable for, while and until loops in hush.
182//config:
183//config:config HUSH_CASE
184//config: bool "Support case ... esac statement"
185//config: default y
186//config: depends on HUSH
187//config: help
188//config: Enable case ... esac statement in hush. +400 bytes.
189//config:
190//config:config HUSH_FUNCTIONS
191//config: bool "Support funcname() { commands; } syntax"
192//config: default y
193//config: depends on HUSH
194//config: help
195//config: Enable support for shell functions in hush. +800 bytes.
196//config:
197//config:config HUSH_LOCAL
198//config: bool "Support local builtin"
199//config: default y
200//config: depends on HUSH_FUNCTIONS
201//config: help
202//config: Enable support for local variables in functions.
203//config:
204//config:config HUSH_RANDOM_SUPPORT
205//config: bool "Pseudorandom generator and $RANDOM variable"
206//config: default y
207//config: depends on HUSH
208//config: help
209//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
210//config: Each read of "$RANDOM" will generate a new pseudorandom value.
211//config:
212//config:config HUSH_EXPORT_N
213//config: bool "Support 'export -n' option"
214//config: default y
215//config: depends on HUSH
216//config: help
217//config: export -n unexports variables. It is a bash extension.
218//config:
219//config:config HUSH_MODE_X
220//config: bool "Support 'hush -x' option and 'set -x' command"
221//config: default y
222//config: depends on HUSH
223//config: help
224//config: This instructs hush to print commands before execution.
225//config: Adds ~300 bytes.
226//config:
227
228//usage:#define hush_trivial_usage NOUSAGE_STR
229//usage:#define hush_full_usage ""
230//usage:#define lash_trivial_usage NOUSAGE_STR
231//usage:#define lash_full_usage ""
232//usage:#define msh_trivial_usage NOUSAGE_STR
233//usage:#define msh_full_usage ""
234
104 235
105/* Build knobs */ 236/* Build knobs */
106#define LEAK_HUNTING 0 237#define LEAK_HUNTING 0
@@ -117,6 +248,10 @@
117 * and therefore waitpid will return the same result as last time) 248 * and therefore waitpid will return the same result as last time)
118 */ 249 */
119#define ENABLE_HUSH_FAST 0 250#define ENABLE_HUSH_FAST 0
251/* TODO: implement simplified code for users which do not need ${var%...} ops
252 * So far ${var%...} ops are always enabled:
253 */
254#define ENABLE_HUSH_DOLLAR_OPS 1
120 255
121 256
122#if BUILD_AS_NOMMU 257#if BUILD_AS_NOMMU
@@ -128,9 +263,7 @@
128# define USE_FOR_MMU(...) 263# define USE_FOR_MMU(...)
129#endif 264#endif
130 265
131#define SKIP_definitions 1 266#include "NUM_APPLETS.h"
132#include "applet_tables.h"
133#undef SKIP_definitions
134#if NUM_APPLETS == 1 267#if NUM_APPLETS == 1
135/* STANDALONE does not make sense, and won't compile */ 268/* STANDALONE does not make sense, and won't compile */
136# undef CONFIG_FEATURE_SH_STANDALONE 269# undef CONFIG_FEATURE_SH_STANDALONE
@@ -529,7 +662,13 @@ struct globals {
529 */ 662 */
530 smallint flag_return_in_progress; 663 smallint flag_return_in_progress;
531#endif 664#endif
532 smallint fake_mode; 665 smallint n_mode;
666#if ENABLE_HUSH_MODE_X
667 smallint x_mode;
668# define G_x_mode (G.x_mode)
669#else
670# define G_x_mode 0
671#endif
533 smallint exiting; /* used to prevent EXIT trap recursion */ 672 smallint exiting; /* used to prevent EXIT trap recursion */
534 /* These four support $?, $#, and $1 */ 673 /* These four support $?, $#, and $1 */
535 smalluint last_exitcode; 674 smalluint last_exitcode;
@@ -550,6 +689,7 @@ struct globals {
550 const char *cwd; 689 const char *cwd;
551 struct variable *top_var; /* = &G.shell_ver (set in main()) */ 690 struct variable *top_var; /* = &G.shell_ver (set in main()) */
552 struct variable shell_ver; 691 struct variable shell_ver;
692 char **expanded_assignments;
553#if ENABLE_HUSH_FUNCTIONS 693#if ENABLE_HUSH_FUNCTIONS
554 struct function *top_func; 694 struct function *top_func;
555# if ENABLE_HUSH_LOCAL 695# if ENABLE_HUSH_LOCAL
@@ -1217,7 +1357,7 @@ static void hush_exit(int exitcode)
1217 1357
1218static int check_and_run_traps(int sig) 1358static int check_and_run_traps(int sig)
1219{ 1359{
1220 static const struct timespec zero_timespec = { 0, 0 }; 1360 static const struct timespec zero_timespec;
1221 smalluint save_rcode; 1361 smalluint save_rcode;
1222 int last_sig = 0; 1362 int last_sig = 0;
1223 1363
@@ -1321,9 +1461,23 @@ static struct variable *get_local_var(const char *name)
1321 1461
1322static const char* FAST_FUNC get_local_var_value(const char *name) 1462static const char* FAST_FUNC get_local_var_value(const char *name)
1323{ 1463{
1324 struct variable **pp = get_ptr_to_local_var(name); 1464 struct variable **vpp;
1325 if (pp) 1465
1326 return strchr((*pp)->varstr, '=') + 1; 1466 if (G.expanded_assignments) {
1467 char **cpp = G.expanded_assignments;
1468 int len = strlen(name);
1469 while (*cpp) {
1470 char *cp = *cpp;
1471 if (strncmp(cp, name, len) == 0 && cp[len] == '=')
1472 return cp + len + 1;
1473 cpp++;
1474 }
1475 }
1476
1477 vpp = get_ptr_to_local_var(name);
1478 if (vpp)
1479 return strchr((*vpp)->varstr, '=') + 1;
1480
1327 if (strcmp(name, "PPID") == 0) 1481 if (strcmp(name, "PPID") == 0)
1328 return utoa(G.root_ppid); 1482 return utoa(G.root_ppid);
1329 // bash compat: UID? EUID? 1483 // bash compat: UID? EUID?
@@ -2683,7 +2837,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2683 } 2837 }
2684 } 2838 }
2685 } else if (exp_op == ':') { 2839 } else if (exp_op == ':') {
2686#if ENABLE_HUSH_BASH_COMPAT 2840#if ENABLE_HUSH_BASH_COMPAT && ENABLE_SH_MATH_SUPPORT
2687 /* It's ${var:N[:M]} bashism. 2841 /* It's ${var:N[:M]} bashism.
2688 * Note that in encoded form it has TWO parts: 2842 * Note that in encoded form it has TWO parts:
2689 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> 2843 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
@@ -2952,11 +3106,14 @@ static char* expand_strvec_to_string(char **argv)
2952static char **expand_assignments(char **argv, int count) 3106static char **expand_assignments(char **argv, int count)
2953{ 3107{
2954 int i; 3108 int i;
2955 char **p = NULL; 3109 char **p;
3110
3111 G.expanded_assignments = p = NULL;
2956 /* Expand assignments into one string each */ 3112 /* Expand assignments into one string each */
2957 for (i = 0; i < count; i++) { 3113 for (i = 0; i < count; i++) {
2958 p = add_string_to_strings(p, expand_string_to_string(argv[i])); 3114 G.expanded_assignments = p = add_string_to_strings(p, expand_string_to_string(argv[i]));
2959 } 3115 }
3116 G.expanded_assignments = NULL;
2960 return p; 3117 return p;
2961} 3118}
2962 3119
@@ -3210,15 +3367,11 @@ static void setup_heredoc(struct redir_struct *redir)
3210#if !BB_MMU 3367#if !BB_MMU
3211 to_free = NULL; 3368 to_free = NULL;
3212#endif 3369#endif
3213 pid = vfork(); 3370 pid = xvfork();
3214 if (pid < 0)
3215 bb_perror_msg_and_die("vfork");
3216 if (pid == 0) { 3371 if (pid == 0) {
3217 /* child */ 3372 /* child */
3218 disable_restore_tty_pgrp_on_exit(); 3373 disable_restore_tty_pgrp_on_exit();
3219 pid = BB_MMU ? fork() : vfork(); 3374 pid = BB_MMU ? xfork() : xvfork();
3220 if (pid < 0)
3221 bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork");
3222 if (pid != 0) 3375 if (pid != 0)
3223 _exit(0); 3376 _exit(0);
3224 /* grandchild */ 3377 /* grandchild */
@@ -3694,6 +3847,35 @@ static void execvp_or_die(char **argv)
3694 _exit(127); /* bash compat */ 3847 _exit(127); /* bash compat */
3695} 3848}
3696 3849
3850#if ENABLE_HUSH_MODE_X
3851static void dump_cmd_in_x_mode(char **argv)
3852{
3853 if (G_x_mode && argv) {
3854 /* We want to output the line in one write op */
3855 char *buf, *p;
3856 int len;
3857 int n;
3858
3859 len = 3;
3860 n = 0;
3861 while (argv[n])
3862 len += strlen(argv[n++]) + 1;
3863 buf = xmalloc(len);
3864 buf[0] = '+';
3865 p = buf + 1;
3866 n = 0;
3867 while (argv[n])
3868 p += sprintf(p, " %s", argv[n++]);
3869 *p++ = '\n';
3870 *p = '\0';
3871 fputs(buf, stderr);
3872 free(buf);
3873 }
3874}
3875#else
3876# define dump_cmd_in_x_mode(argv) ((void)0)
3877#endif
3878
3697#if BB_MMU 3879#if BB_MMU
3698#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ 3880#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
3699 pseudo_exec_argv(argv, assignment_cnt, argv_expanded) 3881 pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
@@ -3715,11 +3897,18 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
3715{ 3897{
3716 char **new_env; 3898 char **new_env;
3717 3899
3718 /* Case when we are here: ... | var=val | ... */ 3900 new_env = expand_assignments(argv, assignment_cnt);
3719 if (!argv[assignment_cnt]) 3901 dump_cmd_in_x_mode(new_env);
3902
3903 if (!argv[assignment_cnt]) {
3904 /* Case when we are here: ... | var=val | ...
3905 * (note that we do not exit early, i.e., do not optimize out
3906 * expand_assignments(): think about ... | var=`sleep 1` | ...
3907 */
3908 free_strings(new_env);
3720 _exit(EXIT_SUCCESS); 3909 _exit(EXIT_SUCCESS);
3910 }
3721 3911
3722 new_env = expand_assignments(argv, assignment_cnt);
3723#if BB_MMU 3912#if BB_MMU
3724 set_vars_and_save_old(new_env); 3913 set_vars_and_save_old(new_env);
3725 free(new_env); /* optional */ 3914 free(new_env); /* optional */
@@ -3729,6 +3918,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
3729 nommu_save->new_env = new_env; 3918 nommu_save->new_env = new_env;
3730 nommu_save->old_vars = set_vars_and_save_old(new_env); 3919 nommu_save->old_vars = set_vars_and_save_old(new_env);
3731#endif 3920#endif
3921
3732 if (argv_expanded) { 3922 if (argv_expanded) {
3733 argv = argv_expanded; 3923 argv = argv_expanded;
3734 } else { 3924 } else {
@@ -3737,6 +3927,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
3737 nommu_save->argv = argv; 3927 nommu_save->argv = argv;
3738#endif 3928#endif
3739 } 3929 }
3930 dump_cmd_in_x_mode(argv);
3740 3931
3741#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU 3932#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
3742 if (strchr(argv[0], '/') != NULL) 3933 if (strchr(argv[0], '/') != NULL)
@@ -4144,15 +4335,36 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
4144 * backgrounded: cmd & { list } & 4335 * backgrounded: cmd & { list } &
4145 * subshell: ( list ) [&] 4336 * subshell: ( list ) [&]
4146 */ 4337 */
4338#if !ENABLE_HUSH_MODE_X
4339#define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, char argv_expanded) \
4340 redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel)
4341#endif
4342static int redirect_and_varexp_helper(char ***new_env_p, struct variable **old_vars_p, struct command *command, int squirrel[3], char **argv_expanded)
4343{
4344 /* setup_redirects acts on file descriptors, not FILEs.
4345 * This is perfect for work that comes after exec().
4346 * Is it really safe for inline use? Experimentally,
4347 * things seem to work. */
4348 int rcode = setup_redirects(command, squirrel);
4349 if (rcode == 0) {
4350 char **new_env = expand_assignments(command->argv, command->assignment_cnt);
4351 *new_env_p = new_env;
4352 dump_cmd_in_x_mode(new_env);
4353 dump_cmd_in_x_mode(argv_expanded);
4354 if (old_vars_p)
4355 *old_vars_p = set_vars_and_save_old(new_env);
4356 }
4357 return rcode;
4358}
4147static NOINLINE int run_pipe(struct pipe *pi) 4359static NOINLINE int run_pipe(struct pipe *pi)
4148{ 4360{
4149 static const char *const null_ptr = NULL; 4361 static const char *const null_ptr = NULL;
4150 int i; 4362
4151 int nextin; 4363 int cmd_no;
4364 int next_infd;
4152 struct command *command; 4365 struct command *command;
4153 char **argv_expanded; 4366 char **argv_expanded;
4154 char **argv; 4367 char **argv;
4155 char *p;
4156 /* it is not always needed, but we aim to smaller code */ 4368 /* it is not always needed, but we aim to smaller code */
4157 int squirrel[] = { -1, -1, -1 }; 4369 int squirrel[] = { -1, -1, -1 };
4158 int rcode; 4370 int rcode;
@@ -4162,7 +4374,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
4162 4374
4163 IF_HUSH_JOB(pi->pgrp = -1;) 4375 IF_HUSH_JOB(pi->pgrp = -1;)
4164 pi->stopped_cmds = 0; 4376 pi->stopped_cmds = 0;
4165 command = &(pi->cmds[0]); 4377 command = &pi->cmds[0];
4166 argv_expanded = NULL; 4378 argv_expanded = NULL;
4167 4379
4168 if (pi->num_cmds != 1 4380 if (pi->num_cmds != 1
@@ -4229,29 +4441,59 @@ static NOINLINE int run_pipe(struct pipe *pi)
4229 if (argv[command->assignment_cnt] == NULL) { 4441 if (argv[command->assignment_cnt] == NULL) {
4230 /* Assignments, but no command */ 4442 /* Assignments, but no command */
4231 /* Ensure redirects take effect (that is, create files). 4443 /* Ensure redirects take effect (that is, create files).
4232 * Try "a=t >file": */ 4444 * Try "a=t >file" */
4445#if 0 /* A few cases in testsuite fail with this code. FIXME */
4446 rcode = redirect_and_varexp_helper(&new_env, /*old_vars:*/ NULL, command, squirrel, /*argv_expanded:*/ NULL);
4447 /* Set shell variables */
4448 if (new_env) {
4449 argv = new_env;
4450 while (*argv) {
4451 set_local_var(*argv, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
4452 /* Do we need to flag set_local_var() errors?
4453 * "assignment to readonly var" and "putenv error"
4454 */
4455 argv++;
4456 }
4457 }
4458 /* Redirect error sets $? to 1. Otherwise,
4459 * if evaluating assignment value set $?, retain it.
4460 * Try "false; q=`exit 2`; echo $?" - should print 2: */
4461 if (rcode == 0)
4462 rcode = G.last_exitcode;
4463 /* Exit, _skipping_ variable restoring code: */
4464 goto clean_up_and_ret0;
4465
4466#else /* Older, bigger, but more correct code */
4467
4233 rcode = setup_redirects(command, squirrel); 4468 rcode = setup_redirects(command, squirrel);
4234 restore_redirects(squirrel); 4469 restore_redirects(squirrel);
4235 /* Set shell variables */ 4470 /* Set shell variables */
4471 if (G_x_mode)
4472 bb_putchar_stderr('+');
4236 while (*argv) { 4473 while (*argv) {
4237 p = expand_string_to_string(*argv); 4474 char *p = expand_string_to_string(*argv);
4475 if (G_x_mode)
4476 fprintf(stderr, " %s", p);
4238 debug_printf_exec("set shell var:'%s'->'%s'\n", 4477 debug_printf_exec("set shell var:'%s'->'%s'\n",
4239 *argv, p); 4478 *argv, p);
4240 set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); 4479 set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
4480 /* Do we need to flag set_local_var() errors?
4481 * "assignment to readonly var" and "putenv error"
4482 */
4241 argv++; 4483 argv++;
4242 } 4484 }
4243 /* Redirect error sets $? to 1. Othervise, 4485 if (G_x_mode)
4486 bb_putchar_stderr('\n');
4487 /* Redirect error sets $? to 1. Otherwise,
4244 * if evaluating assignment value set $?, retain it. 4488 * if evaluating assignment value set $?, retain it.
4245 * Try "false; q=`exit 2`; echo $?" - should print 2: */ 4489 * Try "false; q=`exit 2`; echo $?" - should print 2: */
4246 if (rcode == 0) 4490 if (rcode == 0)
4247 rcode = G.last_exitcode; 4491 rcode = G.last_exitcode;
4248 /* Do we need to flag set_local_var() errors?
4249 * "assignment to readonly var" and "putenv error"
4250 */
4251 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) 4492 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
4252 debug_leave(); 4493 debug_leave();
4253 debug_printf_exec("run_pipe: return %d\n", rcode); 4494 debug_printf_exec("run_pipe: return %d\n", rcode);
4254 return rcode; 4495 return rcode;
4496#endif
4255 } 4497 }
4256 4498
4257 /* Expand the rest into (possibly) many strings each */ 4499 /* Expand the rest into (possibly) many strings each */
@@ -4292,14 +4534,8 @@ static NOINLINE int run_pipe(struct pipe *pi)
4292 goto clean_up_and_ret1; 4534 goto clean_up_and_ret1;
4293 } 4535 }
4294 } 4536 }
4295 /* setup_redirects acts on file descriptors, not FILEs. 4537 rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded);
4296 * This is perfect for work that comes after exec().
4297 * Is it really safe for inline use? Experimentally,
4298 * things seem to work. */
4299 rcode = setup_redirects(command, squirrel);
4300 if (rcode == 0) { 4538 if (rcode == 0) {
4301 new_env = expand_assignments(argv, command->assignment_cnt);
4302 old_vars = set_vars_and_save_old(new_env);
4303 if (!funcp) { 4539 if (!funcp) {
4304 debug_printf_exec(": builtin '%s' '%s'...\n", 4540 debug_printf_exec(": builtin '%s' '%s'...\n",
4305 x->b_cmd, argv_expanded[1]); 4541 x->b_cmd, argv_expanded[1]);
@@ -4322,12 +4558,11 @@ static NOINLINE int run_pipe(struct pipe *pi)
4322 } 4558 }
4323#endif 4559#endif
4324 } 4560 }
4325#if ENABLE_FEATURE_SH_STANDALONE
4326 clean_up_and_ret: 4561 clean_up_and_ret:
4327#endif
4328 restore_redirects(squirrel);
4329 unset_vars(new_env); 4562 unset_vars(new_env);
4330 add_vars(old_vars); 4563 add_vars(old_vars);
4564/* clean_up_and_ret0: */
4565 restore_redirects(squirrel);
4331 clean_up_and_ret1: 4566 clean_up_and_ret1:
4332 free(argv_expanded); 4567 free(argv_expanded);
4333 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) 4568 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
@@ -4336,20 +4571,18 @@ static NOINLINE int run_pipe(struct pipe *pi)
4336 return rcode; 4571 return rcode;
4337 } 4572 }
4338 4573
4339#if ENABLE_FEATURE_SH_STANDALONE 4574 if (ENABLE_FEATURE_SH_STANDALONE) {
4340 i = find_applet_by_name(argv_expanded[0]); 4575 int n = find_applet_by_name(argv_expanded[0]);
4341 if (i >= 0 && APPLET_IS_NOFORK(i)) { 4576 if (n >= 0 && APPLET_IS_NOFORK(n)) {
4342 rcode = setup_redirects(command, squirrel); 4577 rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded);
4343 if (rcode == 0) { 4578 if (rcode == 0) {
4344 new_env = expand_assignments(argv, command->assignment_cnt); 4579 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
4345 old_vars = set_vars_and_save_old(new_env); 4580 argv_expanded[0], argv_expanded[1]);
4346 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", 4581 rcode = run_nofork_applet(n, argv_expanded);
4347 argv_expanded[0], argv_expanded[1]); 4582 }
4348 rcode = run_nofork_applet(i, argv_expanded); 4583 goto clean_up_and_ret;
4349 } 4584 }
4350 goto clean_up_and_ret;
4351 } 4585 }
4352#endif
4353 /* It is neither builtin nor applet. We must fork. */ 4586 /* It is neither builtin nor applet. We must fork. */
4354 } 4587 }
4355 4588
@@ -4360,9 +4593,10 @@ static NOINLINE int run_pipe(struct pipe *pi)
4360 4593
4361 /* Going to fork a child per each pipe member */ 4594 /* Going to fork a child per each pipe member */
4362 pi->alive_cmds = 0; 4595 pi->alive_cmds = 0;
4363 nextin = 0; 4596 next_infd = 0;
4364 4597
4365 for (i = 0; i < pi->num_cmds; i++) { 4598 cmd_no = 0;
4599 while (cmd_no < pi->num_cmds) {
4366 struct fd_pair pipefds; 4600 struct fd_pair pipefds;
4367#if !BB_MMU 4601#if !BB_MMU
4368 volatile nommu_save_t nommu_save; 4602 volatile nommu_save_t nommu_save;
@@ -4371,7 +4605,8 @@ static NOINLINE int run_pipe(struct pipe *pi)
4371 nommu_save.argv = NULL; 4605 nommu_save.argv = NULL;
4372 nommu_save.argv_from_re_execing = NULL; 4606 nommu_save.argv_from_re_execing = NULL;
4373#endif 4607#endif
4374 command = &(pi->cmds[i]); 4608 command = &pi->cmds[cmd_no];
4609 cmd_no++;
4375 if (command->argv) { 4610 if (command->argv) {
4376 debug_printf_exec(": pipe member '%s' '%s'...\n", 4611 debug_printf_exec(": pipe member '%s' '%s'...\n",
4377 command->argv[0], command->argv[1]); 4612 command->argv[0], command->argv[1]);
@@ -4382,7 +4617,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
4382 /* pipes are inserted between pairs of commands */ 4617 /* pipes are inserted between pairs of commands */
4383 pipefds.rd = 0; 4618 pipefds.rd = 0;
4384 pipefds.wr = 1; 4619 pipefds.wr = 1;
4385 if ((i + 1) < pi->num_cmds) 4620 if (cmd_no < pi->num_cmds)
4386 xpiped_pair(pipefds); 4621 xpiped_pair(pipefds);
4387 4622
4388 command->pid = BB_MMU ? fork() : vfork(); 4623 command->pid = BB_MMU ? fork() : vfork();
@@ -4415,7 +4650,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
4415 if (open(bb_dev_null, O_RDONLY)) 4650 if (open(bb_dev_null, O_RDONLY))
4416 xopen("/", O_RDONLY); 4651 xopen("/", O_RDONLY);
4417 } else { 4652 } else {
4418 xmove_fd(nextin, 0); 4653 xmove_fd(next_infd, 0);
4419 } 4654 }
4420 xmove_fd(pipefds.wr, 1); 4655 xmove_fd(pipefds.wr, 1);
4421 if (pipefds.rd > 1) 4656 if (pipefds.rd > 1)
@@ -4452,7 +4687,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
4452 argv_expanded = NULL; 4687 argv_expanded = NULL;
4453 if (command->pid < 0) { /* [v]fork failed */ 4688 if (command->pid < 0) { /* [v]fork failed */
4454 /* Clearly indicate, was it fork or vfork */ 4689 /* Clearly indicate, was it fork or vfork */
4455 bb_perror_msg(BB_MMU ? "fork" : "vfork"); 4690 bb_perror_msg(BB_MMU ? "vfork"+1 : "vfork");
4456 } else { 4691 } else {
4457 pi->alive_cmds++; 4692 pi->alive_cmds++;
4458#if ENABLE_HUSH_JOB 4693#if ENABLE_HUSH_JOB
@@ -4462,12 +4697,12 @@ static NOINLINE int run_pipe(struct pipe *pi)
4462#endif 4697#endif
4463 } 4698 }
4464 4699
4465 if (i) 4700 if (cmd_no > 1)
4466 close(nextin); 4701 close(next_infd);
4467 if ((i + 1) < pi->num_cmds) 4702 if (cmd_no < pi->num_cmds)
4468 close(pipefds.wr); 4703 close(pipefds.wr);
4469 /* Pass read (output) pipe end to next iteration */ 4704 /* Pass read (output) pipe end to next iteration */
4470 nextin = pipefds.rd; 4705 next_infd = pipefds.rd;
4471 } 4706 }
4472 4707
4473 if (!pi->alive_cmds) { 4708 if (!pi->alive_cmds) {
@@ -4899,7 +5134,7 @@ static int run_and_free_list(struct pipe *pi)
4899{ 5134{
4900 int rcode = 0; 5135 int rcode = 0;
4901 debug_printf_exec("run_and_free_list entered\n"); 5136 debug_printf_exec("run_and_free_list entered\n");
4902 if (!G.fake_mode) { 5137 if (!G.n_mode) {
4903 debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds); 5138 debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds);
4904 rcode = run_list(pi); 5139 rcode = run_list(pi);
4905 } 5140 }
@@ -5588,10 +5823,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
5588# endif 5823# endif
5589 5824
5590 xpipe(channel); 5825 xpipe(channel);
5591 pid = BB_MMU ? fork() : vfork(); 5826 pid = BB_MMU ? xfork() : xvfork();
5592 if (pid < 0)
5593 bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork");
5594
5595 if (pid == 0) { /* child */ 5827 if (pid == 0) { /* child */
5596 disable_restore_tty_pgrp_on_exit(); 5828 disable_restore_tty_pgrp_on_exit();
5597 /* Process substitution is not considered to be usual 5829 /* Process substitution is not considered to be usual
@@ -5829,7 +6061,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
5829 /* command remains "open", available for possible redirects */ 6061 /* command remains "open", available for possible redirects */
5830} 6062}
5831 6063
5832#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT 6064#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS
5833/* Subroutines for copying $(...) and `...` things */ 6065/* Subroutines for copying $(...) and `...` things */
5834static void add_till_backquote(o_string *dest, struct in_str *input); 6066static void add_till_backquote(o_string *dest, struct in_str *input);
5835/* '...' */ 6067/* '...' */
@@ -5930,9 +6162,9 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
5930{ 6162{
5931 int ch; 6163 int ch;
5932 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; 6164 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
5933#if ENABLE_HUSH_BASH_COMPAT 6165# if ENABLE_HUSH_BASH_COMPAT
5934 char end_char2 = end_ch >> 8; 6166 char end_char2 = end_ch >> 8;
5935#endif 6167# endif
5936 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); 6168 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
5937 6169
5938 while (1) { 6170 while (1) {
@@ -5985,7 +6217,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
5985 } 6217 }
5986 return ch; 6218 return ch;
5987} 6219}
5988#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */ 6220#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS */
5989 6221
5990/* Return code: 0 for OK, 1 for syntax error */ 6222/* Return code: 0 for OK, 1 for syntax error */
5991#if BB_MMU 6223#if BB_MMU
@@ -6091,7 +6323,11 @@ static int parse_dollar(o_string *as_string,
6091 again: 6323 again:
6092 if (!BB_MMU) 6324 if (!BB_MMU)
6093 pos = dest->length; 6325 pos = dest->length;
6326#if ENABLE_HUSH_DOLLAR_OPS
6094 last_ch = add_till_closing_bracket(dest, input, end_ch); 6327 last_ch = add_till_closing_bracket(dest, input, end_ch);
6328#else
6329#error Simple code to only allow ${var} is not implemented
6330#endif
6095 if (as_string) { 6331 if (as_string) {
6096 o_addstr(as_string, dest->data + pos); 6332 o_addstr(as_string, dest->data + pos);
6097 o_addchr(as_string, last_ch); 6333 o_addchr(as_string, last_ch);
@@ -6924,8 +7160,8 @@ static int set_mode(const char cstate, const char mode)
6924{ 7160{
6925 int state = (cstate == '-' ? 1 : 0); 7161 int state = (cstate == '-' ? 1 : 0);
6926 switch (mode) { 7162 switch (mode) {
6927 case 'n': G.fake_mode = state; break; 7163 case 'n': G.n_mode = state; break;
6928 case 'x': /*G.debug_mode = state;*/ break; 7164 case 'x': IF_HUSH_MODE_X(G_x_mode = state;) break;
6929 default: return EXIT_FAILURE; 7165 default: return EXIT_FAILURE;
6930 } 7166 }
6931 return EXIT_SUCCESS; 7167 return EXIT_SUCCESS;
@@ -7154,7 +7390,7 @@ int hush_main(int argc, char **argv)
7154#endif 7390#endif
7155 case 'n': 7391 case 'n':
7156 case 'x': 7392 case 'x':
7157 if (!set_mode('-', opt)) 7393 if (set_mode('-', opt) == 0) /* no error */
7158 break; 7394 break;
7159 default: 7395 default:
7160#ifndef BB_VER 7396#ifndef BB_VER
diff --git a/shell/hush_test/hush-vars/var_serial.right b/shell/hush_test/hush-vars/var_serial.right
new file mode 100644
index 000000000..42aa33057
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_serial.right
@@ -0,0 +1,5 @@
1Assignments only: c=a
2Assignments and a command: c=a
3Assignments and a builtin: c=a
4Assignments and a function: c=a
5Done
diff --git a/shell/hush_test/hush-vars/var_serial.tests b/shell/hush_test/hush-vars/var_serial.tests
new file mode 100755
index 000000000..6b4a4cdf7
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_serial.tests
@@ -0,0 +1,22 @@
1a=a
2
3b=b
4c=c
5# Second assignment depends on the first:
6b=$a c=$b
7echo Assignments only: c=$c
8
9b=b
10c=c
11b=$a c=$b "$THIS_SH" -c 'echo Assignments and a command: c=$c'
12
13b=b
14c=c
15b=$a c=$b eval 'echo Assignments and a builtin: c=$c'
16
17b=b
18c=c
19f() { echo Assignments and a function: c=$c; }
20b=$a c=$b f
21
22echo Done