hush: more backtick and quoting fixes...
authorDenis Vlasenko <vda.linux@googlemail.com>
Tue, 10 Jun 2008 22:39:37 +0000 (22:39 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Tue, 10 Jun 2008 22:39:37 +0000 (22:39 -0000)
shell/hush.c
shell/hush_test/hush-psubst/tick3.right [new file with mode: 0644]
shell/hush_test/hush-psubst/tick3.tests [new file with mode: 0755]

index e57f6e37164318b013abef8b35fcafa539f16522..735526a1d42b210e2a128dee8cec3143b25fd356 100644 (file)
@@ -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: \\<eof>\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 <newline>.  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 (file)
index 0000000..dc84e92
--- /dev/null
@@ -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 (executable)
index 0000000..97b45e4
--- /dev/null
@@ -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:$?