aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-07-28 23:04:34 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-07-28 23:04:34 +0000
commitbcb25537d02b50ce26678defcf4f39d0c89f5b3b (patch)
tree4a8a8068c8078d893fb1036e5e5d33634f26cb7d
parentcf22c89f9a7d19166fa038d3bb2bac3011f946fd (diff)
downloadbusybox-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.c171
-rw-r--r--shell/hush_doc.txt13
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
233typedef enum { 234typedef 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
256typedef enum { 257typedef 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
263typedef enum { 264typedef 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
361typedef struct { 362typedef 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? */
383struct in_str { 384typedef 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" 407enum {
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);
713static int builtin_source(char **argv); 720static int builtin_source(char **argv);
714static int builtin_umask(char **argv); 721static int builtin_umask(char **argv);
715static int builtin_unset(char **argv); 722static int builtin_unset(char **argv);
723static int builtin_break(char **argv);
724static 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
4515static int builtin_break(char **argv UNUSED_PARAM)
4516{
4517 flag_break_continue = BC_BREAK;
4518 return EXIT_SUCCESS;
4519}
4520
4521static 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
5Command parsing results in "pipe" structures. "Pipe" structure 5Command parsing results in a list of "pipe" structures.
6does not always correspond to what sh language calls "pipe", 6This list correspond not only to usual "pipe1 || pipe2 && pipe3"
7it also controls execution of if, while, etc statements. 7lists, but it also controls execution of if, while, etc statements.
8Every such statement is a list for hush. List consists of pipes.
8 9
9struct pipe fields: 10struct 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
18as one pipe struct with one progs[0] element which is a "group" - 19as one pipe struct with one progs[0] element which is a "group" -
19struct child_prog can contain a list of pipes. Sometimes these 20struct 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
21statement (if, while, etc) sits inside its own "pipe" struct). 22statement (if, while, etc) sits inside its own group.
22 23
23res_word controls statement execution. Examples: 24res_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)
42pipe 1 res_word=NONE followup=SEQ 43pipe 1 res_word=NONE followup=SEQ
43 44
45Above you see that if is a list, and it sits in a {} group
46implicitly created by hush. Also note two THEN res_word's -
47it is explained below.
48
44"if true; then { echo Hello; true; }; fi" - 49"if true; then { echo Hello; true; }; fi" -
45pipe 0 res_word=NONE followup=SEQ 50pipe 0 res_word=NONE followup=SEQ
46 prog 0 group {}: 51 prog 0 group {}: