diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-06-16 12:47:11 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-06-16 12:47:11 +0000 |
commit | 82dfec3e4e6d2b8354d901a525f9acff35369512 (patch) | |
tree | a3ebda0c5f23bdaae1aba29d57b6793abf696785 /shell | |
parent | 31e128636535a58be64636c8de93a6dad8b39ac0 (diff) | |
download | busybox-w32-82dfec3e4e6d2b8354d901a525f9acff35369512.tar.gz busybox-w32-82dfec3e4e6d2b8354d901a525f9acff35369512.tar.bz2 busybox-w32-82dfec3e4e6d2b8354d901a525f9acff35369512.zip |
hush: fix hush-bugs/glob_and_vars.tests testcase:
globbing is now done _after_ variable/`cmd` substitution
function old new delta
expand_strvec_to_strvec 7 353 +346
expand_variables 1348 1383 +35
add_string_to_strings - 28 +28
globhack 114 - -114
done_word 778 579 -199
------------------------------------------------------------------------------
(add/remove: 1/1 grow/shrink: 2/1 up/down: 409/-313) Total: 96 bytes
Diffstat (limited to 'shell')
-rw-r--r-- | shell/hush.c | 109 | ||||
-rw-r--r-- | shell/hush_test/hush-bugs/empty_for.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-bugs/empty_for.tests | 3 | ||||
-rwxr-xr-x | shell/hush_test/hush-bugs/glob_and_vars.tests | 2 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/glob_and_vars.right (renamed from shell/hush_test/hush-bugs/glob_and_vars.right) | 0 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/glob_and_vars.tests | 2 |
6 files changed, 70 insertions, 47 deletions
diff --git a/shell/hush.c b/shell/hush.c index bc192b38b..7c5a44274 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -481,10 +481,6 @@ static int run_list(struct pipe *pi); | |||
481 | static void pseudo_exec_argv(char **ptrs2free, char **argv) ATTRIBUTE_NORETURN; | 481 | static void pseudo_exec_argv(char **ptrs2free, char **argv) ATTRIBUTE_NORETURN; |
482 | static void pseudo_exec(char **ptrs2free, struct child_prog *child) ATTRIBUTE_NORETURN; | 482 | static void pseudo_exec(char **ptrs2free, struct child_prog *child) ATTRIBUTE_NORETURN; |
483 | static int run_pipe(struct pipe *pi); | 483 | static int run_pipe(struct pipe *pi); |
484 | /* extended glob support: */ | ||
485 | static char **globhack(const char *src, char **strings); | ||
486 | static int glob_needed(const char *s); | ||
487 | static int xglob(o_string *dest, char ***pglob); | ||
488 | /* variable assignment: */ | 484 | /* variable assignment: */ |
489 | static int is_assignment(const char *s); | 485 | static int is_assignment(const char *s); |
490 | /* data structure manipulation: */ | 486 | /* data structure manipulation: */ |
@@ -591,6 +587,17 @@ static char **alloc_ptrs(char **argv) | |||
591 | } | 587 | } |
592 | #endif | 588 | #endif |
593 | 589 | ||
590 | #ifdef DEBUG_EXPAND | ||
591 | static void debug_print_strings(const char *prefix, char **vv) | ||
592 | { | ||
593 | fprintf(stderr, "%s:\n", prefix); | ||
594 | while (*vv) | ||
595 | fprintf(stderr, " '%s'\n", *vv++); | ||
596 | } | ||
597 | #else | ||
598 | #define debug_print_strings(prefix, vv) ((void)0) | ||
599 | #endif | ||
600 | |||
594 | 601 | ||
595 | /* Function prototypes for builtins */ | 602 | /* Function prototypes for builtins */ |
596 | static int builtin_cd(char **argv); | 603 | static int builtin_cd(char **argv); |
@@ -2280,7 +2287,9 @@ static int run_list(struct pipe *pi) | |||
2280 | if (!pi->next->progs->argv) | 2287 | if (!pi->next->progs->argv) |
2281 | continue; | 2288 | continue; |
2282 | /* create list of variable values */ | 2289 | /* create list of variable values */ |
2290 | debug_print_strings("for_list made from", pi->next->progs->argv); | ||
2283 | for_list = expand_strvec_to_strvec(pi->next->progs->argv); | 2291 | for_list = expand_strvec_to_strvec(pi->next->progs->argv); |
2292 | debug_print_strings("for_list", for_list); | ||
2284 | for_lcur = for_list; | 2293 | for_lcur = for_list; |
2285 | for_varname = pi->progs->argv[0]; | 2294 | for_varname = pi->progs->argv[0]; |
2286 | pi->progs->argv[0] = NULL; | 2295 | pi->progs->argv[0] = NULL; |
@@ -2465,13 +2474,10 @@ static int run_and_free_list(struct pipe *pi) | |||
2465 | return rcode; | 2474 | return rcode; |
2466 | } | 2475 | } |
2467 | 2476 | ||
2468 | /* The API for glob is arguably broken. This routine pushes a non-matching | 2477 | /* Remove non-backslashed backslashes and add to "strings" vector. |
2469 | * string into the output structure, removing non-backslashed backslashes. | ||
2470 | * If someone can prove me wrong, by performing this function within the | ||
2471 | * original glob(3) api, feel free to rewrite this routine into oblivion. | ||
2472 | * XXX broken if the last character is '\\', check that before calling. | 2478 | * XXX broken if the last character is '\\', check that before calling. |
2473 | */ | 2479 | */ |
2474 | static char **globhack(const char *src, char **strings) | 2480 | static char **add_unq_string_to_strings(char **strings, const char *src) |
2475 | { | 2481 | { |
2476 | int cnt; | 2482 | int cnt; |
2477 | const char *s; | 2483 | const char *s; |
@@ -2503,33 +2509,20 @@ static int glob_needed(const char *s) | |||
2503 | return 0; | 2509 | return 0; |
2504 | } | 2510 | } |
2505 | 2511 | ||
2506 | static int xglob(o_string *dest, char ***pglob) | 2512 | static int xglob(char ***pglob, const char *pattern) |
2507 | { | 2513 | { |
2508 | /* short-circuit for null word */ | 2514 | if (glob_needed(pattern)) { |
2509 | /* we can code this better when the debug_printf's are gone */ | ||
2510 | if (dest->length == 0) { | ||
2511 | if (dest->nonnull) { | ||
2512 | /* bash man page calls this an "explicit" null */ | ||
2513 | *pglob = globhack(dest->data, *pglob); | ||
2514 | } | ||
2515 | return 0; | ||
2516 | } | ||
2517 | |||
2518 | if (glob_needed(dest->data)) { | ||
2519 | glob_t globdata; | 2515 | glob_t globdata; |
2520 | int gr; | 2516 | int gr; |
2521 | 2517 | ||
2522 | memset(&globdata, 0, sizeof(globdata)); | 2518 | memset(&globdata, 0, sizeof(globdata)); |
2523 | gr = glob(dest->data, 0, NULL, &globdata); | 2519 | gr = glob(pattern, 0, NULL, &globdata); |
2524 | debug_printf("glob returned %d\n", gr); | 2520 | debug_printf("glob returned %d\n", gr); |
2525 | if (gr == GLOB_NOSPACE) | 2521 | if (gr == GLOB_NOSPACE) |
2526 | bb_error_msg_and_die("out of memory during glob"); | 2522 | bb_error_msg_and_die("out of memory during glob"); |
2527 | if (gr == GLOB_NOMATCH) { | 2523 | if (gr == GLOB_NOMATCH) { |
2528 | debug_printf("globhack returned %d\n", gr); | ||
2529 | /* quote removal, or more accurately, backslash removal */ | ||
2530 | *pglob = globhack(dest->data, *pglob); | ||
2531 | globfree(&globdata); | 2524 | globfree(&globdata); |
2532 | return 0; | 2525 | goto literal; |
2533 | } | 2526 | } |
2534 | if (gr != 0) { /* GLOB_ABORTED ? */ | 2527 | if (gr != 0) { /* GLOB_ABORTED ? */ |
2535 | bb_error_msg("glob(3) error %d", gr); | 2528 | bb_error_msg("glob(3) error %d", gr); |
@@ -2540,7 +2533,10 @@ static int xglob(o_string *dest, char ***pglob) | |||
2540 | return gr; | 2533 | return gr; |
2541 | } | 2534 | } |
2542 | 2535 | ||
2543 | *pglob = globhack(dest->data, *pglob); | 2536 | literal: |
2537 | /* quote removal, or more accurately, backslash removal */ | ||
2538 | *pglob = add_unq_string_to_strings(*pglob, pattern); | ||
2539 | debug_print_strings("after xglob", *pglob); | ||
2544 | return 0; | 2540 | return 0; |
2545 | } | 2541 | } |
2546 | 2542 | ||
@@ -2733,7 +2729,7 @@ static char **expand_variables(char **argv, char or_mask) | |||
2733 | n = expand_vars_to_list(&output, n, *v++, or_mask); | 2729 | n = expand_vars_to_list(&output, n, *v++, or_mask); |
2734 | o_debug_list("expand_variables", &output, n); | 2730 | o_debug_list("expand_variables", &output, n); |
2735 | 2731 | ||
2736 | /* output.data (malloced) gets returned in "list" */ | 2732 | /* output.data (malloced in one block) gets returned in "list" */ |
2737 | list = o_finalize_list(&output, n); | 2733 | list = o_finalize_list(&output, n); |
2738 | 2734 | ||
2739 | #ifdef DEBUG_EXPAND | 2735 | #ifdef DEBUG_EXPAND |
@@ -2750,9 +2746,27 @@ static char **expand_variables(char **argv, char or_mask) | |||
2750 | 2746 | ||
2751 | static char **expand_strvec_to_strvec(char **argv) | 2747 | static char **expand_strvec_to_strvec(char **argv) |
2752 | { | 2748 | { |
2753 | return expand_variables(argv, 0); | 2749 | char **exp; |
2750 | char **res = NULL; | ||
2751 | |||
2752 | debug_print_strings("expand_strvec_to_strvec: pre expand", argv); | ||
2753 | exp = argv = expand_variables(argv, 0); | ||
2754 | debug_print_strings("expand_strvec_to_strvec: post expand", argv); | ||
2755 | while (*argv) { | ||
2756 | int r = xglob(&res, *argv); | ||
2757 | if (r) | ||
2758 | bb_error_msg("xglob returned %d on '%s'", r, *argv); | ||
2759 | //TODO: testcase for bad glob pattern behavior | ||
2760 | argv++; | ||
2761 | } | ||
2762 | free(exp); | ||
2763 | debug_print_strings("expand_strvec_to_strvec: res", res); | ||
2764 | return res; | ||
2754 | } | 2765 | } |
2755 | 2766 | ||
2767 | /* used for expansion of right hand of assignments */ | ||
2768 | /* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs | ||
2769 | * "v=/bin/c*" */ | ||
2756 | static char *expand_string_to_string(const char *str) | 2770 | static char *expand_string_to_string(const char *str) |
2757 | { | 2771 | { |
2758 | char *argv[2], **list; | 2772 | char *argv[2], **list; |
@@ -2769,6 +2783,7 @@ static char *expand_string_to_string(const char *str) | |||
2769 | return (char*)list; | 2783 | return (char*)list; |
2770 | } | 2784 | } |
2771 | 2785 | ||
2786 | /* used for eval */ | ||
2772 | static char* expand_strvec_to_string(char **argv) | 2787 | static char* expand_strvec_to_string(char **argv) |
2773 | { | 2788 | { |
2774 | char **list; | 2789 | char **list; |
@@ -3106,7 +3121,6 @@ static int done_word(o_string *word, struct p_context *ctx) | |||
3106 | { | 3121 | { |
3107 | struct child_prog *child = ctx->child; | 3122 | struct child_prog *child = ctx->child; |
3108 | char ***glob_target; | 3123 | char ***glob_target; |
3109 | int gr; | ||
3110 | 3124 | ||
3111 | debug_printf_parse("done_word entered: '%s' %p\n", word->data, child); | 3125 | debug_printf_parse("done_word entered: '%s' %p\n", word->data, child); |
3112 | if (word->length == 0 && !word->nonnull) { | 3126 | if (word->length == 0 && !word->nonnull) { |
@@ -3131,12 +3145,10 @@ static int done_word(o_string *word, struct p_context *ctx) | |||
3131 | } | 3145 | } |
3132 | glob_target = &child->argv; | 3146 | glob_target = &child->argv; |
3133 | } | 3147 | } |
3134 | //BUG! globbing should be done after variable expansion! | 3148 | |
3135 | //See glob_and_vars testcase | 3149 | if (word->length || word->nonnull) { |
3136 | gr = xglob(word, glob_target); | 3150 | *glob_target = add_string_to_strings(*glob_target, xstrdup(word->data)); |
3137 | if (gr != 0) { | 3151 | debug_print_strings("glob_target appended", *glob_target); |
3138 | debug_printf_parse("done_word return 1: xglob returned %d\n", gr); | ||
3139 | return 1; | ||
3140 | } | 3152 | } |
3141 | 3153 | ||
3142 | o_reset(word); | 3154 | o_reset(word); |
@@ -3371,6 +3383,11 @@ static int process_command_subs(o_string *dest, | |||
3371 | o_addQchr(dest, '\n'); | 3383 | o_addQchr(dest, '\n'); |
3372 | eol_cnt--; | 3384 | eol_cnt--; |
3373 | } | 3385 | } |
3386 | /* Even unquoted `echo '\'` results in two backslashes | ||
3387 | * (which are converted into one by globbing later) */ | ||
3388 | if (!dest->o_quote && ch == '\\') { | ||
3389 | o_addchr(dest, ch); | ||
3390 | } | ||
3374 | o_addQchr(dest, ch); | 3391 | o_addQchr(dest, ch); |
3375 | } | 3392 | } |
3376 | 3393 | ||
@@ -3440,7 +3457,7 @@ static void add_till_single_quote(o_string *dest, struct in_str *input) | |||
3440 | break; | 3457 | break; |
3441 | if (ch == '\'') | 3458 | if (ch == '\'') |
3442 | break; | 3459 | break; |
3443 | o_addqchr(dest, ch); | 3460 | o_addchr(dest, ch); |
3444 | } | 3461 | } |
3445 | } | 3462 | } |
3446 | /* "...\"...`..`...." - do we need to handle "...$(..)..." too? */ | 3463 | /* "...\"...`..`...." - do we need to handle "...$(..)..." too? */ |
@@ -3451,15 +3468,15 @@ static void add_till_double_quote(o_string *dest, struct in_str *input) | |||
3451 | if (ch == '"') | 3468 | if (ch == '"') |
3452 | break; | 3469 | break; |
3453 | if (ch == '\\') { /* \x. Copy both chars. */ | 3470 | if (ch == '\\') { /* \x. Copy both chars. */ |
3454 | o_addqchr(dest, ch); | 3471 | o_addchr(dest, ch); |
3455 | ch = i_getch(input); | 3472 | ch = i_getch(input); |
3456 | } | 3473 | } |
3457 | if (ch == EOF) | 3474 | if (ch == EOF) |
3458 | break; | 3475 | break; |
3459 | o_addqchr(dest, ch); | 3476 | o_addchr(dest, ch); |
3460 | if (ch == '`') { | 3477 | if (ch == '`') { |
3461 | add_till_backquote(dest, input); | 3478 | add_till_backquote(dest, input); |
3462 | o_addqchr(dest, ch); | 3479 | o_addchr(dest, ch); |
3463 | continue; | 3480 | continue; |
3464 | } | 3481 | } |
3465 | //if (ch == '$') ... | 3482 | //if (ch == '$') ... |
@@ -3488,12 +3505,12 @@ static void add_till_backquote(o_string *dest, struct in_str *input) | |||
3488 | if (ch == '\\') { /* \x. Copy both chars unless it is \` */ | 3505 | if (ch == '\\') { /* \x. Copy both chars unless it is \` */ |
3489 | int ch2 = i_getch(input); | 3506 | int ch2 = i_getch(input); |
3490 | if (ch2 != '`' && ch2 != '$' && ch2 != '\\') | 3507 | if (ch2 != '`' && ch2 != '$' && ch2 != '\\') |
3491 | o_addqchr(dest, ch); | 3508 | o_addchr(dest, ch); |
3492 | ch = ch2; | 3509 | ch = ch2; |
3493 | } | 3510 | } |
3494 | if (ch == EOF) | 3511 | if (ch == EOF) |
3495 | break; | 3512 | break; |
3496 | o_addqchr(dest, ch); | 3513 | o_addchr(dest, ch); |
3497 | } | 3514 | } |
3498 | } | 3515 | } |
3499 | /* Process $(cmd) - copy contents until ")" is seen. Complicated by | 3516 | /* Process $(cmd) - copy contents until ")" is seen. Complicated by |
@@ -3520,22 +3537,22 @@ static void add_till_closing_curly_brace(o_string *dest, struct in_str *input) | |||
3520 | if (ch == ')') | 3537 | if (ch == ')') |
3521 | if (--count < 0) | 3538 | if (--count < 0) |
3522 | break; | 3539 | break; |
3523 | o_addqchr(dest, ch); | 3540 | o_addchr(dest, ch); |
3524 | if (ch == '\'') { | 3541 | if (ch == '\'') { |
3525 | add_till_single_quote(dest, input); | 3542 | add_till_single_quote(dest, input); |
3526 | o_addqchr(dest, ch); | 3543 | o_addchr(dest, ch); |
3527 | continue; | 3544 | continue; |
3528 | } | 3545 | } |
3529 | if (ch == '"') { | 3546 | if (ch == '"') { |
3530 | add_till_double_quote(dest, input); | 3547 | add_till_double_quote(dest, input); |
3531 | o_addqchr(dest, ch); | 3548 | o_addchr(dest, ch); |
3532 | continue; | 3549 | continue; |
3533 | } | 3550 | } |
3534 | if (ch == '\\') { /* \x. Copy verbatim. Important for \(, \) */ | 3551 | if (ch == '\\') { /* \x. Copy verbatim. Important for \(, \) */ |
3535 | ch = i_getch(input); | 3552 | ch = i_getch(input); |
3536 | if (ch == EOF) | 3553 | if (ch == EOF) |
3537 | break; | 3554 | break; |
3538 | o_addqchr(dest, ch); | 3555 | o_addchr(dest, ch); |
3539 | continue; | 3556 | continue; |
3540 | } | 3557 | } |
3541 | } | 3558 | } |
diff --git a/shell/hush_test/hush-bugs/empty_for.right b/shell/hush_test/hush-bugs/empty_for.right new file mode 100644 index 000000000..290d39b7e --- /dev/null +++ b/shell/hush_test/hush-bugs/empty_for.right | |||
@@ -0,0 +1 @@ | |||
OK: 0 | |||
diff --git a/shell/hush_test/hush-bugs/empty_for.tests b/shell/hush_test/hush-bugs/empty_for.tests new file mode 100755 index 000000000..0cb52e849 --- /dev/null +++ b/shell/hush_test/hush-bugs/empty_for.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | false | ||
2 | for a in; do echo "HELLO"; done | ||
3 | echo OK: $? | ||
diff --git a/shell/hush_test/hush-bugs/glob_and_vars.tests b/shell/hush_test/hush-bugs/glob_and_vars.tests index c8e0c067d..482cf9d8a 100755 --- a/shell/hush_test/hush-bugs/glob_and_vars.tests +++ b/shell/hush_test/hush-bugs/glob_and_vars.tests | |||
@@ -1,2 +1,2 @@ | |||
1 | v=. | 1 | v=. |
2 | echo $v/glob_and_vars.* | 2 | echo $v/glob_and_vars.[tr]* |
diff --git a/shell/hush_test/hush-bugs/glob_and_vars.right b/shell/hush_test/hush-vars/glob_and_vars.right index 3ac7ec5ff..3ac7ec5ff 100644 --- a/shell/hush_test/hush-bugs/glob_and_vars.right +++ b/shell/hush_test/hush-vars/glob_and_vars.right | |||
diff --git a/shell/hush_test/hush-vars/glob_and_vars.tests b/shell/hush_test/hush-vars/glob_and_vars.tests new file mode 100755 index 000000000..482cf9d8a --- /dev/null +++ b/shell/hush_test/hush-vars/glob_and_vars.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | v=. | ||
2 | echo $v/glob_and_vars.[tr]* | ||