about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Doc/Zsh/expn.yo6
-rw-r--r--Src/subst.c49
-rw-r--r--Src/utils.c59
3 files changed, 96 insertions, 18 deletions
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 1008ea4ae..6b33c9a81 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -581,12 +581,14 @@ Capitalize the resulting words.  `Words' in this case refers to sequences
 of alphanumeric characters separated by non-alphanumerics, em(not) to words
 that result from field splitting.
 )
+item(tt(V))(
+Make any special characters in the resulting words visible.
+)
 item(tt(q))(
 Quote the resulting words with backslashes. 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. If it is
-given four times, no real quoting is done, but any special characters
-in the resulting words will be in a human-readable form.
+given four times, the words are quoted in single quotes preceded a tt($).
 )
 item(tt(Q))(
 Remove one level of quotes from the resulting words.
diff --git a/Src/subst.c b/Src/subst.c
index 45b58aef6..ffe6217f0 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -723,6 +723,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
     int sortit = 0, casind = 0;
     int casmod = 0;
     int quotemod = 0, quotetype = 0, quoteerr = 0;
+    int visiblemod = 0;
     char *sep = NULL, *spsep = NULL;
     char *premul = NULL, *postmul = NULL, *preone = NULL, *postone = NULL;
     char *replstr = NULL;	/* replacement string for /orig/repl */
@@ -826,6 +827,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		    casind = 1;
 		    break;
 
+		case 'V':
+		    visiblemod++;
+		    break;
+
 		case 'q':
 		    quotemod++, quotetype++;
 		    break;
@@ -1628,20 +1633,20 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	    ap = aval;
 
 	    if (quotemod > 0) {
-		if (quotetype == 3)
-		    for (; *ap; ap++)
-			*ap = nicedupstring(*ap);
-		else if (quotetype) {
+		if (quotetype) {
 		    int sl;
 		    char *tmp;
 
 		    for (; *ap; ap++) {
+			int pre = quotetype != 3 ? 1 : 2;
 			tmp = bslashquote(*ap, NULL, quotetype);
 			sl = strlen(tmp);
-			*ap = (char *) zhalloc(sl + 3);
-			strcpy((*ap) + 1, tmp);
-			ap[0][0] = ap[0][sl + 1] = (quotetype == 1 ? '\'' : '"');
-			ap[0][sl + 2] = '\0';
+			*ap = (char *) zhalloc(pre + sl + 2);
+			strcpy((*ap) + pre, tmp);
+			ap[0][pre - 1] = ap[0][pre + sl] = (quotetype != 2 ? '\'' : '"');
+			ap[0][pre + sl + 1] = '\0';
+			if (quotetype == 3)
+			  ap[0][0] = '$';
 		    }
 		} else
 		    for (; *ap; ap++)
@@ -1668,18 +1673,19 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	    if (!copied)
 		val = dupstring(val), copied = 1;
 	    if (quotemod > 0) {
-		if (quotetype == 3)
-		    val = nicedupstring(val);
-		else if (quotetype) {
+		if (quotetype) {
+		    int pre = quotetype != 3 ? 1 : 2;
 		    int sl;
 		    char *tmp;
 
 		    tmp = bslashquote(val, NULL, quotetype);
 		    sl = strlen(tmp);
-		    val = (char *) zhalloc(sl + 3);
-		    strcpy(val + 1, tmp);
-		    val[0] = val[sl + 1] = (quotetype == 1 ? '\'' : '"');
-		    val[sl + 2] = '\0';
+		    val = (char *) zhalloc(pre + sl + 2);
+		    strcpy(val + pre, tmp);
+		    val[pre - 1] = val[pre + sl] = (quotetype != 2 ? '\'' : '"');
+		    val[pre + sl + 1] = '\0';
+		    if (quotetype == 3)
+		      val[0] = '$';
 		} else
 		    val = bslashquote(val, NULL, 0);
 	    } else {
@@ -1700,6 +1706,19 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	    }
 	}
     }
+    if (visiblemod) {
+	if (isarr) {
+	    char **ap;
+	    if (!copied)
+		aval = arrdup(aval), copied = 1;
+	    for (ap = aval; *ap; ap++)
+		*ap = nicedupstring(*ap);
+	} else {
+	    if (!copied)
+		val = dupstring(val), copied = 1;
+	    val = nicedupstring(val);
+	}
+    }
     if (isarr) {
 	char *x;
 	char *y;
diff --git a/Src/utils.c b/Src/utils.c
index 5f9e89882..7494837cf 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -2984,7 +2984,64 @@ bslashquote(const char *s, char **e, int instring)
     for (; *u; u++) {
 	if (e && *e == u)
 	    *e = v, sf = 1;
-	if (ispecial(*u) &&
+	if (instring == 3) {
+	  int c = *u;
+	  if (c == Meta) {
+	    c = *++u ^ 32;
+	  }
+	  c &= 0xff;
+	  if(isprint(c)) {
+	    switch (c) {
+	    case '\\':
+	    case '\'':
+	      *v++ = '\\';
+	      *v++ = c;
+	      break;
+
+	    default:
+	      if(imeta(c)) {
+		*v++ = Meta;
+		*v++ = c ^ 32;
+	      }
+	      else {
+		if (isset(BANGHIST) && c == bangchar) {
+		  *v++ = '\\';
+		}
+		*v++ = c;
+	      }
+	      break;
+	    }
+	  }
+	  else {
+	    switch (c) {
+	    case '\0':
+	      *v++ = '\\';
+	      *v++ = '0';
+	      if ('0' <= u[1] && u[1] <= '7') {
+		*v++ = '0';
+		*v++ = '0';
+	      }
+	      break;
+
+	    case '\007': *v++ = '\\'; *v++ = 'a'; break;
+	    case '\b': *v++ = '\\'; *v++ = 'b'; break;
+	    case '\f': *v++ = '\\'; *v++ = 'f'; break;
+	    case '\n': *v++ = '\\'; *v++ = 'n'; break;
+	    case '\r': *v++ = '\\'; *v++ = 'r'; break;
+	    case '\t': *v++ = '\\'; *v++ = 't'; break;
+	    case '\v': *v++ = '\\'; *v++ = 'v'; break;
+
+	    default:
+	      *v++ = '\\';
+	      *v++ = '0' + ((c >> 6) & 7);
+	      *v++ = '0' + ((c >> 3) & 7);
+	      *v++ = '0' + (c & 7);
+	      break;
+	    }
+	  }
+	  continue;
+	}
+	else if (ispecial(*u) &&
 	    (!instring ||
 	     (isset(BANGHIST) && *u == (char)bangchar) ||
 	     (instring == 2 &&