aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2010-05-21 19:52:01 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2010-05-21 19:52:01 +0200
commit7436950a7516d1f4498285ccc81bf6d926f3af5e (patch)
tree392a5f5eaed215eaacaded7bc92456729161e1d0
parent3f78cec34745069cf0a92a16dfccff66d98ef5ba (diff)
downloadbusybox-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.c118
-rw-r--r--shell/hush_test/hush-vars/var3.right2
-rw-r--r--shell/hush_test/hush-vars/var_posix1.right6
-rwxr-xr-xshell/hush_test/hush-vars/var_posix1.tests9
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 */
5880static void add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl) 5894#define DOUBLE_CLOSE_CHAR_FLAG 0x80
5895static 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 @@
1hush: syntax error: unterminated ${name} 1hush: invalid number '1q'
2hush: syntax error: unterminated ${name} 2hush: 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
23ababcdcd 23ababcdcd
24Empty: 24Empty:
25ababcdcd}_tail 25ababcdcd}_tail
26ababcdcd_tail
26ababcd 27ababcd
27ababcd 28ababcd
28ababcd 29ababcd
@@ -32,5 +33,8 @@ ababcdc
32ababcdcd 33ababcdcd
33Empty: 34Empty:
34ababcdcd}_tail 35ababcdcd}_tail
36ababcdcd_tail
35ababcdcd 37ababcdcd
36end 38ab
39ab
40End
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##?}
31echo ${var#*} 31echo ${var#*}
32echo Empty:${var##*} 32echo Empty:${var##*}
33echo ${var#}}_tail 33echo ${var#}}_tail
34# UNFIXED BUG: echo ${var#\}}_tail 34echo ${var#\}}_tail
35 35
36echo ${var%cd} 36echo ${var%cd}
37echo ${var%%cd} 37echo ${var%%cd}
@@ -42,7 +42,10 @@ echo ${var%%?}
42echo ${var%*} 42echo ${var%*}
43echo Empty:${var%%*} 43echo Empty:${var%%*}
44echo ${var#}}_tail 44echo ${var#}}_tail
45# UNFIXED BUG: echo ${var#\}}_tail 45echo ${var#\}}_tail
46echo ${var%\\*} 46echo ${var%\\*}
47 47
48echo end 48a=ab}; echo ${a%\}};
49a=abc; c=c; echo ${a%${c}}
50
51echo End