diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-28 23:04:34 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-28 23:04:34 +0000 |
commit | bcb25537d02b50ce26678defcf4f39d0c89f5b3b (patch) | |
tree | 4a8a8068c8078d893fb1036e5e5d33634f26cb7d | |
parent | cf22c89f9a7d19166fa038d3bb2bac3011f946fd (diff) | |
download | busybox-w32-bcb25537d02b50ce26678defcf4f39d0c89f5b3b.tar.gz busybox-w32-bcb25537d02b50ce26678defcf4f39d0c89f5b3b.tar.bz2 busybox-w32-bcb25537d02b50ce26678defcf4f39d0c89f5b3b.zip |
hush: implement break and continue
function old new delta
bltins 252 276 +24
builtin_continue - 12 +12
builtin_break - 12 +12
static.version_str 18 17 -1
run_list 1984 1948 -36
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 1/2 up/down: 48/-27) Total: 11 bytes
-rw-r--r-- | shell/hush.c | 171 | ||||
-rw-r--r-- | shell/hush_doc.txt | 13 |
2 files changed, 107 insertions, 77 deletions
diff --git a/shell/hush.c b/shell/hush.c index ca2a1d21f..21cb36564 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -54,7 +54,7 @@ | |||
54 | * to-do: | 54 | * to-do: |
55 | * port selected bugfixes from post-0.49 busybox lash - done? | 55 | * port selected bugfixes from post-0.49 busybox lash - done? |
56 | * change { and } from special chars to reserved words | 56 | * change { and } from special chars to reserved words |
57 | * builtins: break, continue, eval, return, set, trap, ulimit | 57 | * builtins: return, trap, ulimit |
58 | * test magic exec | 58 | * test magic exec |
59 | * check setting of global_argc and global_argv | 59 | * check setting of global_argc and global_argv |
60 | * follow IFS rules more precisely, including update semantics | 60 | * follow IFS rules more precisely, including update semantics |
@@ -74,6 +74,7 @@ | |||
74 | #include <fnmatch.h> | 74 | #include <fnmatch.h> |
75 | #endif | 75 | #endif |
76 | 76 | ||
77 | #define HUSH_VER_STR "0.9" | ||
77 | 78 | ||
78 | #if !BB_MMU && ENABLE_HUSH_TICK | 79 | #if !BB_MMU && ENABLE_HUSH_TICK |
79 | //#undef ENABLE_HUSH_TICK | 80 | //#undef ENABLE_HUSH_TICK |
@@ -230,7 +231,7 @@ void xxfree(void *ptr) | |||
230 | #define SPECIAL_VAR_SYMBOL 3 | 231 | #define SPECIAL_VAR_SYMBOL 3 |
231 | #define PARSEFLAG_EXIT_FROM_LOOP 1 | 232 | #define PARSEFLAG_EXIT_FROM_LOOP 1 |
232 | 233 | ||
233 | typedef enum { | 234 | typedef enum redir_type { |
234 | REDIRECT_INPUT = 1, | 235 | REDIRECT_INPUT = 1, |
235 | REDIRECT_OVERWRITE = 2, | 236 | REDIRECT_OVERWRITE = 2, |
236 | REDIRECT_APPEND = 3, | 237 | REDIRECT_APPEND = 3, |
@@ -253,14 +254,14 @@ static const struct { | |||
253 | { O_RDWR, 1, "<>" } | 254 | { O_RDWR, 1, "<>" } |
254 | }; | 255 | }; |
255 | 256 | ||
256 | typedef enum { | 257 | typedef enum pipe_style { |
257 | PIPE_SEQ = 1, | 258 | PIPE_SEQ = 1, |
258 | PIPE_AND = 2, | 259 | PIPE_AND = 2, |
259 | PIPE_OR = 3, | 260 | PIPE_OR = 3, |
260 | PIPE_BG = 4, | 261 | PIPE_BG = 4, |
261 | } pipe_style; | 262 | } pipe_style; |
262 | 263 | ||
263 | typedef enum { | 264 | typedef enum reserved_style { |
264 | RES_NONE = 0, | 265 | RES_NONE = 0, |
265 | #if ENABLE_HUSH_IF | 266 | #if ENABLE_HUSH_IF |
266 | RES_IF , | 267 | RES_IF , |
@@ -358,7 +359,7 @@ struct variable { | |||
358 | smallint flg_read_only; | 359 | smallint flg_read_only; |
359 | }; | 360 | }; |
360 | 361 | ||
361 | typedef struct { | 362 | typedef struct o_string { |
362 | char *data; | 363 | char *data; |
363 | int length; /* position where data is appended */ | 364 | int length; /* position where data is appended */ |
364 | int maxlen; | 365 | int maxlen; |
@@ -378,9 +379,9 @@ enum { | |||
378 | /* Used for initialization: o_string foo = NULL_O_STRING; */ | 379 | /* Used for initialization: o_string foo = NULL_O_STRING; */ |
379 | #define NULL_O_STRING { NULL } | 380 | #define NULL_O_STRING { NULL } |
380 | 381 | ||
381 | /* I can almost use ordinary FILE *. Is open_memstream() universally | 382 | /* I can almost use ordinary FILE*. Is open_memstream() universally |
382 | * available? Where is it documented? */ | 383 | * available? Where is it documented? */ |
383 | struct in_str { | 384 | typedef struct in_str { |
384 | const char *p; | 385 | const char *p; |
385 | /* eof_flag=1: last char in ->p is really an EOF */ | 386 | /* eof_flag=1: last char in ->p is really an EOF */ |
386 | char eof_flag; /* meaningless if ->p == NULL */ | 387 | char eof_flag; /* meaningless if ->p == NULL */ |
@@ -392,7 +393,7 @@ struct in_str { | |||
392 | FILE *file; | 393 | FILE *file; |
393 | int (*get) (struct in_str *); | 394 | int (*get) (struct in_str *); |
394 | int (*peek) (struct in_str *); | 395 | int (*peek) (struct in_str *); |
395 | }; | 396 | } in_str; |
396 | #define i_getch(input) ((input)->get(input)) | 397 | #define i_getch(input) ((input)->get(input)) |
397 | #define i_peek(input) ((input)->peek(input)) | 398 | #define i_peek(input) ((input)->peek(input)) |
398 | 399 | ||
@@ -403,7 +404,11 @@ enum { | |||
403 | CHAR_SPECIAL = 3, /* example: $ */ | 404 | CHAR_SPECIAL = 3, /* example: $ */ |
404 | }; | 405 | }; |
405 | 406 | ||
406 | #define HUSH_VER_STR "0.02" | 407 | enum { |
408 | BC_BREAK = 1, | ||
409 | BC_CONTINUE = 2, | ||
410 | }; | ||
411 | |||
407 | 412 | ||
408 | /* "Globals" within this file */ | 413 | /* "Globals" within this file */ |
409 | 414 | ||
@@ -429,6 +434,7 @@ struct globals { | |||
429 | struct pipe *toplevel_list; | 434 | struct pipe *toplevel_list; |
430 | smallint ctrl_z_flag; | 435 | smallint ctrl_z_flag; |
431 | #endif | 436 | #endif |
437 | smallint flag_break_continue; | ||
432 | smallint fake_mode; | 438 | smallint fake_mode; |
433 | /* these three support $?, $#, and $1 */ | 439 | /* these three support $?, $#, and $1 */ |
434 | smalluint last_return_code; | 440 | smalluint last_return_code; |
@@ -481,6 +487,7 @@ enum { run_list_level = 0 }; | |||
481 | #define global_argc (G.global_argc ) | 487 | #define global_argc (G.global_argc ) |
482 | #define last_return_code (G.last_return_code) | 488 | #define last_return_code (G.last_return_code) |
483 | #define ifs (G.ifs ) | 489 | #define ifs (G.ifs ) |
490 | #define flag_break_continue (G.flag_break_continue) | ||
484 | #define fake_mode (G.fake_mode ) | 491 | #define fake_mode (G.fake_mode ) |
485 | #define cwd (G.cwd ) | 492 | #define cwd (G.cwd ) |
486 | #define last_bg_pid (G.last_bg_pid ) | 493 | #define last_bg_pid (G.last_bg_pid ) |
@@ -713,6 +720,8 @@ static int builtin_shift(char **argv); | |||
713 | static int builtin_source(char **argv); | 720 | static int builtin_source(char **argv); |
714 | static int builtin_umask(char **argv); | 721 | static int builtin_umask(char **argv); |
715 | static int builtin_unset(char **argv); | 722 | static int builtin_unset(char **argv); |
723 | static int builtin_break(char **argv); | ||
724 | static int builtin_continue(char **argv); | ||
716 | //static int builtin_not_written(char **argv); | 725 | //static int builtin_not_written(char **argv); |
717 | 726 | ||
718 | /* Table of built-in functions. They can be forked or not, depending on | 727 | /* Table of built-in functions. They can be forked or not, depending on |
@@ -742,10 +751,10 @@ static const struct built_in_command bltins[] = { | |||
742 | #if ENABLE_HUSH_JOB | 751 | #if ENABLE_HUSH_JOB |
743 | BLTIN("bg" , builtin_fg_bg, "Resume a job in the background"), | 752 | BLTIN("bg" , builtin_fg_bg, "Resume a job in the background"), |
744 | #endif | 753 | #endif |
745 | // BLTIN("break" , builtin_not_written, "Exit for, while or until loop"), | 754 | BLTIN("break" , builtin_break, "Exit from a loop"), |
746 | BLTIN("cd" , builtin_cd, "Change directory"), | 755 | BLTIN("cd" , builtin_cd, "Change directory"), |
747 | // BLTIN("continue", builtin_not_written, "Continue for, while or until loop"), | 756 | BLTIN("continue", builtin_continue, "Start new loop iteration"), |
748 | BLTIN("echo" , builtin_echo, "Write strings to stdout"), | 757 | BLTIN("echo" , builtin_echo, "Write to stdout"), |
749 | BLTIN("eval" , builtin_eval, "Construct and run shell command"), | 758 | BLTIN("eval" , builtin_eval, "Construct and run shell command"), |
750 | BLTIN("exec" , builtin_exec, "Execute command, don't return to shell"), | 759 | BLTIN("exec" , builtin_exec, "Execute command, don't return to shell"), |
751 | BLTIN("exit" , builtin_exit, "Exit"), | 760 | BLTIN("exit" , builtin_exit, "Exit"), |
@@ -2016,29 +2025,25 @@ static int run_list(struct pipe *pi) | |||
2016 | char *case_word = NULL; | 2025 | char *case_word = NULL; |
2017 | #endif | 2026 | #endif |
2018 | #if ENABLE_HUSH_LOOPS | 2027 | #if ENABLE_HUSH_LOOPS |
2019 | struct pipe *loop_top = loop_top; /* just for compiler */ | 2028 | struct pipe *loop_top = NULL; |
2020 | char *for_varname = NULL; | 2029 | char *for_varname = NULL; |
2021 | char **for_lcur = NULL; | 2030 | char **for_lcur = NULL; |
2022 | char **for_list = NULL; | 2031 | char **for_list = NULL; |
2023 | smallint flag_run_loop = 0; | ||
2024 | smallint flag_goto_looptop = 0; | ||
2025 | #endif | 2032 | #endif |
2026 | smallint flag_skip = 1; | 2033 | smallint flag_skip = 1; |
2027 | smalluint rcode = 0; /* probably just for compiler */ | 2034 | smalluint rcode = 0; /* probably just for compiler */ |
2028 | #if ENABLE_HUSH_IF | 2035 | #if ENABLE_HUSH_IF |
2029 | smalluint cond_code = 0; | 2036 | smalluint cond_code = 0; |
2030 | ///experimentally off: last_cond_code seems to be bogus | ||
2031 | ///smalluint last_cond_code = 0; /* need double-buffer to handle "elif" */ | ||
2032 | #else | 2037 | #else |
2033 | enum { cond_code = 0, /* ///last_cond_code = 0 */ }; | 2038 | enum { cond_code = 0, }; |
2034 | #endif | 2039 | #endif |
2035 | /*reserved_style*/ smallint rword IF_HAS_NO_KEYWORDS(= RES_NONE); | 2040 | /*enum reserved_style*/ smallint rword = RES_NONE; |
2036 | /*reserved_style*/ smallint skip_more_for_this_rword = RES_XXXX; | 2041 | /*enum reserved_style*/ smallint skip_more_for_this_rword = RES_XXXX; |
2037 | 2042 | ||
2038 | debug_printf_exec("run_list start lvl %d\n", run_list_level + 1); | 2043 | debug_printf_exec("run_list start lvl %d\n", run_list_level + 1); |
2039 | 2044 | ||
2040 | #if ENABLE_HUSH_LOOPS | 2045 | #if ENABLE_HUSH_LOOPS |
2041 | /* check syntax for "for" */ | 2046 | /* Check syntax for "for" */ |
2042 | for (struct pipe *cpipe = pi; cpipe; cpipe = cpipe->next) { | 2047 | for (struct pipe *cpipe = pi; cpipe; cpipe = cpipe->next) { |
2043 | if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN) | 2048 | if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN) |
2044 | continue; | 2049 | continue; |
@@ -2108,20 +2113,16 @@ static int run_list(struct pipe *pi) | |||
2108 | } | 2113 | } |
2109 | #endif /* JOB */ | 2114 | #endif /* JOB */ |
2110 | 2115 | ||
2111 | /* Go through list of pipes, (maybe) executing them */ | 2116 | /* Go through list of pipes, (maybe) executing them. */ |
2112 | for (; pi; pi = USE_HUSH_LOOPS( flag_goto_looptop ? loop_top : ) pi->next) { | 2117 | for (; pi; pi = USE_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { |
2113 | IF_HAS_KEYWORDS(rword = pi->res_word;) | 2118 | IF_HAS_KEYWORDS(rword = pi->res_word;) |
2114 | IF_HAS_NO_KEYWORDS(rword = RES_NONE;) | 2119 | IF_HAS_NO_KEYWORDS(rword = RES_NONE;) |
2115 | debug_printf_exec(": rword=%d cond_code=%d last_cond_code=%d skip_more=%d flag_run_loop=%d\n", | 2120 | debug_printf_exec(": rword=%d cond_code=%d skip_more=%d\n", |
2116 | rword, cond_code, last_cond_code, skip_more_for_this_rword, flag_run_loop); | 2121 | rword, cond_code, skip_more_for_this_rword); |
2117 | #if ENABLE_HUSH_LOOPS | 2122 | #if ENABLE_HUSH_LOOPS |
2118 | if (rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) { | 2123 | if (rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) { |
2119 | /* start of a loop: remember it */ | 2124 | /* start of a loop: remember where loop starts */ |
2120 | flag_goto_looptop = 0; /* not yet reached final "done" */ | 2125 | loop_top = pi; |
2121 | // if (!loop_top) { /* hmm why this check is needed? */ | ||
2122 | // flag_run_loop = 0; /* suppose loop condition is false (for now) */ | ||
2123 | loop_top = pi; /* remember where loop starts */ | ||
2124 | // } | ||
2125 | } | 2126 | } |
2126 | #endif | 2127 | #endif |
2127 | if (rword == skip_more_for_this_rword && flag_skip) { | 2128 | if (rword == skip_more_for_this_rword && flag_skip) { |
@@ -2134,18 +2135,21 @@ static int run_list(struct pipe *pi) | |||
2134 | flag_skip = 1; | 2135 | flag_skip = 1; |
2135 | skip_more_for_this_rword = RES_XXXX; | 2136 | skip_more_for_this_rword = RES_XXXX; |
2136 | #if ENABLE_HUSH_IF | 2137 | #if ENABLE_HUSH_IF |
2137 | /// if (rword == RES_THEN) // || rword == RES_ELSE) | 2138 | if (cond_code) { |
2138 | /// cond_code = last_cond_code; | 2139 | if (rword == RES_THEN) { |
2139 | if (rword == RES_THEN && cond_code) | 2140 | /* "if <false> THEN cmd": skip cmd */ |
2140 | continue; /* "if <false> THEN cmd": skip cmd */ | 2141 | continue; |
2141 | if (rword == RES_ELSE && !cond_code) | 2142 | } |
2142 | //continue; /* "if <true> then ... ELSE cmd": skip cmd */ | 2143 | } else { |
2143 | break; //TEST | 2144 | if (rword == RES_ELSE || rword == RES_ELIF) { |
2144 | if (rword == RES_ELIF && !cond_code) | 2145 | /* "if <true> then ... ELSE/ELIF cmd": |
2145 | break; /* "if <true> then ... ELIF cmd": skip cmd and all following ones */ | 2146 | * skip cmd and all following ones */ |
2147 | break; | ||
2148 | } | ||
2149 | } | ||
2146 | #endif | 2150 | #endif |
2147 | #if ENABLE_HUSH_LOOPS | 2151 | #if ENABLE_HUSH_LOOPS |
2148 | if (rword == RES_FOR && pi->num_progs) { /* hmm why "&& pi->num_progs"? */ | 2152 | if (rword == RES_FOR) { /* && pi->num_progs - always == 1 */ |
2149 | if (!for_lcur) { | 2153 | if (!for_lcur) { |
2150 | /* first loop through for */ | 2154 | /* first loop through for */ |
2151 | 2155 | ||
@@ -2161,7 +2165,7 @@ static int run_list(struct pipe *pi) | |||
2161 | if (pi->next->res_word == RES_IN) { | 2165 | if (pi->next->res_word == RES_IN) { |
2162 | /* if no variable values after "in" we skip "for" */ | 2166 | /* if no variable values after "in" we skip "for" */ |
2163 | if (!pi->next->progs->argv) | 2167 | if (!pi->next->progs->argv) |
2164 | continue; | 2168 | break; |
2165 | vals = pi->next->progs->argv; | 2169 | vals = pi->next->progs->argv; |
2166 | } /* else: "for var; do..." -> assume "$@" list */ | 2170 | } /* else: "for var; do..." -> assume "$@" list */ |
2167 | /* create list of variable values */ | 2171 | /* create list of variable values */ |
@@ -2171,7 +2175,6 @@ static int run_list(struct pipe *pi) | |||
2171 | debug_print_strings("for_list", for_list); | 2175 | debug_print_strings("for_list", for_list); |
2172 | for_varname = pi->progs->argv[0]; | 2176 | for_varname = pi->progs->argv[0]; |
2173 | pi->progs->argv[0] = NULL; | 2177 | pi->progs->argv[0] = NULL; |
2174 | flag_run_loop = 1; /* "for" has no loop condition, loop... */ | ||
2175 | } | 2178 | } |
2176 | free(pi->progs->argv[0]); | 2179 | free(pi->progs->argv[0]); |
2177 | if (!*for_lcur) { | 2180 | if (!*for_lcur) { |
@@ -2179,33 +2182,22 @@ static int run_list(struct pipe *pi) | |||
2179 | free(for_list); | 2182 | free(for_list); |
2180 | for_list = NULL; | 2183 | for_list = NULL; |
2181 | for_lcur = NULL; | 2184 | for_lcur = NULL; |
2182 | flag_run_loop = 0; /* ... until end of value list */ | ||
2183 | pi->progs->argv[0] = for_varname; | 2185 | pi->progs->argv[0] = for_varname; |
2184 | continue; | 2186 | break; |
2185 | } | 2187 | } |
2186 | /* insert next value from for_lcur */ | 2188 | /* insert next value from for_lcur */ |
2187 | //TODO: does it need escaping? | 2189 | //TODO: does it need escaping? |
2188 | pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++); | 2190 | pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++); |
2189 | } | 2191 | } |
2190 | if (rword == RES_IN) /* "for v IN list; do ..." - no pipe to execute here */ | 2192 | if (rword == RES_IN) /* "for v IN list;..." - "in" has no cmds anyway */ |
2191 | continue; | 2193 | continue; |
2192 | if (rword == RES_DO) { /* "...; DO cmd; cmd" - this pipe is in loop body */ | 2194 | if (rword == RES_DONE) { |
2193 | if (!flag_run_loop) | 2195 | continue; /* "done" has no cmds too */ |
2194 | continue; /* we are skipping this iteration */ | ||
2195 | } | ||
2196 | if (rword == RES_DONE) { /* end of loop? */ | ||
2197 | if (flag_run_loop) { | ||
2198 | flag_goto_looptop = 1; | ||
2199 | // } else { | ||
2200 | // loop_top = NULL; | ||
2201 | } | ||
2202 | continue; //TEST /* "done" has no cmd anyway */ | ||
2203 | } | 2196 | } |
2204 | #endif | 2197 | #endif |
2205 | #if ENABLE_HUSH_CASE | 2198 | #if ENABLE_HUSH_CASE |
2206 | if (rword == RES_CASE) { | 2199 | if (rword == RES_CASE) { |
2207 | case_word = expand_strvec_to_string(pi->progs->argv); | 2200 | case_word = expand_strvec_to_string(pi->progs->argv); |
2208 | //bb_error_msg("case: arg:'%s' case_word:'%s'", pi->progs->argv[0], case_word); | ||
2209 | continue; | 2201 | continue; |
2210 | } | 2202 | } |
2211 | if (rword == RES_MATCH) { | 2203 | if (rword == RES_MATCH) { |
@@ -2215,35 +2207,53 @@ static int run_list(struct pipe *pi) | |||
2215 | /* all prev words didn't match, does this one match? */ | 2207 | /* all prev words didn't match, does this one match? */ |
2216 | pattern = expand_strvec_to_string(pi->progs->argv); | 2208 | pattern = expand_strvec_to_string(pi->progs->argv); |
2217 | /* TODO: which FNM_xxx flags to use? */ | 2209 | /* TODO: which FNM_xxx flags to use? */ |
2218 | /* ///last_ */ cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); | 2210 | cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); |
2219 | //bb_error_msg("fnmatch('%s','%s'):%d", pattern, case_word, cond_code); | ||
2220 | free(pattern); | 2211 | free(pattern); |
2221 | if (/* ///last_ */ cond_code == 0) { /* match! we will execute this branch */ | 2212 | if (cond_code == 0) { /* match! we will execute this branch */ |
2222 | free(case_word); /* make future "word)" stop */ | 2213 | free(case_word); /* make future "word)" stop */ |
2223 | case_word = NULL; | 2214 | case_word = NULL; |
2224 | } | 2215 | } |
2225 | continue; | 2216 | continue; |
2226 | } | 2217 | } |
2227 | if (rword == RES_CASEI) { /* inside of a case branch */ | 2218 | if (rword == RES_CASEI) { /* inside of a case branch */ |
2228 | if (/* ///last_ */ cond_code != 0) | 2219 | if (cond_code != 0) |
2229 | continue; /* not matched yet, skip this pipe */ | 2220 | continue; /* not matched yet, skip this pipe */ |
2230 | } | 2221 | } |
2231 | #endif | 2222 | #endif |
2232 | if (pi->num_progs == 0) | 2223 | if (pi->num_progs == 0) |
2233 | continue; | 2224 | continue; |
2234 | 2225 | ||
2235 | /* After analyzing all keywrds and conditions, we decided | 2226 | /* After analyzing all keywords and conditions, we decided |
2236 | * to execute this pipe */ | 2227 | * to execute this pipe. NB: has to do checkjobs(NULL) |
2228 | * after run_pipe() to collect any background children, | ||
2229 | * even if list execution is to be stopped. */ | ||
2237 | debug_printf_exec(": run_pipe with %d members\n", pi->num_progs); | 2230 | debug_printf_exec(": run_pipe with %d members\n", pi->num_progs); |
2238 | { | 2231 | { |
2239 | int r; | 2232 | int r; |
2233 | flag_break_continue = 0; | ||
2240 | rcode = r = run_pipe(pi); /* NB: rcode is a smallint */ | 2234 | rcode = r = run_pipe(pi); /* NB: rcode is a smallint */ |
2241 | if (r != -1) { | 2235 | if (r != -1) { |
2242 | /* We only ran a builtin: rcode was set by the return value | 2236 | /* we only ran a builtin: rcode is already known |
2243 | * of run_pipe(), and we don't need to wait for anything. */ | 2237 | * and we don't need to wait for anything. */ |
2238 | /* was it "break" or "continue"? */ | ||
2239 | if (flag_break_continue) { | ||
2240 | smallint fbc = flag_break_continue; | ||
2241 | /* we might fall into outer *loop*, | ||
2242 | * don't want to break it too */ | ||
2243 | flag_break_continue = 0; | ||
2244 | if (loop_top) { | ||
2245 | if (fbc == BC_BREAK) | ||
2246 | goto check_jobs_and_break; | ||
2247 | /* "continue": simulate end of loop */ | ||
2248 | rword = RES_DONE; | ||
2249 | continue; | ||
2250 | } | ||
2251 | bb_error_msg("break/continue: only meaningful in a loop"); | ||
2252 | /* bash compat: exit code is still 0 */ | ||
2253 | } | ||
2244 | } else if (pi->followup == PIPE_BG) { | 2254 | } else if (pi->followup == PIPE_BG) { |
2245 | /* What does bash do with attempts to background builtins? */ | 2255 | /* what does bash do with attempts to background builtins? */ |
2246 | /* Even bash 3.2 doesn't do that well with nested bg: | 2256 | /* even bash 3.2 doesn't do that well with nested bg: |
2247 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". | 2257 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". |
2248 | * I'm NOT treating inner &'s as jobs */ | 2258 | * I'm NOT treating inner &'s as jobs */ |
2249 | #if ENABLE_HUSH_JOB | 2259 | #if ENABLE_HUSH_JOB |
@@ -2271,16 +2281,19 @@ static int run_list(struct pipe *pi) | |||
2271 | /* Analyze how result affects subsequent commands */ | 2281 | /* Analyze how result affects subsequent commands */ |
2272 | #if ENABLE_HUSH_IF | 2282 | #if ENABLE_HUSH_IF |
2273 | if (rword == RES_IF || rword == RES_ELIF) | 2283 | if (rword == RES_IF || rword == RES_ELIF) |
2274 | /* ///last_cond_code = */ cond_code = rcode; | 2284 | cond_code = rcode; |
2275 | #endif | 2285 | #endif |
2276 | #if ENABLE_HUSH_LOOPS | 2286 | #if ENABLE_HUSH_LOOPS |
2277 | if (rword == RES_WHILE) { | 2287 | if (rword == RES_WHILE) { |
2278 | flag_run_loop = !rcode; | 2288 | if (rcode) |
2279 | debug_printf_exec(": setting flag_run_loop=%d\n", flag_run_loop); | 2289 | goto check_jobs_and_break; |
2280 | } | 2290 | } |
2281 | if (rword == RES_UNTIL) { | 2291 | if (rword == RES_UNTIL) { |
2282 | flag_run_loop = rcode; | 2292 | if (!rcode) { |
2283 | debug_printf_exec(": setting flag_run_loop=%d\n", flag_run_loop); | 2293 | check_jobs_and_break: |
2294 | checkjobs(NULL); | ||
2295 | break; | ||
2296 | } | ||
2284 | } | 2297 | } |
2285 | #endif | 2298 | #endif |
2286 | if ((rcode == 0 && pi->followup == PIPE_OR) | 2299 | if ((rcode == 0 && pi->followup == PIPE_OR) |
@@ -4498,3 +4511,15 @@ static int builtin_unset(char **argv) | |||
4498 | unset_local_var(argv[1]); | 4511 | unset_local_var(argv[1]); |
4499 | return EXIT_SUCCESS; | 4512 | return EXIT_SUCCESS; |
4500 | } | 4513 | } |
4514 | |||
4515 | static int builtin_break(char **argv UNUSED_PARAM) | ||
4516 | { | ||
4517 | flag_break_continue = BC_BREAK; | ||
4518 | return EXIT_SUCCESS; | ||
4519 | } | ||
4520 | |||
4521 | static int builtin_continue(char **argv UNUSED_PARAM) | ||
4522 | { | ||
4523 | flag_break_continue = BC_CONTINUE; | ||
4524 | return EXIT_SUCCESS; | ||
4525 | } | ||
diff --git a/shell/hush_doc.txt b/shell/hush_doc.txt index 39f7dcee5..c68dc2416 100644 --- a/shell/hush_doc.txt +++ b/shell/hush_doc.txt | |||
@@ -2,9 +2,10 @@ | |||
2 | 2 | ||
3 | Command parsing | 3 | Command parsing |
4 | 4 | ||
5 | Command parsing results in "pipe" structures. "Pipe" structure | 5 | Command parsing results in a list of "pipe" structures. |
6 | does not always correspond to what sh language calls "pipe", | 6 | This list correspond not only to usual "pipe1 || pipe2 && pipe3" |
7 | it also controls execution of if, while, etc statements. | 7 | lists, but it also controls execution of if, while, etc statements. |
8 | Every such statement is a list for hush. List consists of pipes. | ||
8 | 9 | ||
9 | struct pipe fields: | 10 | struct pipe fields: |
10 | smallint res_word - "none" for normal commands, | 11 | smallint res_word - "none" for normal commands, |
@@ -18,7 +19,7 @@ Blocks of commands { pipe; pipe; } and (pipe; pipe) are represented | |||
18 | as one pipe struct with one progs[0] element which is a "group" - | 19 | as one pipe struct with one progs[0] element which is a "group" - |
19 | struct child_prog can contain a list of pipes. Sometimes these | 20 | struct child_prog can contain a list of pipes. Sometimes these |
20 | "groups" are created implicitly, e.g. every control | 21 | "groups" are created implicitly, e.g. every control |
21 | statement (if, while, etc) sits inside its own "pipe" struct). | 22 | statement (if, while, etc) sits inside its own group. |
22 | 23 | ||
23 | res_word controls statement execution. Examples: | 24 | res_word controls statement execution. Examples: |
24 | 25 | ||
@@ -41,6 +42,10 @@ res_word=NONE followup=SEQ | |||
41 | pipe 4 res_word=NONE followup=(null) | 42 | pipe 4 res_word=NONE followup=(null) |
42 | pipe 1 res_word=NONE followup=SEQ | 43 | pipe 1 res_word=NONE followup=SEQ |
43 | 44 | ||
45 | Above you see that if is a list, and it sits in a {} group | ||
46 | implicitly created by hush. Also note two THEN res_word's - | ||
47 | it is explained below. | ||
48 | |||
44 | "if true; then { echo Hello; true; }; fi" - | 49 | "if true; then { echo Hello; true; }; fi" - |
45 | pipe 0 res_word=NONE followup=SEQ | 50 | pipe 0 res_word=NONE followup=SEQ |
46 | prog 0 group {}: | 51 | prog 0 group {}: |