diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-05-14 16:19:34 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-05-14 16:19:34 +0000 |
| commit | 03eb8bf6ce2cef8f30402b7c2b18e8479f9da1ea (patch) | |
| tree | 2ca7e7d1fba638187467c1597f2746b0163c9e76 /shell | |
| parent | 602d13cba552fadb8481283aa7872a4b9f206c48 (diff) | |
| download | busybox-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')
| -rw-r--r-- | shell/hush.c | 367 | ||||
| -rw-r--r-- | shell/hush_test/hush-bugs/quote3.right | 8 | ||||
| -rw-r--r-- | shell/hush_test/hush-bugs/quote3.tests | 12 | ||||
| -rw-r--r-- | shell/hush_test/hush-vars/var_subst_in_for.right | 40 | ||||
| -rw-r--r-- | shell/hush_test/hush-vars/var_subst_in_for.tests | 40 |
5 files changed, 404 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 */ |
| 115 | static const char *indenter(int i) | 121 | static 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 | ||
| 233 | struct pipe { | 245 | struct pipe { |
| 234 | struct pipe *next; | 246 | struct pipe *next; |
| @@ -430,7 +442,7 @@ static void delete_finished_bg_job(struct pipe *pi); | |||
| 430 | int checkjobs_and_fg_shell(struct pipe* fg_pipe); /* never called */ | 442 | int checkjobs_and_fg_shell(struct pipe* fg_pipe); /* never called */ |
| 431 | #endif | 443 | #endif |
| 432 | /* local variable support */ | 444 | /* local variable support */ |
| 433 | static char **make_list_in(char **inp, char *name); | 445 | static char **do_variable_expansion(char **argv); |
| 434 | static char *insert_var_value(char *inp); | 446 | static char *insert_var_value(char *inp); |
| 435 | static const char *get_local_var(const char *var); | 447 | static const char *get_local_var(const char *var); |
| 436 | static int set_local_var(const char *s, int flg_export); | 448 | static 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 | ||
| 2291 | static 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 */ | ||
| 2313 | static 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 */ | 2327 | static 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- */ | ||
| 2384 | static 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 */ | ||
| 2417 | static 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 | |||
| 2512 | static 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 | |||
| 2335 | static char *insert_var_value(char *inp) | 2561 | static 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 */ |
| 2408 | static const char *get_local_var(const char *s) | 2649 | static 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 */ |
| 2989 | static char* make_string(char ** inp) | 3230 | static char* make_string(char **inp) |
| 2990 | { | 3231 | { |
| 2991 | char *p; | 3232 | char *p; |
| 2992 | char *str = NULL; | 3233 | char *str = NULL; |
diff --git a/shell/hush_test/hush-bugs/quote3.right b/shell/hush_test/hush-bugs/quote3.right new file mode 100644 index 000000000..11443f54b --- /dev/null +++ b/shell/hush_test/hush-bugs/quote3.right | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | Testing: in $empty"" | ||
| 2 | .. | ||
| 3 | Testing: in "$*" | ||
| 4 | .abc d e. | ||
| 5 | Testing: in "$@" | ||
| 6 | .abc. | ||
| 7 | .d e. | ||
| 8 | Finished | ||
diff --git a/shell/hush_test/hush-bugs/quote3.tests b/shell/hush_test/hush-bugs/quote3.tests new file mode 100644 index 000000000..c52e040cc --- /dev/null +++ b/shell/hush_test/hush-bugs/quote3.tests | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | if test $# = 0; then | ||
| 2 | exec "$THIS_SH" quote3.tests abc "d e" | ||
| 3 | fi | ||
| 4 | |||
| 5 | echo 'Testing: in $empty""' | ||
| 6 | empty='' | ||
| 7 | for a in $empty""; do echo ".$a."; done | ||
| 8 | echo 'Testing: in "$*"' | ||
| 9 | for a in "$*"; do echo ".$a."; done | ||
| 10 | echo 'Testing: in "$@"' | ||
| 11 | for a in "$@"; do echo ".$a."; done | ||
| 12 | echo Finished | ||
diff --git a/shell/hush_test/hush-vars/var_subst_in_for.right b/shell/hush_test/hush-vars/var_subst_in_for.right new file mode 100644 index 000000000..c8aca1c12 --- /dev/null +++ b/shell/hush_test/hush-vars/var_subst_in_for.right | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | Testing: in x y z | ||
| 2 | .x. | ||
| 3 | .y. | ||
| 4 | .z. | ||
| 5 | Testing: in u $empty v | ||
| 6 | .u. | ||
| 7 | .v. | ||
| 8 | Testing: in u " $empty" v | ||
| 9 | .u. | ||
| 10 | . . | ||
| 11 | .v. | ||
| 12 | Testing: in u $empty $empty$a v | ||
| 13 | .u. | ||
| 14 | .a. | ||
| 15 | .v. | ||
| 16 | Testing: in $a_b | ||
| 17 | .a. | ||
| 18 | .b. | ||
| 19 | Testing: in $* | ||
| 20 | .abc. | ||
| 21 | .d. | ||
| 22 | .e. | ||
| 23 | Testing: in $@ | ||
| 24 | .abc. | ||
| 25 | .d. | ||
| 26 | .e. | ||
| 27 | Testing: in -$*- | ||
| 28 | .-abc. | ||
| 29 | .d. | ||
| 30 | .e-. | ||
| 31 | Testing: in -$@- | ||
| 32 | .-abc. | ||
| 33 | .d. | ||
| 34 | .e-. | ||
| 35 | Testing: in $a_b -$a_b- | ||
| 36 | .a. | ||
| 37 | .b. | ||
| 38 | .-a. | ||
| 39 | .b-. | ||
| 40 | Finished | ||
diff --git a/shell/hush_test/hush-vars/var_subst_in_for.tests b/shell/hush_test/hush-vars/var_subst_in_for.tests new file mode 100644 index 000000000..4d1c11201 --- /dev/null +++ b/shell/hush_test/hush-vars/var_subst_in_for.tests | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | if test $# = 0; then | ||
| 2 | exec "$THIS_SH" var_subst_in_for.tests abc "d e" | ||
| 3 | fi | ||
| 4 | |||
| 5 | echo 'Testing: in x y z' | ||
| 6 | for a in x y z; do echo ".$a."; done | ||
| 7 | |||
| 8 | echo 'Testing: in u $empty v' | ||
| 9 | empty='' | ||
| 10 | for a in u $empty v; do echo ".$a."; done | ||
| 11 | |||
| 12 | echo 'Testing: in u " $empty" v' | ||
| 13 | empty='' | ||
| 14 | for a in u " $empty" v; do echo ".$a."; done | ||
| 15 | |||
| 16 | echo 'Testing: in u $empty $empty$a v' | ||
| 17 | a='a' | ||
| 18 | for a in u $empty $empty$a v; do echo ".$a."; done | ||
| 19 | |||
| 20 | echo 'Testing: in $a_b' | ||
| 21 | a_b='a b' | ||
| 22 | for a in $a_b; do echo ".$a."; done | ||
| 23 | |||
| 24 | echo 'Testing: in $*' | ||
| 25 | for a in $*; do echo ".$a."; done | ||
| 26 | |||
| 27 | echo 'Testing: in $@' | ||
| 28 | for a in $@; do echo ".$a."; done | ||
| 29 | |||
| 30 | echo 'Testing: in -$*-' | ||
| 31 | for a in -$*-; do echo ".$a."; done | ||
| 32 | |||
| 33 | echo 'Testing: in -$@-' | ||
| 34 | for a in -$@-; do echo ".$a."; done | ||
| 35 | |||
| 36 | echo 'Testing: in $a_b -$a_b-' | ||
| 37 | a_b='a b' | ||
| 38 | for a in $a_b -$a_b-; do echo ".$a."; done | ||
| 39 | |||
| 40 | echo Finished | ||
