diff options
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 130 |
1 files changed, 88 insertions, 42 deletions
diff --git a/shell/hush.c b/shell/hush.c index 0f0151a..41d5fca 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -171,6 +171,7 @@ #define debug_printf_env(...) do {} while (0) #define debug_printf_jobs(...) do {} while (0) #define debug_printf_expand(...) do {} while (0) +#define debug_printf_varexp(...) do {} while (0) #define debug_printf_glob(...) do {} while (0) #define debug_printf_list(...) do {} while (0) #define debug_printf_subst(...) do {} while (0) @@ -743,6 +744,10 @@ static const struct built_in_command bltins2[] = { # define DEBUG_EXPAND 0 #endif +#ifndef debug_printf_varexp +# define debug_printf_varexp(...) (indent(), fprintf(stderr, __VA_ARGS__)) +#endif + #ifndef debug_printf_glob # define debug_printf_glob(...) (indent(), fprintf(stderr, __VA_ARGS__)) # define DEBUG_GLOB 1 @@ -1817,11 +1822,11 @@ static void o_addblock(o_string *o, const char *str, int len) o->data[o->length] = '\0'; } -#if !BB_MMU static void o_addstr(o_string *o, const char *str) { o_addblock(o, str, strlen(str)); } +#if !BB_MMU static void nommu_addchr(o_string *o, int ch) { if (o) @@ -2597,12 +2602,19 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char } else { /* maybe handle parameter expansion */ exp_saveptr = var + strcspn(var, "%#:-=+?"); - exp_save = *exp_saveptr; - if (exp_save) { - exp_word = exp_saveptr; - if (exp_save == ':') - exp_word++; - exp_op = *exp_word++; + exp_op = exp_save = *exp_saveptr; + if (exp_op) { + exp_word = exp_saveptr + 1; + if (exp_op == ':') { + exp_op = *exp_word++; + if (ENABLE_HUSH_BASH_COMPAT + && (exp_op == '\0' || !strchr("%#:-=+?"+3, exp_op)) + ) { + /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */ + exp_op = ':'; + exp_word--; + } + } *exp_saveptr = '\0'; } } @@ -2656,39 +2668,42 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char *loc = '\0'; } } - } else if (!strchr("%#:-=+?"+3, exp_op)) { + } else if (exp_op == ':') { #if ENABLE_HUSH_BASH_COMPAT - /* exp_op is ':' and next char isn't a subst operator. - * Assuming it's ${var:[N][:M]} bashism. - * TODO: N, M can be expressions similar to $((EXPR)): 2+2, 2+var etc + /* It's ${var:N[:M]} bashism. + * Note that in encoded form it has TWO parts: + * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> */ - char *end; - unsigned len = INT_MAX; - unsigned beg = 0; - end = --exp_word; - if (*exp_word != ':') /* not ${var::...} */ - beg = bb_strtou(exp_word, &end, 0); - //bb_error_msg("beg:'%s'=%u end:'%s'", exp_word, beg, end); - if (*end == ':') { - if (end[1] != '\0') /* not ${var:NUM:} */ - len = bb_strtou(end + 1, &end, 0); - else { - len = 0; - end++; - } - //bb_error_msg("len:%u end:'%s'", len, end); - } - if (*end == '\0') { - //bb_error_msg("from val:'%s'", val); + arith_t beg, len; + int errcode = 0; + + beg = expand_and_evaluate_arith(exp_word, &errcode); + debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); + *p++ = SPECIAL_VAR_SYMBOL; + exp_word = p; + p = strchr(p, SPECIAL_VAR_SYMBOL); + *p = '\0'; + len = expand_and_evaluate_arith(exp_word, &errcode); + debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); + + if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */ + if (beg < 0) /* bash compat */ + beg = 0; + debug_printf_varexp("from val:'%s'\n", val); if (len == 0 || !val || beg >= strlen(val)) val = ""; - else + else { + /* Paranoia. What if user entered 9999999999999 + * which fits in arith_t but not int? */ + if (len >= INT_MAX) + len = INT_MAX; val = to_be_freed = xstrndup(val + beg, len); - //bb_error_msg("val:'%s'", val); + } + debug_printf_varexp("val:'%s'\n", val); } else #endif { - die_if_script("malformed ${%s...}", var); + die_if_script("malformed ${%s:...}", var); val = ""; } } else { /* one of "-=+?" */ @@ -5891,21 +5906,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 * - * Also adapted to eat ${var%...} constructs, since ... part + * Also adapted to eat ${var%...} and $((...)) constructs, since ... part * can contain arbitrary constructs, just like $(cmd). + * In bash compat mode, it needs to also be able to stop on '}' or ':' + * for ${var:N[:M]} parsing. */ #define DOUBLE_CLOSE_CHAR_FLAG 0x80 -static void add_till_closing_bracket(o_string *dest, struct in_str *input, char end_ch) +static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch) { + int ch; char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; - end_ch &= (DOUBLE_CLOSE_CHAR_FLAG-1); +#if ENABLE_HUSH_BASH_COMPAT + char end_char2 = end_ch >> 8; +#endif + end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); + while (1) { - int ch = i_getch(input); + ch = i_getch(input); if (ch == EOF) { syntax_error_unterm_ch(end_ch); /*xfunc_die(); - redundant */ } - if (ch == end_ch) { + if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) { if (!dbl) break; /* we look for closing )) of $((EXPR)) */ @@ -5947,6 +5969,7 @@ static void add_till_closing_bracket(o_string *dest, struct in_str *input, char continue; } } + return ch; } #endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */ @@ -6033,22 +6056,45 @@ static int handle_dollar(o_string *as_string, break; if (!isalnum(ch) && ch != '_') { + unsigned end_ch; + unsigned char last_ch; /* handle parameter expansions * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 */ if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */ goto bad_dollar_syntax; - /* Eat everything until closing '}' */ o_addchr(dest, ch); + + /* Eat everything until closing '}' (or ':') */ + end_ch = '}'; + if (ENABLE_HUSH_BASH_COMPAT + && ch == ':' + && !strchr("%#:-=+?"+3, i_peek(input)) + ) { + /* It's ${var:N[:M]} thing */ + end_ch = '}' * 0x100 + ':'; + } + again: if (!BB_MMU) pos = dest->length; - add_till_closing_bracket(dest, input, '}'); -#if !BB_MMU + last_ch = add_till_closing_bracket(dest, input, end_ch); if (as_string) { o_addstr(as_string, dest->data + pos); - o_addchr(as_string, '}'); + o_addchr(as_string, last_ch); + } + + if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) { + /* close the first block: */ + o_addchr(dest, SPECIAL_VAR_SYMBOL); + /* while parsing N from ${var:N[:M]}... */ + if ((end_ch & 0xff) == last_ch) { + /* ...got ':' - parse the rest */ + end_ch = '}'; + goto again; + } + /* ...got '}', not ':' - it's ${var:N}! emulate :999999999 */ + o_addstr(dest, "999999999"); } -#endif break; } } |