Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

PATCH: quote parameter expansion from GLOB_SUBST



While I'm waiting for some last changes for 5.0.8 to roll in, this adds
the (b) parameter flag to backslash-quote against pattern characters
only, needed to protect contents against later GLOB_SUBST including
${~....}.  The doc is supposed to explain.  We could use this at various
places in the completion system instead of the hard-to-figure-out mess
you need instead.

pws

diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 85191ed..9bba4f4 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -1014,6 +1014,25 @@ form of single quoting is used that only quotes the string if needed to
 protect special characters.  Typically this form gives the most readable
 output.
 )
+item(tt(b))(
+Quote with backslashes only characters that are special to pattern
+matching. This is useful when the contents of the variable are to be
+tested using tt(GLOB_SUBST), including the tt(${~)var(...)tt(}) switch.
+
+Quoting using one of the tt(q) family of flags does not work
+for this purpose since quotes are not stripped from non-pattern
+characters by tt(GLOB_SUBST).  In other words,
+
+example(foo='a\ b'
+[[ 'a b' = ${~foo} ]])
+
+fails, whereas
+
+example(foo='a\*b'
+[[ 'a*b' = ${~foo} ]])
+
+succeeds.  The tt(b) flag ensures the correct quoting.
+)
 item(tt(Q))(
 Remove one level of quotes from the resulting words.
 )
diff --git a/Src/subst.c b/Src/subst.c
index f52bcdf..bf80495 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -1592,7 +1592,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
     /* combination of (L), (U) and (C) flags. */
     int casmod = CASMOD_NONE;
     /*
-     * quotemod says we are doing either (q) (positive), (Q) (negative)
+     * quotemod says we are doing either (q/b) (positive), (Q) (negative)
      * or not (0).  quotetype counts the q's for the first case.
      * quoterr is simply (X) but gets passed around a lot because the
      * combination (eX) needs it.
@@ -1861,6 +1861,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
 			quotemod++, quotetype++;
 		    }
 		    break;
+		case 'b':
+		    if (quotemod || quotetype != QT_NONE)
+			goto flagerr;
+		    quotemod = 1;
+		    quotetype = QT_BACKSLASH_PATTERN;
+		    break;
 		case 'Q':
 		    quotemod--;
 		    break;
@@ -3460,7 +3466,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
     if (quotemod) {
 	int pre = 0, post = 0;
 
-	if (quotemod > 0 && quotetype > QT_BACKSLASH) {
+	if (quotemod > 0) {
 	    switch (quotetype)
 	    {
 	    case QT_DOLLARS:
@@ -3471,6 +3477,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
 
 	    case QT_SINGLE_OPTIONAL:
 		/* quotes will be added for us */
+	    case QT_BACKSLASH:
+	    case QT_BACKSLASH_PATTERN:
+		/* no quotes */
 		break;
 
 	    default:
diff --git a/Src/utils.c b/Src/utils.c
index 13d4b83..271c800 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -3694,6 +3694,8 @@ inittyptab(void)
 	typtab[bangchar] |= ISPECIAL;
     } else
 	typtab_flags &= ~ZTF_BANGCHAR;
+    for (s = PATCHARS; *s; s++)
+	typtab[STOUC(*s)] |= IPATTERN;
 
     unqueue_signals();
 }
@@ -5075,6 +5077,10 @@ quotestring(const char *s, char **e, int instring)
 	alloclen = slen * 7 + 1;
 	break;
 
+    case QT_BACKSLASH_PATTERN:
+	alloclen = slen * 2  + 1;
+	break;
+
     case QT_SINGLE_OPTIONAL:
 	/*
 	 * Here, we may need to add single quotes.
@@ -5094,7 +5100,7 @@ quotestring(const char *s, char **e, int instring)
     quotestart = v = buf = zshcalloc(alloclen);
 
     DPUTS(instring < QT_BACKSLASH || instring == QT_BACKTICK ||
-	  instring > QT_SINGLE_OPTIONAL,
+	  instring > QT_BACKSLASH_PATTERN,
 	  "BUG: bad quote type in quotestring");
     u = s;
     if (instring == QT_DOLLARS) {
@@ -5134,9 +5140,18 @@ quotestring(const char *s, char **e, int instring)
 		u = uend;
 	    }
 	}
-    }
-    else
-    {
+    } else if (instring == QT_BACKSLASH_PATTERN) {
+	while (*u) {
+	    if (e && !sf && *e == u) {
+		*e = v;
+		sf = 1;
+	    }
+
+	    if (ipattern(*u))
+		*v++ = '\\';
+	    *v++ = *u++;
+	}
+    } else {
 	if (shownull) {
 	    /* We can't show an empty string with just backslash quoting. */
 	    if (!*u) {
diff --git a/Src/zsh.h b/Src/zsh.h
index 486ad80..5479198 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -215,6 +215,10 @@ struct mathfunc {
 
 #define SPECCHARS "#$^*()=|{}[]`<>?~;&\n\t \\\'\""
 
+/* chars that need to be quoted for pattern matching */
+
+#define PATCHARS "#^*()|[]<>?~"
+
 /*
  * Types of quote.  This is used in various places, so care needs
  * to be taken when changing them.  (Oooh, don't you look surprised.)
@@ -249,6 +253,12 @@ enum {
      */
     QT_SINGLE_OPTIONAL,
     /*
+     * Only quote pattern characters.
+     * ${(b)foo} guarantees that ${~foo} matches the string
+     * contained in foo.
+     */
+    QT_BACKSLASH_PATTERN,
+    /*
      * As QT_BACKSLASH, but a NULL string is shown as ''.
      */
     QT_BACKSLASH_SHOWNULL
diff --git a/Src/ztype.h b/Src/ztype.h
index b73e3f8..76589b1 100644
--- a/Src/ztype.h
+++ b/Src/ztype.h
@@ -42,6 +42,7 @@
 #define IMETA    (1 << 12)
 #define IWSEP    (1 << 13)
 #define INULL    (1 << 14)
+#define IPATTERN (1 << 15)
 #define zistype(X,Y) (typtab[STOUC(X)] & Y)
 #define idigit(X) zistype(X,IDIGIT)
 #define ialnum(X) zistype(X,IALNUM)
@@ -58,6 +59,7 @@
 #define imeta(X) zistype(X,IMETA)
 #define iwsep(X) zistype(X,IWSEP)
 #define inull(X) zistype(X,INULL)
+#define ipattern(X) zistype(X,IPATTERN)
 
 /*
  * Bit flags for typtab_flags --- preserved after
diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst
index 42c7b4e..17a59cb 100644
--- a/Test/D04parameter.ztst
+++ b/Test/D04parameter.ztst
@@ -1699,3 +1699,7 @@
 >
 >
 >Four
+
+  funnychars='The qu*nk br!wan f@x j/mps o[]r the la~# ^"&;'
+  [[ $funnychars = ${~${(b)funnychars}} ]]
+0:${(b)...} quoting protects from GLOB_SUBST



Messages sorted by: Reverse Date, Date, Thread, Author