From 6269db883ad6989a578bac299deab9c4285bb1df Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 10 May 2015 19:19:34 +0100 Subject: 35067: Add (b) parameter flag for pattern char backslashing. Doc tweak from Daniel in 35071. Includes test. --- ChangeLog | 7 +++++++ Doc/Zsh/expn.yo | 19 +++++++++++++++++++ Src/subst.c | 13 +++++++++++-- Src/utils.c | 23 +++++++++++++++++++---- Src/zsh.h | 10 ++++++++++ Src/ztype.h | 2 ++ Test/D04parameter.ztst | 4 ++++ 7 files changed, 72 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7c2b62990..5563797ba 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2015-05-10 Peter Stephenson + + * 35067 (doc tweak from Daniel, 35071): Doc/Zsh/expn.yo, + Src/subst.c, Src/utils.c, Src/zsh.h, Src/ztype.h, + Test/D04parameter.ztst: add ${(b)foo} for backslash + quoting of patterns. + 2015-05-10 Mikael Magnusson * 35065: Src/zsh.h: Don't treat NUL as a combining character diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 85191edeb..8e5ab4bfd 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(pattern=${(q)str} +[[ $str = ${~pattern} ]]) + +works if tt($str) is tt('a*b') but not if it is tt('a b'), whereas + +example(pattern=${(b)str} +[[ $str = ${~pattern} ]]) + +is always true for any possible value of tt($str). +) item(tt(Q))( Remove one level of quotes from the resulting words. ) diff --git a/Src/subst.c b/Src/subst.c index f52bcdfc8..bf80495bd 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 13d4b83d4..271c800fd 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 bb52e106d..b2ab1dfae 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.) @@ -248,6 +252,12 @@ enum { * This is only useful as an argument to quotestring(). */ 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 ''. */ diff --git a/Src/ztype.h b/Src/ztype.h index b73e3f840..76589b152 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 42c7b4ec6..17a59cb2c 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 -- cgit 1.4.1