diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2010-05-21 19:52:01 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-05-21 19:52:01 +0200 |
| commit | 7436950a7516d1f4498285ccc81bf6d926f3af5e (patch) | |
| tree | 392a5f5eaed215eaacaded7bc92456729161e1d0 /shell | |
| parent | 3f78cec34745069cf0a92a16dfccff66d98ef5ba (diff) | |
| download | busybox-w32-7436950a7516d1f4498285ccc81bf6d926f3af5e.tar.gz busybox-w32-7436950a7516d1f4498285ccc81bf6d926f3af5e.tar.bz2 busybox-w32-7436950a7516d1f4498285ccc81bf6d926f3af5e.zip | |
hush: fix a=abc; c=c; echo ${a%${c}}
function old new delta
expand_vars_to_list 2229 2302 +73
add_till_closing_paren 286 313 +27
handle_dollar 623 574 -49
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/1 up/down: 100/-49) Total: 51 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 118 | ||||
| -rw-r--r-- | shell/hush_test/hush-vars/var3.right | 2 | ||||
| -rw-r--r-- | shell/hush_test/hush-vars/var_posix1.right | 6 | ||||
| -rwxr-xr-x | shell/hush_test/hush-vars/var_posix1.tests | 9 |
4 files changed, 75 insertions, 60 deletions
diff --git a/shell/hush.c b/shell/hush.c index 6d91a534a..a3df5edcd 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -2638,11 +2638,21 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char | |||
| 2638 | if (exp_op == *exp_word) /* ## or %% */ | 2638 | if (exp_op == *exp_word) /* ## or %% */ |
| 2639 | exp_word++; | 2639 | exp_word++; |
| 2640 | val = to_be_freed = xstrdup(val); | 2640 | val = to_be_freed = xstrdup(val); |
| 2641 | loc = scan(to_be_freed, exp_word, match_at_left); | 2641 | { |
| 2642 | if (match_at_left) /* # or ## */ | 2642 | char *exp_exp_word = expand_pseudo_dquoted(exp_word); |
| 2643 | val = loc; | 2643 | if (exp_exp_word) |
| 2644 | else if (loc) /* % or %% and match was found */ | 2644 | exp_word = exp_exp_word; |
| 2645 | *loc = '\0'; | 2645 | loc = scan(to_be_freed, exp_word, match_at_left); |
| 2646 | //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'", | ||
| 2647 | // exp_op, to_be_freed, exp_word, loc); | ||
| 2648 | free(exp_exp_word); | ||
| 2649 | } | ||
| 2650 | if (loc) { /* match was found */ | ||
| 2651 | if (match_at_left) /* # or ## */ | ||
| 2652 | val = loc; | ||
| 2653 | else /* % or %% */ | ||
| 2654 | *loc = '\0'; | ||
| 2655 | } | ||
| 2646 | } | 2656 | } |
| 2647 | } else if (!strchr("%#:-=+?"+3, exp_op)) { | 2657 | } else if (!strchr("%#:-=+?"+3, exp_op)) { |
| 2648 | #if ENABLE_HUSH_BASH_COMPAT | 2658 | #if ENABLE_HUSH_BASH_COMPAT |
| @@ -5876,20 +5886,28 @@ static void add_till_backquote(o_string *dest, struct in_str *input) | |||
| 5876 | * echo $(echo '(TEST)' BEST) (TEST) BEST | 5886 | * echo $(echo '(TEST)' BEST) (TEST) BEST |
| 5877 | * echo $(echo 'TEST)' BEST) TEST) BEST | 5887 | * echo $(echo 'TEST)' BEST) TEST) BEST |
| 5878 | * echo $(echo \(\(TEST\) BEST) ((TEST) BEST | 5888 | * echo $(echo \(\(TEST\) BEST) ((TEST) BEST |
| 5889 | * | ||
| 5890 | * BUG: enter: echo $(( `printf '(\x28 1'` + `echo 2))` )) | ||
| 5891 | * on the command line, press Enter. You get > prompt which is impossible | ||
| 5892 | * to exit with ^C. | ||
| 5879 | */ | 5893 | */ |
| 5880 | static void add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl) | 5894 | #define DOUBLE_CLOSE_CHAR_FLAG 0x80 |
| 5895 | static void add_till_closing_paren(o_string *dest, struct in_str *input, char end_ch) | ||
| 5881 | { | 5896 | { |
| 5882 | int count = 0; | 5897 | int count = 0; |
| 5898 | char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; | ||
| 5899 | end_ch &= (DOUBLE_CLOSE_CHAR_FLAG-1); | ||
| 5883 | while (1) { | 5900 | while (1) { |
| 5884 | int ch = i_getch(input); | 5901 | int ch = i_getch(input); |
| 5885 | if (ch == EOF) { | 5902 | if (ch == EOF) { |
| 5886 | syntax_error_unterm_ch(')'); | 5903 | syntax_error_unterm_ch(')'); |
| 5887 | /*xfunc_die(); - redundant */ | 5904 | /*xfunc_die(); - redundant */ |
| 5888 | } | 5905 | } |
| 5889 | if (ch == '(') | 5906 | if (ch == '(' || ch == '{') |
| 5890 | count++; | 5907 | count++; |
| 5891 | if (ch == ')') { | 5908 | if (ch == ')' || ch == '}') { |
| 5892 | if (--count < 0) { | 5909 | count--; |
| 5910 | if (count < 0 && ch == end_ch) { | ||
| 5893 | if (!dbl) | 5911 | if (!dbl) |
| 5894 | break; | 5912 | break; |
| 5895 | if (i_peek(input) == ')') { | 5913 | if (i_peek(input) == ')') { |
| @@ -5969,62 +5987,52 @@ static int handle_dollar(o_string *as_string, | |||
| 5969 | case '@': /* args */ | 5987 | case '@': /* args */ |
| 5970 | goto make_one_char_var; | 5988 | goto make_one_char_var; |
| 5971 | case '{': { | 5989 | case '{': { |
| 5972 | bool first_char, all_digits; | 5990 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
| 5973 | bool in_expansion_param; | ||
| 5974 | 5991 | ||
| 5975 | ch = i_getch(input); | 5992 | ch = i_getch(input); /* eat '{' */ |
| 5976 | nommu_addchr(as_string, ch); | 5993 | nommu_addchr(as_string, ch); |
| 5977 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
| 5978 | 5994 | ||
| 5979 | // TODO: need to handle "a=ab}; echo ${a%\}}" | 5995 | ch = i_getch(input); /* first char after '{' */ |
| 5980 | // and "a=abc; c=c; echo ${a%${c}}" | 5996 | nommu_addchr(as_string, ch); |
| 5981 | in_expansion_param = false; | 5997 | /* It should be ${?}, or ${#var}, |
| 5982 | first_char = true; | 5998 | * or even ${?+subst} - operator acting on a special variable, |
| 5983 | all_digits = false; | 5999 | * or the beginning of variable name. |
| 6000 | */ | ||
| 6001 | if (!strchr("$!?#*@_", ch) && !isalnum(ch)) { /* not one of those */ | ||
| 6002 | bad_dollar_syntax: | ||
| 6003 | syntax_error_unterm_str("${name}"); | ||
| 6004 | debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); | ||
| 6005 | return 1; | ||
| 6006 | } | ||
| 6007 | ch |= quote_mask; | ||
| 6008 | |||
| 6009 | /* It's possible to just call add_till_closing_paren() at this point. | ||
| 6010 | * However, this regresses some of our testsuite cases | ||
| 6011 | * which check invalid constructs like ${%}. | ||
| 6012 | * Oh well... let's check that the var name part is fine... */ | ||
| 6013 | |||
| 5984 | while (1) { | 6014 | while (1) { |
| 6015 | o_addchr(dest, ch); | ||
| 6016 | debug_printf_parse(": '%c'\n", ch); | ||
| 6017 | |||
| 5985 | ch = i_getch(input); | 6018 | ch = i_getch(input); |
| 5986 | nommu_addchr(as_string, ch); | 6019 | nommu_addchr(as_string, ch); |
| 5987 | if (ch == '}') { | 6020 | if (ch == '}') |
| 5988 | break; | 6021 | break; |
| 5989 | } | ||
| 5990 | 6022 | ||
| 5991 | if (first_char) { | 6023 | if (!isalnum(ch) && ch != '_') { |
| 5992 | if (ch == '#') { | ||
| 5993 | /* ${#var}: length of var contents */ | ||
| 5994 | goto char_ok; | ||
| 5995 | } | ||
| 5996 | if (isdigit(ch)) { | ||
| 5997 | all_digits = true; | ||
| 5998 | goto char_ok; | ||
| 5999 | } | ||
| 6000 | /* They're being verbose and doing ${?} */ | ||
| 6001 | if (i_peek(input) == '}' && strchr("$!?#*@_", ch)) | ||
| 6002 | goto char_ok; | ||
| 6003 | } | ||
| 6004 | |||
| 6005 | if (!in_expansion_param | ||
| 6006 | && ( (all_digits && !isdigit(ch)) /* met non-digit: 123w */ | ||
| 6007 | || (!all_digits && !isalnum(ch) && ch != '_') /* met non-name char: abc% */ | ||
| 6008 | ) | ||
| 6009 | ) { | ||
| 6010 | /* handle parameter expansions | 6024 | /* handle parameter expansions |
| 6011 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 | 6025 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 |
| 6012 | */ | 6026 | */ |
| 6013 | if (first_char /* bad (empty var name): "${%..." */ | 6027 | if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */ |
| 6014 | || !strchr("%#:-=+?", ch) /* bad: "${var<bad_char>..." */ | 6028 | goto bad_dollar_syntax; |
| 6015 | ) { | 6029 | /* Eat everything until closing '}' */ |
| 6016 | syntax_error_unterm_str("${name}"); | 6030 | o_addchr(dest, ch); |
| 6017 | debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); | 6031 | //TODO: add nommu_addchr hack here |
| 6018 | return 1; | 6032 | add_till_closing_paren(dest, input, '}'); |
| 6019 | } | 6033 | break; |
| 6020 | in_expansion_param = true; | ||
| 6021 | } | 6034 | } |
| 6022 | char_ok: | 6035 | } |
| 6023 | debug_printf_parse(": '%c'\n", ch); | ||
| 6024 | o_addchr(dest, ch | quote_mask); | ||
| 6025 | quote_mask = 0; | ||
| 6026 | first_char = false; | ||
| 6027 | } /* while (1) */ | ||
| 6028 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 6036 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
| 6029 | break; | 6037 | break; |
| 6030 | } | 6038 | } |
| @@ -6044,7 +6052,7 @@ static int handle_dollar(o_string *as_string, | |||
| 6044 | # if !BB_MMU | 6052 | # if !BB_MMU |
| 6045 | pos = dest->length; | 6053 | pos = dest->length; |
| 6046 | # endif | 6054 | # endif |
| 6047 | add_till_closing_paren(dest, input, true); | 6055 | add_till_closing_paren(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG); |
| 6048 | # if !BB_MMU | 6056 | # if !BB_MMU |
| 6049 | if (as_string) { | 6057 | if (as_string) { |
| 6050 | o_addstr(as_string, dest->data + pos); | 6058 | o_addstr(as_string, dest->data + pos); |
| @@ -6062,7 +6070,7 @@ static int handle_dollar(o_string *as_string, | |||
| 6062 | # if !BB_MMU | 6070 | # if !BB_MMU |
| 6063 | pos = dest->length; | 6071 | pos = dest->length; |
| 6064 | # endif | 6072 | # endif |
| 6065 | add_till_closing_paren(dest, input, false); | 6073 | add_till_closing_paren(dest, input, ')'); |
| 6066 | # if !BB_MMU | 6074 | # if !BB_MMU |
| 6067 | if (as_string) { | 6075 | if (as_string) { |
| 6068 | o_addstr(as_string, dest->data + pos); | 6076 | o_addstr(as_string, dest->data + pos); |
diff --git a/shell/hush_test/hush-vars/var3.right b/shell/hush_test/hush-vars/var3.right index 5e28d2fab..40e67fdf5 100644 --- a/shell/hush_test/hush-vars/var3.right +++ b/shell/hush_test/hush-vars/var3.right | |||
| @@ -1,2 +1,2 @@ | |||
| 1 | hush: syntax error: unterminated ${name} | 1 | hush: invalid number '1q' |
| 2 | hush: syntax error: unterminated ${name} | 2 | hush: syntax error: unterminated ${name} |
diff --git a/shell/hush_test/hush-vars/var_posix1.right b/shell/hush_test/hush-vars/var_posix1.right index e6cba2758..813437e2f 100644 --- a/shell/hush_test/hush-vars/var_posix1.right +++ b/shell/hush_test/hush-vars/var_posix1.right | |||
| @@ -23,6 +23,7 @@ babcdcd | |||
| 23 | ababcdcd | 23 | ababcdcd |
| 24 | Empty: | 24 | Empty: |
| 25 | ababcdcd}_tail | 25 | ababcdcd}_tail |
| 26 | ababcdcd_tail | ||
| 26 | ababcd | 27 | ababcd |
| 27 | ababcd | 28 | ababcd |
| 28 | ababcd | 29 | ababcd |
| @@ -32,5 +33,8 @@ ababcdc | |||
| 32 | ababcdcd | 33 | ababcdcd |
| 33 | Empty: | 34 | Empty: |
| 34 | ababcdcd}_tail | 35 | ababcdcd}_tail |
| 36 | ababcdcd_tail | ||
| 35 | ababcdcd | 37 | ababcdcd |
| 36 | end | 38 | ab |
| 39 | ab | ||
| 40 | End | ||
diff --git a/shell/hush_test/hush-vars/var_posix1.tests b/shell/hush_test/hush-vars/var_posix1.tests index c1f64094d..e48fd98c7 100755 --- a/shell/hush_test/hush-vars/var_posix1.tests +++ b/shell/hush_test/hush-vars/var_posix1.tests | |||
| @@ -31,7 +31,7 @@ echo ${var##?} | |||
| 31 | echo ${var#*} | 31 | echo ${var#*} |
| 32 | echo Empty:${var##*} | 32 | echo Empty:${var##*} |
| 33 | echo ${var#}}_tail | 33 | echo ${var#}}_tail |
| 34 | # UNFIXED BUG: echo ${var#\}}_tail | 34 | echo ${var#\}}_tail |
| 35 | 35 | ||
| 36 | echo ${var%cd} | 36 | echo ${var%cd} |
| 37 | echo ${var%%cd} | 37 | echo ${var%%cd} |
| @@ -42,7 +42,10 @@ echo ${var%%?} | |||
| 42 | echo ${var%*} | 42 | echo ${var%*} |
| 43 | echo Empty:${var%%*} | 43 | echo Empty:${var%%*} |
| 44 | echo ${var#}}_tail | 44 | echo ${var#}}_tail |
| 45 | # UNFIXED BUG: echo ${var#\}}_tail | 45 | echo ${var#\}}_tail |
| 46 | echo ${var%\\*} | 46 | echo ${var%\\*} |
| 47 | 47 | ||
| 48 | echo end | 48 | a=ab}; echo ${a%\}}; |
| 49 | a=abc; c=c; echo ${a%${c}} | ||
| 50 | |||
| 51 | echo End | ||
