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>
This commit is contained in:
Denys Vlasenko 2010-05-21 19:52:01 +02:00
parent 3f78cec347
commit 7436950a75
4 changed files with 76 additions and 61 deletions

View File

@ -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 %% */ if (exp_op == *exp_word) /* ## or %% */
exp_word++; exp_word++;
val = to_be_freed = xstrdup(val); val = to_be_freed = xstrdup(val);
loc = scan(to_be_freed, exp_word, match_at_left); {
if (match_at_left) /* # or ## */ char *exp_exp_word = expand_pseudo_dquoted(exp_word);
val = loc; if (exp_exp_word)
else if (loc) /* % or %% and match was found */ exp_word = exp_exp_word;
*loc = '\0'; 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)) { } else if (!strchr("%#:-=+?"+3, exp_op)) {
#if ENABLE_HUSH_BASH_COMPAT #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 * 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; int count = 0;
char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
end_ch &= (DOUBLE_CLOSE_CHAR_FLAG-1);
while (1) { while (1) {
int ch = i_getch(input); int ch = i_getch(input);
if (ch == EOF) { if (ch == EOF) {
syntax_error_unterm_ch(')'); syntax_error_unterm_ch(')');
/*xfunc_die(); - redundant */ /*xfunc_die(); - redundant */
} }
if (ch == '(') if (ch == '(' || ch == '{')
count++; count++;
if (ch == ')') { if (ch == ')' || ch == '}') {
if (--count < 0) { count--;
if (count < 0 && ch == end_ch) {
if (!dbl) if (!dbl)
break; break;
if (i_peek(input) == ')') { if (i_peek(input) == ')') {
@ -5969,62 +5987,52 @@ static int handle_dollar(o_string *as_string,
case '@': /* args */ case '@': /* args */
goto make_one_char_var; goto make_one_char_var;
case '{': { case '{': {
bool first_char, all_digits;
bool in_expansion_param;
ch = i_getch(input);
nommu_addchr(as_string, ch);
o_addchr(dest, SPECIAL_VAR_SYMBOL); o_addchr(dest, SPECIAL_VAR_SYMBOL);
// TODO: need to handle "a=ab}; echo ${a%\}}" ch = i_getch(input); /* eat '{' */
// and "a=abc; c=c; echo ${a%${c}}" nommu_addchr(as_string, ch);
in_expansion_param = false;
first_char = true; ch = i_getch(input); /* first char after '{' */
all_digits = false; 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) { while (1) {
o_addchr(dest, ch);
debug_printf_parse(": '%c'\n", ch);
ch = i_getch(input); ch = i_getch(input);
nommu_addchr(as_string, ch); nommu_addchr(as_string, ch);
if (ch == '}') { if (ch == '}')
break; break;
}
if (first_char) { if (!isalnum(ch) && ch != '_') {
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% */
)
) {
/* handle parameter expansions /* handle parameter expansions
* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
*/ */
if (first_char /* bad (empty var name): "${%..." */ if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */
|| !strchr("%#:-=+?", ch) /* bad: "${var<bad_char>..." */ goto bad_dollar_syntax;
) { /* Eat everything until closing '}' */
syntax_error_unterm_str("${name}"); o_addchr(dest, ch);
debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); //TODO: add nommu_addchr hack here
return 1; add_till_closing_paren(dest, input, '}');
} break;
in_expansion_param = true;
} }
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); o_addchr(dest, SPECIAL_VAR_SYMBOL);
break; break;
} }
@ -6044,7 +6052,7 @@ static int handle_dollar(o_string *as_string,
# if !BB_MMU # if !BB_MMU
pos = dest->length; pos = dest->length;
# endif # endif
add_till_closing_paren(dest, input, true); add_till_closing_paren(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG);
# if !BB_MMU # if !BB_MMU
if (as_string) { if (as_string) {
o_addstr(as_string, dest->data + pos); o_addstr(as_string, dest->data + pos);
@ -6062,7 +6070,7 @@ static int handle_dollar(o_string *as_string,
# if !BB_MMU # if !BB_MMU
pos = dest->length; pos = dest->length;
# endif # endif
add_till_closing_paren(dest, input, false); add_till_closing_paren(dest, input, ')');
# if !BB_MMU # if !BB_MMU
if (as_string) { if (as_string) {
o_addstr(as_string, dest->data + pos); o_addstr(as_string, dest->data + pos);

View File

@ -1,2 +1,2 @@
hush: syntax error: unterminated ${name} hush: invalid number '1q'
hush: syntax error: unterminated ${name} hush: syntax error: unterminated ${name}

View File

@ -23,6 +23,7 @@ babcdcd
ababcdcd ababcdcd
Empty: Empty:
ababcdcd}_tail ababcdcd}_tail
ababcdcd_tail
ababcd ababcd
ababcd ababcd
ababcd ababcd
@ -32,5 +33,8 @@ ababcdc
ababcdcd ababcdcd
Empty: Empty:
ababcdcd}_tail ababcdcd}_tail
ababcdcd_tail
ababcdcd ababcdcd
end ab
ab
End

View File

@ -31,7 +31,7 @@ echo ${var##?}
echo ${var#*} echo ${var#*}
echo Empty:${var##*} echo Empty:${var##*}
echo ${var#}}_tail echo ${var#}}_tail
# UNFIXED BUG: echo ${var#\}}_tail echo ${var#\}}_tail
echo ${var%cd} echo ${var%cd}
echo ${var%%cd} echo ${var%%cd}
@ -42,7 +42,10 @@ echo ${var%%?}
echo ${var%*} echo ${var%*}
echo Empty:${var%%*} echo Empty:${var%%*}
echo ${var#}}_tail echo ${var#}}_tail
# UNFIXED BUG: echo ${var#\}}_tail echo ${var#\}}_tail
echo ${var%\\*} echo ${var%\\*}
echo end a=ab}; echo ${a%\}};
a=abc; c=c; echo ${a%${c}}
echo End