hush: fix a bunch of obscure while/until/continue bugs

function                                             old     new   delta
run_list                                            1159    1214     +55
done_pipe                                            106     123     +17
done_command                                          86      98     +12
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/0 up/down: 84/0)               Total: 84 bytes
This commit is contained in:
Denis Vlasenko 2009-04-06 18:08:35 +00:00
parent 8f8d013afc
commit cd418a2670
10 changed files with 66 additions and 20 deletions

View File

@ -2994,7 +2994,7 @@ static void debug_print_tree(struct pipe *pi, int lvl)
struct command *command = &pi->cmds[prn]; struct command *command = &pi->cmds[prn];
char **argv = command->argv; char **argv = command->argv;
fprintf(stderr, "%*s prog %d assignment_cnt:%d", fprintf(stderr, "%*s cmd %d assignment_cnt:%d",
lvl*2, "", prn, lvl*2, "", prn,
command->assignment_cnt); command->assignment_cnt);
if (command->group) { if (command->group) {
@ -3038,8 +3038,8 @@ static int run_list(struct pipe *pi)
#else #else
enum { cond_code = 0 }; enum { cond_code = 0 };
#endif #endif
/*enum reserved_style*/ smallint rword; smallint rword; /* enum reserved_style */
/*enum reserved_style*/ smallint last_rword; smallint last_rword; /* ditto */
debug_printf_exec("run_list start lvl %d\n", G.run_list_level + 1); debug_printf_exec("run_list start lvl %d\n", G.run_list_level + 1);
@ -3307,8 +3307,7 @@ static int run_list(struct pipe *pi)
if (G.run_list_level == 1) if (G.run_list_level == 1)
insert_bg_job(pi); insert_bg_job(pi);
#endif #endif
rcode = EXIT_SUCCESS; G.last_return_code = rcode = EXIT_SUCCESS;
G.last_return_code = EXIT_SUCCESS;
debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
} else { } else {
#if ENABLE_HUSH_JOB #if ENABLE_HUSH_JOB
@ -3334,17 +3333,23 @@ static int run_list(struct pipe *pi)
cond_code = rcode; cond_code = rcode;
#endif #endif
#if ENABLE_HUSH_LOOPS #if ENABLE_HUSH_LOOPS
if (rword == RES_WHILE) { /* Beware of "while false; true; do ..."! */
if (rcode) { if (pi->next && pi->next->res_word == RES_DO) {
rcode = 0; /* "while false; do...done" - exitcode 0 */ if (rword == RES_WHILE) {
goto check_jobs_and_break; if (rcode) {
/* "while false; do...done" - exitcode 0 */
G.last_return_code = rcode = EXIT_SUCCESS;
debug_printf_exec(": while expr is false: breaking (exitcode:EXIT_SUCCESS)\n");
goto check_jobs_and_break;
}
} }
} if (rword == RES_UNTIL) {
if (rword == RES_UNTIL) { if (!rcode) {
if (!rcode) { debug_printf_exec(": until expr is true: breaking\n");
check_jobs_and_break: check_jobs_and_break:
checkjobs(NULL); checkjobs(NULL);
break; break;
}
} }
} }
#endif #endif
@ -3498,10 +3503,12 @@ static int done_command(struct parse_context *ctx)
&& command->redirects == NULL && command->redirects == NULL
) { ) {
debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
memset(command, 0, sizeof(*command)); /* paranoia */
return pi->num_cmds; return pi->num_cmds;
} }
pi->num_cmds++; pi->num_cmds++;
debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds); debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
//debug_print_tree(ctx->list_head, 20);
} else { } else {
debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds); debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds);
} }
@ -3526,16 +3533,26 @@ static void done_pipe(struct parse_context *ctx, pipe_style type)
/* Close previous command */ /* Close previous command */
not_null = done_command(ctx); not_null = done_command(ctx);
ctx->pipe->followup = type; ctx->pipe->followup = type;
IF_HAS_KEYWORDS(ctx->pipe->pi_inverted = ctx->ctx_inverted;) #if HAS_KEYWORDS
IF_HAS_KEYWORDS(ctx->ctx_inverted = 0;) ctx->pipe->pi_inverted = ctx->ctx_inverted;
IF_HAS_KEYWORDS(ctx->pipe->res_word = ctx->ctx_res_w;) ctx->ctx_inverted = 0;
ctx->pipe->res_word = ctx->ctx_res_w;
#endif
/* Without this check, even just <enter> on command line generates /* Without this check, even just <enter> on command line generates
* tree of three NOPs (!). Which is harmless but annoying. * tree of three NOPs (!). Which is harmless but annoying.
* IOW: it is safe to do it unconditionally. * IOW: it is safe to do it unconditionally.
* RES_NONE case is for "for a in; do ..." (empty IN set) * RES_NONE case is for "for a in; do ..." (empty IN set)
* to work, possibly other cases too. */ * and other cases to work. */
if (not_null IF_HAS_KEYWORDS(|| ctx->ctx_res_w != RES_NONE)) { if (not_null
#if HAS_KEYWORDS
|| ctx->ctx_res_w == RES_FI
|| ctx->ctx_res_w == RES_DONE
|| ctx->ctx_res_w == RES_FOR
|| ctx->ctx_res_w == RES_IN
|| ctx->ctx_res_w == RES_ESAC
#endif
) {
struct pipe *new_p; struct pipe *new_p;
debug_printf_parse("done_pipe: adding new pipe: " debug_printf_parse("done_pipe: adding new pipe: "
"not_null:%d ctx->ctx_res_w:%d\n", "not_null:%d ctx->ctx_res_w:%d\n",
@ -3564,6 +3581,7 @@ static void done_pipe(struct parse_context *ctx, pipe_style type)
* ctx->command = &ctx->pipe->cmds[0]; * ctx->command = &ctx->pipe->cmds[0];
*/ */
done_command(ctx); done_command(ctx);
//debug_print_tree(ctx->list_head, 10);
} }
debug_printf_parse("done_pipe return\n"); debug_printf_parse("done_pipe return\n");
} }
@ -5483,6 +5501,7 @@ static int builtin_exec(char **argv)
static int builtin_exit(char **argv) static int builtin_exit(char **argv)
{ {
debug_printf_exec("%s()\n", __func__);
// TODO: bash does it ONLY on top-level sh exit (+interacive only?) // TODO: bash does it ONLY on top-level sh exit (+interacive only?)
//puts("exit"); /* bash does it */ //puts("exit"); /* bash does it */
// TODO: warn if we have background jobs: "There are stopped jobs" // TODO: warn if we have background jobs: "There are stopped jobs"

View File

@ -0,0 +1 @@
Ok:1

View File

@ -0,0 +1,3 @@
e=''
(while test $e && exit 1; true; do e=1; continue; done)
echo Ok:$?

View File

@ -0,0 +1,2 @@
0
0

View File

@ -0,0 +1,3 @@
# Test that "continue" does affect exitcode (sets to 0)
e=''
while echo $?; test $e && exit; true; do e=1; false; continue; done

View File

@ -0,0 +1,3 @@
1
1
Ok:0

View File

@ -0,0 +1,11 @@
x=1
until test "$x" = 4; do echo $x; x=4; done
# We had a bug in multi-line form
x=1
until test "$x" = 4; do
echo $x
x=4
done
echo Ok:$?

View File

@ -0,0 +1,2 @@
Hello
OK:0

View File

@ -0,0 +1,2 @@
while echo Hello; false; do echo NOT SHOWN; done
echo OK:$?

View File

@ -46,7 +46,7 @@ do_test()
test -x "$x" || continue test -x "$x" || continue
name="${x%%.tests}" name="${x%%.tests}"
test -f "$name.right" || continue test -f "$name.right" || continue
# echo Running test: "$name.right" # echo Running test: "$x"
{ {
"$THIS_SH" "./$x" >"$name.xx" 2>&1 "$THIS_SH" "./$x" >"$name.xx" 2>&1
diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail"