hush: fix break'ing out of {} and () groups; with testcase
function old new delta builtin_break 93 129 +36 builtin_continue 21 47 +26 run_list 1973 1976 +3
This commit is contained in:
parent
d91afa33fd
commit
fcf37c3183
25
shell/hush.c
25
shell/hush.c
@ -446,6 +446,7 @@ struct globals {
|
|||||||
int global_argc;
|
int global_argc;
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
unsigned depth_break_continue;
|
unsigned depth_break_continue;
|
||||||
|
unsigned depth_of_loop;
|
||||||
#endif
|
#endif
|
||||||
pid_t last_bg_pid;
|
pid_t last_bg_pid;
|
||||||
const char *ifs;
|
const char *ifs;
|
||||||
@ -496,6 +497,7 @@ enum { run_list_level = 0 };
|
|||||||
#define ifs (G.ifs )
|
#define ifs (G.ifs )
|
||||||
#define flag_break_continue (G.flag_break_continue )
|
#define flag_break_continue (G.flag_break_continue )
|
||||||
#define depth_break_continue (G.depth_break_continue)
|
#define depth_break_continue (G.depth_break_continue)
|
||||||
|
#define depth_of_loop (G.depth_of_loop )
|
||||||
#define fake_mode (G.fake_mode )
|
#define fake_mode (G.fake_mode )
|
||||||
#define cwd (G.cwd )
|
#define cwd (G.cwd )
|
||||||
#define last_bg_pid (G.last_bg_pid )
|
#define last_bg_pid (G.last_bg_pid )
|
||||||
@ -2153,13 +2155,14 @@ static int run_list(struct pipe *pi)
|
|||||||
if (rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) {
|
if (rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) {
|
||||||
/* start of a loop: remember where loop starts */
|
/* start of a loop: remember where loop starts */
|
||||||
loop_top = pi;
|
loop_top = pi;
|
||||||
|
depth_of_loop++;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (rword == skip_more_for_this_rword && flag_skip) {
|
if (rword == skip_more_for_this_rword && flag_skip) {
|
||||||
/* it is "<false> && CMD" or "<true> || CMD"
|
|
||||||
* and we should not execute CMD */
|
|
||||||
if (pi->followup == PIPE_SEQ)
|
if (pi->followup == PIPE_SEQ)
|
||||||
flag_skip = 0;
|
flag_skip = 0;
|
||||||
|
/* it is "<false> && CMD" or "<true> || CMD"
|
||||||
|
* and we should not execute CMD */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
flag_skip = 1;
|
flag_skip = 1;
|
||||||
@ -2277,16 +2280,14 @@ static int run_list(struct pipe *pi)
|
|||||||
depth_break_continue--;
|
depth_break_continue--;
|
||||||
if (depth_break_continue == 0)
|
if (depth_break_continue == 0)
|
||||||
flag_break_continue = 0;
|
flag_break_continue = 0;
|
||||||
|
/* else: e.g. "continue 2" should *break* once, *then* continue */
|
||||||
|
} /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
|
||||||
if (depth_break_continue != 0 || fbc == BC_BREAK)
|
if (depth_break_continue != 0 || fbc == BC_BREAK)
|
||||||
goto check_jobs_and_break;
|
goto check_jobs_and_break;
|
||||||
/* "continue": simulate end of loop */
|
/* "continue": simulate end of loop */
|
||||||
rword = RES_DONE;
|
rword = RES_DONE;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
flag_break_continue = 0;
|
|
||||||
bb_error_msg("break/continue: only meaningful in a loop");
|
|
||||||
/* bash compat: exit code is still 0 */
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
} else if (pi->followup == PIPE_BG) {
|
} else if (pi->followup == PIPE_BG) {
|
||||||
/* what does bash do with attempts to background builtins? */
|
/* what does bash do with attempts to background builtins? */
|
||||||
@ -2358,6 +2359,8 @@ static int run_list(struct pipe *pi)
|
|||||||
#endif
|
#endif
|
||||||
debug_printf_exec("run_list lvl %d return %d\n", run_list_level + 1, rcode);
|
debug_printf_exec("run_list lvl %d return %d\n", run_list_level + 1, rcode);
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
|
if (loop_top)
|
||||||
|
depth_of_loop--;
|
||||||
free(for_list);
|
free(for_list);
|
||||||
#endif
|
#endif
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
@ -4554,6 +4557,10 @@ static int builtin_unset(char **argv)
|
|||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
static int builtin_break(char **argv)
|
static int builtin_break(char **argv)
|
||||||
{
|
{
|
||||||
|
if (depth_of_loop == 0) {
|
||||||
|
bb_error_msg("%s: only meaningful in a loop", "break");
|
||||||
|
return EXIT_SUCCESS; /* bash compat */
|
||||||
|
}
|
||||||
flag_break_continue++; /* BC_BREAK = 1 */
|
flag_break_continue++; /* BC_BREAK = 1 */
|
||||||
depth_break_continue = 1;
|
depth_break_continue = 1;
|
||||||
if (argv[1]) {
|
if (argv[1]) {
|
||||||
@ -4564,12 +4571,18 @@ static int builtin_break(char **argv)
|
|||||||
depth_break_continue = UINT_MAX;
|
depth_break_continue = UINT_MAX;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (depth_of_loop > depth_break_continue)
|
||||||
|
depth_break_continue = depth_of_loop;
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int builtin_continue(char **argv)
|
static int builtin_continue(char **argv)
|
||||||
{
|
{
|
||||||
|
if (depth_of_loop) {
|
||||||
flag_break_continue++; /* BC_CONTINUE = 2 = 1+1 */
|
flag_break_continue++; /* BC_CONTINUE = 2 = 1+1 */
|
||||||
return builtin_break(argv);
|
return builtin_break(argv);
|
||||||
|
}
|
||||||
|
bb_error_msg("%s: only meaningful in a loop", "continue");
|
||||||
|
return EXIT_SUCCESS; /* bash compat */
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
13
shell/hush_test/hush-misc/break5.right
Normal file
13
shell/hush_test/hush-misc/break5.right
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
A
|
||||||
|
B
|
||||||
|
0
|
||||||
|
A:a
|
||||||
|
B
|
||||||
|
D
|
||||||
|
A:b
|
||||||
|
B
|
||||||
|
D
|
||||||
|
A:c
|
||||||
|
B
|
||||||
|
D
|
||||||
|
0
|
4
shell/hush_test/hush-misc/break5.tests
Executable file
4
shell/hush_test/hush-misc/break5.tests
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
while true; do echo A; { echo B; break; echo C; }; echo D; done
|
||||||
|
echo $?
|
||||||
|
for v in a b c; do echo A:$v; (echo B; break; echo C); echo D; done
|
||||||
|
echo $?
|
Loading…
Reference in New Issue
Block a user