summaryrefslogtreecommitdiff
path: root/shell/hush.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-05-14 16:19:34 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-05-14 16:19:34 +0000
commit03eb8bf6ce2cef8f30402b7c2b18e8479f9da1ea (patch)
tree2ca7e7d1fba638187467c1597f2746b0163c9e76 /shell/hush.c
parent602d13cba552fadb8481283aa7872a4b9f206c48 (diff)
downloadbusybox-w32-03eb8bf6ce2cef8f30402b7c2b18e8479f9da1ea.tar.gz
busybox-w32-03eb8bf6ce2cef8f30402b7c2b18e8479f9da1ea.tar.bz2
busybox-w32-03eb8bf6ce2cef8f30402b7c2b18e8479f9da1ea.zip
hush: move towards more correct variable expansion
hush: fix a few cases in FOR v IN ... construct unfortunately, code growth is big - ~600 bytes
Diffstat (limited to 'shell/hush.c')
-rw-r--r--shell/hush.c367
1 files changed, 304 insertions, 63 deletions
diff --git a/shell/hush.c b/shell/hush.c
index dfb819122..803970b57 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -85,13 +85,14 @@
85 85
86/* If you comment out one of these below, it will be #defined later 86/* If you comment out one of these below, it will be #defined later
87 * to perform debug printfs to stderr: */ 87 * to perform debug printfs to stderr: */
88#define debug_printf(...) do {} while (0) 88#define debug_printf(...) do {} while (0)
89/* Finer-grained debug switches */ 89/* Finer-grained debug switches */
90#define debug_printf_parse(...) do {} while (0) 90#define debug_printf_parse(...) do {} while (0)
91#define debug_print_tree(a, b) do {} while (0) 91#define debug_print_tree(a, b) do {} while (0)
92#define debug_printf_exec(...) do {} while (0) 92#define debug_printf_exec(...) do {} while (0)
93#define debug_printf_jobs(...) do {} while (0) 93#define debug_printf_jobs(...) do {} while (0)
94#define debug_printf_clean(...) do {} while (0) 94#define debug_printf_expand(...) do {} while (0)
95#define debug_printf_clean(...) do {} while (0)
95 96
96#ifndef debug_printf 97#ifndef debug_printf
97#define debug_printf(...) fprintf(stderr, __VA_ARGS__) 98#define debug_printf(...) fprintf(stderr, __VA_ARGS__)
@@ -110,6 +111,11 @@
110#define DEBUG_SHELL_JOBS 1 111#define DEBUG_SHELL_JOBS 1
111#endif 112#endif
112 113
114#ifndef debug_printf_expand
115#define debug_printf_expand(...) fprintf(stderr, __VA_ARGS__)
116#define DEBUG_EXPAND 1
117#endif
118
113#ifndef debug_printf_clean 119#ifndef debug_printf_clean
114/* broken, of course, but OK for testing */ 120/* broken, of course, but OK for testing */
115static const char *indenter(int i) 121static const char *indenter(int i)
@@ -229,6 +235,12 @@ struct child_prog {
229 int sp; /* number of SPECIAL_VAR_SYMBOL */ 235 int sp; /* number of SPECIAL_VAR_SYMBOL */
230 int type; 236 int type;
231}; 237};
238// sp counting seems to be broken...
239/* argv vector may contain variable references (^Cvar^C, ^C0^C etc)
240 * and on execution these are substituted with their values.
241 * Substitution can make _several_ words out of one argv[n]!
242 * Example: argv[0]=='.^C*^C.' here: echo .$*.
243 */
232 244
233struct pipe { 245struct pipe {
234 struct pipe *next; 246 struct pipe *next;
@@ -430,7 +442,7 @@ static void delete_finished_bg_job(struct pipe *pi);
430int checkjobs_and_fg_shell(struct pipe* fg_pipe); /* never called */ 442int checkjobs_and_fg_shell(struct pipe* fg_pipe); /* never called */
431#endif 443#endif
432/* local variable support */ 444/* local variable support */
433static char **make_list_in(char **inp, char *name); 445static char **do_variable_expansion(char **argv);
434static char *insert_var_value(char *inp); 446static char *insert_var_value(char *inp);
435static const char *get_local_var(const char *var); 447static const char *get_local_var(const char *var);
436static int set_local_var(const char *s, int flg_export); 448static int set_local_var(const char *s, int flg_export);
@@ -1265,10 +1277,8 @@ static void restore_redirects(int squirrel[])
1265 for (i = 0; i < 3; i++) { 1277 for (i = 0; i < 3; i++) {
1266 fd = squirrel[i]; 1278 fd = squirrel[i];
1267 if (fd != -1) { 1279 if (fd != -1) {
1268 /* No error checking. I sure wouldn't know what 1280 /* We simply die on error */
1269 * to do with an error if I found one! */ 1281 xmove_fd(fd, i);
1270 dup2(fd, i);
1271 close(fd);
1272 } 1282 }
1273 } 1283 }
1274} 1284}
@@ -1916,9 +1926,9 @@ static int run_list_real(struct pipe *pi)
1916 enum { level = 0 }; 1926 enum { level = 0 };
1917#endif 1927#endif
1918 1928
1919 char *save_name = NULL; 1929 char *for_varname = NULL;
1920 char **list = NULL; 1930 char **for_lcur = NULL;
1921 char **save_list = NULL; 1931 char **for_list = NULL;
1922 struct pipe *rpipe; 1932 struct pipe *rpipe;
1923 int flag_rep = 0; 1933 int flag_rep = 0;
1924 int save_num_progs; 1934 int save_num_progs;
@@ -2018,30 +2028,29 @@ static int run_list_real(struct pipe *pi)
2018 if (rmode == RES_ELIF && !if_code) 2028 if (rmode == RES_ELIF && !if_code)
2019 break; 2029 break;
2020 if (rmode == RES_FOR && pi->num_progs) { 2030 if (rmode == RES_FOR && pi->num_progs) {
2021 if (!list) { 2031 if (!for_lcur) {
2022 /* if no variable values after "in" we skip "for" */ 2032 /* if no variable values after "in" we skip "for" */
2023 if (!pi->next->progs->argv) 2033 if (!pi->next->progs->argv)
2024 continue; 2034 continue;
2025 /* create list of variable values */ 2035 /* create list of variable values */
2026 list = make_list_in(pi->next->progs->argv, 2036 for_list = do_variable_expansion(pi->next->progs->argv);
2027 pi->progs->argv[0]); 2037 for_lcur = for_list;
2028 save_list = list; 2038 for_varname = pi->progs->argv[0];
2029 save_name = pi->progs->argv[0];
2030 pi->progs->argv[0] = NULL; 2039 pi->progs->argv[0] = NULL;
2031 flag_rep = 1; 2040 flag_rep = 1;
2032 } 2041 }
2033 if (!*list) { 2042 free(pi->progs->argv[0]);
2034 free(pi->progs->argv[0]); 2043 if (!*for_lcur) {
2035 free(save_list); 2044 free(for_list);
2036 list = NULL; 2045 for_lcur = NULL;
2037 flag_rep = 0; 2046 flag_rep = 0;
2038 pi->progs->argv[0] = save_name; 2047 pi->progs->argv[0] = for_varname;
2039 pi->progs->glob_result.gl_pathv[0] = pi->progs->argv[0]; 2048 pi->progs->glob_result.gl_pathv[0] = pi->progs->argv[0];
2040 continue; 2049 continue;
2041 } 2050 }
2042 /* insert new value from list for variable */ 2051 /* insert next value from for_lcur */
2043 free(pi->progs->argv[0]); 2052 //vda: does it need escaping?
2044 pi->progs->argv[0] = *list++; 2053 pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++);
2045 pi->progs->glob_result.gl_pathv[0] = pi->progs->argv[0]; 2054 pi->progs->glob_result.gl_pathv[0] = pi->progs->argv[0];
2046 } 2055 }
2047 if (rmode == RES_IN) 2056 if (rmode == RES_IN)
@@ -2288,50 +2297,267 @@ static int xglob(o_string *dest, int flags, glob_t *pglob)
2288 return gr; 2297 return gr;
2289} 2298}
2290 2299
2291static char **make_list_in(char **inp, char *name) 2300
2301/* do_variable_expansion() takes a list of strings, expands
2302 * all variable references within and returns a pointer to
2303 * a list of expanded strings, possibly with larger number
2304 * of strings. (Think VAR="a b"; echo $VAR).
2305 * This new list is allocated as a single malloc block.
2306 * NULL-terminated list of char* pointers is at the beginning of it,
2307 * followed by strings themself.
2308 * Caller can deallocate entire list by single free(list). */
2309
2310/* Helpers first:
2311 * count_XXX estimates, how large block do we need. It's okay
2312 * to over-estimate sizes a bit, if it makes code simpler */
2313static int count_ifs(const char *str)
2292{ 2314{
2293 int len, i; 2315 int cnt = 0;
2294#if 0 2316 debug_printf_expand("count_ifs('%s') ifs='%s'", str, ifs);
2295 int name_len = strlen(name); 2317 while (1) {
2296#endif 2318 str += strcspn(str, ifs);
2297 int n; 2319 if (!*str) break;
2298 char **list; 2320 str++; // str += strspn(str, ifs); ?
2299 char *p1, *p2, *p3; 2321 cnt++; // cnt += strspn(str, ifs); ?
2322 }
2323 debug_printf_expand(" return %d\n", cnt);
2324 return cnt;
2325}
2300 2326
2301 /* create list of variable values */ 2327static void count_var_expansion_space(int *countp, int *lenp, char *arg)
2302 list = xmalloc(sizeof(*list)); 2328{
2303 n = 0; 2329 char first_ch;
2304 for (i = 0; inp[i]; i++) { 2330 int i;
2305 p3 = insert_var_value(inp[i]); 2331 int len = *lenp;
2306 p1 = p3; 2332 int count = *countp;
2307 while (*p1) { 2333 const char *val;
2308 if (*p1 == ' ') { 2334 char *p;
2309 p1++; 2335
2310 continue; 2336 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL))) {
2337 len += p - arg;
2338 arg = ++p;
2339 p = strchr(p, SPECIAL_VAR_SYMBOL);
2340 first_ch = arg[0];
2341
2342 switch (first_ch & 0x7f) {
2343 /* high bit in 1st_ch indicates that var is double-quoted */
2344 case '$': /* pid */
2345 case '!': /* bg pid */
2346 case '?': /* exitcode */
2347 case '#': /* argc */
2348 len += sizeof(int)*3 + 1; /* enough for int */
2349 break;
2350 case '*':
2351 case '@':
2352 for (i = 1; i < global_argc; i++) {
2353 len += strlen(global_argv[i]) + 1;
2354 count++;
2355 if (!(first_ch & 0x80))
2356 count += count_ifs(global_argv[i]);
2357 }
2358 break;
2359 default:
2360 *p = '\0';
2361 arg[0] = first_ch & 0x7f;
2362 val = lookup_param(arg);
2363 arg[0] = first_ch;
2364 *p = SPECIAL_VAR_SYMBOL;
2365
2366 if (val) {
2367 len += strlen(val) + 1;
2368 if (!(first_ch & 0x80))
2369 count += count_ifs(val);
2311 } 2370 }
2312 p2 = strchrnul(p1, ' ');
2313 len = p2 - p1;
2314 /* we use n + 2 in realloc for list, because we add
2315 * new element and then we will add NULL element */
2316 list = xrealloc(list, sizeof(*list) * (n + 2));
2317 list[n] = xasprintf("%s=%.*s", name, len, p1);
2318#if 0 /* faster, but more code */
2319 list[n] = xmalloc(2 + name_len + len);
2320 strcpy(list[n], name);
2321 list[n][name_len] = '=';
2322 strncat(&(list[n][name_len + 1]), p1, len);
2323 list[n][name_len + len + 1] = '\0';
2324#endif
2325 n++;
2326 p1 = p2;
2327 } 2371 }
2328 if (p3 != inp[i]) 2372 arg = ++p;
2329 free(p3);
2330 } 2373 }
2374
2375 len += strlen(arg) + 1;
2376 count++;
2377 *lenp = len;
2378 *countp = count;
2379}
2380
2381/* Store given string, finalizing the word and starting new one whenever
2382 * we encounter ifs char(s). This is used for expanding variable values.
2383 * End-of-string does NOT finalize word, because cases like echo -$VAR- */
2384static int expand_on_ifs(char **list, int n, char **posp, const char *str)
2385{
2386 char *pos = *posp;
2387 while (1) {
2388 int word_len = strcspn(str, ifs);
2389 if (word_len) {
2390 memcpy(pos, str, word_len); /* store non-ifs chars */
2391 pos += word_len;
2392 str += word_len;
2393 if (!*str) /* EOL - do not finalize word */
2394 break;
2395 *pos++ = '\0';
2396 if (n) debug_printf_expand("expand_on_ifs finalized list[%d]=%p '%s' "
2397 "strlen=%d next=%p pos=%p\n", n-1, list[n-1], list[n-1],
2398 strlen(list[n-1]), list[n-1] + strlen(list[n-1]) + 1, pos);
2399 list[n++] = pos;
2400 }
2401 if (!*str) break; /* EOL, not ifs char */
2402 str += strspn(str, ifs); /* skip ifs chars */
2403 }
2404 *posp = pos;
2405 return n;
2406}
2407
2408/* Expand all variable references in given string, adding words to list[]
2409 * at n, n+1,... positions. Return updated n (so that list[n] is next one
2410 * to be filled). This routine is extremely tricky: has to deal with
2411 * variables/parameters with whitespace, $* and $@, and constructs like
2412 * 'echo -$*-'. If you play here, you must run testsuite afterwards! */
2413/* NB: support for double-quoted expansion is not used yet (we never set
2414 * magic bit 0x80 elsewhere...) */
2415/* NB: another bug is that we cannot detect empty strings yet:
2416 * "" or $empty"" expands to zero words, has to expand to empty word */
2417static int expand_vars_to_list(char **list, int n, char **posp, char *arg)
2418{
2419 char first_ch, ored_ch;
2420 const char *val;
2421 char *p;
2422 char *pos = *posp;
2423
2424 ored_ch = 0;
2425
2426 if (n) debug_printf_expand("expand_vars_to_list finalized list[%d]=%p '%s' "
2427 "strlen=%d next=%p pos=%p\n", n-1, list[n-1], list[n-1],
2428 strlen(list[n-1]), list[n-1] + strlen(list[n-1]) + 1, pos);
2429 list[n++] = pos;
2430
2431 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL))) {
2432 memcpy(pos, arg, p - arg);
2433 pos += (p - arg);
2434 arg = ++p;
2435 p = strchr(p, SPECIAL_VAR_SYMBOL);
2436
2437 first_ch = arg[0];
2438 ored_ch |= first_ch;
2439 val = NULL;
2440 switch (first_ch & 0x7f) {
2441 /* Highest bit in first_ch indicates that var is double-quoted */
2442 case '$': /* pid */
2443 /* FIXME: (echo $$) should still print pid of main shell */
2444 val = utoa(getpid());
2445 break;
2446 case '!': /* bg pid */
2447 val = last_bg_pid ? utoa(last_bg_pid) : (char*)"";
2448 break;
2449 case '?': /* exitcode */
2450 val = utoa(last_return_code);
2451 break;
2452 case '#': /* argc */
2453 val = utoa(global_argc ? global_argc-1 : 0);
2454 break;
2455 case '*':
2456 case '@':
2457 if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
2458 int i = 1;
2459 while (i < global_argc) {
2460 n = expand_on_ifs(list, n, &pos, global_argv[i]);
2461 debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, global_argc-1);
2462 if (global_argv[i++][0] && i < global_argc) {
2463 /* this argv[] is not empty and not last:
2464 * put terminating NUL, start new word */
2465 *pos++ = '\0';
2466 if (n) debug_printf_expand("expand_vars_to_list 2 finalized list[%d]=%p '%s' "
2467 "strlen=%d next=%p pos=%p\n", n-1, list[n-1], list[n-1],
2468 strlen(list[n-1]), list[n-1] + strlen(list[n-1]) + 1, pos);
2469 list[n++] = pos;
2470 }
2471 }
2472 } else if (first_ch == ('@'|0x80)) { /* quoted $@ */
2473 /* TODO */
2474 } else { /* quoted $* */
2475 /* TODO */
2476 }
2477 break;
2478 default:
2479 *p = '\0';
2480 arg[0] = first_ch & 0x7f;
2481 val = lookup_param(arg);
2482 arg[0] = first_ch;
2483 *p = SPECIAL_VAR_SYMBOL;
2484 if (!(first_ch & 0x80)) { /* unquoted var */
2485 if (val) {
2486 n = expand_on_ifs(list, n, &pos, val);
2487 val = NULL;
2488 }
2489 }
2490 }
2491 if (val) {
2492 strcpy(pos, val);
2493 pos += strlen(val);
2494 }
2495 arg = ++p;
2496 }
2497 debug_printf_expand("expand_vars_to_list adding tail '%s' at %p\n", arg, pos);
2498 strcpy(pos, arg);
2499 pos += strlen(arg) + 1;
2500 if (pos == list[n-1] + 1) { /* expansion is empty */
2501 if (!(ored_ch & 0x80)) { /* all vars were not quoted... */
2502 debug_printf_expand("expand_vars_to_list list[%d] empty, going back\n", n);
2503 pos--;
2504 n--;
2505 }
2506 }
2507
2508 *posp = pos;
2509 return n;
2510}
2511
2512static char **do_variable_expansion(char **argv)
2513{
2514 int n;
2515 int count = 1;
2516 int len = 0;
2517 char *pos, **v, **list;
2518
2519 v = argv;
2520 if (!*v) debug_printf_expand("count_var_expansion_space: "
2521 "argv[0]=NULL count=%d len=%d alloc_space=%d\n",
2522 count, len, sizeof(char*) * count + len);
2523 while (*v) {
2524 count_var_expansion_space(&count, &len, *v);
2525 debug_printf_expand("count_var_expansion_space: "
2526 "'%s' count=%d len=%d alloc_space=%d\n",
2527 *v, count, len, sizeof(char*) * count + len);
2528 v++;
2529 }
2530 len += sizeof(char*) * count; /* total to alloc */
2531 list = xmalloc(len);
2532 pos = (char*)(list + count);
2533 debug_printf_expand("list=%p, list[0] should be %p\n", list, pos);
2534 n = 0;
2535 v = argv;
2536 while (*v)
2537 n = expand_vars_to_list(list, n, &pos, *v++);
2538
2539 if(n) debug_printf_expand("finalized list[%d]=%p '%s' "
2540 "strlen=%d next=%p pos=%p\n", n-1, list[n-1], list[n-1],
2541 strlen(list[n-1]), list[n-1] + strlen(list[n-1]) + 1, pos);
2331 list[n] = NULL; 2542 list[n] = NULL;
2543
2544#ifdef DEBUG_EXPAND
2545 {
2546 int m = 0;
2547 while (m <= n) {
2548 debug_printf_expand("list[%d]=%p '%s'\n", m, list[m], list[m]);
2549 m++;
2550 }
2551 debug_printf_expand("used_space=%d\n", pos - (char*)list);
2552 }
2553#endif
2554 /* To be removed / made conditional later. */
2555 if (pos - (char*)list > len)
2556 bb_error_msg_and_die("BUG in varexp");
2332 return list; 2557 return list;
2333} 2558}
2334 2559
2560
2335static char *insert_var_value(char *inp) 2561static char *insert_var_value(char *inp)
2336{ 2562{
2337 int res_str_len = 0; 2563 int res_str_len = 0;
@@ -2404,6 +2630,21 @@ static char *insert_var_value(char *inp)
2404 return (res_str == NULL) ? inp : res_str; 2630 return (res_str == NULL) ? inp : res_str;
2405} 2631}
2406 2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2407/* This is used to get/check local shell variables */ 2648/* This is used to get/check local shell variables */
2408static const char *get_local_var(const char *s) 2649static const char *get_local_var(const char *s)
2409{ 2650{
@@ -2986,7 +3227,7 @@ static const char *lookup_param(const char *src)
2986} 3227}
2987 3228
2988/* Make new string for parser */ 3229/* Make new string for parser */
2989static char* make_string(char ** inp) 3230static char* make_string(char **inp)
2990{ 3231{
2991 char *p; 3232 char *p;
2992 char *str = NULL; 3233 char *str = NULL;