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-7436950a7516d1f4498285ccc81bf6d926f3af5e.tar.gz
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
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 5e28d2fab..40e67fdf5 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 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
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 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##?}
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