[git commit master] hush: fix a=abc; c=c; echo ${a%${c}}

Denys Vlasenko vda.linux at googlemail.com
Fri May 21 17:52:01 UTC 2010


commit: http://git.busybox.net/busybox/commit/?id=7436950a7516d1f4498285ccc81bf6d926f3af5e
branch: http://git.busybox.net/busybox/commit/?id=refs/heads/master

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 at googlemail.com>
---
 shell/hush.c                               |  118 +++++++++++++++-------------
 shell/hush_test/hush-vars/var3.right       |    2 +-
 shell/hush_test/hush-vars/var_posix1.right |    6 +-
 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 6d91a53..a3df5ed 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
 						if (exp_op == *exp_word)	/* ## or %% */
 							exp_word++;
 						val = to_be_freed = xstrdup(val);
-						loc = scan(to_be_freed, exp_word, match_at_left);
-						if (match_at_left) /* # or ## */
-							val = loc;
-						else if (loc) /* % or %% and match was found */
-							*loc = '\0';
+						{
+							char *exp_exp_word = expand_pseudo_dquoted(exp_word);
+							if (exp_exp_word)
+								exp_word = exp_exp_word;
+							loc = scan(to_be_freed, exp_word, match_at_left);
+							//bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'",
+							//		exp_op, to_be_freed, exp_word, loc);
+							free(exp_exp_word);
+						}
+						if (loc) { /* match was found */
+							if (match_at_left) /* # or ## */
+								val = loc;
+							else /* % or %% */
+								*loc = '\0';
+						}
 					}
 				} else if (!strchr("%#:-=+?"+3, exp_op)) {
 #if ENABLE_HUSH_BASH_COMPAT
@@ -5876,20 +5886,28 @@ static void add_till_backquote(o_string *dest, struct in_str *input)
  * echo $(echo '(TEST)' BEST)           (TEST) BEST
  * echo $(echo 'TEST)' BEST)            TEST) BEST
  * echo $(echo \(\(TEST\) BEST)         ((TEST) BEST
+ *
+ * BUG: enter: echo $(( `printf '(\x28 1'` + `echo 2))` ))
+ * on the command line, press Enter. You get > prompt which is impossible
+ * to exit with ^C.
  */
-static void add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl)
+#define DOUBLE_CLOSE_CHAR_FLAG 0x80
+static void add_till_closing_paren(o_string *dest, struct in_str *input, char end_ch)
 {
 	int count = 0;
+	char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
+	end_ch &= (DOUBLE_CLOSE_CHAR_FLAG-1);
 	while (1) {
 		int ch = i_getch(input);
 		if (ch == EOF) {
 			syntax_error_unterm_ch(')');
 			/*xfunc_die(); - redundant */
 		}
-		if (ch == '(')
+		if (ch == '(' || ch == '{')
 			count++;
-		if (ch == ')') {
-			if (--count < 0) {
+		if (ch == ')' || ch == '}') {
+			count--;
+			if (count < 0 && ch == end_ch) {
 				if (!dbl)
 					break;
 				if (i_peek(input) == ')') {
@@ -5969,62 +5987,52 @@ static int handle_dollar(o_string *as_string,
 	case '@': /* args */
 		goto make_one_char_var;
 	case '{': {
-		bool first_char, all_digits;
-		bool in_expansion_param;
+		o_addchr(dest, SPECIAL_VAR_SYMBOL);
 
-		ch = i_getch(input);
+		ch = i_getch(input); /* eat '{' */
 		nommu_addchr(as_string, ch);
-		o_addchr(dest, SPECIAL_VAR_SYMBOL);
 
-// TODO: need to handle "a=ab}; echo ${a%\}}"
-// and "a=abc; c=c; echo ${a%${c}}"
-		in_expansion_param = false;
-		first_char = true;
-		all_digits = false;
+		ch = i_getch(input); /* first char after '{' */
+		nommu_addchr(as_string, ch);
+		/* It should be ${?}, or ${#var},
+		 * or even ${?+subst} - operator acting on a special variable,
+		 * or the beginning of variable name.
+		 */
+		if (!strchr("$!?#*@_", ch) && !isalnum(ch)) { /* not one of those */
+ bad_dollar_syntax:
+			syntax_error_unterm_str("${name}");
+			debug_printf_parse("handle_dollar return 1: unterminated ${name}\n");
+			return 1;
+		}
+		ch |= quote_mask;
+
+		/* It's possible to just call add_till_closing_paren() at this point.
+		 * However, this regresses some of our testsuite cases
+		 * which check invalid constructs like ${%}.
+		 * Oh well... let's check that the var name part is fine... */
+
 		while (1) {
+			o_addchr(dest, ch);
+			debug_printf_parse(": '%c'\n", ch);
+
 			ch = i_getch(input);
 			nommu_addchr(as_string, ch);
-			if (ch == '}') {
+			if (ch == '}')
 				break;
-			}
 
-			if (first_char) {
-				if (ch == '#') {
-					/* ${#var}: length of var contents */
-					goto char_ok;
-				}
-				if (isdigit(ch)) {
-					all_digits = true;
-					goto char_ok;
-				}
-				/* They're being verbose and doing ${?} */
-				if (i_peek(input) == '}' && strchr("$!?#*@_", ch))
-					goto char_ok;
-			}
-
-			if (!in_expansion_param
-			 && (  (all_digits && !isdigit(ch)) /* met non-digit: 123w */
-			    || (!all_digits && !isalnum(ch) && ch != '_') /* met non-name char: abc% */
-			    )
-			) {
+			if (!isalnum(ch) && ch != '_') {
 				/* handle parameter expansions
 				 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
 				 */
-				if (first_char /* bad (empty var name): "${%..." */
-				 || !strchr("%#:-=+?", ch) /* bad: "${var<bad_char>..." */
-				) {
-					syntax_error_unterm_str("${name}");
-					debug_printf_parse("handle_dollar return 1: unterminated ${name}\n");
-					return 1;
-				}
-				in_expansion_param = true;
+				if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */
+					goto bad_dollar_syntax;
+				/* Eat everything until closing '}' */
+				o_addchr(dest, ch);
+//TODO: add nommu_addchr hack here
+				add_till_closing_paren(dest, input, '}');
+				break;
 			}
- char_ok:
-			debug_printf_parse(": '%c'\n", ch);
-			o_addchr(dest, ch | quote_mask);
-			quote_mask = 0;
-			first_char = false;
-		} /* while (1) */
+		}
 		o_addchr(dest, SPECIAL_VAR_SYMBOL);
 		break;
 	}
@@ -6044,7 +6052,7 @@ static int handle_dollar(o_string *as_string,
 #  if !BB_MMU
 			pos = dest->length;
 #  endif
-			add_till_closing_paren(dest, input, true);
+			add_till_closing_paren(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG);
 #  if !BB_MMU
 			if (as_string) {
 				o_addstr(as_string, dest->data + pos);
@@ -6062,7 +6070,7 @@ static int handle_dollar(o_string *as_string,
 #  if !BB_MMU
 		pos = dest->length;
 #  endif
-		add_till_closing_paren(dest, input, false);
+		add_till_closing_paren(dest, input, ')');
 #  if !BB_MMU
 		if (as_string) {
 			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 5e28d2f..40e67fd 100644
--- a/shell/hush_test/hush-vars/var3.right
+++ b/shell/hush_test/hush-vars/var3.right
@@ -1,2 +1,2 @@
-hush: syntax error: unterminated ${name}
+hush: invalid number '1q'
 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 e6cba27..813437e 100644
--- a/shell/hush_test/hush-vars/var_posix1.right
+++ b/shell/hush_test/hush-vars/var_posix1.right
@@ -23,6 +23,7 @@ babcdcd
 ababcdcd
 Empty:
 ababcdcd}_tail
+ababcdcd_tail
 ababcd
 ababcd
 ababcd
@@ -32,5 +33,8 @@ ababcdc
 ababcdcd
 Empty:
 ababcdcd}_tail
+ababcdcd_tail
 ababcdcd
-end
+ab
+ab
+End
diff --git a/shell/hush_test/hush-vars/var_posix1.tests b/shell/hush_test/hush-vars/var_posix1.tests
index c1f6409..e48fd98 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##?}
 echo ${var#*}
 echo Empty:${var##*}
 echo ${var#}}_tail
-# UNFIXED BUG: echo ${var#\}}_tail
+echo ${var#\}}_tail
 
 echo ${var%cd}
 echo ${var%%cd}
@@ -42,7 +42,10 @@ echo ${var%%?}
 echo ${var%*}
 echo Empty:${var%%*}
 echo ${var#}}_tail
-# UNFIXED BUG: echo ${var#\}}_tail
+echo ${var#\}}_tail
 echo ${var%\\*}
 
-echo end
+a=ab}; echo ${a%\}};
+a=abc; c=c; echo ${a%${c}}
+
+echo End
-- 
1.6.3.3



More information about the busybox-cvs mailing list