aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2017-07-14 13:36:48 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2017-07-14 13:36:48 +0200
commit9fda609a60506f4c1f73f0034cafd2b3434f4df3 (patch)
treed3ea98fe9f47577162a884ec6cd1a6b233e72bc4
parent75e90b15482184db83f03c67b53d4220888c6c9d (diff)
downloadbusybox-w32-9fda609a60506f4c1f73f0034cafd2b3434f4df3.tar.gz
busybox-w32-9fda609a60506f4c1f73f0034cafd2b3434f4df3.tar.bz2
busybox-w32-9fda609a60506f4c1f73f0034cafd2b3434f4df3.zip
hush: add support for "set -e"
function old new delta run_list 978 1046 +68 o_opt_strings 24 32 +8 reset_traps_to_defaults 136 142 +6 pick_sighandler 57 60 +3 packed_usage 31772 31770 -2 hush_main 983 961 -22 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 4/2 up/down: 85/-24) Total: 61 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/hush.c66
-rw-r--r--shell/hush_test/hush-misc/errexit1.right1
-rwxr-xr-xshell/hush_test/hush-misc/errexit1.tests5
-rw-r--r--shell/hush_test/hush-signals/signal8.right3
-rwxr-xr-xshell/hush_test/hush-signals/signal8.tests18
-rw-r--r--shell/hush_test/hush-signals/signal9.right3
-rwxr-xr-xshell/hush_test/hush-signals/signal9.tests21
7 files changed, 96 insertions, 21 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 89cd47d8f..553c8e64a 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -49,7 +49,6 @@
49 * [un]alias, command, fc, getopts, newgrp, readonly, times 49 * [un]alias, command, fc, getopts, newgrp, readonly, times
50 * make complex ${var%...} constructs support optional 50 * make complex ${var%...} constructs support optional
51 * make here documents optional 51 * make here documents optional
52 * set -e (some ash testsuite entries use it, want to adopt those)
53 * 52 *
54 * Bash compat TODO: 53 * Bash compat TODO:
55 * redirection of stdout+stderr: &> and >& 54 * redirection of stdout+stderr: &> and >&
@@ -286,7 +285,7 @@
286 * therefore we don't show them either. 285 * therefore we don't show them either.
287 */ 286 */
288//usage:#define hush_trivial_usage 287//usage:#define hush_trivial_usage
289//usage: "[-nxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" 288//usage: "[-enxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
290//usage:#define hush_full_usage "\n\n" 289//usage:#define hush_full_usage "\n\n"
291//usage: "Unix shell interpreter" 290//usage: "Unix shell interpreter"
292 291
@@ -747,6 +746,7 @@ struct function {
747static const char o_opt_strings[] ALIGN1 = 746static const char o_opt_strings[] ALIGN1 =
748 "pipefail\0" 747 "pipefail\0"
749 "noexec\0" 748 "noexec\0"
749 "errexit\0"
750#if ENABLE_HUSH_MODE_X 750#if ENABLE_HUSH_MODE_X
751 "xtrace\0" 751 "xtrace\0"
752#endif 752#endif
@@ -754,6 +754,7 @@ static const char o_opt_strings[] ALIGN1 =
754enum { 754enum {
755 OPT_O_PIPEFAIL, 755 OPT_O_PIPEFAIL,
756 OPT_O_NOEXEC, 756 OPT_O_NOEXEC,
757 OPT_O_ERREXIT,
757#if ENABLE_HUSH_MODE_X 758#if ENABLE_HUSH_MODE_X
758 OPT_O_XTRACE, 759 OPT_O_XTRACE,
759#endif 760#endif
@@ -810,6 +811,25 @@ struct globals {
810#else 811#else
811# define G_saved_tty_pgrp 0 812# define G_saved_tty_pgrp 0
812#endif 813#endif
814 /* How deeply are we in context where "set -e" is ignored */
815 int errexit_depth;
816 /* "set -e" rules (do we follow them correctly?):
817 * Exit if pipe, list, or compound command exits with a non-zero status.
818 * Shell does not exit if failed command is part of condition in
819 * if/while, part of && or || list except the last command, any command
820 * in a pipe but the last, or if the command's return value is being
821 * inverted with !. If a compound command other than a subshell returns a
822 * non-zero status because a command failed while -e was being ignored, the
823 * shell does not exit. A trap on ERR, if set, is executed before the shell
824 * exits [ERR is a bashism].
825 *
826 * If a compound command or function executes in a context where -e is
827 * ignored, none of the commands executed within are affected by the -e
828 * setting. If a compound command or function sets -e while executing in a
829 * context where -e is ignored, that setting does not have any effect until
830 * the compound command or the command containing the function call completes.
831 */
832
813 char o_opt[NUM_OPT_O]; 833 char o_opt[NUM_OPT_O];
814#if ENABLE_HUSH_MODE_X 834#if ENABLE_HUSH_MODE_X
815# define G_x_mode (G.o_opt[OPT_O_XTRACE]) 835# define G_x_mode (G.o_opt[OPT_O_XTRACE])
@@ -5159,7 +5179,7 @@ static struct pipe *parse_stream(char **pstring,
5159 * and it will match } earlier (not here). */ 5179 * and it will match } earlier (not here). */
5160 syntax_error_unexpected_ch(ch); 5180 syntax_error_unexpected_ch(ch);
5161 G.last_exitcode = 2; 5181 G.last_exitcode = 2;
5162 goto parse_error1; 5182 goto parse_error2;
5163 default: 5183 default:
5164 if (HUSH_DEBUG) 5184 if (HUSH_DEBUG)
5165 bb_error_msg_and_die("BUG: unexpected %c\n", ch); 5185 bb_error_msg_and_die("BUG: unexpected %c\n", ch);
@@ -5168,7 +5188,7 @@ static struct pipe *parse_stream(char **pstring,
5168 5188
5169 parse_error: 5189 parse_error:
5170 G.last_exitcode = 1; 5190 G.last_exitcode = 1;
5171 parse_error1: 5191 parse_error2:
5172 { 5192 {
5173 struct parse_context *pctx; 5193 struct parse_context *pctx;
5174 IF_HAS_KEYWORDS(struct parse_context *p2;) 5194 IF_HAS_KEYWORDS(struct parse_context *p2;)
@@ -8021,6 +8041,7 @@ static int run_list(struct pipe *pi)
8021 /* Go through list of pipes, (maybe) executing them. */ 8041 /* Go through list of pipes, (maybe) executing them. */
8022 for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { 8042 for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) {
8023 int r; 8043 int r;
8044 int sv_errexit_depth;
8024 8045
8025 if (G.flag_SIGINT) 8046 if (G.flag_SIGINT)
8026 break; 8047 break;
@@ -8030,6 +8051,13 @@ static int run_list(struct pipe *pi)
8030 IF_HAS_KEYWORDS(rword = pi->res_word;) 8051 IF_HAS_KEYWORDS(rword = pi->res_word;)
8031 debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n", 8052 debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
8032 rword, cond_code, last_rword); 8053 rword, cond_code, last_rword);
8054
8055 sv_errexit_depth = G.errexit_depth;
8056 if (IF_HAS_KEYWORDS(rword == RES_IF || rword == RES_ELIF ||)
8057 pi->followup != PIPE_SEQ
8058 ) {
8059 G.errexit_depth++;
8060 }
8033#if ENABLE_HUSH_LOOPS 8061#if ENABLE_HUSH_LOOPS
8034 if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) 8062 if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR)
8035 && loop_top == NULL /* avoid bumping G.depth_of_loop twice */ 8063 && loop_top == NULL /* avoid bumping G.depth_of_loop twice */
@@ -8243,6 +8271,14 @@ static int run_list(struct pipe *pi)
8243 check_and_run_traps(); 8271 check_and_run_traps();
8244 } 8272 }
8245 8273
8274 /* Handle "set -e" */
8275 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
8276 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth);
8277 if (G.errexit_depth == 0)
8278 hush_exit(rcode);
8279 }
8280 G.errexit_depth = sv_errexit_depth;
8281
8246 /* Analyze how result affects subsequent commands */ 8282 /* Analyze how result affects subsequent commands */
8247#if ENABLE_HUSH_IF 8283#if ENABLE_HUSH_IF
8248 if (rword == RES_IF || rword == RES_ELIF) 8284 if (rword == RES_IF || rword == RES_ELIF)
@@ -8422,22 +8458,9 @@ static int set_mode(int state, char mode, const char *o_opt)
8422 G.o_opt[idx] = state; 8458 G.o_opt[idx] = state;
8423 break; 8459 break;
8424 } 8460 }
8425/* TODO: set -e 8461 case 'e':
8426Exit if pipe, list, or compound command exits with a non-zero status. 8462 G.o_opt[OPT_O_ERREXIT] = state;
8427Shell does not exit if failed command is part of condition in 8463 break;
8428if/while, part of && or || list except the last command, any command
8429in a pipe but the last, or if the command's return value is being
8430inverted with !. If a compound command other than a subshell returns a
8431non-zero status because a command failed while -e was being ignored, the
8432shell does not exit. A trap on ERR, if set, is executed before the shell
8433exits [ERR is a bashism].
8434
8435If a compound command or function executes in a context where -e is
8436ignored, none of the commands executed within are affected by the -e
8437setting. If a compound command or function sets -e while executing in a
8438context where -e is ignored, that setting does not have any effect until
8439the compound command or the command containing the function call completes.
8440*/
8441 default: 8464 default:
8442 return EXIT_FAILURE; 8465 return EXIT_FAILURE;
8443 } 8466 }
@@ -8564,7 +8587,7 @@ int hush_main(int argc, char **argv)
8564 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; 8587 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
8565 builtin_argc = 0; 8588 builtin_argc = 0;
8566 while (1) { 8589 while (1) {
8567 opt = getopt(argc, argv, "+c:xinsl" 8590 opt = getopt(argc, argv, "+c:exinsl"
8568#if !BB_MMU 8591#if !BB_MMU
8569 "<:$:R:V:" 8592 "<:$:R:V:"
8570# if ENABLE_HUSH_FUNCTIONS 8593# if ENABLE_HUSH_FUNCTIONS
@@ -8682,6 +8705,7 @@ int hush_main(int argc, char **argv)
8682#endif 8705#endif
8683 case 'n': 8706 case 'n':
8684 case 'x': 8707 case 'x':
8708 case 'e':
8685 if (set_mode(1, opt, NULL) == 0) /* no error */ 8709 if (set_mode(1, opt, NULL) == 0) /* no error */
8686 break; 8710 break;
8687 default: 8711 default:
diff --git a/shell/hush_test/hush-misc/errexit1.right b/shell/hush_test/hush-misc/errexit1.right
new file mode 100644
index 000000000..d86bac9de
--- /dev/null
+++ b/shell/hush_test/hush-misc/errexit1.right
@@ -0,0 +1 @@
OK
diff --git a/shell/hush_test/hush-misc/errexit1.tests b/shell/hush_test/hush-misc/errexit1.tests
new file mode 100755
index 000000000..7b4a15634
--- /dev/null
+++ b/shell/hush_test/hush-misc/errexit1.tests
@@ -0,0 +1,5 @@
1set -e
2(true)
3echo OK
4(false)
5echo FAIL
diff --git a/shell/hush_test/hush-signals/signal8.right b/shell/hush_test/hush-signals/signal8.right
new file mode 100644
index 000000000..39572f30e
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal8.right
@@ -0,0 +1,3 @@
1Removing traps
2End of exit_func
3Done: 0
diff --git a/shell/hush_test/hush-signals/signal8.tests b/shell/hush_test/hush-signals/signal8.tests
new file mode 100755
index 000000000..731af7477
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal8.tests
@@ -0,0 +1,18 @@
1"$THIS_SH" -c '
2exit_func() {
3 echo "Removing traps"
4 trap - EXIT TERM INT
5 echo "End of exit_func"
6}
7set -e
8trap exit_func EXIT TERM INT
9sleep 2
10exit 77
11' &
12
13sleep 1
14# BUG: ash kills -PGRP, but in non-interactive shell we do not create pgrps!
15# In this case, bash kills by PID, not PGRP.
16kill -TERM %1
17wait
18echo Done: $?
diff --git a/shell/hush_test/hush-signals/signal9.right b/shell/hush_test/hush-signals/signal9.right
new file mode 100644
index 000000000..39572f30e
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal9.right
@@ -0,0 +1,3 @@
1Removing traps
2End of exit_func
3Done: 0
diff --git a/shell/hush_test/hush-signals/signal9.tests b/shell/hush_test/hush-signals/signal9.tests
new file mode 100755
index 000000000..18e71012b
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal9.tests
@@ -0,0 +1,21 @@
1# Note: the inner script is a test which checks for a different bug
2# (ordering between INT handler and exit on "set -e"),
3# but so far I did not figure out how to simulate it non-interactively.
4
5"$THIS_SH" -c '
6exit_func() {
7 echo "Removing traps"
8 trap - EXIT TERM INT
9 echo "End of exit_func"
10}
11set -e
12trap exit_func EXIT TERM INT
13sleep 2
14exit 77
15' &
16
17child=$!
18sleep 1
19kill -TERM $child
20wait
21echo Done: $?