summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--Src/builtin.c2
-rw-r--r--Src/subst.c6
-rw-r--r--Src/utils.c39
-rw-r--r--Src/zsh.h6
5 files changed, 45 insertions, 15 deletions
diff --git a/ChangeLog b/ChangeLog
index 7fd3d0c1e..e2f427227 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2010-05-27  Peter Stephenson  <p.w.stephenson@ntlworld.com>
 
+	* 27965 plus as per 27966: Src/builtin.c, Src/subst.c,
+	Src/utils.c, Src/zsh.h: Use $'\n' quoting instead of literal
+	newline for ${(q)...} to avoid lines getting split unexpectedly.
+	Quote empty strings as ''.
+
 	* 27976: Doc/Zsh/expn.yo: add yet more to the my-brain-hurts
 	description of how parameter expansion is ordered.
 
@@ -13164,5 +13169,5 @@
 
 *****************************************************
 * This is used by the shell to define $ZSH_PATCHLEVEL
-* $Revision: 1.4983 $
+* $Revision: 1.4984 $
 *****************************************************
diff --git a/Src/builtin.c b/Src/builtin.c
index 3f26f0304..e9e8e3bff 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -4233,7 +4233,7 @@ bin_print(char *name, char **args, Options ops, int func)
 		break;
 	    case 'q':
 		stringval = curarg ?
-		    quotestring(curarg, NULL, QT_BACKSLASH) : &nullstr;
+		    quotestring(curarg, NULL, QT_BACKSLASH_SHOWNULL) : &nullstr;
 		*d = 's';
 		print_val(stringval);
 		break;
diff --git a/Src/subst.c b/Src/subst.c
index 80a56410a..304add6f9 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -2896,7 +2896,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		    }
 		} else
 		    for (; *ap; ap++)
-			*ap = quotestring(*ap, NULL, QT_BACKSLASH);
+			*ap = quotestring(*ap, NULL, QT_BACKSLASH_SHOWNULL);
 	    } else {
 		int one = noerrs, oef = errflag, haserr = 0;
 
@@ -2933,7 +2933,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		    if (quotetype == QT_DOLLARS)
 		      val[0] = '$';
 		} else
-		    val = quotestring(val, NULL, QT_BACKSLASH);
+		    val = quotestring(val, NULL, QT_BACKSLASH_SHOWNULL);
 	    } else {
 		int one = noerrs, oef = errflag, haserr;
 
@@ -3490,7 +3490,7 @@ modify(char **str, char **ptr)
 			    subst(&copy, hsubl, hsubr, gbal);
 			break;
 		    case 'q':
-			copy = quotestring(copy, NULL, QT_BACKSLASH);
+			copy = quotestring(copy, NULL, QT_BACKSLASH_SHOWNULL);
 			break;
 		    case 'Q':
 			{
diff --git a/Src/utils.c b/Src/utils.c
index 1d7b3109a..184b2f354 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -4576,7 +4576,7 @@ quotestring(const char *s, char **e, int instring)
     char *v;
     int alloclen;
     char *buf;
-    int sf = 0;
+    int sf = 0, shownull;
     /*
      * quotesub is used with QT_SINGLE_OPTIONAL.
      * quotesub = 0:  mechanism not active
@@ -4585,11 +4585,18 @@ quotestring(const char *s, char **e, int instring)
      * quotesub = 2:  mechanism active, added opening "'"; need
      *                closing "'".
      */
-    int quotesub = 0;
+    int quotesub = 0, slen;
     char *quotestart;
     convchar_t cc;
     const char *uend;
 
+    slen = strlen(s);
+    if (instring == QT_BACKSLASH_SHOWNULL) {
+	shownull = 1;
+	instring = QT_BACKSLASH;
+    } else {
+	shownull = 0;
+    }
     switch (instring)
     {
     case QT_BACKSLASH:
@@ -4598,21 +4605,24 @@ quotestring(const char *s, char **e, int instring)
 	 * Keep memory usage within limits by allocating temporary
 	 * storage and using heap for correct size at end.
 	 */
-	alloclen = strlen(s) * 7 + 1;
+	alloclen = slen * 7 + 1;
+	if (!*s && shownull)
+	    alloclen += 2;	/* for '' */
 	break;
 
     case QT_SINGLE_OPTIONAL:
 	/*
 	 * Here, we may need to add single quotes.
 	 */
-	alloclen = strlen(s) * 4 + 3;
+	alloclen = slen * 4 + 3;
 	quotesub = 1;
 	break;
 
     default:
-	alloclen = strlen(s) * 4 + 1;
+	alloclen = slen * 4 + 1;
 	break;
     }
+
     tt = quotestart = v = buf = zshcalloc(alloclen);
 
     DPUTS(instring < QT_BACKSLASH || instring == QT_BACKTICK ||
@@ -4659,6 +4669,13 @@ quotestring(const char *s, char **e, int instring)
     }
     else
     {
+	if (shownull) {
+	    /* We can't show an empty string with just backslash quoting. */
+	    if (!*u) {
+		*v++ = '\'';
+		*v++ = '\'';
+	    }
+	}
 	/*
 	 * Here there are syntactic special characters, so
 	 * we start by going through bytewise.
@@ -4771,15 +4788,19 @@ quotestring(const char *s, char **e, int instring)
 		    continue;
 		} else if (*u == '\n' ||
 			   (instring == QT_SINGLE && *u == '\'')) {
-		    if (unset(RCQUOTES)) {
+		    if (*u == '\n') {
+			*v++ = '$';
+			*v++ = '\'';
+			*v++ = '\\';
+			*v++ = 'n';
+			*v++ = '\'';
+		    } else if (unset(RCQUOTES)) {
 			*v++ = '\'';
 			if (*u == '\'')
 			    *v++ = '\\';
 			*v++ = *u;
 			*v++ = '\'';
-		    } else if (*u == '\n')
-			*v++ = '"', *v++ = '\n', *v++ = '"';
-		    else
+		    } else
 			*v++ = '\'', *v++ = '\'';
 		    u++;
 		    continue;
diff --git a/Src/zsh.h b/Src/zsh.h
index 1b1175cb2..77281aa75 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -218,7 +218,11 @@ enum {
      * Single quotes, but the default is not to quote unless necessary.
      * This is only useful as an argument to quotestring().
      */
-    QT_SINGLE_OPTIONAL
+    QT_SINGLE_OPTIONAL,
+    /*
+     * As QT_BACKSLASH, but a NULL string is shown as ''.
+     */
+    QT_BACKSLASH_SHOWNULL
 };
 
 #define QT_IS_SINGLE(x)	((x) == QT_SINGLE || (x) == QT_SINGLE_OPTIONAL)