summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 89cd47d..553c8e6 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -49,7 +49,6 @@
* [un]alias, command, fc, getopts, newgrp, readonly, times
* make complex ${var%...} constructs support optional
* make here documents optional
- * set -e (some ash testsuite entries use it, want to adopt those)
*
* Bash compat TODO:
* redirection of stdout+stderr: &> and >&
@@ -286,7 +285,7 @@
* therefore we don't show them either.
*/
//usage:#define hush_trivial_usage
-//usage: "[-nxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
+//usage: "[-enxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
//usage:#define hush_full_usage "\n\n"
//usage: "Unix shell interpreter"
@@ -747,6 +746,7 @@ struct function {
static const char o_opt_strings[] ALIGN1 =
"pipefail\0"
"noexec\0"
+ "errexit\0"
#if ENABLE_HUSH_MODE_X
"xtrace\0"
#endif
@@ -754,6 +754,7 @@ static const char o_opt_strings[] ALIGN1 =
enum {
OPT_O_PIPEFAIL,
OPT_O_NOEXEC,
+ OPT_O_ERREXIT,
#if ENABLE_HUSH_MODE_X
OPT_O_XTRACE,
#endif
@@ -810,6 +811,25 @@ struct globals {
#else
# define G_saved_tty_pgrp 0
#endif
+ /* How deeply are we in context where "set -e" is ignored */
+ int errexit_depth;
+ /* "set -e" rules (do we follow them correctly?):
+ * Exit if pipe, list, or compound command exits with a non-zero status.
+ * Shell does not exit if failed command is part of condition in
+ * if/while, part of && or || list except the last command, any command
+ * in a pipe but the last, or if the command's return value is being
+ * inverted with !. If a compound command other than a subshell returns a
+ * non-zero status because a command failed while -e was being ignored, the
+ * shell does not exit. A trap on ERR, if set, is executed before the shell
+ * exits [ERR is a bashism].
+ *
+ * If a compound command or function executes in a context where -e is
+ * ignored, none of the commands executed within are affected by the -e
+ * setting. If a compound command or function sets -e while executing in a
+ * context where -e is ignored, that setting does not have any effect until
+ * the compound command or the command containing the function call completes.
+ */
+
char o_opt[NUM_OPT_O];
#if ENABLE_HUSH_MODE_X
# define G_x_mode (G.o_opt[OPT_O_XTRACE])
@@ -5159,7 +5179,7 @@ static struct pipe *parse_stream(char **pstring,
* and it will match } earlier (not here). */
syntax_error_unexpected_ch(ch);
G.last_exitcode = 2;
- goto parse_error1;
+ goto parse_error2;
default:
if (HUSH_DEBUG)
bb_error_msg_and_die("BUG: unexpected %c\n", ch);
@@ -5168,7 +5188,7 @@ static struct pipe *parse_stream(char **pstring,
parse_error:
G.last_exitcode = 1;
- parse_error1:
+ parse_error2:
{
struct parse_context *pctx;
IF_HAS_KEYWORDS(struct parse_context *p2;)
@@ -8021,6 +8041,7 @@ static int run_list(struct pipe *pi)
/* Go through list of pipes, (maybe) executing them. */
for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) {
int r;
+ int sv_errexit_depth;
if (G.flag_SIGINT)
break;
@@ -8030,6 +8051,13 @@ static int run_list(struct pipe *pi)
IF_HAS_KEYWORDS(rword = pi->res_word;)
debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
rword, cond_code, last_rword);
+
+ sv_errexit_depth = G.errexit_depth;
+ if (IF_HAS_KEYWORDS(rword == RES_IF || rword == RES_ELIF ||)
+ pi->followup != PIPE_SEQ
+ ) {
+ G.errexit_depth++;
+ }
#if ENABLE_HUSH_LOOPS
if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR)
&& loop_top == NULL /* avoid bumping G.depth_of_loop twice */
@@ -8243,6 +8271,14 @@ static int run_list(struct pipe *pi)
check_and_run_traps();
}
+ /* Handle "set -e" */
+ if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
+ debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth);
+ if (G.errexit_depth == 0)
+ hush_exit(rcode);
+ }
+ G.errexit_depth = sv_errexit_depth;
+
/* Analyze how result affects subsequent commands */
#if ENABLE_HUSH_IF
if (rword == RES_IF || rword == RES_ELIF)
@@ -8422,22 +8458,9 @@ static int set_mode(int state, char mode, const char *o_opt)
G.o_opt[idx] = state;
break;
}
-/* TODO: set -e
-Exit if pipe, list, or compound command exits with a non-zero status.
-Shell does not exit if failed command is part of condition in
-if/while, part of && or || list except the last command, any command
-in a pipe but the last, or if the command's return value is being
-inverted with !. If a compound command other than a subshell returns a
-non-zero status because a command failed while -e was being ignored, the
-shell does not exit. A trap on ERR, if set, is executed before the shell
-exits [ERR is a bashism].
-
-If a compound command or function executes in a context where -e is
-ignored, none of the commands executed within are affected by the -e
-setting. If a compound command or function sets -e while executing in a
-context where -e is ignored, that setting does not have any effect until
-the compound command or the command containing the function call completes.
-*/
+ case 'e':
+ G.o_opt[OPT_O_ERREXIT] = state;
+ break;
default:
return EXIT_FAILURE;
}
@@ -8564,7 +8587,7 @@ int hush_main(int argc, char **argv)
flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
builtin_argc = 0;
while (1) {
- opt = getopt(argc, argv, "+c:xinsl"
+ opt = getopt(argc, argv, "+c:exinsl"
#if !BB_MMU
"<:$:R:V:"
# if ENABLE_HUSH_FUNCTIONS
@@ -8682,6 +8705,7 @@ int hush_main(int argc, char **argv)
#endif
case 'n':
case 'x':
+ case 'e':
if (set_mode(1, opt, NULL) == 0) /* no error */
break;
default:
diff --git a/shell/hush_test/hush-misc/errexit1.right b/shell/hush_test/hush-misc/errexit1.right
new file mode 100644
index 0000000..d86bac9
--- /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 0000000..7b4a156
--- /dev/null
+++ b/shell/hush_test/hush-misc/errexit1.tests
@@ -0,0 +1,5 @@
+set -e
+(true)
+echo OK
+(false)
+echo FAIL
diff --git a/shell/hush_test/hush-signals/signal8.right b/shell/hush_test/hush-signals/signal8.right
new file mode 100644
index 0000000..39572f3
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal8.right
@@ -0,0 +1,3 @@
+Removing traps
+End of exit_func
+Done: 0
diff --git a/shell/hush_test/hush-signals/signal8.tests b/shell/hush_test/hush-signals/signal8.tests
new file mode 100755
index 0000000..731af74
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal8.tests
@@ -0,0 +1,18 @@
+"$THIS_SH" -c '
+exit_func() {
+ echo "Removing traps"
+ trap - EXIT TERM INT
+ echo "End of exit_func"
+}
+set -e
+trap exit_func EXIT TERM INT
+sleep 2
+exit 77
+' &
+
+sleep 1
+# BUG: ash kills -PGRP, but in non-interactive shell we do not create pgrps!
+# In this case, bash kills by PID, not PGRP.
+kill -TERM %1
+wait
+echo Done: $?
diff --git a/shell/hush_test/hush-signals/signal9.right b/shell/hush_test/hush-signals/signal9.right
new file mode 100644
index 0000000..39572f3
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal9.right
@@ -0,0 +1,3 @@
+Removing traps
+End of exit_func
+Done: 0
diff --git a/shell/hush_test/hush-signals/signal9.tests b/shell/hush_test/hush-signals/signal9.tests
new file mode 100755
index 0000000..18e7101
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal9.tests
@@ -0,0 +1,21 @@
+# Note: the inner script is a test which checks for a different bug
+# (ordering between INT handler and exit on "set -e"),
+# but so far I did not figure out how to simulate it non-interactively.
+
+"$THIS_SH" -c '
+exit_func() {
+ echo "Removing traps"
+ trap - EXIT TERM INT
+ echo "End of exit_func"
+}
+set -e
+trap exit_func EXIT TERM INT
+sleep 2
+exit 77
+' &
+
+child=$!
+sleep 1
+kill -TERM $child
+wait
+echo Done: $?