summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--Doc/Zsh/expn.yo24
-rw-r--r--Src/subst.c49
-rw-r--r--Src/utils.c107
-rw-r--r--Src/zsh.h7
-rw-r--r--Test/D04parameter.ztst14
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}