diff options
author | Ron Yorston <rmy@pobox.com> | 2017-11-03 14:16:08 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2017-11-03 14:16:08 +0000 |
commit | d6ce08aeb85b3698ddaa281016b70e16aeb9fb35 (patch) | |
tree | 02ad9bc0684859515fe891f3d6b0a1086e0db156 /shell | |
parent | ab450021a99ba66126cc6d668fb06ec3829a572b (diff) | |
parent | a5060b8364faa7c677c8950f1315c451403b0660 (diff) | |
download | busybox-w32-d6ce08aeb85b3698ddaa281016b70e16aeb9fb35.tar.gz busybox-w32-d6ce08aeb85b3698ddaa281016b70e16aeb9fb35.tar.bz2 busybox-w32-d6ce08aeb85b3698ddaa281016b70e16aeb9fb35.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
-rw-r--r-- | shell/ash.c | 105 | ||||
-rw-r--r-- | shell/ash_test/ash-parsing/comment2.right | 4 | ||||
-rwxr-xr-x | shell/ash_test/ash-parsing/comment2.tests | 13 | ||||
-rw-r--r-- | shell/ash_test/ash-standalone/nofork_env.right | 9 | ||||
-rwxr-xr-x | shell/ash_test/ash-standalone/nofork_env.tests | 15 | ||||
-rw-r--r-- | shell/hush.c | 17 | ||||
-rw-r--r-- | shell/hush_test/hush-parsing/comment2.right | 4 | ||||
-rwxr-xr-x | shell/hush_test/hush-parsing/comment2.tests | 13 | ||||
-rw-r--r-- | shell/hush_test/hush-standalone/nofork_env.right | 9 | ||||
-rwxr-xr-x | shell/hush_test/hush-standalone/nofork_env.tests | 15 |
10 files changed, 179 insertions, 25 deletions
diff --git a/shell/ash.c b/shell/ash.c index 0d65a225b..81845dc60 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -1399,16 +1399,9 @@ ash_msg_and_raise_error(const char *msg, ...) | |||
1399 | } | 1399 | } |
1400 | 1400 | ||
1401 | /* | 1401 | /* |
1402 | * Use '%m' to append error string on platforms that support it, '%s' and | ||
1403 | * strerror() on those that don't. | ||
1404 | * | ||
1405 | * 'fmt' must be a string literal. | 1402 | * 'fmt' must be a string literal. |
1406 | */ | 1403 | */ |
1407 | #ifdef HAVE_PRINTF_PERCENTM | 1404 | #define ash_msg_and_raise_perror(fmt, ...) ash_msg_and_raise_error(fmt ": "STRERROR_FMT, ##__VA_ARGS__ STRERROR_ERRNO) |
1408 | #define ash_msg_and_raise_perror(fmt, ...) ash_msg_and_raise_error(fmt ": %m", ##__VA_ARGS__) | ||
1409 | #else | ||
1410 | #define ash_msg_and_raise_perror(fmt, ...) ash_msg_and_raise_error(fmt ": %s", ##__VA_ARGS__, strerror(errno)) | ||
1411 | #endif | ||
1412 | 1405 | ||
1413 | static void raise_error_syntax(const char *) NORETURN; | 1406 | static void raise_error_syntax(const char *) NORETURN; |
1414 | static void | 1407 | static void |
@@ -2474,8 +2467,11 @@ listsetvar(struct strlist *list_set_var, int flags) | |||
2474 | /* | 2467 | /* |
2475 | * Generate a list of variables satisfying the given conditions. | 2468 | * Generate a list of variables satisfying the given conditions. |
2476 | */ | 2469 | */ |
2470 | #if !ENABLE_FEATURE_SH_NOFORK | ||
2471 | # define listvars(on, off, lp, end) listvars(on, off, end) | ||
2472 | #endif | ||
2477 | static char ** | 2473 | static char ** |
2478 | listvars(int on, int off, char ***end) | 2474 | listvars(int on, int off, struct strlist *lp, char ***end) |
2479 | { | 2475 | { |
2480 | struct var **vpp; | 2476 | struct var **vpp; |
2481 | struct var *vp; | 2477 | struct var *vp; |
@@ -2488,12 +2484,40 @@ listvars(int on, int off, char ***end) | |||
2488 | do { | 2484 | do { |
2489 | for (vp = *vpp; vp; vp = vp->next) { | 2485 | for (vp = *vpp; vp; vp = vp->next) { |
2490 | if ((vp->flags & mask) == on) { | 2486 | if ((vp->flags & mask) == on) { |
2487 | #if ENABLE_FEATURE_SH_NOFORK | ||
2488 | /* If variable with the same name is both | ||
2489 | * exported and temporarily set for a command: | ||
2490 | * export ZVAR=5 | ||
2491 | * ZVAR=6 printenv | ||
2492 | * then "ZVAR=6" will be both in vartab and | ||
2493 | * lp lists. Do not pass it twice to printenv. | ||
2494 | */ | ||
2495 | struct strlist *lp1 = lp; | ||
2496 | while (lp1) { | ||
2497 | if (strcmp(lp1->text, vp->var_text) == 0) | ||
2498 | goto skip; | ||
2499 | lp1 = lp1->next; | ||
2500 | } | ||
2501 | #endif | ||
2491 | if (ep == stackstrend()) | 2502 | if (ep == stackstrend()) |
2492 | ep = growstackstr(); | 2503 | ep = growstackstr(); |
2493 | *ep++ = (char*)vp->var_text; | 2504 | *ep++ = (char*)vp->var_text; |
2505 | #if ENABLE_FEATURE_SH_NOFORK | ||
2506 | skip: ; | ||
2507 | #endif | ||
2494 | } | 2508 | } |
2495 | } | 2509 | } |
2496 | } while (++vpp < vartab + VTABSIZE); | 2510 | } while (++vpp < vartab + VTABSIZE); |
2511 | |||
2512 | #if ENABLE_FEATURE_SH_NOFORK | ||
2513 | while (lp) { | ||
2514 | if (ep == stackstrend()) | ||
2515 | ep = growstackstr(); | ||
2516 | *ep++ = lp->text; | ||
2517 | lp = lp->next; | ||
2518 | } | ||
2519 | #endif | ||
2520 | |||
2497 | if (ep == stackstrend()) | 2521 | if (ep == stackstrend()) |
2498 | ep = growstackstr(); | 2522 | ep = growstackstr(); |
2499 | if (end) | 2523 | if (end) |
@@ -8250,7 +8274,7 @@ static void shellexec(char *prog, char **argv, const char *path, int idx) | |||
8250 | int exerrno; | 8274 | int exerrno; |
8251 | int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ | 8275 | int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ |
8252 | 8276 | ||
8253 | envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL); | 8277 | envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL); |
8254 | if ((strchr(prog, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(prog, '\\'))) | 8278 | if ((strchr(prog, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(prog, '\\'))) |
8255 | #if ENABLE_FEATURE_SH_STANDALONE | 8279 | #if ENABLE_FEATURE_SH_STANDALONE |
8256 | || (applet_no = find_applet_by_name(prog)) >= 0 | 8280 | || (applet_no = find_applet_by_name(prog)) >= 0 |
@@ -10395,7 +10419,11 @@ evalcommand(union node *cmd, int flags) | |||
10395 | /* find_command() encodes applet_no as (-2 - applet_no) */ | 10419 | /* find_command() encodes applet_no as (-2 - applet_no) */ |
10396 | int applet_no = (- cmdentry.u.index - 2); | 10420 | int applet_no = (- cmdentry.u.index - 2); |
10397 | if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) { | 10421 | if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) { |
10398 | listsetvar(varlist.list, VEXPORT|VSTACK); | 10422 | char **sv_environ; |
10423 | |||
10424 | INT_OFF; | ||
10425 | sv_environ = environ; | ||
10426 | environ = listvars(VEXPORT, VUNSET, varlist.list, /*end:*/ NULL); | ||
10399 | /* | 10427 | /* |
10400 | * Run <applet>_main(). | 10428 | * Run <applet>_main(). |
10401 | * Signals (^C) can't interrupt here. | 10429 | * Signals (^C) can't interrupt here. |
@@ -10404,8 +10432,8 @@ evalcommand(union node *cmd, int flags) | |||
10404 | * and/or wait for user input ineligible for NOFORK: | 10432 | * and/or wait for user input ineligible for NOFORK: |
10405 | * for example, "yes" or "rm" (rm -i waits for input). | 10433 | * for example, "yes" or "rm" (rm -i waits for input). |
10406 | */ | 10434 | */ |
10407 | INT_OFF; | ||
10408 | status = run_nofork_applet(applet_no, argv); | 10435 | status = run_nofork_applet(applet_no, argv); |
10436 | environ = sv_environ; | ||
10409 | /* | 10437 | /* |
10410 | * Try enabling NOFORK for "yes" applet. | 10438 | * Try enabling NOFORK for "yes" applet. |
10411 | * ^C _will_ stop it (write returns EINTR), | 10439 | * ^C _will_ stop it (write returns EINTR), |
@@ -11344,7 +11372,7 @@ showvars(const char *sep_prefix, int on, int off) | |||
11344 | const char *sep; | 11372 | const char *sep; |
11345 | char **ep, **epend; | 11373 | char **ep, **epend; |
11346 | 11374 | ||
11347 | ep = listvars(on, off, &epend); | 11375 | ep = listvars(on, off, /*strlist:*/ NULL, &epend); |
11348 | qsort(ep, epend - ep, sizeof(char *), vpcmp); | 11376 | qsort(ep, epend - ep, sizeof(char *), vpcmp); |
11349 | 11377 | ||
11350 | sep = *sep_prefix ? " " : sep_prefix; | 11378 | sep = *sep_prefix ? " " : sep_prefix; |
@@ -11353,9 +11381,17 @@ showvars(const char *sep_prefix, int on, int off) | |||
11353 | const char *p; | 11381 | const char *p; |
11354 | const char *q; | 11382 | const char *q; |
11355 | 11383 | ||
11356 | p = strchrnul(*ep, '='); | 11384 | p = endofname(*ep); |
11385 | /* Used to have simple "p = strchrnul(*ep, '=')" here instead, but this | ||
11386 | * makes "export -p" to have output not suitable for "eval": | ||
11387 | * import os | ||
11388 | * os.environ["test-test"]="test" | ||
11389 | * if os.fork() == 0: | ||
11390 | * os.execv("ash", [ 'ash', '-c', 'eval $(export -p); echo OK' ]) # fixes this | ||
11391 | * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) | ||
11392 | */ | ||
11357 | q = nullstr; | 11393 | q = nullstr; |
11358 | if (*p) | 11394 | if (*p == '=') |
11359 | q = single_quote(++p); | 11395 | q = single_quote(++p); |
11360 | out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q); | 11396 | out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q); |
11361 | } | 11397 | } |
@@ -13146,7 +13182,24 @@ expandstr(const char *ps, int syntax_type) | |||
13146 | 13182 | ||
13147 | saveprompt = doprompt; | 13183 | saveprompt = doprompt; |
13148 | doprompt = 0; | 13184 | doprompt = 0; |
13149 | readtoken1(pgetc(), syntax_type, FAKEEOFMARK, 0); | 13185 | |
13186 | /* readtoken1() might die horribly. | ||
13187 | * Try a prompt with syntactically wrong command: | ||
13188 | * PS1='$(date "+%H:%M:%S) > ' | ||
13189 | */ | ||
13190 | { | ||
13191 | volatile int saveint; | ||
13192 | struct jmploc *volatile savehandler = exception_handler; | ||
13193 | struct jmploc jmploc; | ||
13194 | SAVE_INT(saveint); | ||
13195 | if (setjmp(jmploc.loc) == 0) { | ||
13196 | exception_handler = &jmploc; | ||
13197 | readtoken1(pgetc(), syntax_type, FAKEEOFMARK, 0); | ||
13198 | } | ||
13199 | exception_handler = savehandler; | ||
13200 | RESTORE_INT(saveint); | ||
13201 | } | ||
13202 | |||
13150 | doprompt = saveprompt; | 13203 | doprompt = saveprompt; |
13151 | 13204 | ||
13152 | popfile(); | 13205 | popfile(); |
@@ -13215,7 +13268,7 @@ evalstring(char *s, int flags) | |||
13215 | 13268 | ||
13216 | exception_handler = savehandler; | 13269 | exception_handler = savehandler; |
13217 | if (ex) | 13270 | if (ex) |
13218 | longjmp(exception_handler->loc, ex); | 13271 | longjmp(exception_handler->loc, ex); |
13219 | 13272 | ||
13220 | return status; | 13273 | return status; |
13221 | } | 13274 | } |
@@ -14069,8 +14122,8 @@ umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
14069 | } | 14122 | } |
14070 | } else { | 14123 | } else { |
14071 | char *modestr = *argptr; | 14124 | char *modestr = *argptr; |
14072 | /* numeric umasks are taken as-is */ | 14125 | /* numeric umasks are taken as-is */ |
14073 | /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */ | 14126 | /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */ |
14074 | if (!isdigit(modestr[0])) | 14127 | if (!isdigit(modestr[0])) |
14075 | mask ^= 0777; | 14128 | mask ^= 0777; |
14076 | mask = bb_parse_mode(modestr, mask); | 14129 | mask = bb_parse_mode(modestr, mask); |
@@ -14236,8 +14289,18 @@ init(void) | |||
14236 | } | 14289 | } |
14237 | #endif | 14290 | #endif |
14238 | for (envp = environ; envp && *envp; envp++) { | 14291 | for (envp = environ; envp && *envp; envp++) { |
14239 | p = endofname(*envp); | 14292 | /* Used to have |
14240 | if (p != *envp && *p == '=') { | 14293 | * p = endofname(*envp); |
14294 | * if (p != *envp && *p == '=') { | ||
14295 | * here to weed out badly-named variables, but this breaks | ||
14296 | * scenarios where people do want them passed to children: | ||
14297 | * import os | ||
14298 | * os.environ["test-test"]="test" | ||
14299 | * if os.fork() == 0: | ||
14300 | * os.execv("ash", [ 'ash', '-c', 'eval $(export -p); echo OK' ]) # fixes this | ||
14301 | * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this | ||
14302 | */ | ||
14303 | if (strchr(*envp, '=')) { | ||
14241 | setvareq(*envp, VEXPORT|VTEXTFIXED); | 14304 | setvareq(*envp, VEXPORT|VTEXTFIXED); |
14242 | } | 14305 | } |
14243 | } | 14306 | } |
diff --git a/shell/ash_test/ash-parsing/comment2.right b/shell/ash_test/ash-parsing/comment2.right new file mode 100644 index 000000000..ee6e49a5a --- /dev/null +++ b/shell/ash_test/ash-parsing/comment2.right | |||
@@ -0,0 +1,4 @@ | |||
1 | Ok1 | ||
2 | Ok2 | ||
3 | Ok5 | ||
4 | Ok6 | ||
diff --git a/shell/ash_test/ash-parsing/comment2.tests b/shell/ash_test/ash-parsing/comment2.tests new file mode 100755 index 000000000..b7adad96a --- /dev/null +++ b/shell/ash_test/ash-parsing/comment2.tests | |||
@@ -0,0 +1,13 @@ | |||
1 | echo "`echo Ok1 #comment is ignored`" | ||
2 | echo `echo Ok2 #comment is ignored` | ||
3 | # | ||
4 | # Surprisingly, bash does not handle comments in $() | ||
5 | # the same way as in ``. "#" causes the rest of the line, _including_ )", | ||
6 | # to be ignored. These lines would cause an error: | ||
7 | #echo "$(echo Ok3 #comment is ignored)" | ||
8 | #echo $(echo Ok4 #comment is ignored) | ||
9 | # | ||
10 | echo "$(echo Ok5 #comment is ignored | ||
11 | )" | ||
12 | echo $(echo Ok6 #comment is ignored | ||
13 | ) | ||
diff --git a/shell/ash_test/ash-standalone/nofork_env.right b/shell/ash_test/ash-standalone/nofork_env.right new file mode 100644 index 000000000..3f16ff458 --- /dev/null +++ b/shell/ash_test/ash-standalone/nofork_env.right | |||
@@ -0,0 +1,9 @@ | |||
1 | ZVAR=1 | ||
2 | ZVAR=2 | ||
3 | ZVAR=3 | ||
4 | ZVAR=4 | ||
5 | ZVAR=5 | ||
6 | ZVAR=6 | ||
7 | ZVAR=7 | ||
8 | ZVAR=8 | ||
9 | Ok:0 | ||
diff --git a/shell/ash_test/ash-standalone/nofork_env.tests b/shell/ash_test/ash-standalone/nofork_env.tests new file mode 100755 index 000000000..111e564d2 --- /dev/null +++ b/shell/ash_test/ash-standalone/nofork_env.tests | |||
@@ -0,0 +1,15 @@ | |||
1 | # ash had a bug where NOFORKed applet (env/printenv) was not seeing new exported variables | ||
2 | |||
3 | (export ZVAR=1; printenv) | grep ^ZVAR= | ||
4 | (ZVAR=2 printenv) | grep ^ZVAR= | ||
5 | |||
6 | (export ZVAR=3; env) | grep ^ZVAR= | ||
7 | (ZVAR=4 env) | grep ^ZVAR= | ||
8 | |||
9 | export ZVAR=5; printenv | grep ^ZVAR= | ||
10 | ZVAR=6 printenv | grep ^ZVAR= | ||
11 | |||
12 | export ZVAR=7; env | grep ^ZVAR= | ||
13 | ZVAR=8 env | grep ^ZVAR= | ||
14 | |||
15 | echo Ok:$? | ||
diff --git a/shell/hush.c b/shell/hush.c index d27550ba0..708555ac4 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -5140,14 +5140,23 @@ static struct pipe *parse_stream(char **pstring, | |||
5140 | case '#': | 5140 | case '#': |
5141 | if (dest.length == 0 && !dest.has_quoted_part) { | 5141 | if (dest.length == 0 && !dest.has_quoted_part) { |
5142 | /* skip "#comment" */ | 5142 | /* skip "#comment" */ |
5143 | /* note: we do not add it to &ctx.as_string */ | ||
5144 | /* TODO: in bash: | ||
5145 | * comment inside $() goes to the next \n, even inside quoted string (!): | ||
5146 | * cmd "$(cmd2 #comment)" - syntax error | ||
5147 | * cmd "`cmd2 #comment`" - ok | ||
5148 | * We accept both (comment ends where command subst ends, in both cases). | ||
5149 | */ | ||
5143 | while (1) { | 5150 | while (1) { |
5144 | ch = i_peek(input); | 5151 | ch = i_peek(input); |
5145 | if (ch == EOF || ch == '\n') | 5152 | if (ch == '\n') { |
5153 | nommu_addchr(&ctx.as_string, '\n'); | ||
5154 | break; | ||
5155 | } | ||
5156 | ch = i_getch(input); | ||
5157 | if (ch == EOF) | ||
5146 | break; | 5158 | break; |
5147 | i_getch(input); | ||
5148 | /* note: we do not add it to &ctx.as_string */ | ||
5149 | } | 5159 | } |
5150 | nommu_addchr(&ctx.as_string, '\n'); | ||
5151 | continue; /* back to top of while (1) */ | 5160 | continue; /* back to top of while (1) */ |
5152 | } | 5161 | } |
5153 | break; | 5162 | break; |
diff --git a/shell/hush_test/hush-parsing/comment2.right b/shell/hush_test/hush-parsing/comment2.right new file mode 100644 index 000000000..ee6e49a5a --- /dev/null +++ b/shell/hush_test/hush-parsing/comment2.right | |||
@@ -0,0 +1,4 @@ | |||
1 | Ok1 | ||
2 | Ok2 | ||
3 | Ok5 | ||
4 | Ok6 | ||
diff --git a/shell/hush_test/hush-parsing/comment2.tests b/shell/hush_test/hush-parsing/comment2.tests new file mode 100755 index 000000000..b7adad96a --- /dev/null +++ b/shell/hush_test/hush-parsing/comment2.tests | |||
@@ -0,0 +1,13 @@ | |||
1 | echo "`echo Ok1 #comment is ignored`" | ||
2 | echo `echo Ok2 #comment is ignored` | ||
3 | # | ||
4 | # Surprisingly, bash does not handle comments in $() | ||
5 | # the same way as in ``. "#" causes the rest of the line, _including_ )", | ||
6 | # to be ignored. These lines would cause an error: | ||
7 | #echo "$(echo Ok3 #comment is ignored)" | ||
8 | #echo $(echo Ok4 #comment is ignored) | ||
9 | # | ||
10 | echo "$(echo Ok5 #comment is ignored | ||
11 | )" | ||
12 | echo $(echo Ok6 #comment is ignored | ||
13 | ) | ||
diff --git a/shell/hush_test/hush-standalone/nofork_env.right b/shell/hush_test/hush-standalone/nofork_env.right new file mode 100644 index 000000000..3f16ff458 --- /dev/null +++ b/shell/hush_test/hush-standalone/nofork_env.right | |||
@@ -0,0 +1,9 @@ | |||
1 | ZVAR=1 | ||
2 | ZVAR=2 | ||
3 | ZVAR=3 | ||
4 | ZVAR=4 | ||
5 | ZVAR=5 | ||
6 | ZVAR=6 | ||
7 | ZVAR=7 | ||
8 | ZVAR=8 | ||
9 | Ok:0 | ||
diff --git a/shell/hush_test/hush-standalone/nofork_env.tests b/shell/hush_test/hush-standalone/nofork_env.tests new file mode 100755 index 000000000..111e564d2 --- /dev/null +++ b/shell/hush_test/hush-standalone/nofork_env.tests | |||
@@ -0,0 +1,15 @@ | |||
1 | # ash had a bug where NOFORKed applet (env/printenv) was not seeing new exported variables | ||
2 | |||
3 | (export ZVAR=1; printenv) | grep ^ZVAR= | ||
4 | (ZVAR=2 printenv) | grep ^ZVAR= | ||
5 | |||
6 | (export ZVAR=3; env) | grep ^ZVAR= | ||
7 | (ZVAR=4 env) | grep ^ZVAR= | ||
8 | |||
9 | export ZVAR=5; printenv | grep ^ZVAR= | ||
10 | ZVAR=6 printenv | grep ^ZVAR= | ||
11 | |||
12 | export ZVAR=7; env | grep ^ZVAR= | ||
13 | ZVAR=8 env | grep ^ZVAR= | ||
14 | |||
15 | echo Ok:$? | ||