aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2010-11-14 02:01:50 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2010-11-14 02:01:50 +0100
commit6696eac274b5dcf5932b211fe9ee748d8268a39c (patch)
tree9a088e5b9680d4657f6ebe3ad266d5502deb1f8b
parentc08c3f5d262acab7082cca88d0b2a329184b133b (diff)
downloadbusybox-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.c124
-rw-r--r--shell/hush_test/hush-misc/pipefail.right40
-rwxr-xr-xshell/hush_test/hush-misc/pipefail.tests45
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 */
671static const char o_opt_strings[] ALIGN1 = "pipefail\0";
672enum {
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) */
642struct globals { 680struct 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
7343static int set_mode(const char cstate, const char mode) 7390static 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 @@
1Default:
2true | true:
30
41
5true | false:
61
70
8false | true:
90
101
11exit 2 | exit 3 | exit 4:
124
130
14Pipefail on:
15true | true:
160
171
18true | false:
191
200
21false | true:
221
230
24exit 2 | exit 3 | exit 4:
254
260
27Pipefail off:
28true | true:
290
301
31true | false:
321
330
34false | true:
350
361
37exit 2 | exit 3 | exit 4:
384
390
40Done
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 @@
1echo Default:
2echo "true | true:"
3 true | true; echo $?
4! true | true; echo $?
5echo "true | false:"
6 true | false; echo $?
7! true | false; echo $?
8echo "false | true:"
9 false | true; echo $?
10! false | true; echo $?
11echo "exit 2 | exit 3 | exit 4:"
12 exit 2 | exit 3 | exit 4; echo $?
13! exit 2 | exit 3 | exit 4; echo $?
14
15echo Pipefail on:
16set -o pipefail
17echo "true | true:"
18 true | true; echo $?
19! true | true; echo $?
20echo "true | false:"
21 true | false; echo $?
22! true | false; echo $?
23echo "false | true:"
24 false | true; echo $?
25! false | true; echo $?
26echo "exit 2 | exit 3 | exit 4:"
27 exit 2 | exit 3 | exit 4; echo $?
28! exit 2 | exit 3 | exit 4; echo $?
29
30echo Pipefail off:
31set +o pipefail
32echo "true | true:"
33 true | true; echo $?
34! true | true; echo $?
35echo "true | false:"
36 true | false; echo $?
37! true | false; echo $?
38echo "false | true:"
39 false | true; echo $?
40! false | true; echo $?
41echo "exit 2 | exit 3 | exit 4:"
42 exit 2 | exit 3 | exit 4; echo $?
43! exit 2 | exit 3 | exit 4; echo $?
44
45echo Done