From 5e052cab1753f64ddeaaa1f30242eba12f78737b Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Mon, 28 Jul 2008 15:15:09 +0000 Subject: [PATCH] hush: explain run_list() in detail; small optimizations function old new delta hush_main 785 786 +1 expand_variables 1447 1448 +1 builtin_exit 48 49 +1 builtin_eval 54 55 +1 run_list 2075 2055 -20 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 4/1 up/down: 4/-20) Total: -16 bytes --- shell/hush.c | 201 ++++++++++++++++++++++++++++----------------------- 1 file changed, 109 insertions(+), 92 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 5a565b392..56374fa2a 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -431,12 +431,12 @@ struct globals { #endif smallint fake_mode; /* these three support $?, $#, and $1 */ + smalluint last_return_code; char **global_argv; int global_argc; - int last_return_code; + pid_t last_bg_pid; const char *ifs; const char *cwd; - unsigned last_bg_pid; struct variable *top_var; /* = &shell_ver (set in main()) */ struct variable shell_ver; #if ENABLE_FEATURE_SH_STANDALONE @@ -2012,54 +2012,53 @@ static void debug_print_tree(struct pipe *pi, int lvl) * global data until exec/_exit (we can be a child after vfork!) */ static int run_list(struct pipe *pi) { - struct pipe *rpipe; +#if ENABLE_HUSH_CASE + char *case_word = NULL; +#endif #if ENABLE_HUSH_LOOPS + struct pipe *loop_top; char *for_varname = NULL; char **for_lcur = NULL; char **for_list = NULL; - int flag_rep = 0; -#endif -#if ENABLE_HUSH_CASE - char *case_word = NULL; + smallint flag_run_loop = 0; + smallint flag_goto_looptop = 0; #endif - int flag_skip = 1; - int rcode = 0; /* probably for gcc only */ - int flag_restore = 0; + smallint flag_skip = 1; + smalluint rcode = 0; /* probably for gcc only */ #if ENABLE_HUSH_IF - int if_code = 0, next_if_code = 0; /* need double-buffer to handle elif */ + smalluint cond_code = 0; + smalluint last_cond_code = 0; /* need double-buffer to handle "elif" */ #else - enum { if_code = 0, next_if_code = 0 }; + enum { cond_code = 0, last_cond_code = 0 }; #endif - reserved_style rword IF_HAS_NO_KEYWORDS(= RES_NONE); - reserved_style skip_more_for_this_rword = RES_XXXX; + /*reserved_style*/ smallint rword IF_HAS_NO_KEYWORDS(= RES_NONE); + /*reserved_style*/ smallint skip_more_for_this_rword = RES_XXXX; debug_printf_exec("run_list start lvl %d\n", run_list_level + 1); #if ENABLE_HUSH_LOOPS /* check syntax for "for" */ - for (rpipe = pi; rpipe; rpipe = rpipe->next) { - if (rpipe->res_word != RES_FOR && rpipe->res_word != RES_IN) + for (loop_top = pi; loop_top; loop_top = loop_top->next) { + if (loop_top->res_word != RES_FOR && loop_top->res_word != RES_IN) continue; /* current word is FOR or IN (BOLD in comments below) */ - if (rpipe->next == NULL) { + if (loop_top->next == NULL) { syntax("malformed for"); debug_printf_exec("run_list lvl %d return 1\n", run_list_level); return 1; } /* "FOR v; do ..." and "for v IN a b; do..." are ok */ - if (rpipe->next->res_word == RES_DO) + if (loop_top->next->res_word == RES_DO) continue; /* next word is not "do". It must be "in" then ("FOR v in ...") */ - if (rpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */ - || rpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */ + if (loop_top->res_word == RES_IN /* "for v IN a b; not_do..."? */ + || loop_top->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */ ) { syntax("malformed for"); debug_printf_exec("run_list lvl %d return 1\n", run_list_level); return 1; } } -#else - rpipe = NULL; #endif /* Past this point, all code paths should jump to ret: label @@ -2108,21 +2107,24 @@ static int run_list(struct pipe *pi) } #endif /* JOB */ - for (; pi; pi = flag_restore ? rpipe : pi->next) { + /* Go through list of pipes, (maybe) executing them */ + for (; pi; pi = USE_HUSH_LOOPS( flag_goto_looptop ? loop_top : ) pi->next) { IF_HAS_KEYWORDS(rword = pi->res_word;) IF_HAS_NO_KEYWORDS(rword = RES_NONE;) + debug_printf_exec(": rword=%d cond_code=%d last_cond_code=%d skip_more=%d flag_run_loop=%d\n", + rword, cond_code, last_cond_code, skip_more_for_this_rword, flag_run_loop); #if ENABLE_HUSH_LOOPS if (rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) { - flag_restore = 0; - if (!rpipe) { - flag_rep = 0; - rpipe = pi; + /* start of a loop: remember it */ + flag_goto_looptop = 0; /* not yet reached final "done" */ + if (!loop_top) { /* hmm why this check is needed? */ + flag_run_loop = 0; /* suppose loop condition is false (for now) */ + loop_top = pi; /* remember where loop starts */ } } #endif - debug_printf_exec(": rword=%d if_code=%d next_if_code=%d skip_more=%d\n", - rword, if_code, next_if_code, skip_more_for_this_rword); if (rword == skip_more_for_this_rword && flag_skip) { + /* it is " && CMD ... */ if (pi->followup == PIPE_SEQ) flag_skip = 0; continue; @@ -2131,13 +2133,14 @@ static int run_list(struct pipe *pi) skip_more_for_this_rword = RES_XXXX; #if ENABLE_HUSH_IF if (rword == RES_THEN || rword == RES_ELSE) - if_code = next_if_code; - if (rword == RES_THEN && if_code) - continue; - if (rword == RES_ELSE && !if_code) - continue; - if (rword == RES_ELIF && !if_code) - break; + cond_code = last_cond_code; + if (rword == RES_THEN && cond_code) + continue; /* "if THEN cmd": skip cmd */ + if (rword == RES_ELSE && !cond_code) + continue; /* "if then ... ELSE cmd": skip cmd */ +// TODO: break;? + if (rword == RES_ELIF && !cond_code) + break; /* "if then ... ELIF cmd": skip cmd and all following ones */ #endif #if ENABLE_HUSH_LOOPS if (rword == RES_FOR && pi->num_progs) { @@ -2153,7 +2156,7 @@ static int run_list(struct pipe *pi) char **vals; vals = (char**)encoded_dollar_at_argv; - if (rpipe->next->res_word == RES_IN) { + if (loop_top->next->res_word == RES_IN) { /* if no variable values after "in" we skip "for" */ if (!pi->next->progs->argv) continue; @@ -2166,7 +2169,7 @@ static int run_list(struct pipe *pi) debug_print_strings("for_list", for_list); for_varname = pi->progs->argv[0]; pi->progs->argv[0] = NULL; - flag_rep = 1; + flag_run_loop = 1; /* "for" has no loop condition, loop... */ } free(pi->progs->argv[0]); if (!*for_lcur) { @@ -2174,26 +2177,27 @@ static int run_list(struct pipe *pi) free(for_list); for_list = NULL; for_lcur = NULL; - flag_rep = 0; + flag_run_loop = 0; /* ... until end of value list */ pi->progs->argv[0] = for_varname; continue; } /* insert next value from for_lcur */ - /* vda: does it need escaping? */ +//TODO: does it need escaping? pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++); } - if (rword == RES_IN) + if (rword == RES_IN) /* "for v IN list; do ..." - no pipe to execute here */ continue; - if (rword == RES_DO) { - if (!flag_rep) - continue; + if (rword == RES_DO) { /* "...; DO cmd; cmd" - this pipe is in loop body */ + if (!flag_run_loop) + continue; /* we are skipping this iteration */ } - if (rword == RES_DONE) { - if (flag_rep) { - flag_restore = 1; + if (rword == RES_DONE) { /* end of loop? */ + if (flag_run_loop) { + flag_goto_looptop = 1; } else { - rpipe = NULL; + loop_top = NULL; } +//TODO: continue;? DONE has no cmd anyway } #endif #if ENABLE_HUSH_CASE @@ -2203,69 +2207,82 @@ static int run_list(struct pipe *pi) continue; } if (rword == RES_MATCH) { - if (case_word) { - char *pattern = expand_strvec_to_string(pi->progs->argv); - /* TODO: which FNM_xxx flags to use? */ - next_if_code = fnmatch(pattern, case_word, /*flags:*/ 0); - //bb_error_msg("fnmatch('%s','%s'):%d", pattern, case_word, next_if_code); - free(pattern); - if (next_if_code == 0) { - free(case_word); - case_word = NULL; - } - continue; + char *pattern; + if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */ + break; + /* all prev words didn't match, does this one match? */ + pattern = expand_strvec_to_string(pi->progs->argv); + /* TODO: which FNM_xxx flags to use? */ + last_cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); + //bb_error_msg("fnmatch('%s','%s'):%d", pattern, case_word, last_cond_code); + free(pattern); + if (last_cond_code == 0) { /* match! we will execute this branch */ + free(case_word); /* make future "word)" stop */ + case_word = NULL; } - break; + continue; } - if (rword == RES_CASEI) { - if (next_if_code != 0) - continue; + if (rword == RES_CASEI) { /* inside of a case branch */ + if (last_cond_code != 0) + continue; /* not matched yet, skip this pipe */ } #endif - if (pi->num_progs == 0) continue; + + /* After analyzing all keywrds and conditions, we decided + * to execute this pipe */ debug_printf_exec(": run_pipe with %d members\n", pi->num_progs); - rcode = run_pipe(pi); - if (rcode != -1) { - /* We only ran a builtin: rcode was set by the return value - * of run_pipe(), and we don't need to wait for anything. */ - } else if (pi->followup == PIPE_BG) { - /* What does bash do with attempts to background builtins? */ - /* Even bash 3.2 doesn't do that well with nested bg: - * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". - * I'm NOT treating inner &'s as jobs */ + { + int r; + rcode = r = run_pipe(pi); /* NB: rcode is a smallint */ + if (r != -1) { + /* We only ran a builtin: rcode was set by the return value + * of run_pipe(), and we don't need to wait for anything. */ + } else if (pi->followup == PIPE_BG) { + /* What does bash do with attempts to background builtins? */ + /* Even bash 3.2 doesn't do that well with nested bg: + * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". + * I'm NOT treating inner &'s as jobs */ #if ENABLE_HUSH_JOB - if (run_list_level == 1) - insert_bg_job(pi); + if (run_list_level == 1) + insert_bg_job(pi); #endif - rcode = EXIT_SUCCESS; - } else { + rcode = 0; /* EXIT_SUCCESS */ + } else { #if ENABLE_HUSH_JOB - if (run_list_level == 1 && interactive_fd) { - /* waits for completion, then fg's main shell */ - rcode = checkjobs_and_fg_shell(pi); - } else -#endif - { /* this one just waits for completion */ - rcode = checkjobs(pi); + if (run_list_level == 1 && interactive_fd) { + /* waits for completion, then fg's main shell */ + rcode = checkjobs_and_fg_shell(pi); + debug_printf_exec(": checkjobs_and_fg_shell returned %d\n", rcode); + } else +#endif + { /* this one just waits for completion */ + rcode = checkjobs(pi); + debug_printf_exec(": checkjobs returned %d\n", rcode); + } } - debug_printf_exec(": checkjobs returned %d\n", rcode); } debug_printf_exec(": setting last_return_code=%d\n", rcode); last_return_code = rcode; + + /* Analyze how result affects subsequent commands */ #if ENABLE_HUSH_IF if (rword == RES_IF || rword == RES_ELIF) - next_if_code = rcode; /* can be overwritten a number of times */ + last_cond_code = rcode; #endif #if ENABLE_HUSH_LOOPS - if (rword == RES_WHILE) - flag_rep = !last_return_code; - if (rword == RES_UNTIL) - flag_rep = last_return_code; + if (rword == RES_WHILE) { + flag_run_loop = !rcode; + debug_printf_exec(": setting flag_run_loop=%d\n", flag_run_loop); + } + if (rword == RES_UNTIL) { + flag_run_loop = rcode; + debug_printf_exec(": setting flag_run_loop=%d\n", flag_run_loop); + } #endif - if ((rcode == EXIT_SUCCESS && pi->followup == PIPE_OR) - || (rcode != EXIT_SUCCESS && pi->followup == PIPE_AND) + if ((rcode == 0 && pi->followup == PIPE_OR) + || (rcode != 0 && pi->followup == PIPE_AND) ) { skip_more_for_this_rword = rword; } -- 2.25.1