From 87f40bac149dcaf1025abd745decafb3a8ac4e0c Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Tue, 10 Jun 2008 22:39:37 +0000 Subject: [PATCH] hush: more backtick and quoting fixes... --- shell/hush.c | 73 ++++++++++++++++++------- shell/hush_test/hush-psubst/tick3.right | 6 ++ shell/hush_test/hush-psubst/tick3.tests | 11 ++++ 3 files changed, 71 insertions(+), 19 deletions(-) create mode 100644 shell/hush_test/hush-psubst/tick3.right create mode 100755 shell/hush_test/hush-psubst/tick3.tests diff --git a/shell/hush.c b/shell/hush.c index e57f6e371..735526a1d 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -1221,6 +1221,23 @@ static void o_addqchr(o_string *o, int ch, int quote) o_addchr(o, ch); } +static void o_addqstr(o_string *o, const char *str, int len, int quote) +{ + char ch; + if (!quote || str[strcspn(str, "*?[\\")] == '\0') { + o_addstr(o, str, len); + return; + } + while (len) { + ch = *str++; + if (ch && strchr("*?[\\", ch)) { + o_addchr(o, '\\'); + } + o_addchr(o, ch); + len--; + } +} + /* A special kind of o_string for $VAR and `cmd` expansion. * It contains char* list[] at the beginning, which is grown in 16 element * increments. Actual string data starts at the next multiple of 16. @@ -2445,7 +2462,7 @@ static int run_and_free_list(struct pipe *pi) * In the long run that function can be merged with run_list, * but doing that now would hobble the debugging effort. */ free_pipe_list(pi, /* indent: */ 0); - debug_printf_exec("run_nad_free_list return %d\n", rcode); + debug_printf_exec("run_and_free_list return %d\n", rcode); return rcode; } @@ -2549,7 +2566,7 @@ static int expand_on_ifs(o_string *output, int n, const char *str) while (1) { int word_len = strcspn(str, ifs); if (word_len) { - o_addstr(output, str, word_len); /* store non-ifs chars */ + o_addqstr(output, str, word_len, output->o_quote); str += word_len; } if (!*str) /* EOL - do not finalize word */ @@ -2590,7 +2607,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { o_string subst_result = NULL_O_STRING; - o_addstr(output, arg, p - arg); + o_addqstr(output, arg, p - arg, output->o_quote); o_debug_list("expand_vars_to_list[1]", output, n); arg = ++p; p = strchr(p, SPECIAL_VAR_SYMBOL); @@ -2636,7 +2653,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) * and in this case should treat it like '$*' - see 'else...' below */ if (first_ch == ('@'|0x80) && !or_mask) { /* quoted $@ */ while (1) { - o_addstr(output, global_argv[i], strlen(global_argv[i])); + o_addqstr(output, global_argv[i], strlen(global_argv[i]), output->o_quote); if (++i >= global_argc) break; o_addchr(output, '\0'); @@ -2645,7 +2662,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) } } else { /* quoted $*: add as one word */ while (1) { - o_addstr(output, global_argv[i], strlen(global_argv[i])); + o_addqstr(output, global_argv[i], strlen(global_argv[i]), output->o_quote); if (!global_argv[++i]) break; if (ifs[0]) @@ -2684,8 +2701,9 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) } } /* else: quoted $VAR, val will be appended below */ } - if (val) - o_addstr(output, val, strlen(val)); + if (val) { + o_addqstr(output, val, strlen(val), output->o_quote); + } o_free(&subst_result); arg = ++p; @@ -2693,7 +2711,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) if (arg[0]) { o_debug_list("expand_vars_to_list[a]", output, n); - o_addstr(output, arg, strlen(arg) + 1); + o_addqstr(output, arg, strlen(arg) + 1, output->o_quote); o_debug_list("expand_vars_to_list[b]", output, n); } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ && !(ored_ch & 0x80) /* and all vars were not quoted. */ @@ -3389,7 +3407,7 @@ static void add_till_single_quote(o_string *dest, struct in_str *input) break; if (ch == '\'') break; - o_addchr(dest, ch); + o_addqchr(dest, ch, 1); } } /* "...\"...`..`...." - do we need to handle "...$(..)..." too? */ @@ -3400,15 +3418,15 @@ static void add_till_double_quote(o_string *dest, struct in_str *input) if (ch == '"') break; if (ch == '\\') { /* \x. Copy both chars. */ - o_addchr(dest, ch); + o_addqchr(dest, ch, 1); ch = i_getch(input); } if (ch == EOF) break; - o_addchr(dest, ch); + o_addqchr(dest, ch, 1); if (ch == '`') { add_till_backquote(dest, input); - o_addchr(dest, ch); + o_addqchr(dest, ch, 1); continue; } // if (ch == '$') ... @@ -3432,18 +3450,17 @@ static void add_till_backquote(o_string *dest, struct in_str *input) { while (1) { int ch = i_getch(input); -//bb_error_msg("ADD '%c'", ch); if (ch == '`') break; if (ch == '\\') { /* \x. Copy both chars unless it is \` */ int ch2 = i_getch(input); if (ch2 != '`' && ch2 != '$' && ch2 != '\\') - o_addchr(dest, ch); + o_addqchr(dest, ch, 1); ch = ch2; } if (ch == EOF) break; - o_addchr(dest, ch); + o_addqchr(dest, ch, 1); } } /* Process $(cmd) - copy contents until ")" is seen. Complicated by @@ -3470,15 +3487,15 @@ static void add_till_closing_curly_brace(o_string *dest, struct in_str *input) if (ch == ')') if (--count < 0) break; - o_addchr(dest, ch); + o_addqchr(dest, ch, 1); if (ch == '\'') { add_till_single_quote(dest, input); - o_addchr(dest, ch); + o_addqchr(dest, ch, 1); continue; } if (ch == '"') { add_till_double_quote(dest, input); - o_addchr(dest, ch); + o_addqchr(dest, ch, 1); continue; } } @@ -3638,7 +3655,25 @@ static int parse_stream(o_string *dest, struct p_context *ctx, debug_printf_parse("parse_stream return 1: \\\n"); return 1; } - o_addqchr(dest, i_getch(input), dest->o_quote); + /* bash: + * "The backslash retains its special meaning [in "..."] + * only when followed by one of the following characters: + * $, `, ", \, or . A double quote may be quoted + * within double quotes by preceding it with a backslash. + * If enabled, history expansion will be performed unless + * an ! appearing in double quotes is escaped using + * a backslash. The backslash preceding the ! is not removed." + */ + if (dest->o_quote) { + if (strchr("$`\"\\", next) != NULL) { + o_addqchr(dest, i_getch(input), 1); + } else { + o_addqchr(dest, '\\', 1); + } + } else { + o_addchr(dest, '\\'); + o_addchr(dest, i_getch(input)); + } break; case '$': if (handle_dollar(dest, input) != 0) { diff --git a/shell/hush_test/hush-psubst/tick3.right b/shell/hush_test/hush-psubst/tick3.right new file mode 100644 index 000000000..dc84e9263 --- /dev/null +++ b/shell/hush_test/hush-psubst/tick3.right @@ -0,0 +1,6 @@ +\TESTZZBEST +$TEST +Q +a\bc +a"c +done:0 diff --git a/shell/hush_test/hush-psubst/tick3.tests b/shell/hush_test/hush-psubst/tick3.tests new file mode 100755 index 000000000..97b45e4b4 --- /dev/null +++ b/shell/hush_test/hush-psubst/tick3.tests @@ -0,0 +1,11 @@ +#!/bin/sh +TEST=Q +# \` is special +echo `echo '\'TEST\`echo ZZ\`BEST` +# \$ and \\ are special +echo `echo \\$TEST` +echo `echo \$TEST` +echo a`echo \\\\b`c +# \" etc are NOT special (passed verbatim WITH \)! +echo a`echo \"`c +echo done:$?