Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ tests=\
test/readonly.sh \
test/redir.sh \
test/return.sh \
test/set-e.sh \
test/subshell.sh \
test/syntax.sh \
test/ulimit.sh \
Expand Down
4 changes: 2 additions & 2 deletions include/shell/task.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ int expand_word(struct mrsh_context *ctx, const struct mrsh_word *word,
struct mrsh_array *fields);
int run_simple_command(struct mrsh_context *ctx, struct mrsh_simple_command *sc);
int run_command(struct mrsh_context *ctx, struct mrsh_command *cmd);
int run_and_or_list(struct mrsh_context *ctx, struct mrsh_and_or_list *and_or_list);
int run_and_or_list(struct mrsh_context *ctx, struct mrsh_and_or_list *and_or_list, bool allow_errexit);
int run_pipeline(struct mrsh_context *ctx, struct mrsh_pipeline *pipeline);
int run_command_list_array(struct mrsh_context *ctx, struct mrsh_array *array);
int run_command_list_array(struct mrsh_context *ctx, struct mrsh_array *array, bool allow_errexit);

#endif
44 changes: 28 additions & 16 deletions shell/task/task.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ static int run_subshell(struct mrsh_context *ctx, struct mrsh_array *array) {
}
}

int ret = run_command_list_array(ctx, array);
int ret = run_command_list_array(ctx, array, true);
if (ret < 0) {
exit(127);
}
Expand All @@ -54,13 +54,13 @@ static int run_subshell(struct mrsh_context *ctx, struct mrsh_array *array) {
}

static int run_if_clause(struct mrsh_context *ctx, struct mrsh_if_clause *ic) {
int ret = run_command_list_array(ctx, &ic->condition);
int ret = run_command_list_array(ctx, &ic->condition, false);
if (ret < 0) {
return ret;
}

if (ret == 0) {
return run_command_list_array(ctx, &ic->body);
return run_command_list_array(ctx, &ic->body, true);
} else {
if (ic->else_part) {
return run_command(ctx, ic->else_part);
Expand All @@ -76,7 +76,7 @@ static int run_loop_clause(struct mrsh_context *ctx, struct mrsh_loop_clause *lc

int loop_ret = 0;
while (ctx->state->exit == -1) {
int ret = run_command_list_array(ctx, &lc->condition);
int ret = run_command_list_array(ctx, &lc->condition, false);
if (ret == TASK_STATUS_INTERRUPTED) {
goto interrupt;
} else if (ret < 0) {
Expand All @@ -96,7 +96,7 @@ static int run_loop_clause(struct mrsh_context *ctx, struct mrsh_loop_clause *lc
break;
}

loop_ret = run_command_list_array(ctx, &lc->body);
loop_ret = run_command_list_array(ctx, &lc->body, true);
if (loop_ret == TASK_STATUS_INTERRUPTED) {
goto interrupt;
} else if (loop_ret < 0) {
Expand Down Expand Up @@ -154,7 +154,7 @@ static int run_for_clause(struct mrsh_context *ctx, struct mrsh_for_clause *fc)
MRSH_VAR_ATTRIB_NONE);
word_index++;

loop_ret = run_command_list_array(ctx, &fc->body);
loop_ret = run_command_list_array(ctx, &fc->body, true);
if (loop_ret == TASK_STATUS_INTERRUPTED) {
goto interrupt;
} else if (loop_ret < 0) {
Expand Down Expand Up @@ -233,7 +233,7 @@ static int run_case_clause(struct mrsh_context *ctx, struct mrsh_case_clause *cc
}

if (selected) {
case_ret = run_command_list_array(ctx, &ci->body);
case_ret = run_command_list_array(ctx, &ci->body, true);
break;
}
}
Expand Down Expand Up @@ -261,7 +261,7 @@ int run_command(struct mrsh_context *ctx, struct mrsh_command *cmd) {
return run_simple_command(ctx, sc);
case MRSH_BRACE_GROUP:;
struct mrsh_brace_group *bg = mrsh_command_get_brace_group(cmd);
return run_command_list_array(ctx, &bg->body);
return run_command_list_array(ctx, &bg->body, true);
case MRSH_SUBSHELL:;
struct mrsh_subshell *s = mrsh_command_get_subshell(cmd);
return run_subshell(ctx, &s->body);
Expand All @@ -286,14 +286,26 @@ int run_command(struct mrsh_context *ctx, struct mrsh_command *cmd) {
abort();
}

int run_and_or_list(struct mrsh_context *ctx, struct mrsh_and_or_list *and_or_list) {
int run_and_or_list(struct mrsh_context *ctx, struct mrsh_and_or_list *and_or_list, bool allow_errexit) {
switch (and_or_list->type) {
case MRSH_AND_OR_LIST_PIPELINE:;
struct mrsh_pipeline *pl = mrsh_and_or_list_get_pipeline(and_or_list);
return run_pipeline(ctx, pl);
struct mrsh_state *state = ctx->state;
int ret = run_pipeline(ctx, pl);

/* Test for 'set -e' if context allows it. */
if ((state->options & MRSH_OPT_ERREXIT) && ret > 0 && allow_errexit && !pl->bang) {
struct mrsh_call_frame_priv *frame_priv = call_frame_get_priv(state->frame);

state->exit = ret;
frame_priv->branch_control = MRSH_BRANCH_EXIT;
return TASK_STATUS_INTERRUPTED;
}
return ret;

case MRSH_AND_OR_LIST_BINOP:;
struct mrsh_binop *binop = mrsh_and_or_list_get_binop(and_or_list);
int left_status = run_and_or_list(ctx, binop->left);
int left_status = run_and_or_list(ctx, binop->left, false);
switch (binop->type) {
case MRSH_BINOP_AND:
if (left_status != 0) {
Expand All @@ -306,7 +318,7 @@ int run_and_or_list(struct mrsh_context *ctx, struct mrsh_and_or_list *and_or_li
}
break;
}
return run_and_or_list(ctx, binop->right);
return run_and_or_list(ctx, binop->right, allow_errexit);
}
abort();
}
Expand All @@ -325,7 +337,7 @@ static struct mrsh_process *init_async_child(struct mrsh_context *ctx, pid_t pid
return proc;
}

int run_command_list_array(struct mrsh_context *ctx, struct mrsh_array *array) {
int run_command_list_array(struct mrsh_context *ctx, struct mrsh_array *array, bool allow_errexit) {
struct mrsh_state *state = ctx->state;
struct mrsh_state_priv *priv = state_get_priv(state);

Expand Down Expand Up @@ -366,7 +378,7 @@ int run_command_list_array(struct mrsh_context *ctx, struct mrsh_array *array) {
}
}

int ret = run_and_or_list(&child_ctx, list->and_or_list);
int ret = run_and_or_list(&child_ctx, list->and_or_list, allow_errexit);
if (ret < 0) {
exit(127);
}
Expand All @@ -376,7 +388,7 @@ int run_command_list_array(struct mrsh_context *ctx, struct mrsh_array *array) {
ret = 0;
init_async_child(&child_ctx, pid);
} else {
ret = run_and_or_list(ctx, list->and_or_list);
ret = run_and_or_list(ctx, list->and_or_list, allow_errexit);
if (ret < 0) {
return ret;
}
Expand Down Expand Up @@ -433,7 +445,7 @@ void mrsh_destroy_terminated_jobs(struct mrsh_state *state) {

int mrsh_run_program(struct mrsh_state *state, struct mrsh_program *prog) {
struct mrsh_context ctx = { .state = state };
int ret = run_command_list_array(&ctx, &prog->body);
int ret = run_command_list_array(&ctx, &prog->body, true);
run_pending_traps(state);
return ret;
}
Expand Down
31 changes: 31 additions & 0 deletions test/loop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ while [ "$n" != "fdsa" ]; do
done
echo stop

echo "basic until loop"
n=asdf
echo start
until [ "$n" = "fdsa" ]; do
echo "n: $n"
n="fdsa"
echo "n: $n"
done
echo stop

echo "continue in while loop should skip the iteration"
n=asdf
echo start
Expand All @@ -20,6 +30,16 @@ while [ "$n" != fdsa ]; do
done
echo stop

echo "continue in until loop should skip the iteration"
n=asdf
echo start
until [ "$n" = fdsa ]; do
n=fdsa
continue
echo "this shouldn't be printed"
done
echo stop

echo "break in while loop should stop the loop"
n=asdf
echo start
Expand All @@ -31,6 +51,17 @@ while true; do
done
echo stop

echo "break in until loop should stop the loop"
n=asdf
echo start
until false; do
if [ "$n" = fdsa ]; then
break
fi
n=fdsa
done
echo stop

echo "exit in infinite loop should exit immediately"
while true
do
Expand Down
1 change: 1 addition & 0 deletions test/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ test_files = [
'readonly.sh',
'redir.sh',
'return.sh',
'set-e.sh',
'subshell.sh',
'syntax.sh',
'ulimit.sh',
Expand Down
57 changes: 57 additions & 0 deletions test/set-e.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/bin/sh

set -e

# Pipeline with bang never exits with set -e
! false
! true
! false | cat
echo $? # 1

# Toggle it off and on few times
set +e; false; set -e
set +e; set +e; false; set -e; set -e

# set -e is ignored for any command of an AND-OR list other than the last.
false || echo This is printed
false || ./i_dont_exist || echo This is printed
./i_dont_exist && echo This is not printed

# It follows that AND-OR list with bang in last stage is immune to set -e
false || ! true

# Tests with command builtin
! command ./i_dont_exist
command ./i_dont_exist && echo This is not printed
command -v cd

# The -e setting shall be ignored when executing the compound list following
# the while, until, if, or elif reserved word
while false; do echo Loop with zero iterations; done
until false; do echo Loop with one iteration due to break; break; done
n=ab; until false ; [ "$n" = "ba" ]; do echo Loop with one iteration; n=ba; done
if false; then echo This is not printed; fi
if true; then true ; elif false; then echo This is not printed; fi

# Contrived, test that set +e and set -e take effect and return correct return
# value inside if's compound list
if set +e; false; then echo This is not printed; elif set -e; then echo This is printed; fi

# Here the false command causes the subshell to exit without executing
# echo one; however, echo two is executed because the exit status of the
# pipeline (false; echo one) | cat is zero.
(false; echo one) | cat; echo two

# Here the false in command substitution causes echo not be executed
for c in $(false; echo This is not printed); do echo $c; done

# Does not exit, because export successfully sets X to the empty string.
export X=$(false); echo This is printed

# This does exit and echo is not executed. At least in mrsh and bash.
# Commented out, as dash gets this wrong.
# readonly X; export X=$(false); echo This is not printed

# Finally exit shell, break out from infinite loop
while true; do false; echo This is not printed; done
echo This is not printed either