diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-28 15:15:09 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-28 15:15:09 +0000 |
commit | 5e052cab1753f64ddeaaa1f30242eba12f78737b (patch) | |
tree | ccdd81102a3eea20e53ebae1a308c789dd51f403 | |
parent | 38c89210d6b7ffdd4e1c67686559b8b4c33611c7 (diff) | |
download | busybox-w32-5e052cab1753f64ddeaaa1f30242eba12f78737b.tar.gz busybox-w32-5e052cab1753f64ddeaaa1f30242eba12f78737b.tar.bz2 busybox-w32-5e052cab1753f64ddeaaa1f30242eba12f78737b.zip |
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
-rw-r--r-- | shell/hush.c | 201 |
1 files 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 { | |||
431 | #endif | 431 | #endif |
432 | smallint fake_mode; | 432 | smallint fake_mode; |
433 | /* these three support $?, $#, and $1 */ | 433 | /* these three support $?, $#, and $1 */ |
434 | smalluint last_return_code; | ||
434 | char **global_argv; | 435 | char **global_argv; |
435 | int global_argc; | 436 | int global_argc; |
436 | int last_return_code; | 437 | pid_t last_bg_pid; |
437 | const char *ifs; | 438 | const char *ifs; |
438 | const char *cwd; | 439 | const char *cwd; |
439 | unsigned last_bg_pid; | ||
440 | struct variable *top_var; /* = &shell_ver (set in main()) */ | 440 | struct variable *top_var; /* = &shell_ver (set in main()) */ |
441 | struct variable shell_ver; | 441 | struct variable shell_ver; |
442 | #if ENABLE_FEATURE_SH_STANDALONE | 442 | #if ENABLE_FEATURE_SH_STANDALONE |
@@ -2012,54 +2012,53 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
2012 | * global data until exec/_exit (we can be a child after vfork!) */ | 2012 | * global data until exec/_exit (we can be a child after vfork!) */ |
2013 | static int run_list(struct pipe *pi) | 2013 | static int run_list(struct pipe *pi) |
2014 | { | 2014 | { |
2015 | struct pipe *rpipe; | 2015 | #if ENABLE_HUSH_CASE |
2016 | char *case_word = NULL; | ||
2017 | #endif | ||
2016 | #if ENABLE_HUSH_LOOPS | 2018 | #if ENABLE_HUSH_LOOPS |
2019 | struct pipe *loop_top; | ||
2017 | char *for_varname = NULL; | 2020 | char *for_varname = NULL; |
2018 | char **for_lcur = NULL; | 2021 | char **for_lcur = NULL; |
2019 | char **for_list = NULL; | 2022 | char **for_list = NULL; |
2020 | int flag_rep = 0; | 2023 | smallint flag_run_loop = 0; |
2021 | #endif | 2024 | smallint flag_goto_looptop = 0; |
2022 | #if ENABLE_HUSH_CASE | ||
2023 | char *case_word = NULL; | ||
2024 | #endif | 2025 | #endif |
2025 | int flag_skip = 1; | 2026 | smallint flag_skip = 1; |
2026 | int rcode = 0; /* probably for gcc only */ | 2027 | smalluint rcode = 0; /* probably for gcc only */ |
2027 | int flag_restore = 0; | ||
2028 | #if ENABLE_HUSH_IF | 2028 | #if ENABLE_HUSH_IF |
2029 | int if_code = 0, next_if_code = 0; /* need double-buffer to handle elif */ | 2029 | smalluint cond_code = 0; |
2030 | smalluint last_cond_code = 0; /* need double-buffer to handle "elif" */ | ||
2030 | #else | 2031 | #else |
2031 | enum { if_code = 0, next_if_code = 0 }; | 2032 | enum { cond_code = 0, last_cond_code = 0 }; |
2032 | #endif | 2033 | #endif |
2033 | reserved_style rword IF_HAS_NO_KEYWORDS(= RES_NONE); | 2034 | /*reserved_style*/ smallint rword IF_HAS_NO_KEYWORDS(= RES_NONE); |
2034 | reserved_style skip_more_for_this_rword = RES_XXXX; | 2035 | /*reserved_style*/ smallint skip_more_for_this_rword = RES_XXXX; |
2035 | 2036 | ||
2036 | debug_printf_exec("run_list start lvl %d\n", run_list_level + 1); | 2037 | debug_printf_exec("run_list start lvl %d\n", run_list_level + 1); |
2037 | 2038 | ||
2038 | #if ENABLE_HUSH_LOOPS | 2039 | #if ENABLE_HUSH_LOOPS |
2039 | /* check syntax for "for" */ | 2040 | /* check syntax for "for" */ |
2040 | for (rpipe = pi; rpipe; rpipe = rpipe->next) { | 2041 | for (loop_top = pi; loop_top; loop_top = loop_top->next) { |
2041 | if (rpipe->res_word != RES_FOR && rpipe->res_word != RES_IN) | 2042 | if (loop_top->res_word != RES_FOR && loop_top->res_word != RES_IN) |
2042 | continue; | 2043 | continue; |
2043 | /* current word is FOR or IN (BOLD in comments below) */ | 2044 | /* current word is FOR or IN (BOLD in comments below) */ |
2044 | if (rpipe->next == NULL) { | 2045 | if (loop_top->next == NULL) { |
2045 | syntax("malformed for"); | 2046 | syntax("malformed for"); |
2046 | debug_printf_exec("run_list lvl %d return 1\n", run_list_level); | 2047 | debug_printf_exec("run_list lvl %d return 1\n", run_list_level); |
2047 | return 1; | 2048 | return 1; |
2048 | } | 2049 | } |
2049 | /* "FOR v; do ..." and "for v IN a b; do..." are ok */ | 2050 | /* "FOR v; do ..." and "for v IN a b; do..." are ok */ |
2050 | if (rpipe->next->res_word == RES_DO) | 2051 | if (loop_top->next->res_word == RES_DO) |
2051 | continue; | 2052 | continue; |
2052 | /* next word is not "do". It must be "in" then ("FOR v in ...") */ | 2053 | /* next word is not "do". It must be "in" then ("FOR v in ...") */ |
2053 | if (rpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */ | 2054 | if (loop_top->res_word == RES_IN /* "for v IN a b; not_do..."? */ |
2054 | || rpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */ | 2055 | || loop_top->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */ |
2055 | ) { | 2056 | ) { |
2056 | syntax("malformed for"); | 2057 | syntax("malformed for"); |
2057 | debug_printf_exec("run_list lvl %d return 1\n", run_list_level); | 2058 | debug_printf_exec("run_list lvl %d return 1\n", run_list_level); |
2058 | return 1; | 2059 | return 1; |
2059 | } | 2060 | } |
2060 | } | 2061 | } |
2061 | #else | ||
2062 | rpipe = NULL; | ||
2063 | #endif | 2062 | #endif |
2064 | 2063 | ||
2065 | /* Past this point, all code paths should jump to ret: label | 2064 | /* Past this point, all code paths should jump to ret: label |
@@ -2108,21 +2107,24 @@ static int run_list(struct pipe *pi) | |||
2108 | } | 2107 | } |
2109 | #endif /* JOB */ | 2108 | #endif /* JOB */ |
2110 | 2109 | ||
2111 | for (; pi; pi = flag_restore ? rpipe : pi->next) { | 2110 | /* Go through list of pipes, (maybe) executing them */ |
2111 | for (; pi; pi = USE_HUSH_LOOPS( flag_goto_looptop ? loop_top : ) pi->next) { | ||
2112 | IF_HAS_KEYWORDS(rword = pi->res_word;) | 2112 | IF_HAS_KEYWORDS(rword = pi->res_word;) |
2113 | IF_HAS_NO_KEYWORDS(rword = RES_NONE;) | 2113 | IF_HAS_NO_KEYWORDS(rword = RES_NONE;) |
2114 | debug_printf_exec(": rword=%d cond_code=%d last_cond_code=%d skip_more=%d flag_run_loop=%d\n", | ||
2115 | rword, cond_code, last_cond_code, skip_more_for_this_rword, flag_run_loop); | ||
2114 | #if ENABLE_HUSH_LOOPS | 2116 | #if ENABLE_HUSH_LOOPS |
2115 | if (rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) { | 2117 | if (rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) { |
2116 | flag_restore = 0; | 2118 | /* start of a loop: remember it */ |
2117 | if (!rpipe) { | 2119 | flag_goto_looptop = 0; /* not yet reached final "done" */ |
2118 | flag_rep = 0; | 2120 | if (!loop_top) { /* hmm why this check is needed? */ |
2119 | rpipe = pi; | 2121 | flag_run_loop = 0; /* suppose loop condition is false (for now) */ |
2122 | loop_top = pi; /* remember where loop starts */ | ||
2120 | } | 2123 | } |
2121 | } | 2124 | } |
2122 | #endif | 2125 | #endif |
2123 | debug_printf_exec(": rword=%d if_code=%d next_if_code=%d skip_more=%d\n", | ||
2124 | rword, if_code, next_if_code, skip_more_for_this_rword); | ||
2125 | if (rword == skip_more_for_this_rword && flag_skip) { | 2126 | if (rword == skip_more_for_this_rword && flag_skip) { |
2127 | /* it is "<false> && CMD ... */ | ||
2126 | if (pi->followup == PIPE_SEQ) | 2128 | if (pi->followup == PIPE_SEQ) |
2127 | flag_skip = 0; | 2129 | flag_skip = 0; |
2128 | continue; | 2130 | continue; |
@@ -2131,13 +2133,14 @@ static int run_list(struct pipe *pi) | |||
2131 | skip_more_for_this_rword = RES_XXXX; | 2133 | skip_more_for_this_rword = RES_XXXX; |
2132 | #if ENABLE_HUSH_IF | 2134 | #if ENABLE_HUSH_IF |
2133 | if (rword == RES_THEN || rword == RES_ELSE) | 2135 | if (rword == RES_THEN || rword == RES_ELSE) |
2134 | if_code = next_if_code; | 2136 | cond_code = last_cond_code; |
2135 | if (rword == RES_THEN && if_code) | 2137 | if (rword == RES_THEN && cond_code) |
2136 | continue; | 2138 | continue; /* "if <false> THEN cmd": skip cmd */ |
2137 | if (rword == RES_ELSE && !if_code) | 2139 | if (rword == RES_ELSE && !cond_code) |
2138 | continue; | 2140 | continue; /* "if <true> then ... ELSE cmd": skip cmd */ |
2139 | if (rword == RES_ELIF && !if_code) | 2141 | // TODO: break;? |
2140 | break; | 2142 | if (rword == RES_ELIF && !cond_code) |
2143 | break; /* "if <true> then ... ELIF cmd": skip cmd and all following ones */ | ||
2141 | #endif | 2144 | #endif |
2142 | #if ENABLE_HUSH_LOOPS | 2145 | #if ENABLE_HUSH_LOOPS |
2143 | if (rword == RES_FOR && pi->num_progs) { | 2146 | if (rword == RES_FOR && pi->num_progs) { |
@@ -2153,7 +2156,7 @@ static int run_list(struct pipe *pi) | |||
2153 | char **vals; | 2156 | char **vals; |
2154 | 2157 | ||
2155 | vals = (char**)encoded_dollar_at_argv; | 2158 | vals = (char**)encoded_dollar_at_argv; |
2156 | if (rpipe->next->res_word == RES_IN) { | 2159 | if (loop_top->next->res_word == RES_IN) { |
2157 | /* if no variable values after "in" we skip "for" */ | 2160 | /* if no variable values after "in" we skip "for" */ |
2158 | if (!pi->next->progs->argv) | 2161 | if (!pi->next->progs->argv) |
2159 | continue; | 2162 | continue; |
@@ -2166,7 +2169,7 @@ static int run_list(struct pipe *pi) | |||
2166 | debug_print_strings("for_list", for_list); | 2169 | debug_print_strings("for_list", for_list); |
2167 | for_varname = pi->progs->argv[0]; | 2170 | for_varname = pi->progs->argv[0]; |
2168 | pi->progs->argv[0] = NULL; | 2171 | pi->progs->argv[0] = NULL; |
2169 | flag_rep = 1; | 2172 | flag_run_loop = 1; /* "for" has no loop condition, loop... */ |
2170 | } | 2173 | } |
2171 | free(pi->progs->argv[0]); | 2174 | free(pi->progs->argv[0]); |
2172 | if (!*for_lcur) { | 2175 | if (!*for_lcur) { |
@@ -2174,26 +2177,27 @@ static int run_list(struct pipe *pi) | |||
2174 | free(for_list); | 2177 | free(for_list); |
2175 | for_list = NULL; | 2178 | for_list = NULL; |
2176 | for_lcur = NULL; | 2179 | for_lcur = NULL; |
2177 | flag_rep = 0; | 2180 | flag_run_loop = 0; /* ... until end of value list */ |
2178 | pi->progs->argv[0] = for_varname; | 2181 | pi->progs->argv[0] = for_varname; |
2179 | continue; | 2182 | continue; |
2180 | } | 2183 | } |
2181 | /* insert next value from for_lcur */ | 2184 | /* insert next value from for_lcur */ |
2182 | /* vda: does it need escaping? */ | 2185 | //TODO: does it need escaping? |
2183 | pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++); | 2186 | pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++); |
2184 | } | 2187 | } |
2185 | if (rword == RES_IN) | 2188 | if (rword == RES_IN) /* "for v IN list; do ..." - no pipe to execute here */ |
2186 | continue; | 2189 | continue; |
2187 | if (rword == RES_DO) { | 2190 | if (rword == RES_DO) { /* "...; DO cmd; cmd" - this pipe is in loop body */ |
2188 | if (!flag_rep) | 2191 | if (!flag_run_loop) |
2189 | continue; | 2192 | continue; /* we are skipping this iteration */ |
2190 | } | 2193 | } |
2191 | if (rword == RES_DONE) { | 2194 | if (rword == RES_DONE) { /* end of loop? */ |
2192 | if (flag_rep) { | 2195 | if (flag_run_loop) { |
2193 | flag_restore = 1; | 2196 | flag_goto_looptop = 1; |
2194 | } else { | 2197 | } else { |
2195 | rpipe = NULL; | 2198 | loop_top = NULL; |
2196 | } | 2199 | } |
2200 | //TODO: continue;? DONE has no cmd anyway | ||
2197 | } | 2201 | } |
2198 | #endif | 2202 | #endif |
2199 | #if ENABLE_HUSH_CASE | 2203 | #if ENABLE_HUSH_CASE |
@@ -2203,69 +2207,82 @@ static int run_list(struct pipe *pi) | |||
2203 | continue; | 2207 | continue; |
2204 | } | 2208 | } |
2205 | if (rword == RES_MATCH) { | 2209 | if (rword == RES_MATCH) { |
2206 | if (case_word) { | 2210 | char *pattern; |
2207 | char *pattern = expand_strvec_to_string(pi->progs->argv); | 2211 | if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */ |
2208 | /* TODO: which FNM_xxx flags to use? */ | 2212 | break; |
2209 | next_if_code = fnmatch(pattern, case_word, /*flags:*/ 0); | 2213 | /* all prev words didn't match, does this one match? */ |
2210 | //bb_error_msg("fnmatch('%s','%s'):%d", pattern, case_word, next_if_code); | 2214 | pattern = expand_strvec_to_string(pi->progs->argv); |
2211 | free(pattern); | 2215 | /* TODO: which FNM_xxx flags to use? */ |
2212 | if (next_if_code == 0) { | 2216 | last_cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); |
2213 | free(case_word); | 2217 | //bb_error_msg("fnmatch('%s','%s'):%d", pattern, case_word, last_cond_code); |
2214 | case_word = NULL; | 2218 | free(pattern); |
2215 | } | 2219 | if (last_cond_code == 0) { /* match! we will execute this branch */ |
2216 | continue; | 2220 | free(case_word); /* make future "word)" stop */ |
2221 | case_word = NULL; | ||
2217 | } | 2222 | } |
2218 | break; | 2223 | continue; |
2219 | } | 2224 | } |
2220 | if (rword == RES_CASEI) { | 2225 | if (rword == RES_CASEI) { /* inside of a case branch */ |
2221 | if (next_if_code != 0) | 2226 | if (last_cond_code != 0) |
2222 | continue; | 2227 | continue; /* not matched yet, skip this pipe */ |
2223 | } | 2228 | } |
2224 | #endif | 2229 | #endif |
2225 | |||
2226 | if (pi->num_progs == 0) | 2230 | if (pi->num_progs == 0) |
2227 | continue; | 2231 | continue; |
2232 | |||
2233 | /* After analyzing all keywrds and conditions, we decided | ||
2234 | * to execute this pipe */ | ||
2228 | debug_printf_exec(": run_pipe with %d members\n", pi->num_progs); | 2235 | debug_printf_exec(": run_pipe with %d members\n", pi->num_progs); |
2229 | rcode = run_pipe(pi); | 2236 | { |
2230 | if (rcode != -1) { | 2237 | int r; |
2231 | /* We only ran a builtin: rcode was set by the return value | 2238 | rcode = r = run_pipe(pi); /* NB: rcode is a smallint */ |
2232 | * of run_pipe(), and we don't need to wait for anything. */ | 2239 | if (r != -1) { |
2233 | } else if (pi->followup == PIPE_BG) { | 2240 | /* We only ran a builtin: rcode was set by the return value |
2234 | /* What does bash do with attempts to background builtins? */ | 2241 | * of run_pipe(), and we don't need to wait for anything. */ |
2235 | /* Even bash 3.2 doesn't do that well with nested bg: | 2242 | } else if (pi->followup == PIPE_BG) { |
2236 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". | 2243 | /* What does bash do with attempts to background builtins? */ |
2237 | * I'm NOT treating inner &'s as jobs */ | 2244 | /* Even bash 3.2 doesn't do that well with nested bg: |
2245 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". | ||
2246 | * I'm NOT treating inner &'s as jobs */ | ||
2238 | #if ENABLE_HUSH_JOB | 2247 | #if ENABLE_HUSH_JOB |
2239 | if (run_list_level == 1) | 2248 | if (run_list_level == 1) |
2240 | insert_bg_job(pi); | 2249 | insert_bg_job(pi); |
2241 | #endif | 2250 | #endif |
2242 | rcode = EXIT_SUCCESS; | 2251 | rcode = 0; /* EXIT_SUCCESS */ |
2243 | } else { | 2252 | } else { |
2244 | #if ENABLE_HUSH_JOB | 2253 | #if ENABLE_HUSH_JOB |
2245 | if (run_list_level == 1 && interactive_fd) { | 2254 | if (run_list_level == 1 && interactive_fd) { |
2246 | /* waits for completion, then fg's main shell */ | 2255 | /* waits for completion, then fg's main shell */ |
2247 | rcode = checkjobs_and_fg_shell(pi); | 2256 | rcode = checkjobs_and_fg_shell(pi); |
2248 | } else | 2257 | debug_printf_exec(": checkjobs_and_fg_shell returned %d\n", rcode); |
2249 | #endif | 2258 | } else |
2250 | { /* this one just waits for completion */ | 2259 | #endif |
2251 | rcode = checkjobs(pi); | 2260 | { /* this one just waits for completion */ |
2261 | rcode = checkjobs(pi); | ||
2262 | debug_printf_exec(": checkjobs returned %d\n", rcode); | ||
2263 | } | ||
2252 | } | 2264 | } |
2253 | debug_printf_exec(": checkjobs returned %d\n", rcode); | ||
2254 | } | 2265 | } |
2255 | debug_printf_exec(": setting last_return_code=%d\n", rcode); | 2266 | debug_printf_exec(": setting last_return_code=%d\n", rcode); |
2256 | last_return_code = rcode; | 2267 | last_return_code = rcode; |
2268 | |||
2269 | /* Analyze how result affects subsequent commands */ | ||
2257 | #if ENABLE_HUSH_IF | 2270 | #if ENABLE_HUSH_IF |
2258 | if (rword == RES_IF || rword == RES_ELIF) | 2271 | if (rword == RES_IF || rword == RES_ELIF) |
2259 | next_if_code = rcode; /* can be overwritten a number of times */ | 2272 | last_cond_code = rcode; |
2260 | #endif | 2273 | #endif |
2261 | #if ENABLE_HUSH_LOOPS | 2274 | #if ENABLE_HUSH_LOOPS |
2262 | if (rword == RES_WHILE) | 2275 | if (rword == RES_WHILE) { |
2263 | flag_rep = !last_return_code; | 2276 | flag_run_loop = !rcode; |
2264 | if (rword == RES_UNTIL) | 2277 | debug_printf_exec(": setting flag_run_loop=%d\n", flag_run_loop); |
2265 | flag_rep = last_return_code; | 2278 | } |
2279 | if (rword == RES_UNTIL) { | ||
2280 | flag_run_loop = rcode; | ||
2281 | debug_printf_exec(": setting flag_run_loop=%d\n", flag_run_loop); | ||
2282 | } | ||
2266 | #endif | 2283 | #endif |
2267 | if ((rcode == EXIT_SUCCESS && pi->followup == PIPE_OR) | 2284 | if ((rcode == 0 && pi->followup == PIPE_OR) |
2268 | || (rcode != EXIT_SUCCESS && pi->followup == PIPE_AND) | 2285 | || (rcode != 0 && pi->followup == PIPE_AND) |
2269 | ) { | 2286 | ) { |
2270 | skip_more_for_this_rword = rword; | 2287 | skip_more_for_this_rword = rword; |
2271 | } | 2288 | } |