diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2011-08-01 18:16:43 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2011-08-01 18:16:43 +0200 |
commit | 6e42b89b8d136316e1b97b56cf885e8ef9d64caf (patch) | |
tree | b22a7d3e771d5262c39058764f13ac62c5cfe731 /shell | |
parent | 4fb53fb08ce3da8eac13438ce613df20e523c75d (diff) | |
download | busybox-w32-6e42b89b8d136316e1b97b56cf885e8ef9d64caf.tar.gz busybox-w32-6e42b89b8d136316e1b97b56cf885e8ef9d64caf.tar.bz2 busybox-w32-6e42b89b8d136316e1b97b56cf885e8ef9d64caf.zip |
hush: fix remaining known two bugs with IFS expansion. Closes 4027.
function old new delta
expand_vars_to_list 1054 1140 +86
parse_stream 2425 2479 +54
expand_on_ifs 258 310 +52
builtin_umask 133 132 -1
done_word 820 779 -41
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/2 up/down: 192/-42) Total: 150 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
-rw-r--r-- | shell/hush.c | 82 | ||||
-rw-r--r-- | shell/hush_test/hush-parsing/starquoted2.right | 3 | ||||
-rwxr-xr-x | shell/hush_test/hush-parsing/starquoted2.tests | 6 |
3 files changed, 61 insertions, 30 deletions
diff --git a/shell/hush.c b/shell/hush.c index 503cb770b..6b3027771 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -3265,14 +3265,6 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3265 | ) { | 3265 | ) { |
3266 | p += 3; | 3266 | p += 3; |
3267 | } | 3267 | } |
3268 | if (p == word->data || p[0] != '\0') { | ||
3269 | /* saw no "$@", or not only "$@" but some | ||
3270 | * real text is there too */ | ||
3271 | /* insert "empty variable" reference, this makes | ||
3272 | * e.g. "", $empty"" etc to not disappear */ | ||
3273 | o_addchr(word, SPECIAL_VAR_SYMBOL); | ||
3274 | o_addchr(word, SPECIAL_VAR_SYMBOL); | ||
3275 | } | ||
3276 | } | 3268 | } |
3277 | command->argv = add_string_to_strings(command->argv, xstrdup(word->data)); | 3269 | command->argv = add_string_to_strings(command->argv, xstrdup(word->data)); |
3278 | debug_print_strings("word appended to argv", command->argv); | 3270 | debug_print_strings("word appended to argv", command->argv); |
@@ -4516,20 +4508,30 @@ static struct pipe *parse_stream(char **pstring, | |||
4516 | break; | 4508 | break; |
4517 | case '\'': | 4509 | case '\'': |
4518 | dest.has_quoted_part = 1; | 4510 | dest.has_quoted_part = 1; |
4519 | while (1) { | 4511 | if (next == '\'' && !ctx.pending_redirect) { |
4520 | ch = i_getch(input); | 4512 | insert_empty_quoted_str_marker: |
4521 | if (ch == EOF) { | 4513 | nommu_addchr(&ctx.as_string, next); |
4522 | syntax_error_unterm_ch('\''); | 4514 | i_getch(input); /* eat second ' */ |
4523 | goto parse_error; | 4515 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); |
4516 | o_addchr(&dest, SPECIAL_VAR_SYMBOL); | ||
4517 | } else { | ||
4518 | while (1) { | ||
4519 | ch = i_getch(input); | ||
4520 | if (ch == EOF) { | ||
4521 | syntax_error_unterm_ch('\''); | ||
4522 | goto parse_error; | ||
4523 | } | ||
4524 | nommu_addchr(&ctx.as_string, ch); | ||
4525 | if (ch == '\'') | ||
4526 | break; | ||
4527 | o_addqchr(&dest, ch); | ||
4524 | } | 4528 | } |
4525 | nommu_addchr(&ctx.as_string, ch); | ||
4526 | if (ch == '\'') | ||
4527 | break; | ||
4528 | o_addqchr(&dest, ch); | ||
4529 | } | 4529 | } |
4530 | break; | 4530 | break; |
4531 | case '"': | 4531 | case '"': |
4532 | dest.has_quoted_part = 1; | 4532 | dest.has_quoted_part = 1; |
4533 | if (next == '"' && !ctx.pending_redirect) | ||
4534 | goto insert_empty_quoted_str_marker; | ||
4533 | if (dest.o_assignment == NOT_ASSIGNMENT) | 4535 | if (dest.o_assignment == NOT_ASSIGNMENT) |
4534 | dest.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; | 4536 | dest.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; |
4535 | if (!encode_string(&ctx.as_string, &dest, input, '"', /*process_bkslash:*/ 1)) | 4537 | if (!encode_string(&ctx.as_string, &dest, input, '"', /*process_bkslash:*/ 1)) |
@@ -4751,9 +4753,14 @@ static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len | |||
4751 | 4753 | ||
4752 | /* Store given string, finalizing the word and starting new one whenever | 4754 | /* Store given string, finalizing the word and starting new one whenever |
4753 | * we encounter IFS char(s). This is used for expanding variable values. | 4755 | * we encounter IFS char(s). This is used for expanding variable values. |
4754 | * End-of-string does NOT finalize word: think about 'echo -$VAR-' */ | 4756 | * End-of-string does NOT finalize word: think about 'echo -$VAR-'. |
4755 | static int expand_on_ifs(o_string *output, int n, const char *str) | 4757 | * Return in *ended_with_ifs: |
4758 | * 1 - ended with IFS char, else 0 (this includes case of empty str). | ||
4759 | */ | ||
4760 | static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const char *str) | ||
4756 | { | 4761 | { |
4762 | int last_is_ifs = 0; | ||
4763 | |||
4757 | while (1) { | 4764 | while (1) { |
4758 | int word_len; | 4765 | int word_len; |
4759 | 4766 | ||
@@ -4774,27 +4781,36 @@ static int expand_on_ifs(o_string *output, int n, const char *str) | |||
4774 | /*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */ | 4781 | /*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */ |
4775 | /*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */ | 4782 | /*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */ |
4776 | } | 4783 | } |
4784 | last_is_ifs = 0; | ||
4777 | str += word_len; | 4785 | str += word_len; |
4778 | if (!*str) /* EOL - do not finalize word */ | 4786 | if (!*str) /* EOL - do not finalize word */ |
4779 | break; | 4787 | break; |
4780 | goto finalize; /* optimization (can just fall thru) */ | ||
4781 | } | 4788 | } |
4782 | /* Case "v=' a'; echo ''$v": we do need to finalize empty word */ | 4789 | |
4790 | /* We know str here points to at least one IFS char */ | ||
4791 | last_is_ifs = 1; | ||
4792 | str += strspn(str, G.ifs); /* skip IFS chars */ | ||
4793 | if (!*str) /* EOL - do not finalize word */ | ||
4794 | break; | ||
4795 | |||
4796 | /* Start new word... but not always! */ | ||
4797 | /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */ | ||
4783 | if (output->has_quoted_part | 4798 | if (output->has_quoted_part |
4784 | /* Case "v=' a'; echo $v": | 4799 | /* Case "v=' a'; echo $v": |
4785 | * here nothing precedes the space in $v expansion, | 4800 | * here nothing precedes the space in $v expansion, |
4786 | * therefore we should not finish the word | 4801 | * therefore we should not finish the word |
4787 | * (IOW: if there *is* word to finalize, only then do it) | 4802 | * (IOW: if there *is* word to finalize, only then do it): |
4788 | */ | 4803 | */ |
4789 | || (output->length && output->data[output->length - 1]) | 4804 | || (n > 0 && output->data[output->length - 1]) |
4790 | ) { | 4805 | ) { |
4791 | finalize: | ||
4792 | o_addchr(output, '\0'); | 4806 | o_addchr(output, '\0'); |
4793 | debug_print_list("expand_on_ifs", output, n); | 4807 | debug_print_list("expand_on_ifs", output, n); |
4794 | n = o_save_ptr(output, n); | 4808 | n = o_save_ptr(output, n); |
4795 | } | 4809 | } |
4796 | str += strspn(str, G.ifs); /* skip IFS chars */ | ||
4797 | } | 4810 | } |
4811 | |||
4812 | if (ended_with_ifs) | ||
4813 | *ended_with_ifs = last_is_ifs; | ||
4798 | debug_print_list("expand_on_ifs[1]", output, n); | 4814 | debug_print_list("expand_on_ifs[1]", output, n); |
4799 | return n; | 4815 | return n; |
4800 | } | 4816 | } |
@@ -5209,6 +5225,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
5209 | * expansion of right-hand side of assignment == 1-element expand. | 5225 | * expansion of right-hand side of assignment == 1-element expand. |
5210 | */ | 5226 | */ |
5211 | char cant_be_null = 0; /* only bit 0x80 matters */ | 5227 | char cant_be_null = 0; /* only bit 0x80 matters */ |
5228 | int ended_in_ifs = 0; /* did last unquoted expansion end with IFS chars? */ | ||
5212 | char *p; | 5229 | char *p; |
5213 | 5230 | ||
5214 | debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg, | 5231 | debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg, |
@@ -5227,6 +5244,13 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
5227 | #if ENABLE_SH_MATH_SUPPORT | 5244 | #if ENABLE_SH_MATH_SUPPORT |
5228 | char arith_buf[sizeof(arith_t)*3 + 2]; | 5245 | char arith_buf[sizeof(arith_t)*3 + 2]; |
5229 | #endif | 5246 | #endif |
5247 | |||
5248 | if (ended_in_ifs) { | ||
5249 | o_addchr(output, '\0'); | ||
5250 | n = o_save_ptr(output, n); | ||
5251 | ended_in_ifs = 0; | ||
5252 | } | ||
5253 | |||
5230 | o_addblock(output, arg, p - arg); | 5254 | o_addblock(output, arg, p - arg); |
5231 | debug_print_list("expand_vars_to_list[1]", output, n); | 5255 | debug_print_list("expand_vars_to_list[1]", output, n); |
5232 | arg = ++p; | 5256 | arg = ++p; |
@@ -5255,7 +5279,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
5255 | cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ | 5279 | cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ |
5256 | if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ | 5280 | if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ |
5257 | while (G.global_argv[i]) { | 5281 | while (G.global_argv[i]) { |
5258 | n = expand_on_ifs(output, n, G.global_argv[i]); | 5282 | n = expand_on_ifs(NULL, output, n, G.global_argv[i]); |
5259 | debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1); | 5283 | debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1); |
5260 | if (G.global_argv[i++][0] && G.global_argv[i]) { | 5284 | if (G.global_argv[i++][0] && G.global_argv[i]) { |
5261 | /* this argv[] is not empty and not last: | 5285 | /* this argv[] is not empty and not last: |
@@ -5332,7 +5356,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
5332 | debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, | 5356 | debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, |
5333 | !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); | 5357 | !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); |
5334 | if (val && val[0]) { | 5358 | if (val && val[0]) { |
5335 | n = expand_on_ifs(output, n, val); | 5359 | n = expand_on_ifs(&ended_in_ifs, output, n, val); |
5336 | val = NULL; | 5360 | val = NULL; |
5337 | } | 5361 | } |
5338 | } else { /* quoted $VAR, val will be appended below */ | 5362 | } else { /* quoted $VAR, val will be appended below */ |
@@ -5361,6 +5385,10 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
5361 | } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */ | 5385 | } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */ |
5362 | 5386 | ||
5363 | if (arg[0]) { | 5387 | if (arg[0]) { |
5388 | if (ended_in_ifs) { | ||
5389 | o_addchr(output, '\0'); | ||
5390 | n = o_save_ptr(output, n); | ||
5391 | } | ||
5364 | debug_print_list("expand_vars_to_list[a]", output, n); | 5392 | debug_print_list("expand_vars_to_list[a]", output, n); |
5365 | /* this part is literal, and it was already pre-quoted | 5393 | /* this part is literal, and it was already pre-quoted |
5366 | * if needed (much earlier), do not use o_addQstr here! */ | 5394 | * if needed (much earlier), do not use o_addQstr here! */ |
diff --git a/shell/hush_test/hush-parsing/starquoted2.right b/shell/hush_test/hush-parsing/starquoted2.right index e1562ed6d..1bff408ca 100644 --- a/shell/hush_test/hush-parsing/starquoted2.right +++ b/shell/hush_test/hush-parsing/starquoted2.right | |||
@@ -1,4 +1,7 @@ | |||
1 | Should be printed | 1 | Should be printed |
2 | Would not be printed by bash | ||
3 | Would not be printed by bash | ||
4 | Would not be printed by bash | ||
2 | Should be printed | 5 | Should be printed |
3 | Empty: | 6 | Empty: |
4 | Empty: | 7 | Empty: |
diff --git a/shell/hush_test/hush-parsing/starquoted2.tests b/shell/hush_test/hush-parsing/starquoted2.tests index f305c4cd9..7c5ff45b8 100755 --- a/shell/hush_test/hush-parsing/starquoted2.tests +++ b/shell/hush_test/hush-parsing/starquoted2.tests | |||
@@ -8,9 +8,9 @@ for a in "$@"; do echo Should not be printed; done | |||
8 | # Yes, believe it or not, bash is mesmerized by "$@" and stops | 8 | # Yes, believe it or not, bash is mesmerized by "$@" and stops |
9 | # treating "" as "this word cannot be expanded to nothing, | 9 | # treating "" as "this word cannot be expanded to nothing, |
10 | # but must be at least null string". Now it can be expanded to nothing. | 10 | # but must be at least null string". Now it can be expanded to nothing. |
11 | for a in "$@"""; do echo Should not be printed; done | 11 | for a in "$@"""; do echo Would not be printed by bash; done |
12 | for a in """$@"; do echo Should not be printed; done | 12 | for a in """$@"; do echo Would not be printed by bash; done |
13 | for a in """$@"''"$@"''; do echo Should not be printed; done | 13 | for a in """$@"''"$@"''; do echo Would not be printed by bash; done |
14 | for a in ""; do echo Should be printed; done | 14 | for a in ""; do echo Should be printed; done |
15 | 15 | ||
16 | # Bug 207: "$@" expands to nothing, and we erroneously glob "%s\n" twice: | 16 | # Bug 207: "$@" expands to nothing, and we erroneously glob "%s\n" twice: |