ash: fix obscure case of replacing + globbing + backslashes
authorDenys Vlasenko <vda.linux@googlemail.com>
Sat, 26 Jun 2010 20:55:44 +0000 (22:55 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sat, 26 Jun 2010 20:55:44 +0000 (22:55 +0200)
function                                             old     new   delta
subevalvar                                          1152    1178     +26
readtoken1                                          3267    3275      +8
redirect                                            1284    1286      +2
expandarg                                            957     958      +1
expdir                                                 4       -      -4
evalcommand                                         1219    1209     -10
expmeta                                              481     469     -12
------------------------------------------------------------------------------
(add/remove: 0/1 grow/shrink: 4/2 up/down: 37/-26)             Total: 11 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash.c
shell/ash_test/ash-quoting/dollar_repl_slash_bash1.right [new file with mode: 0644]
shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests [new file with mode: 0755]

index cfd8154ef8e8abd9a2baa3b2ac4c2b1daef169b3..0337a5535cc4f056906c98a8c38f6feae08c1244 100644 (file)
@@ -6184,8 +6184,7 @@ subevalvar(char *p, char *str, int strloc, int subtype,
        char *startp;
        char *loc;
        char *rmesc, *rmescend;
-       IF_ASH_BASH_COMPAT(char *repl = NULL;)
-       IF_ASH_BASH_COMPAT(char null = '\0';)
+       IF_ASH_BASH_COMPAT(const char *repl = NULL;)
        IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
        int saveherefd = herefd;
        int amount, workloc, resetloc;
@@ -6306,7 +6305,7 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                if (!repl) {
                        repl = parse_sub_pattern(str, varflags & VSQUOTE);
                        if (!repl)
-                               repl = &null;
+                               repl = nullstr;
                }
 
                /* If there's no pattern to match, return the expansion unmolested */
@@ -6357,8 +6356,12 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                                idx = loc;
                        }
 
-                       for (loc = repl; *loc; loc++) {
+                       for (loc = (char*)repl; *loc; loc++) {
                                char *restart_detect = stackblock();
+                               if (quotes && *loc == '\\') {
+                                       STPUTC(CTLESC, expdest);
+                                       len++;
+                               }
                                STPUTC(*loc, expdest);
                                if (stackblock() != restart_detect)
                                        goto restart;
@@ -6368,6 +6371,10 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                        if (subtype == VSREPLACE) {
                                while (*idx) {
                                        char *restart_detect = stackblock();
+                                       if (quotes && *idx == '\\') {
+                                               STPUTC(CTLESC, expdest);
+                                               len++;
+                                       }
                                        STPUTC(*idx, expdest);
                                        if (stackblock() != restart_detect)
                                                goto restart;
@@ -6381,11 +6388,10 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                /* We've put the replaced text into a buffer at workloc, now
                 * move it to the right place and adjust the stack.
                 */
-               startp = stackblock() + startloc;
                STPUTC('\0', expdest);
-               memmove(startp, stackblock() + workloc, len);
-               startp[len++] = '\0';
-               amount = expdest - ((char *)stackblock() + startloc + len - 1);
+               startp = (char *)stackblock() + startloc;
+               memmove(startp, (char *)stackblock() + workloc, len + 1);
+               amount = expdest - (startp + len);
                STADJUST(-amount, expdest);
                return startp;
        }
@@ -6685,7 +6691,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
                 */
                STPUTC('\0', expdest);
                patloc = expdest - (char *)stackblock();
-               if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
+               if (NULL == subevalvar(p, /* str: */ NULL, patloc, subtype,
                                startloc, varflags,
 //TODO: | EXP_REDIR too? All other such places do it too
                                /* quotes: */ flags & (EXP_FULL | EXP_CASE),
@@ -6848,13 +6854,11 @@ addfname(const char *name)
        exparg.lastp = &sp->next;
 }
 
-static char *expdir;
-
 /*
  * Do metacharacter (i.e. *, ?, [...]) expansion.
  */
 static void
-expmeta(char *enddir, char *name)
+expmeta(char *expdir, char *enddir, char *name)
 {
        char *p;
        const char *cp;
@@ -6953,7 +6957,7 @@ expmeta(char *enddir, char *name)
                                for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
                                        continue;
                                p[-1] = '/';
-                               expmeta(p, endname);
+                               expmeta(expdir, p, endname);
                        }
                }
        }
@@ -7035,6 +7039,7 @@ expandmeta(struct strlist *str /*, int flag*/)
        /* TODO - EXP_REDIR */
 
        while (str) {
+               char *expdir;
                struct strlist **savelastp;
                struct strlist *sp;
                char *p;
@@ -7051,8 +7056,7 @@ expandmeta(struct strlist *str /*, int flag*/)
                        int i = strlen(str->text);
                        expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
                }
-
-               expmeta(expdir, p);
+               expmeta(expdir, expdir, p);
                free(expdir);
                if (p != str->text)
                        free(p);
@@ -9101,19 +9105,15 @@ evalcommand(union node *cmd, int flags)
        /* Print the command if xflag is set. */
        if (xflag) {
                int n;
-               const char *p = " %s";
+               const char *p = " %s" + 1;
 
-               p++;
                fdprintf(preverrout_fd, p, expandstr(ps4val()));
-
                sp = varlist.list;
                for (n = 0; n < 2; n++) {
                        while (sp) {
                                fdprintf(preverrout_fd, p, sp->text);
                                sp = sp->next;
-                               if (*p == '%') {
-                                       p--;
-                               }
+                               p = " %s";
                        }
                        sp = arglist.list;
                }
diff --git a/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.right b/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.right
new file mode 100644 (file)
index 0000000..b212c24
--- /dev/null
@@ -0,0 +1,10 @@
+192\.168\.0\.1
+192\.168\.0\.1[
+192\.168\.0\.1[
+192\\.168\\.0\\.1[
+192\.168\.0\.1[
+192\.168\.0\.1
+192\.168\.0\.1[
+192\.168\.0\.1[
+192\\.168\\.0\\.1[
+192\.168\.0\.1[
diff --git a/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests b/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests
new file mode 100755 (executable)
index 0000000..3fa2f18
--- /dev/null
@@ -0,0 +1,21 @@
+# The bug here was triggered by:
+# * performin pathname expansion because we see [
+# * replace operator did not escape \ in replace string
+
+IP=192.168.0.1
+
+rm -f '192.168.0.1['
+echo "${IP//./\\.}"
+echo "${IP//./\\.}"'[' # bug was here
+echo "${IP//./\\.}["   # bug was here
+echo "${IP//./\\\\.}[" # bug was here
+echo "192\.168\.0\.1["
+
+echo >'192.168.0.1['
+echo "${IP//./\\.}"
+echo "${IP//./\\.}"'[' # bug was here
+echo "${IP//./\\.}["   # bug was here
+echo "${IP//./\\\\.}[" # bug was here
+echo "192\.168\.0\.1["
+
+rm -f '192.168.0.1['