diff options
author | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-14 13:08:20 +1000 |
---|---|---|
committer | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-14 13:08:20 +1000 |
commit | 6a6efd31038d7afe977e3059508ae863e65cbdf5 (patch) | |
tree | 5cd69a751e893b83176751c80fcea7a7afeed1ae /shell | |
parent | a6a2325ecf402054132daae169f71edb0fb849e3 (diff) | |
parent | 29082231d0cb1a5b327de5d515b16f332d4dbdaf (diff) | |
download | busybox-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.src | 106 | ||||
-rw-r--r-- | shell/Kbuild.src | 2 | ||||
-rw-r--r-- | shell/ash.c | 48 | ||||
-rw-r--r-- | shell/ash_test/ash-quoting/dollar_repl_slash_bash1.right | 10 | ||||
-rwxr-xr-x | shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests | 21 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir9.right | 2 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir9.tests | 4 | ||||
-rw-r--r-- | shell/cttyhack.c | 3 | ||||
-rw-r--r-- | shell/hush.c | 384 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/var_serial.right | 5 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/var_serial.tests | 22 |
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 | ||
113 | config 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 | |||
127 | config HUSH_BASH_COMPAT | ||
128 | bool "bash-compatible extensions" | ||
129 | default y | ||
130 | depends on HUSH | ||
131 | help | ||
132 | Enable bash-compatible extensions. | ||
133 | |||
134 | config 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 | |||
141 | config 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 | |||
151 | config 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 | |||
162 | config 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 | |||
169 | config 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 | |||
176 | config 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 | |||
183 | config 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 | |||
190 | config 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 | |||
197 | config 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 | |||
204 | config 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 | |||
211 | config 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 | ||
220 | choice | 114 | choice |
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:= | |||
9 | INSERT | 9 | INSERT |
10 | 10 | ||
11 | lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o | 11 | lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o |
12 | lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o | ||
13 | lib-$(CONFIG_CTTYHACK) += cttyhack.o | 12 | lib-$(CONFIG_CTTYHACK) += cttyhack.o |
14 | 13 | ||
15 | lib-$(CONFIG_SH_MATH_SUPPORT) += math.o | 14 | lib-$(CONFIG_SH_MATH_SUPPORT) += math.o |
16 | lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o | 15 | lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o |
17 | lib-$(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 | ||
7174 | static char *expdir; | ||
7175 | |||
7176 | /* | 7178 | /* |
7177 | * Do metacharacter (i.e. *, ?, [...]) expansion. | 7179 | * Do metacharacter (i.e. *, ?, [...]) expansion. |
7178 | */ | 7180 | */ |
7179 | static void | 7181 | static void |
7180 | expmeta(char *enddir, char *name) | 7182 | expmeta(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 @@ | |||
1 | 192\.168\.0\.1 | ||
2 | 192\.168\.0\.1[ | ||
3 | 192\.168\.0\.1[ | ||
4 | 192\\.168\\.0\\.1[ | ||
5 | 192\.168\.0\.1[ | ||
6 | 192\.168\.0\.1 | ||
7 | 192\.168\.0\.1[ | ||
8 | 192\.168\.0\.1[ | ||
9 | 192\\.168\\.0\\.1[ | ||
10 | 192\.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 | |||
5 | IP=192.168.0.1 | ||
6 | |||
7 | rm -f '192.168.0.1[' | ||
8 | echo "${IP//./\\.}" | ||
9 | echo "${IP//./\\.}"'[' # bug was here | ||
10 | echo "${IP//./\\.}[" # bug was here | ||
11 | echo "${IP//./\\\\.}[" # bug was here | ||
12 | echo "192\.168\.0\.1[" | ||
13 | |||
14 | echo >'192.168.0.1[' | ||
15 | echo "${IP//./\\.}" | ||
16 | echo "${IP//./\\.}"'[' # bug was here | ||
17 | echo "${IP//./\\.}[" # bug was here | ||
18 | echo "${IP//./\\\\.}[" # bug was here | ||
19 | echo "192\.168\.0\.1[" | ||
20 | |||
21 | rm -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 @@ | |||
1 | Ok | ||
2 | Done: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 @@ | |||
1 | echo Ok >file.tmp | ||
2 | cat 0<>file.tmp | ||
3 | echo Done:$? | ||
4 | rm 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 | ||
1218 | static int check_and_run_traps(int sig) | 1358 | static 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 | ||
1322 | static const char* FAST_FUNC get_local_var_value(const char *name) | 1462 | static 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) | |||
2952 | static char **expand_assignments(char **argv, int count) | 3106 | static 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 | ||
3851 | static 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 | ||
4342 | static 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 | } | ||
4147 | static NOINLINE int run_pipe(struct pipe *pi) | 4359 | static 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 */ |
5834 | static void add_till_backquote(o_string *dest, struct in_str *input); | 6066 | static 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 @@ | |||
1 | Assignments only: c=a | ||
2 | Assignments and a command: c=a | ||
3 | Assignments and a builtin: c=a | ||
4 | Assignments and a function: c=a | ||
5 | Done | ||
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 @@ | |||
1 | a=a | ||
2 | |||
3 | b=b | ||
4 | c=c | ||
5 | # Second assignment depends on the first: | ||
6 | b=$a c=$b | ||
7 | echo Assignments only: c=$c | ||
8 | |||
9 | b=b | ||
10 | c=c | ||
11 | b=$a c=$b "$THIS_SH" -c 'echo Assignments and a command: c=$c' | ||
12 | |||
13 | b=b | ||
14 | c=c | ||
15 | b=$a c=$b eval 'echo Assignments and a builtin: c=$c' | ||
16 | |||
17 | b=b | ||
18 | c=c | ||
19 | f() { echo Assignments and a function: c=$c; } | ||
20 | b=$a c=$b f | ||
21 | |||
22 | echo Done | ||