aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-07-28 15:15:09 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-07-28 15:15:09 +0000
commit5e052cab1753f64ddeaaa1f30242eba12f78737b (patch)
treeccdd81102a3eea20e53ebae1a308c789dd51f403
parent38c89210d6b7ffdd4e1c67686559b8b4c33611c7 (diff)
downloadbusybox-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.c201
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!) */
2013static int run_list(struct pipe *pi) 2013static 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 }