summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shell/hush.c39
-rw-r--r--shell/hush_test/hush-misc/break5.right13
-rwxr-xr-xshell/hush_test/hush-misc/break5.tests4
3 files changed, 43 insertions, 13 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 8ffc612..eb70c9d 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -446,6 +446,7 @@ struct globals {
int global_argc;
#if ENABLE_HUSH_LOOPS
unsigned depth_break_continue;
+ unsigned depth_of_loop;
#endif
pid_t last_bg_pid;
const char *ifs;
@@ -496,6 +497,7 @@ enum { run_list_level = 0 };
#define ifs (G.ifs )
#define flag_break_continue (G.flag_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 cwd (G.cwd )
#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) {
/* start of a loop: remember where loop starts */
loop_top = pi;
+ depth_of_loop++;
}
#endif
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)
flag_skip = 0;
+ /* it is "<false> && CMD" or "<true> || CMD"
+ * and we should not execute CMD */
continue;
}
flag_skip = 1;
@@ -2277,15 +2280,13 @@ static int run_list(struct pipe *pi)
depth_break_continue--;
if (depth_break_continue == 0)
flag_break_continue = 0;
- if (depth_break_continue != 0 || fbc == BC_BREAK)
- goto check_jobs_and_break;
- /* "continue": simulate end of loop */
- rword = RES_DONE;
- continue;
- }
- flag_break_continue = 0;
- bb_error_msg("break/continue: only meaningful in a loop");
- /* bash compat: exit code is still 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)
+ goto check_jobs_and_break;
+ /* "continue": simulate end of loop */
+ rword = RES_DONE;
+ continue;
}
#endif
} else if (pi->followup == PIPE_BG) {
@@ -2358,6 +2359,8 @@ static int run_list(struct pipe *pi)
#endif
debug_printf_exec("run_list lvl %d return %d\n", run_list_level + 1, rcode);
#if ENABLE_HUSH_LOOPS
+ if (loop_top)
+ depth_of_loop--;
free(for_list);
#endif
#if ENABLE_HUSH_CASE
@@ -4554,6 +4557,10 @@ static int builtin_unset(char **argv)
#if ENABLE_HUSH_LOOPS
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 */
depth_break_continue = 1;
if (argv[1]) {
@@ -4564,12 +4571,18 @@ static int builtin_break(char **argv)
depth_break_continue = UINT_MAX;
}
}
+ if (depth_of_loop > depth_break_continue)
+ depth_break_continue = depth_of_loop;
return EXIT_SUCCESS;
}
static int builtin_continue(char **argv)
{
- flag_break_continue++; /* BC_CONTINUE = 2 = 1+1 */
- return builtin_break(argv);
+ if (depth_of_loop) {
+ flag_break_continue++; /* BC_CONTINUE = 2 = 1+1 */
+ return builtin_break(argv);
+ }
+ bb_error_msg("%s: only meaningful in a loop", "continue");
+ return EXIT_SUCCESS; /* bash compat */
}
#endif
diff --git a/shell/hush_test/hush-misc/break5.right b/shell/hush_test/hush-misc/break5.right
new file mode 100644
index 0000000..0b9df2a
--- /dev/null
+++ b/shell/hush_test/hush-misc/break5.right
@@ -0,0 +1,13 @@
+A
+B
+0
+A:a
+B
+D
+A:b
+B
+D
+A:c
+B
+D
+0
diff --git a/shell/hush_test/hush-misc/break5.tests b/shell/hush_test/hush-misc/break5.tests
new file mode 100755
index 0000000..273e040
--- /dev/null
+++ b/shell/hush_test/hush-misc/break5.tests
@@ -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 $?