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>
This commit is contained in:
parent
75e90b1548
commit
9fda609a60
66
shell/hush.c
66
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:
|
||||
|
1
shell/hush_test/hush-misc/errexit1.right
Normal file
1
shell/hush_test/hush-misc/errexit1.right
Normal file
@ -0,0 +1 @@
|
||||
OK
|
5
shell/hush_test/hush-misc/errexit1.tests
Executable file
5
shell/hush_test/hush-misc/errexit1.tests
Executable file
@ -0,0 +1,5 @@
|
||||
set -e
|
||||
(true)
|
||||
echo OK
|
||||
(false)
|
||||
echo FAIL
|
3
shell/hush_test/hush-signals/signal8.right
Normal file
3
shell/hush_test/hush-signals/signal8.right
Normal file
@ -0,0 +1,3 @@
|
||||
Removing traps
|
||||
End of exit_func
|
||||
Done: 0
|
18
shell/hush_test/hush-signals/signal8.tests
Executable file
18
shell/hush_test/hush-signals/signal8.tests
Executable file
@ -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: $?
|
3
shell/hush_test/hush-signals/signal9.right
Normal file
3
shell/hush_test/hush-signals/signal9.right
Normal file
@ -0,0 +1,3 @@
|
||||
Removing traps
|
||||
End of exit_func
|
||||
Done: 0
|
21
shell/hush_test/hush-signals/signal9.tests
Executable file
21
shell/hush_test/hush-signals/signal9.tests
Executable file
@ -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: $?
|
Loading…
Reference in New Issue
Block a user