diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2010-11-14 02:01:50 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-11-14 02:01:50 +0100 |
commit | 6696eac274b5dcf5932b211fe9ee748d8268a39c (patch) | |
tree | 9a088e5b9680d4657f6ebe3ad266d5502deb1f8b | |
parent | c08c3f5d262acab7082cca88d0b2a329184b133b (diff) | |
download | busybox-w32-6696eac274b5dcf5932b211fe9ee748d8268a39c.tar.gz busybox-w32-6696eac274b5dcf5932b211fe9ee748d8268a39c.tar.bz2 busybox-w32-6696eac274b5dcf5932b211fe9ee748d8268a39c.zip |
hush: add support for "set -o pipefail"
function old new delta
checkjobs 467 517 +50
builtin_set 259 286 +27
o_opt_strings - 10 +10
hush_main 1011 1013 +2
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/0 up/down: 89/0) Total: 89 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/hush.c | 124 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/pipefail.right | 40 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/pipefail.tests | 45 |
3 files changed, 186 insertions, 23 deletions
diff --git a/shell/hush.c b/shell/hush.c index 126aaf074..50e9ce333 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -507,6 +507,7 @@ struct command { | |||
507 | # define CMD_FUNCDEF 3 | 507 | # define CMD_FUNCDEF 3 |
508 | #endif | 508 | #endif |
509 | 509 | ||
510 | smalluint cmd_exitcode; | ||
510 | /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */ | 511 | /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */ |
511 | struct pipe *group; | 512 | struct pipe *group; |
512 | #if !BB_MMU | 513 | #if !BB_MMU |
@@ -637,6 +638,43 @@ struct function { | |||
637 | #endif | 638 | #endif |
638 | 639 | ||
639 | 640 | ||
641 | /* set -/+o OPT support. (TODO: make it optional) | ||
642 | * bash supports the following opts: | ||
643 | * allexport off | ||
644 | * braceexpand on | ||
645 | * emacs on | ||
646 | * errexit off | ||
647 | * errtrace off | ||
648 | * functrace off | ||
649 | * hashall on | ||
650 | * histexpand off | ||
651 | * history on | ||
652 | * ignoreeof off | ||
653 | * interactive-comments on | ||
654 | * keyword off | ||
655 | * monitor on | ||
656 | * noclobber off | ||
657 | * noexec off | ||
658 | * noglob off | ||
659 | * nolog off | ||
660 | * notify off | ||
661 | * nounset off | ||
662 | * onecmd off | ||
663 | * physical off | ||
664 | * pipefail off | ||
665 | * posix off | ||
666 | * privileged off | ||
667 | * verbose off | ||
668 | * vi off | ||
669 | * xtrace off | ||
670 | */ | ||
671 | static const char o_opt_strings[] ALIGN1 = "pipefail\0"; | ||
672 | enum { | ||
673 | OPT_O_PIPEFAIL, | ||
674 | NUM_OPT_O | ||
675 | }; | ||
676 | |||
677 | |||
640 | /* "Globals" within this file */ | 678 | /* "Globals" within this file */ |
641 | /* Sorted roughly by size (smaller offsets == smaller code) */ | 679 | /* Sorted roughly by size (smaller offsets == smaller code) */ |
642 | struct globals { | 680 | struct globals { |
@@ -675,6 +713,7 @@ struct globals { | |||
675 | int last_jobid; | 713 | int last_jobid; |
676 | pid_t saved_tty_pgrp; | 714 | pid_t saved_tty_pgrp; |
677 | struct pipe *job_list; | 715 | struct pipe *job_list; |
716 | char o_opt[NUM_OPT_O]; | ||
678 | # define G_saved_tty_pgrp (G.saved_tty_pgrp) | 717 | # define G_saved_tty_pgrp (G.saved_tty_pgrp) |
679 | #else | 718 | #else |
680 | # define G_saved_tty_pgrp 0 | 719 | # define G_saved_tty_pgrp 0 |
@@ -6315,24 +6354,23 @@ static int checkjobs(struct pipe *fg_pipe) | |||
6315 | if (fg_pipe->cmds[i].pid != childpid) | 6354 | if (fg_pipe->cmds[i].pid != childpid) |
6316 | continue; | 6355 | continue; |
6317 | if (dead) { | 6356 | if (dead) { |
6357 | int ex; | ||
6318 | fg_pipe->cmds[i].pid = 0; | 6358 | fg_pipe->cmds[i].pid = 0; |
6319 | fg_pipe->alive_cmds--; | 6359 | fg_pipe->alive_cmds--; |
6320 | if (i == fg_pipe->num_cmds - 1) { | 6360 | ex = WEXITSTATUS(status); |
6321 | /* last process gives overall exitstatus */ | 6361 | /* bash prints killer signal's name for *last* |
6322 | rcode = WEXITSTATUS(status); | 6362 | * process in pipe (prints just newline for SIGINT). |
6323 | /* bash prints killer signal's name for *last* | 6363 | * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT) |
6324 | * process in pipe (prints just newline for SIGINT). | 6364 | */ |
6325 | * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT) | 6365 | if (WIFSIGNALED(status)) { |
6326 | */ | 6366 | int sig = WTERMSIG(status); |
6327 | if (WIFSIGNALED(status)) { | 6367 | if (i == fg_pipe->num_cmds-1) |
6328 | int sig = WTERMSIG(status); | ||
6329 | printf("%s\n", sig == SIGINT ? "" : get_signame(sig)); | 6368 | printf("%s\n", sig == SIGINT ? "" : get_signame(sig)); |
6330 | /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? | 6369 | /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? |
6331 | * Maybe we need to use sig | 128? */ | 6370 | * Maybe we need to use sig | 128? */ |
6332 | rcode = sig + 128; | 6371 | ex = sig + 128; |
6333 | } | ||
6334 | IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;) | ||
6335 | } | 6372 | } |
6373 | fg_pipe->cmds[i].cmd_exitcode = ex; | ||
6336 | } else { | 6374 | } else { |
6337 | fg_pipe->cmds[i].is_stopped = 1; | 6375 | fg_pipe->cmds[i].is_stopped = 1; |
6338 | fg_pipe->stopped_cmds++; | 6376 | fg_pipe->stopped_cmds++; |
@@ -6341,6 +6379,15 @@ static int checkjobs(struct pipe *fg_pipe) | |||
6341 | fg_pipe->alive_cmds, fg_pipe->stopped_cmds); | 6379 | fg_pipe->alive_cmds, fg_pipe->stopped_cmds); |
6342 | if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) { | 6380 | if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) { |
6343 | /* All processes in fg pipe have exited or stopped */ | 6381 | /* All processes in fg pipe have exited or stopped */ |
6382 | i = fg_pipe->num_cmds; | ||
6383 | while (--i >= 0) { | ||
6384 | rcode = fg_pipe->cmds[i].cmd_exitcode; | ||
6385 | /* usually last process gives overall exitstatus, | ||
6386 | * but with "set -o pipefail", last *failed* process does */ | ||
6387 | if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0) | ||
6388 | break; | ||
6389 | } | ||
6390 | IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;) | ||
6344 | /* Note: *non-interactive* bash does not continue if all processes in fg pipe | 6391 | /* Note: *non-interactive* bash does not continue if all processes in fg pipe |
6345 | * are stopped. Testcase: "cat | cat" in a script (not on command line!) | 6392 | * are stopped. Testcase: "cat | cat" in a script (not on command line!) |
6346 | * and "killall -STOP cat" */ | 6393 | * and "killall -STOP cat" */ |
@@ -7340,13 +7387,41 @@ static void set_fatal_handlers(void) | |||
7340 | } | 7387 | } |
7341 | #endif | 7388 | #endif |
7342 | 7389 | ||
7343 | static int set_mode(const char cstate, const char mode) | 7390 | static int set_mode(int state, char mode, const char *o_opt) |
7344 | { | 7391 | { |
7345 | int state = (cstate == '-' ? 1 : 0); | 7392 | int idx; |
7346 | switch (mode) { | 7393 | switch (mode) { |
7347 | case 'n': G.n_mode = state; break; | 7394 | case 'n': |
7348 | case 'x': IF_HUSH_MODE_X(G_x_mode = state;) break; | 7395 | G.n_mode = state; |
7349 | default: return EXIT_FAILURE; | 7396 | break; |
7397 | case 'x': | ||
7398 | IF_HUSH_MODE_X(G_x_mode = state;) | ||
7399 | break; | ||
7400 | case 'o': | ||
7401 | if (!o_opt) { | ||
7402 | /* "set -+o" without parameter. | ||
7403 | * in bash, set -o produces this output: | ||
7404 | * pipefail off | ||
7405 | * and set +o: | ||
7406 | * set +o pipefail | ||
7407 | * We always use the second form. | ||
7408 | */ | ||
7409 | const char *p = o_opt_strings; | ||
7410 | idx = 0; | ||
7411 | while (*p) { | ||
7412 | printf("set %co %s\n", (G.o_opt[idx] ? '-' : '+'), p); | ||
7413 | idx++; | ||
7414 | p += strlen(p) + 1; | ||
7415 | } | ||
7416 | break; | ||
7417 | } | ||
7418 | idx = index_in_strings(o_opt_strings, o_opt); | ||
7419 | if (idx >= 0) { | ||
7420 | G.o_opt[idx] = state; | ||
7421 | break; | ||
7422 | } | ||
7423 | default: | ||
7424 | return EXIT_FAILURE; | ||
7350 | } | 7425 | } |
7351 | return EXIT_SUCCESS; | 7426 | return EXIT_SUCCESS; |
7352 | } | 7427 | } |
@@ -7586,7 +7661,7 @@ int hush_main(int argc, char **argv) | |||
7586 | #endif | 7661 | #endif |
7587 | case 'n': | 7662 | case 'n': |
7588 | case 'x': | 7663 | case 'x': |
7589 | if (set_mode('-', opt) == 0) /* no error */ | 7664 | if (set_mode(1, opt, NULL) == 0) /* no error */ |
7590 | break; | 7665 | break; |
7591 | default: | 7666 | default: |
7592 | #ifndef BB_VER | 7667 | #ifndef BB_VER |
@@ -8376,15 +8451,18 @@ static int FAST_FUNC builtin_set(char **argv) | |||
8376 | } | 8451 | } |
8377 | 8452 | ||
8378 | do { | 8453 | do { |
8379 | if (!strcmp(arg, "--")) { | 8454 | if (strcmp(arg, "--") == 0) { |
8380 | ++argv; | 8455 | ++argv; |
8381 | goto set_argv; | 8456 | goto set_argv; |
8382 | } | 8457 | } |
8383 | if (arg[0] != '+' && arg[0] != '-') | 8458 | if (arg[0] != '+' && arg[0] != '-') |
8384 | break; | 8459 | break; |
8385 | for (n = 1; arg[n]; ++n) | 8460 | for (n = 1; arg[n]; ++n) { |
8386 | if (set_mode(arg[0], arg[n])) | 8461 | if (set_mode((arg[0] == '-'), arg[n], argv[1])) |
8387 | goto error; | 8462 | goto error; |
8463 | if (arg[n] == 'o' && argv[1]) | ||
8464 | argv++; | ||
8465 | } | ||
8388 | } while ((arg = *++argv) != NULL); | 8466 | } while ((arg = *++argv) != NULL); |
8389 | /* Now argv[0] is 1st argument */ | 8467 | /* Now argv[0] is 1st argument */ |
8390 | 8468 | ||
diff --git a/shell/hush_test/hush-misc/pipefail.right b/shell/hush_test/hush-misc/pipefail.right new file mode 100644 index 000000000..5845d8939 --- /dev/null +++ b/shell/hush_test/hush-misc/pipefail.right | |||
@@ -0,0 +1,40 @@ | |||
1 | Default: | ||
2 | true | true: | ||
3 | 0 | ||
4 | 1 | ||
5 | true | false: | ||
6 | 1 | ||
7 | 0 | ||
8 | false | true: | ||
9 | 0 | ||
10 | 1 | ||
11 | exit 2 | exit 3 | exit 4: | ||
12 | 4 | ||
13 | 0 | ||
14 | Pipefail on: | ||
15 | true | true: | ||
16 | 0 | ||
17 | 1 | ||
18 | true | false: | ||
19 | 1 | ||
20 | 0 | ||
21 | false | true: | ||
22 | 1 | ||
23 | 0 | ||
24 | exit 2 | exit 3 | exit 4: | ||
25 | 4 | ||
26 | 0 | ||
27 | Pipefail off: | ||
28 | true | true: | ||
29 | 0 | ||
30 | 1 | ||
31 | true | false: | ||
32 | 1 | ||
33 | 0 | ||
34 | false | true: | ||
35 | 0 | ||
36 | 1 | ||
37 | exit 2 | exit 3 | exit 4: | ||
38 | 4 | ||
39 | 0 | ||
40 | Done | ||
diff --git a/shell/hush_test/hush-misc/pipefail.tests b/shell/hush_test/hush-misc/pipefail.tests new file mode 100755 index 000000000..9df841861 --- /dev/null +++ b/shell/hush_test/hush-misc/pipefail.tests | |||
@@ -0,0 +1,45 @@ | |||
1 | echo Default: | ||
2 | echo "true | true:" | ||
3 | true | true; echo $? | ||
4 | ! true | true; echo $? | ||
5 | echo "true | false:" | ||
6 | true | false; echo $? | ||
7 | ! true | false; echo $? | ||
8 | echo "false | true:" | ||
9 | false | true; echo $? | ||
10 | ! false | true; echo $? | ||
11 | echo "exit 2 | exit 3 | exit 4:" | ||
12 | exit 2 | exit 3 | exit 4; echo $? | ||
13 | ! exit 2 | exit 3 | exit 4; echo $? | ||
14 | |||
15 | echo Pipefail on: | ||
16 | set -o pipefail | ||
17 | echo "true | true:" | ||
18 | true | true; echo $? | ||
19 | ! true | true; echo $? | ||
20 | echo "true | false:" | ||
21 | true | false; echo $? | ||
22 | ! true | false; echo $? | ||
23 | echo "false | true:" | ||
24 | false | true; echo $? | ||
25 | ! false | true; echo $? | ||
26 | echo "exit 2 | exit 3 | exit 4:" | ||
27 | exit 2 | exit 3 | exit 4; echo $? | ||
28 | ! exit 2 | exit 3 | exit 4; echo $? | ||
29 | |||
30 | echo Pipefail off: | ||
31 | set +o pipefail | ||
32 | echo "true | true:" | ||
33 | true | true; echo $? | ||
34 | ! true | true; echo $? | ||
35 | echo "true | false:" | ||
36 | true | false; echo $? | ||
37 | ! true | false; echo $? | ||
38 | echo "false | true:" | ||
39 | false | true; echo $? | ||
40 | ! false | true; echo $? | ||
41 | echo "exit 2 | exit 3 | exit 4:" | ||
42 | exit 2 | exit 3 | exit 4; echo $? | ||
43 | ! exit 2 | exit 3 | exit 4; echo $? | ||
44 | |||
45 | echo Done | ||