diff options
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | Doc/Zsh/expn.yo | 24 | ||||
-rw-r--r-- | Src/subst.c | 49 | ||||
-rw-r--r-- | Src/utils.c | 107 | ||||
-rw-r--r-- | Src/zsh.h | 7 | ||||
-rw-r--r-- | Test/D04parameter.ztst | 14 |
6 files changed, 181 insertions, 28 deletions
diff --git a/ChangeLog b/ChangeLog index f25811e1b..6aed3f6c0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2009-06-05 Peter Stephenson <pws@csr.com> + + * 27304: Doc/Zsh/expn.yo, Src/subst.c, Src/utils.c, Src/zsh.h, + Test/D04parameter.ztst: add ${(q-)...} flag to do minimal + quoting of arguments with no unnecessary single quotes. + 2009-06-04 Peter Stephenson <pws@csr.com> * Baptiste Daroussin: 27033: Completion/BSD/Command/_portaudit, @@ -11828,5 +11834,5 @@ ***************************************************** * This is used by the shell to define $ZSH_PATCHLEVEL -* $Revision: 1.4712 $ +* $Revision: 1.4713 $ ***************************************************** diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index bab12cc3f..59b6aeb87 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -825,14 +825,22 @@ have `tt(foo=bar)' and `tt(bar=baz)', the strings tt(${(P)foo}), tt(${(P)${foo}}), and tt(${(P)$(echo bar)}) will be expanded to `tt(baz)'. ) item(tt(q))( -Quote the resulting words with backslashes; unprintable or invalid -characters are quoted using the tt($'\)var(NNN)tt(') form, with separate -quotes for each octet. If this flag is given -twice, the resulting words are quoted in single quotes and if it is -given three times, the words are quoted in double quotes; in these forms -no special handling of unprintable or invalid characters is attempted. If -the flag is given four times, the words are quoted in single quotes -preceded by a tt($). +Quote characters that are special to the shell in the resulting words with +backslashes; unprintable or invalid characters are quoted using the +tt($'\)var(NNN)tt(') form, with separate quotes for each octet. + +If this flag is given twice, the resulting words are quoted in single +quotes and if it is given three times, the words are quoted in double +quotes; in these forms no special handling of unprintable or invalid +characters is attempted. If the flag is given four times, the words are +quoted in single quotes preceded by a tt($). Note that in all three of +these forms quoting is done unconditionally, even if this does not change +the way the resulting string would be interpreted by the shell. + +If a tt(q-) is given (only a single tt(q) may appear), a minimal +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(Q))( Remove one level of quotes from the resulting words. diff --git a/Src/subst.c b/Src/subst.c index 5d14c458a..0bb0b798f 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1583,6 +1583,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) switch (c) { case ')': case Outpar: + /* how can this happen? */ break; case '~': case Tilde: @@ -1653,7 +1654,17 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) break; case 'q': - quotemod++, quotetype++; + if (quotetype == QT_DOLLARS) + goto flagerr; + if (s[1] == '-') { + if (quotemod) + goto flagerr; + s++; + quotemod = 1; + quotetype = QT_SINGLE_OPTIONAL; + } else { + quotemod++, quotetype++; + } break; case 'Q': quotemod--; @@ -2801,8 +2812,26 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) * the repetitions of the (q) flag. */ if (quotemod) { - if (quotetype > QT_DOLLARS) - quotetype = QT_DOLLARS; + int pre = 0, post = 0; + + if (quotemod > 0 && quotetype > QT_BACKSLASH) { + switch (quotetype) + { + case QT_DOLLARS: + /* space for "$" */ + pre = 2; + post = 1; + break; + + case QT_SINGLE_OPTIONAL: + /* quotes will be added for us */ + break; + + default: + pre = post = 1; + break; + } + } if (isarr) { char **ap; @@ -2816,13 +2845,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) char *tmp; for (; *ap; ap++) { - int pre = quotetype != QT_DOLLARS ? 1 : 2; tmp = quotestring(*ap, NULL, quotetype); sl = strlen(tmp); - *ap = (char *) zhalloc(pre + sl + 2); + *ap = (char *) zhalloc(pre + sl + post + 1); strcpy((*ap) + pre, tmp); - ap[0][pre - 1] = ap[0][pre + sl] = - (quotetype != QT_DOUBLE ? '\'' : '"'); + if (pre) + ap[0][pre - 1] = ap[0][pre + sl] = + (quotetype != QT_DOUBLE ? '\'' : '"'); ap[0][pre + sl + 1] = '\0'; if (quotetype == QT_DOLLARS) ap[0][0] = '$'; @@ -2853,15 +2882,15 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) val = dupstring(val), copied = 1; if (quotemod > 0) { if (quotetype > QT_BACKSLASH) { - int pre = quotetype != QT_DOLLARS ? 1 : 2; int sl; char *tmp; tmp = quotestring(val, NULL, quotetype); sl = strlen(tmp); val = (char *) zhalloc(pre + sl + 2); strcpy(val + pre, tmp); - val[pre - 1] = val[pre + sl] = - (quotetype != QT_DOUBLE ? '\'' : '"'); + if (pre) + val[pre - 1] = val[pre + sl] = + (quotetype != QT_DOUBLE ? '\'' : '"'); val[pre + sl + 1] = '\0'; if (quotetype == QT_DOLLARS) val[0] = '$'; diff --git a/Src/utils.c b/Src/utils.c index 00d51a320..687a396fa 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -4408,6 +4408,11 @@ addunprintable(char *v, const char *u, const char *uend) * * The last argument is a QT_ value defined in zsh.h other than QT_NONE. * + * Most quote styles other than backslash assume the quotes are to + * be added outside quotestring(). QT_SINGLE_OPTIONAL is different: + * the single quotes are only added where necessary, so the + * whole expression is handled here. + * * The string may be metafied and contain tokens. */ @@ -4417,20 +4422,50 @@ quotestring(const char *s, char **e, int instring) { const char *u, *tt; char *v; + int alloclen; + char *buf; + int sf = 0; /* - * With QT_BACKSLASH we may need to use $'\300' stuff. - * Keep memory usage within limits by allocating temporary - * storage and using heap for correct size at end. + * quotesub is used with QT_SINGLE_OPTIONAL. + * quotesub = 0: mechanism not active + * quotesub = 1: mechanism pending, no "'" yet; + * needs adding at quotestart. + * quotesub = 2: mechanism active, added opening "'"; need + * closing "'". */ - int alloclen = (instring == QT_BACKSLASH ? 7 : 4) * strlen(s) + 1; - char *buf = zshcalloc(alloclen); - int sf = 0; + int quotesub = 0; + char *quotestart; convchar_t cc; const char *uend; - DPUTS(instring < QT_BACKSLASH || instring > QT_DOLLARS, + switch (instring) + { + case QT_BACKSLASH: + /* + * With QT_BACKSLASH we may need to use $'\300' stuff. + * Keep memory usage within limits by allocating temporary + * storage and using heap for correct size at end. + */ + alloclen = strlen(s) * 7 + 1; + break; + + case QT_SINGLE_OPTIONAL: + /* + * Here, we may need to add single quotes. + */ + alloclen = strlen(s) * 4 + 3; + quotesub = 1; + break; + + default: + alloclen = strlen(s) * 4 + 1; + break; + } + tt = quotestart = v = buf = zshcalloc(alloclen); + + DPUTS(instring < QT_BACKSLASH || instring == QT_BACKTICK || + instring > QT_SINGLE_OPTIONAL, "BUG: bad quote type in quotestring"); - tt = v = buf; u = s; if (instring == QT_DOLLARS) { /* @@ -4526,12 +4561,64 @@ quotestring(const char *s, char **e, int instring) (u[-1] == '=' || u[-1] == ':')) || (*u == '~' && isset(EXTENDEDGLOB))) && (instring == QT_BACKSLASH || + instring == QT_SINGLE_OPTIONAL || (isset(BANGHIST) && *u == (char)bangchar && instring != QT_SINGLE) || (instring == QT_DOUBLE && (*u == '$' || *u == '`' || *u == '\"' || *u == '\\')) || (instring == QT_SINGLE && *u == '\''))) { - if (*u == '\n' || (instring == QT_SINGLE && *u == '\'')) { + if (instring == QT_SINGLE_OPTIONAL) { + if (quotesub == 1) { + /* + * We haven't yet had to quote at the start. + */ + if (*u == '\'') { + /* + * We don't need to. + */ + *v++ = '\\'; + } else { + /* + * It's now time to add quotes. + */ + if (v > quotestart) + { + char *addq; + + for (addq = v; addq > quotestart; addq--) + *addq = addq[-1]; + } + *quotestart = '\''; + v++; + quotesub = 2; + } + *v++ = *u++; + /* + * Next place to start quotes is here. + */ + quotestart = v; + } else if (*u == '\'') { + if (unset(RCQUOTES)) { + *v++ = '\''; + *v++ = '\\'; + *v++ = '\''; + /* Don't restart quotes unless we need them */ + quotesub = 1; + quotestart = v; + } else { + /* simplest just to use '' always */ + *v++ = '\''; + *v++ = '\''; + } + /* dealt with */ + u++; + } else { + /* else already quoting, just add */ + *v++ = *u++; + } + continue; + } else if (*u == '\n' || + (instring == QT_SINGLE && *u == '\'')) { if (unset(RCQUOTES)) { *v++ = '\''; if (*u == '\'') @@ -4589,6 +4676,8 @@ quotestring(const char *s, char **e, int instring) } } } + if (quotesub == 2) + *v++ = '\''; *v = '\0'; if (e && *e == u) diff --git a/Src/zsh.h b/Src/zsh.h index 3c1623c1f..3854116c0 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -210,8 +210,15 @@ enum { * in those cases where we need to represent a complete set. */ QT_BACKTICK, + /* + * Single quotes, but the default is not to quote unless necessary. + * This is only useful as an argument to quotestring(). + */ + QT_SINGLE_OPTIONAL }; +#define QT_IS_SINGLE(x) ((x) == QT_SINGLE || (x) == QT_SINGLE_OPTIONAL) + /* * Lexical tokens: unlike the character tokens above, these never * appear in strings and don't necessarily represent a single character. diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 4cd137bbd..358b46ef7 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -319,11 +319,25 @@ print -r ${(qq)foo} print -r ${(qqq)foo} print -r ${(qqqq)foo} + print -r ${(q-)foo} 0:${(q...)...} >playing\ \'stupid\'\ \"games\"\ \\w\\i\\t\\h\ \$quoting. >'playing '\''stupid'\'' "games" \w\i\t\h $quoting.' >"playing 'stupid' \"games\" \\w\\i\\t\\h \$quoting." >$'playing \'stupid\' "games" \\w\\i\\t\\h $quoting.' +>'playing '\'stupid\'' "games" \w\i\t\h $quoting.' + + print -r -- ${(q-):-foo} + print -r -- ${(q-):-foo bar} + print -r -- ${(q-):-"*(.)"} + print -r -- ${(q-):-"wow 'this is cool' or is it?"} + print -r -- ${(q-):-"no-it's-not"} +0:${(q-)...} minimal single quoting +>foo +>'foo bar' +>'*(.)' +>'wow '\''this is cool'\'' or is it?' +>no-it\'s-not foo="'and now' \"even the pubs\" \\a\\r\\e shut." print -r ${(Q)foo} |