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 | |
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>
-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 | ||