hush: fix backslash and terminator handling in <<[-]["]heredoc["]
authorDenys Vlasenko <dvlasenk@redhat.com>
Mon, 6 Sep 2010 09:27:32 +0000 (11:27 +0200)
committerDenys Vlasenko <dvlasenk@redhat.com>
Mon, 6 Sep 2010 09:27:32 +0000 (11:27 +0200)
function                                             old     new   delta
parse_stream                                        2339    2395     +56
expand_pseudo_dquoted                                104     118     +14
parse_stream_dquoted                                 296     300      +4
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/0 up/down: 74/0)               Total: 74 bytes

Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
shell/hush.c
shell/hush_test/hush-misc/heredoc_backslash1.right [new file with mode: 0644]
shell/hush_test/hush-misc/heredoc_backslash1.tests [new file with mode: 0755]

index ef46372de40ecf444718f845d7d9d891548bcf16..e8aef2d7e626cf2070d024deaecc5b0a9a36cd62 100644 (file)
@@ -3162,17 +3162,20 @@ static int redirect_opt_num(o_string *o)
 static char *fetch_till_str(o_string *as_string,
                struct in_str *input,
                const char *word,
-               int skip_tabs)
+               int heredoc_flags)
 {
        o_string heredoc = NULL_O_STRING;
        int past_EOL = 0;
+       int prev = 0; /* not \ */
        int ch;
 
        goto jump_in;
        while (1) {
                ch = i_getch(input);
                nommu_addchr(as_string, ch);
-               if (ch == '\n') {
+               if (ch == '\n'
+                && ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\')
+               ) {
                        if (strcmp(heredoc.data + past_EOL, word) == 0) {
                                heredoc.data[past_EOL] = '\0';
                                debug_printf_parse("parsed heredoc '%s'\n", heredoc.data);
@@ -3185,7 +3188,7 @@ static char *fetch_till_str(o_string *as_string,
                                do {
                                        ch = i_getch(input);
                                        nommu_addchr(as_string, ch);
-                               } while (skip_tabs && ch == '\t');
+                               } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t');
                        } while (ch == '\n');
                }
                if (ch == EOF) {
@@ -3194,6 +3197,7 @@ static char *fetch_till_str(o_string *as_string,
                }
                o_addchr(&heredoc, ch);
                nommu_addchr(as_string, ch);
+               prev = ch;
        }
 }
 
@@ -3223,7 +3227,7 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_
                                        redir->rd_type = REDIRECT_HEREDOC2;
                                        /* redir->rd_dup is (ab)used to indicate <<- */
                                        p = fetch_till_str(&ctx->as_string, input,
-                                               redir->rd_filename, redir->rd_dup & HEREDOC_SKIPTABS);
+                                                       redir->rd_filename, redir->rd_dup);
                                        if (!p) {
                                                syntax_error("unexpected EOF in here document");
                                                return 1;
@@ -3778,8 +3782,9 @@ static int parse_stream_dquoted(o_string *as_string,
                 * 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."
+                * NB: in (unquoted) heredoc, above does not apply to ".
                 */
-               if (strchr("$`\"\\\n", next) != NULL) {
+               if (next == dquote_end || strchr("$`\\\n", next) != NULL) {
                        ch = i_getch(input);
                        if (ch != '\n') {
                                o_addqchr(dest, ch);
@@ -4412,6 +4417,7 @@ static char *expand_pseudo_dquoted(const char *str)
        o_string dest = NULL_O_STRING;
 
        if (!strchr(str, '$')
+        && !strchr(str, '\\')
 #if ENABLE_HUSH_TICK
         && !strchr(str, '`')
 #endif
diff --git a/shell/hush_test/hush-misc/heredoc_backslash1.right b/shell/hush_test/hush-misc/heredoc_backslash1.right
new file mode 100644 (file)
index 0000000..234c017
--- /dev/null
@@ -0,0 +1,27 @@
+Quoted heredoc:
+a\
+       b
+ 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+       -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+c\
+
+Unquoted heredoc:
+a      b
+ 123456 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
+       -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
+cEOF2
+
+Quoted -heredoc:
+a\
+b
+ 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+c\
+
+Unquoted -heredoc:
+a      b
+ 123456 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
+-qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
+cEOF4
+
+Done: 0
diff --git a/shell/hush_test/hush-misc/heredoc_backslash1.tests b/shell/hush_test/hush-misc/heredoc_backslash1.tests
new file mode 100755 (executable)
index 0000000..b70467d
--- /dev/null
@@ -0,0 +1,54 @@
+# Test for correct handling of backslashes.
+# Note that some lines in each heredoc start with a tab.
+
+a=qwerty
+
+echo Quoted heredoc:
+cat <<"EOF1"
+a\
+       b
+ 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+       -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+c\
+EOF1
+echo
+
+echo Unquoted heredoc:
+cat <<EOF2
+a\
+       b
+ 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+       -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+c\
+EOF2
+EOF2
+echo
+
+echo Quoted -heredoc:
+cat <<-"EOF3"
+a\
+       b
+ 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+       -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+c\
+       EOF3
+# In -heredoc case the marker is detected even if it is indented.
+echo
+
+echo Unquoted -heredoc:
+cat <<-EOF4
+a\
+       b
+ 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+       -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
+c\
+EOF4
+       EOF4
+# The marker is not detected if preceding line ends in backslash.
+# TODO: marker should be detected even if it is split by line continuation:
+# EOF\
+# 4
+# but currently hush doesn't do it. (Tab before "4" is not allowed, though.)
+echo
+
+echo "Done: $?"