Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

PATCH: improved(ish) quotes for completion



I noticed another bug with completion which occurs with RCQUOTES.
Something like this:

su -c 'echo ''hello'' <TAB>'

can get into an infinite loop before the offsets for the stripped ''
aren't taken account of.

I noticed it while I was having a go at improving the handling of quotes
in completion in order to get $'...' quoting to work (discussed a couple
of weeks ago).  I haven't finished that (see the TODO in compcore.c (and
what a todo)), but the code should be no worse than it is at present and
could probably do with some shaking down anyway.

I've tried to improve readability (gasp!) by using an enum for the
various quote types consistently throughout the shell.  It's this enum
which gets pushed onto the completion quotation stack, compqstack,
though it's still a string---hence QT_NONE which has to be a NUL.
The values on compqstack are then consistent with the values in the
completion string state variable instring and the arguments to
quotestring(), which used to be called bslashquote() from the days (so
long ago I can't remember) when that's what it did.

compqstack gets converted into a string for output only when the user
asks for the variable (which only a lunatic would do, as far as I can
see), so this should be efficient.  The change in the documentation is
more of a statement of intent than a fact at the moment; I've used
Andrey's suggestion of a $ for $'...' quoting.

Those tricat()'s are ugly and could do with a compqstack_push(), or
something.

Index: Doc/Zsh/compwid.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/compwid.yo,v
retrieving revision 1.39
diff -u -r1.39 compwid.yo
--- Doc/Zsh/compwid.yo	21 Sep 2006 19:54:32 -0000	1.39
+++ Doc/Zsh/compwid.yo	5 Oct 2006 21:02:05 -0000
@@ -140,8 +140,9 @@
 types of quoted strings which are currently broken into parts in this
 fashion.  Its value contains one character for each quoting level.  The
 characters are a single quote or a double quote for strings quoted with
-these characters and a backslash for strings not starting with a quote
-character.  The first character in the value always corresponds to the
+these characters, a dollars sign for strings quoted with
+tt($')var(...)tt(') and a backslash for strings not starting with a
+quote character.  The first character in the value always corresponds to the
 innermost quoting level.
 )
 vindex(context, compstate)
Index: Src/builtin.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/builtin.c,v
retrieving revision 1.163
diff -u -r1.163 builtin.c
--- Src/builtin.c	20 Sep 2006 09:22:34 -0000	1.163
+++ Src/builtin.c	5 Oct 2006 21:02:07 -0000
@@ -3964,7 +3964,8 @@
 		    count += fprintf(fout, "%*c", width, ' ');
 		break;
 	    case 'q':
-		stringval = curarg ? bslashquote(curarg, NULL, 0) : &nullstr;
+		stringval = curarg ?
+		    quotestring(curarg, NULL, QT_BACKSLASH) : &nullstr;
 		*d = 's';
 		print_val(stringval);
 		break;
Index: Src/subst.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/subst.c,v
retrieving revision 1.63
diff -u -r1.63 subst.c
--- Src/subst.c	23 Sep 2006 20:25:06 -0000	1.63
+++ Src/subst.c	5 Oct 2006 21:02:08 -0000
@@ -1394,7 +1394,7 @@
      * quoterr is simply (X) but gets passed around a lot because the
      * combination (eX) needs it.
      */
-    int quotemod = 0, quotetype = 0, quoteerr = 0;
+    int quotemod = 0, quotetype = QT_NONE, quoteerr = 0;
     /*
      * (V) flag: fairly straightforward, except that as with so
      * many flags it's not easy to decide where to put it in the order.
@@ -2835,8 +2835,8 @@
      * the repetitions of the (q) flag.
      */
     if (quotemod) {
-	if (--quotetype > 3)
-	    quotetype = 3;
+	if (quotetype > QT_DOLLARS)
+	    quotetype = QT_DOLLARS;
 	if (isarr) {
 	    char **ap;
 
@@ -2845,24 +2845,25 @@
 	    ap = aval;
 
 	    if (quotemod > 0) {
-		if (quotetype) {
+		if (quotetype > QT_BACKSLASH) {
 		    int sl;
 		    char *tmp;
 
 		    for (; *ap; ap++) {
-			int pre = quotetype != 3 ? 1 : 2;
-			tmp = bslashquote(*ap, NULL, quotetype);
+			int pre = quotetype != QT_DOLLARS ? 1 : 2;
+			tmp = quotestring(*ap, NULL, quotetype);
 			sl = strlen(tmp);
 			*ap = (char *) zhalloc(pre + sl + 2);
 			strcpy((*ap) + pre, tmp);
-			ap[0][pre - 1] = ap[0][pre + sl] = (quotetype != 2 ? '\'' : '"');
+			ap[0][pre - 1] = ap[0][pre + sl] =
+			    (quotetype != QT_DOUBLE ? '\'' : '"');
 			ap[0][pre + sl + 1] = '\0';
-			if (quotetype == 3)
+			if (quotetype == QT_DOLLARS)
 			  ap[0][0] = '$';
 		    }
 		} else
 		    for (; *ap; ap++)
-			*ap = bslashquote(*ap, NULL, 0);
+			*ap = quotestring(*ap, NULL, QT_BACKSLASH);
 	    } else {
 		int one = noerrs, oef = errflag, haserr = 0;
 
@@ -2885,20 +2886,21 @@
 	    if (!copied)
 		val = dupstring(val), copied = 1;
 	    if (quotemod > 0) {
-		if (quotetype) {
-		    int pre = quotetype != 3 ? 1 : 2;
+		if (quotetype > QT_BACKSLASH) {
+		    int pre = quotetype != QT_DOLLARS ? 1 : 2;
 		    int sl;
 		    char *tmp;
-		    tmp = bslashquote(val, NULL, quotetype);
+		    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 != 2 ? '\'' : '"');
+		    val[pre - 1] = val[pre + sl] =
+			(quotetype != QT_DOUBLE ? '\'' : '"');
 		    val[pre + sl + 1] = '\0';
-		    if (quotetype == 3)
+		    if (quotetype == QT_DOLLARS)
 		      val[0] = '$';
 		} else
-		    val = bslashquote(val, NULL, 0);
+		    val = quotestring(val, NULL, QT_BACKSLASH);
 	    } else {
 		int one = noerrs, oef = errflag, haserr;
 
@@ -3387,7 +3389,7 @@
 			    subst(&copy, hsubl, hsubr, gbal);
 			break;
 		    case 'q':
-			copy = bslashquote(copy, NULL, 0);
+			copy = quotestring(copy, NULL, QT_BACKSLASH);
 			break;
 		    case 'Q':
 			{
@@ -3453,7 +3455,7 @@
 		    }
 		    break;
 		case 'q':
-		    *str = bslashquote(*str, NULL, 0);
+		    *str = quotestring(*str, NULL, QT_BACKSLASH);
 		    break;
 		case 'Q':
 		    {
Index: Src/text.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/text.c,v
retrieving revision 1.16
diff -u -r1.16 text.c
--- Src/text.c	10 Jan 2006 16:57:06 -0000	1.16
+++ Src/text.c	5 Oct 2006 21:02:08 -0000
@@ -807,7 +807,7 @@
 		 * quotes certainly isn't right in that case).
 		 */
 		taddchr('\'');
-		taddstr(bslashquote(f->name, NULL, 1));
+		taddstr(quotestring(f->name, NULL, QT_SINGLE));
 		taddchr('\'');
 	    } else
 		taddstr(f->name);
Index: Src/utils.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/utils.c,v
retrieving revision 1.140
diff -u -r1.140 utils.c
--- Src/utils.c	23 Sep 2006 20:25:06 -0000	1.140
+++ Src/utils.c	5 Oct 2006 21:02:09 -0000
@@ -1357,7 +1357,7 @@
 }
 
 /* the default tty state */
- 
+
 /**/
 mod_export struct ttyinfo shttyinfo;
 
@@ -1576,13 +1576,13 @@
  * NULL, the name is relative to $TMPPREFIX; If it is non-NULL, the
  * unique suffix includes a prefixed '.' for improved readability.  If
  * "use_heap" is true, we allocate the returned name on the heap. */
- 
+
 /**/
 mod_export char *
 gettempname(const char *prefix, int use_heap)
 {
     char *ret, *suffix = prefix ? ".XXXXXX" : "XXXXXX";
- 
+
     queue_signals();
     if (!prefix && !(prefix = getsparam("TMPPREFIX")))
 	prefix = DEFAULT_TMPPREFIX;
@@ -1642,7 +1642,7 @@
     *tempname = fn;
     return fd;
 }
- 
+
 /* Check if a string contains a token */
 
 /**/
@@ -1656,7 +1656,7 @@
 }
 
 /* Delete a character in a string */
- 
+
 /**/
 mod_export void
 chuck(char *str)
@@ -3082,7 +3082,7 @@
 		 */
 		switch (itype) {
 		case IWORD:
-		    if (!iswalnum(wc) && 
+		    if (!iswalnum(wc) &&
 			!wmemchr(wordchars_wide.chars, wc,
 				 wordchars_wide.len))
 			return (char *)ptr;
@@ -3820,7 +3820,7 @@
 	if (itok(c)) {
 	    if (c <= Comma)
 		c = ztokens[c - Pound];
-	    else 
+	    else
 		continue;
 	}
 	if (c == Meta)
@@ -3845,7 +3845,7 @@
 	if (itok(c)) {
 	    if (c <= Comma)
 		c = ztokens[c - Pound];
-	    else 
+	    else
 		continue;
 	}
 	if (c == Meta)
@@ -4134,27 +4134,32 @@
     return 0;
 }
 
-/* Quote the string s and return the result.  If e is non-zero, the         *
- * pointer it points to may point to a position in s and in e the position  *
- * of the corresponding character in the quoted string is returned.         *
- * The last argument should be zero if this is to be used outside a string, *
- * one if it is to be quoted for the inside of a single quoted string,      *
- * two if it is for the inside of a double quoted string, and               *
- * three if it is for the inside of a $'...' quoted string.                 *
- * The string may be metafied and contain tokens.                           */
+/*
+ * Quote the string s and return the result.
+ *
+ * If e is non-zero, the
+ * pointer it points to may point to a position in s and in e the position
+ * of the corresponding character in the quoted string is returned.
+ * 
+ * The last argument is a QT_ value defined in zsh.h other than QT_NONE.
+ *
+ * The string may be metafied and contain tokens.
+ */
 
 /**/
 mod_export char *
-bslashquote(const char *s, char **e, int instring)
+quotestring(const char *s, char **e, int instring)
 {
     const char *u, *tt;
     char *v;
     char *buf = hcalloc(4 * strlen(s) + 1);
     int sf = 0;
 
+    DPUTS(instring < QT_BACKSLASH || instring > QT_DOLLARS,
+	  "BUG: bad quote type in quotestring");
     tt = v = buf;
     u = s;
-    if (instring == 3) {
+    if (instring == QT_DOLLARS) {
 	/*
 	 * As we test for printability here we need to be able
 	 * to look for multibyte characters.
@@ -4170,7 +4175,7 @@
 	    }
 	    if (
 #ifdef MULTIBYTE_SUPPORT
-		cc != WEOF && 
+		cc != WEOF &&
 #endif
 		WC_ISPRINT(cc)) {
 		switch (cc) {
@@ -4276,13 +4281,13 @@
 		      (isset(MAGICEQUALSUBST) &&
 		       (u[-1] == '=' || u[-1] == ':')) ||
 		      (*u == '~' && isset(EXTENDEDGLOB))) &&
-		     (!instring ||
+		     (instring == QT_BACKSLASH ||
 		      (isset(BANGHIST) && *u == (char)bangchar &&
-		       instring != 1) ||
-		      (instring == 2 &&
+		       instring != QT_SINGLE) ||
+		      (instring == QT_DOUBLE &&
 		       (*u == '$' || *u == '`' || *u == '\"' || *u == '\\')) ||
-		      (instring == 1 && *u == '\''))) {
-		if (*u == '\n' || (instring == 1 && *u == '\'')) {
+		      (instring == QT_SINGLE && *u == '\''))) {
+		if (*u == '\n' || (instring == QT_SINGLE && *u == '\'')) {
 		    if (unset(RCQUOTES)) {
 			*v++ = '\'';
 			if (*u == '\'')
@@ -4306,7 +4311,7 @@
 
     if (e && *e == u)
 	*e = v, sf = 1;
-    DPUTS(e && !sf, "BUG: Wild pointer *e in bslashquote()");
+    DPUTS(e && !sf, "BUG: Wild pointer *e in quotestring()");
 
     return buf;
 }
@@ -4654,7 +4659,7 @@
 		    *len = t - buf;
 		    return buf;
 		}
-		t += count;  
+		t += count;
 		continue;
 # else
 #  if defined(HAVE_NL_LANGINFO) && defined(CODESET)
Index: Src/zsh.h
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/zsh.h,v
retrieving revision 1.100
diff -u -r1.100 zsh.h
--- Src/zsh.h	20 Sep 2006 09:22:34 -0000	1.100
+++ Src/zsh.h	5 Oct 2006 21:02:11 -0000
@@ -178,6 +178,41 @@
 
 #define SPECCHARS "#$^*()=|{}[]`<>?~;&\n\t \\\'\""
 
+/*
+ * Types of quote.  This is used in various places, so care needs
+ * to be taken when changing them.  (Oooh, don't you look surprised.)
+ * - Passed to quotestring() to indicate style.  This is the ultimate
+ *   destiny of most of the other uses of members of the enum.
+ * - In paramsubst(), to count q's in parameter substitution.
+ * - In the completion code, where we maintain a stack of quotation types.
+ */
+enum {
+    /*
+     * No quote.  Not a valid quote, but useful in the substitution
+     * and completion code to indicate we're not doing any quoting.
+     */
+    QT_NONE,
+    /* Backslash: \ */
+    QT_BACKSLASH,
+    /* Single quote: ' */
+    QT_SINGLE,
+    /* Double quote: " */
+    QT_DOUBLE,
+    /* Print-style quote: $' */
+    QT_DOLLARS,
+    /*
+     * Backtick: `
+     * Not understood by many parts of the code; here for a convenience
+     * in those cases where we need to represent a complete set.
+     */
+    QT_BACKTICK,
+};
+
+/*
+ * Lexical tokens: unlike the character tokens above, these never
+ * appear in strings and don't necessarily represent a single character.
+ */
+
 enum {
     NULLTOK,		/* 0  */
     SEPER,
Index: Src/Zle/compcore.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/compcore.c,v
retrieving revision 1.85
diff -u -r1.85 compcore.c
--- Src/Zle/compcore.c	30 Jul 2006 19:58:31 -0000	1.85
+++ Src/Zle/compcore.c	5 Oct 2006 21:02:12 -0000
@@ -303,11 +303,13 @@
     matchers = newlinklist();
 
     zsfree(compqstack);
-    compqstack = ztrdup("\\");
-    if (instring == 2)
-	compqstack[0] = '"';
-    else if (instring)
-	compqstack[0] = '\'';
+    compqstack = zalloc(2);
+    /*
+     * It looks like we may need to do stuff with backslashes even
+     * if instring is QT_NONE.
+     */
+    *compqstack = (instring == QT_NONE) ? QT_BACKSLASH : (char)instring;
+    compqstack[1] = '\0';
 
     hasunqu = 0;
     useline = (wouldinstab ? -1 : (lst != COMP_LIST_COMPLETE));
@@ -650,13 +652,22 @@
 	compredirect = ztrdup(compredirect);
 	zsfree(compquote);
 	zsfree(compquoting);
-	if (instring) {
-	    if (instring == 1) {
+	if (instring > QT_BACKSLASH) {
+	    switch (instring) {
+	    case QT_SINGLE:
 		compquote = ztrdup("\'");
 		compquoting = ztrdup("single");
-	    } else {
+		break;
+
+	    case QT_DOUBLE:
 		compquote = ztrdup("\"");
 		compquoting = ztrdup("double");
+		break;
+
+	    case QT_DOLLARS:
+		compquote = ztrdup("$'");
+		compquoting = ztrdup("dollars");
+		break;
 	    }
 	    kset |= CP_QUOTE | CP_QUOTING;
 	} else if (inbackt) {
@@ -1026,8 +1037,7 @@
 		p += ign;
 	    while (*p) {
 		if (ign >= 0 || p[1])
-		    s = bslashquote(s, NULL,
-				    (*p == '\'' ? 1 : (*p == '"' ? 2 : 0)));
+		    s = quotestring(s, NULL, *p);
 		p++;
 	    }
 	}
@@ -1290,9 +1300,29 @@
     return str;
 }
 
+/**/
+mod_export char *
+comp_quoting_string(int stype)
+{
+    switch (stype)
+    {
+    case QT_SINGLE:
+	return "'";
+    case QT_DOUBLE:
+	return "\"";
+    case QT_DOLLARS:
+	return "$'";
+    default:			/* shuts up compiler */
+	return "\\";
+    }
+}
+
 /*
  * This is the code behind compset -q, which splits the
  * the current word as if it were a command line.
+ *
+ * This is one of those completion functions that merits the
+ * coveted title "not just ordinarily horrific".
  */
 
 /**/
@@ -1307,7 +1337,7 @@
     int tl, got = 0, i = 0, j, cur = -1, oll, sl, css = 0;
     int remq = 0, dq = 0, odq, sq = 0, osq, issq = 0, sqq = 0, lsq = 0, qa = 0;
     int ois = instring, oib = inbackt, noffs = lp, ona = noaliases;
-    char *tmp, *p, *ns, *ol, sav, *qp, *qs, *ts, qc = '\0';
+    char *tmp, *p, *ns, *ol, sav, *qp, *qs, *ts;
 
     METACHECK();
 
@@ -1334,21 +1364,27 @@
     strcpy(tmp + 2 + noffs, s + noffs);
 
     switch (*compqstack) {
-    case '\\':
+    case QT_NONE:
+#ifdef DEBUG
+	dputs("BUG: head of compstack is NULL");
+#endif
+	break;
+
+    case QT_BACKSLASH:
         remq = 1;
 	tmp = rembslash(tmp);
         break;
-    case '\'':
+
+    case QT_SINGLE:
         issq = 1;
         if (isset(RCQUOTES))
             qa = 1;
         else
             qa = 3;
-
         sq = remsquote(tmp);
-
         break;
-    case '"':
+
+    case QT_DOUBLE:
         for (j = 0, p = tmp; *p; p++, j++)
             if (*p == '\\' && p[1] == '\\') {
                 dq++;
@@ -1360,6 +1396,11 @@
                 if (!*p)
                     break;
             }
+	break;
+
+    case QT_DOLLARS:
+	/* TODO */
+	break;
     }
     odq = dq;
     osq = sq;
@@ -1450,19 +1491,36 @@
 
     untokenize(ts = dupstring(ns));
 
-    if (*ns == Snull || *ns == Dnull) {
-	instring = (*ns == Snull ? 1 : 2);
+    if (*ns == Snull || *ns == Dnull ||
+	((*ns == String || *ns == Qstring) && ns[1] == Snull)) {
+	char *tsptr = ts, *nsptr = ns, sav;
+	switch (*ns) {
+	case Snull:
+	    instring = QT_SINGLE;
+	    break;
+
+	case Dnull:
+	    instring = QT_DOUBLE;
+	    break;
+
+	default:
+	    instring = QT_DOLLARS;
+	    nsptr++;
+	    tsptr++;
+	    break;
+	}
+
 	inbackt = 0;
 	swb++;
-	if (ns[strlen(ns) - 1] == *ns && ns[1])
+	if (nsptr[strlen(nsptr) - 1] == *nsptr && nsptr[1])
 	    swe--;
 	zsfree(autoq);
-	autoq = ztrdup(compqstack[1] ? "" :
-		       multiquote(*ns == Snull ? "'" : "\"", 1));
-	qc = (*ns == Snull ? '\'' : '"');
-	ts++;
+	sav = *++tsptr;
+	*tsptr = '\0';
+	autoq = ztrdup(compqstack[1] ? "" : multiquote(ts, 1));
+	*(ts = tsptr) = sav;
     } else {
-	instring = 0;
+	instring = QT_NONE;
 	zsfree(autoq);
 	autoq = NULL;
     }
@@ -1496,7 +1554,7 @@
     }
     ns = ts;
 
-    if (instring && strchr(compqstack, '\\')) {
+    if (instring && strchr(compqstack, QT_BACKSLASH)) {
 	int rl = strlen(ns), ql = strlen(multiquote(ns, !!compqstack[1]));
 
 	if (ql > rl)
@@ -1525,24 +1583,38 @@
     }
     {
 	int set = CP_QUOTE | CP_QUOTING, unset = 0;
+	char compnewchars[2];
 
-	p = tricat((instring ? (instring == 1 ? "'" : "\"") : "\\"),
-		   compqstack, "");
+	compnewchars[0] =
+	    (char)(instring == QT_NONE ? QT_BACKSLASH : instring);
+	compnewchars[1] = '\0';
+	p = tricat(compnewchars, compqstack, "");
 	zsfree(compqstack);
 	compqstack = p;
 
 	zsfree(compquote);
 	zsfree(compquoting);
-	if (instring == 2) {
+	switch (instring) {
+	case QT_DOUBLE:
 	    compquote = "\"";
 	    compquoting = "double";
-	} else if (instring == 1) {
+	    break;
+
+	case QT_SINGLE:
 	    compquote = "'";
 	    compquoting = "single";
-	} else {
+	    break;
+
+	case QT_DOLLARS:
+	    compquote = "$'";
+	    compquoting = "dollars";
+	    break;
+
+	default:
 	    compquote = compquoting = "";
 	    unset = set;
 	    set = 0;
+	    break;
 	}
 	compquote = ztrdup(compquote);
 	compquoting = ztrdup(compquoting);
@@ -1804,20 +1876,33 @@
 	dat->flags |= parflags;
     if (compquote && (qc = *compquote)) {
 	if (qc == '`') {
-	    instring = 0;
+	    instring = QT_NONE;
+	    /*
+	     * Yes, inbackt has always been set to zero here.  I'm
+	     * sure there's a simple explanation.
+	     */
 	    inbackt = 0;
 	    autoq = "";
 	} else {
-	    char buf[2];
+	    switch (qc) {
+	    case '\'':
+		instring = QT_SINGLE;
+		break;
 
-	    instring = (qc == '\'' ? 1 : 2);
+	    case '"':
+		instring = QT_DOUBLE;
+		break;
+
+	    case '$':
+		instring = QT_DOLLARS;
+		break;
+	    }
 	    inbackt = 0;
-	    buf[0] = qc;
-	    buf[1] = '\0';
-	    autoq = multiquote(buf, 1);
+	    autoq = multiquote(compquote, 1);
 	}
     } else {
-	instring = inbackt = 0;
+	instring = QT_NONE;
+	inbackt = 0;
 	autoq = NULL;
     }
     qipre = ztrdup(compqiprefix ? compqiprefix : "");
@@ -2549,8 +2634,8 @@
                 cm->modec = '\0';
         }
     }
-    if ((*compqstack == '\\' && compqstack[1]) ||
-	(autoq && *compqstack && compqstack[1] == '\\'))
+    if ((*compqstack == QT_BACKSLASH && compqstack[1]) ||
+	(autoq && *compqstack && compqstack[1] == QT_BACKSLASH))
 	cm->flags |= CMF_NOSPACE;
     if (nbrbeg) {
 	int *p;
Index: Src/Zle/compctl.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/compctl.c,v
retrieving revision 1.28
diff -u -r1.28 compctl.c
--- Src/Zle/compctl.c	11 Sep 2006 10:05:23 -0000	1.28
+++ Src/Zle/compctl.c	5 Oct 2006 21:02:13 -0000
@@ -1401,7 +1401,7 @@
 	    untokenize(p);
 	    quotedzputs(p, stdout);
 	} else
-	    quotedzputs(bslashquote(s, NULL, 0), stdout);
+	    quotedzputs(quotestring(s, NULL, QT_BACKSLASH), stdout);
     }
 
     /* loop through flags w/o args that are set, printing them if so */
@@ -1537,7 +1537,7 @@
 		char *p = dupstring(s);
 
 		untokenize(p);
-		quotedzputs(bslashquote(p, NULL, 0), stdout);
+		quotedzputs(quotestring(p, NULL, QT_BACKSLASH), stdout);
 	    }
 	}
 	putchar('\n');
@@ -1735,10 +1735,13 @@
 
 static int addwhat;
 
-/* Convenience macro for calling bslashquote() (formerly quotename()). *
- * This uses the instring variable above.                              */
+/*
+ * Convenience macro for calling quotestring (formerly bslashquote()
+ * (formerly quotename())).
+ * This uses the instring variable exported from zle_tricky.c.
+ */
 
-#define quotename(s, e) bslashquote(s, e, instring)
+#define quotename(s, e) quotestring(s, e, instring)
 
 /* Hook functions */
 
@@ -2279,22 +2282,38 @@
 	char *os = cmdstr, **ow = clwords, **p, **q, qc;
 	int on = clwnum, op = clwpos, ois =  instring, oib = inbackt;
 	char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, *oaq = autoq;
-	char buf[2];
+	char buf[3];
 
 	if (compquote && (qc = *compquote)) {
 	    if (qc == '`') {
-		instring = 0;
+		instring = QT_NONE;
+		/*
+		 * Yes, inbackt has always been set to zero here.  I'm
+		 * sure there's a simple explanation.
+		 */
 		inbackt = 0;
 		autoq = "";
 	    } else {
-		buf[0] = qc;
-		buf[1] = '\0';
-		instring = (qc == '\'' ? 1 : 2);
+		switch (qc) {
+		case '\'':
+		    instring = QT_SINGLE;
+		    break;
+
+		case '"':
+		    instring = QT_DOUBLE;
+		    break;
+
+		case '$':
+		    instring = QT_DOLLARS;
+		    break;
+		}
 		inbackt = 0;
+		strcpy(buf, compquote);
 		autoq = buf;
 	    }
 	} else {
-	    instring = inbackt = 0;
+	    instring = QT_NONE;
+	    inbackt = 0;
 	    autoq = "";
 	}
 	qipre = ztrdup(compqiprefix ? compqiprefix : "");
@@ -2589,7 +2608,7 @@
     int compadd, m = 0, d = 0, t, tt, i, j, a, b, ins;
     char *sc = NULL, *s, *ss;
 
-    ins = (instring ? instring : (inbackt ? 3 : 0));
+    ins = (instring != QT_NONE ? instring : (inbackt ? QT_BACKTICK : 0));
 
     /* This loops over the patterns separated by `-'s. */
     for (compc = occ->ext; compc; compc = compc->next) {
@@ -2607,9 +2626,9 @@
 		    erange = clwnum - 1;
 		    switch (cc->type) {
 		    case CCT_QUOTE:
-			t = ((cc->u.s.s[i][0] == 's' && ins == 1) ||
-			     (cc->u.s.s[i][0] == 'd' && ins == 2) ||
-			     (cc->u.s.s[i][0] == 'b' && ins == 3));
+			t = ((cc->u.s.s[i][0] == 's' && ins == QT_SINGLE) ||
+			     (cc->u.s.s[i][0] == 'd' && ins == QT_DOUBLE) ||
+			     (cc->u.s.s[i][0] == 'b' && ins == QT_BACKTICK));
 			break;
 		    case CCT_POS:
 			tt = clwpos;
@@ -2755,7 +2774,7 @@
     int sl = strlen(ss), tl, got = 0, i = 0, cur = -1, oll = zlemetall, remq;
     int ois = instring, oib = inbackt, ona = noaliases;
     char *tmp, *p, *ns, *ol = zlemetaline, sav, *oaq = autoq;
-    char *qp, *qs, *ts, qc = '\0';
+    char *qp, *qs, *ts;
 
     swb = swe = soffs = 0;
     ns = NULL;
@@ -2774,7 +2793,7 @@
     memcpy(tmp + sl + 1, s, noffs);
     tmp[(scs = zlemetacs = sl + 1 + noffs)] = 'x';
     strcpy(tmp + sl + 2 + noffs, s + noffs);
-    if ((remq = (*compqstack == '\\')))
+    if ((remq = (*compqstack == QT_BACKSLASH)))
 	tmp = rembslash(tmp);
     inpush(dupstrspace(tmp), 0, NULL);
     zlemetaline = tmp;
@@ -2841,17 +2860,35 @@
 
     untokenize(ts = dupstring(ns));
 
-    if (*ns == Snull || *ns == Dnull) {
-	instring = (*ns == Snull ? 1 : 2);
+    if (*ns == Snull || *ns == Dnull ||
+	((*ns == String || *ns == Qstring) && ns[1] == Snull)) {
+	char *tsptr = ts, *nsptr = ns, sav;
+	switch (*ns) {
+	case Snull:
+	    instring = QT_SINGLE;
+	    break;
+
+	case Dnull:
+	    instring = QT_DOUBLE;
+	    break;
+
+	default:
+	    instring = QT_DOLLARS;
+	    nsptr++;
+	    tsptr++;
+	    break;
+	}
+
 	inbackt = 0;
 	swb++;
-	if (ns[strlen(ns) - 1] == *ns && ns[1])
+	if (nsptr[strlen(nsptr) - 1] == *nsptr && nsptr[1])
 	    swe--;
-	autoq = compqstack[1] ? "" : multiquote(*ns == Snull ? "'" : "\"", 1);
-	qc = (*ns == Snull ? '\'' : '"');
-	ts++;
+	sav = *++tsptr;
+	*tsptr = '\0';
+	autoq = compqstack[1] ? "" : multiquote(ts, 1);
+	*(ts = tsptr) = sav;
     } else {
-	instring = 0;
+	instring = QT_NONE;
 	autoq = "";
     }
     for (p = ns, i = swb; *p; p++, i++) {
@@ -2878,7 +2915,7 @@
     }
     ns = ts;
 
-    if (instring && strchr(compqstack, '\\')) {
+    if (instring != QT_NONE && strchr(compqstack, QT_BACKSLASH)) {
 	int rl = strlen(ns), ql = strlen(multiquote(ns, !!compqstack[1]));
 
 	if (ql > rl)
@@ -2904,13 +2941,15 @@
 
     {
 	char **ow = clwords, *os = cmdstr, *oqp = qipre, *oqs = qisuf;
-	char *oqst = compqstack;
+	char *oqst = compqstack, compnewchar[2];
 	int olws = clwsize, olwn = clwnum, olwp = clwpos;
 	int obr = brange, oer = erange, oof = offs;
 	unsigned long occ = ccont;
 
-	compqstack = tricat((instring ? (instring == 1 ? "'" : "\"") : "\\"),
-			    compqstack, "");
+	compnewchar[0] = (char)(instring != QT_NONE ? (char)instring :
+				QT_BACKSLASH);
+	compnewchar[1] = '\0';
+	compqstack = tricat(compnewchar, compqstack, "");
 
 	clwsize = clwnum = countlinknodes(foo);
 	clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
Index: Src/Zle/complete.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/complete.c,v
retrieving revision 1.33
diff -u -r1.33 complete.c
--- Src/Zle/complete.c	30 May 2006 22:35:04 -0000	1.33
+++ Src/Zle/complete.c	5 Oct 2006 21:02:13 -0000
@@ -992,6 +992,8 @@
 { get_unambig_pos, nullstrsetfn, compunsetfn };
 static const struct gsu_scalar insert_pos_gsu =
 { get_insert_pos, nullstrsetfn, compunsetfn };
+static const struct gsu_scalar compqstack_gsu =
+{ get_compqstack, nullstrsetfn, compunsetfn };
 
 static const struct gsu_integer compvarinteger_gsu =
 { intvargetfn, intvarsetfn, compunsetfn };
@@ -1047,7 +1049,7 @@
     { "old_insert", PM_SCALAR, VAL(compoldins), NULL },
     { "vared", PM_SCALAR, VAL(compvared), NULL },
     { "list_lines", PM_INTEGER | PM_READONLY, NULL, GSU(listlines_gsu) },
-    { "all_quotes", PM_SCALAR | PM_READONLY, VAL(compqstack), NULL },
+    { "all_quotes", PM_SCALAR | PM_READONLY, NULL, GSU(compqstack_gsu) },
     { "ignored", PM_INTEGER | PM_READONLY, VAL(compignored), NULL },
     { NULL, 0, NULL, NULL }
 };
@@ -1223,6 +1225,26 @@
 }
 
 /**/
+static char *
+get_compqstack(UNUSED(Param pm))
+{
+    char *p, *ptr, *cqp;
+
+    if (!compqstack)		/* TODO: don't think this can happen... */
+	return "";
+
+    ptr = p = zhalloc(2*strlen(compqstack)+1);
+
+    for (cqp = compqstack; *cqp; cqp++) {
+	char *str = comp_quoting_string(*cqp);
+	*ptr++ = *str;
+    }
+    *ptr = '\0';
+
+    return p;
+}
+
+/**/
 static void
 compunsetfn(Param pm, int exp)
 {
Index: Src/Zle/computil.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/computil.c,v
retrieving revision 1.97
diff -u -r1.97 computil.c
--- Src/Zle/computil.c	27 Sep 2006 16:54:00 -0000	1.97
+++ Src/Zle/computil.c	5 Oct 2006 21:02:14 -0000
@@ -3441,8 +3441,7 @@
     if ((x = (prefix && *str == '=')))
 	*str = 'x';
 
-    ret = bslashquote(str, NULL, (*compqstack == '\'' ? 1 :
-				  (*compqstack == '"' ? 2 : 0)));
+    ret = quotestring(str, NULL, *compqstack);
 
     if (x)
 	*str = *ret = '=';
@@ -4344,7 +4343,7 @@
     for (; (n = *names); names++) {
 	if (!ztat(n, &nst, 0) && S_ISDIR(nst.st_mode)) {
 	    if (tpwd && nst.st_dev == est.st_dev && nst.st_ino == est.st_ino) {
-		addlinknode(ign, bslashquote(n, NULL, 0));
+		addlinknode(ign, quotestring(n, NULL, QT_BACKSLASH));
 		continue;
 	    }
 	    if (tpar && !strncmp((c = dupstring(n)), path, pl)) {
@@ -4360,7 +4359,7 @@
 		if (found || ((e = strrchr(c, '/')) && e > c + pl &&
 			      !ztat(c, &st, 0) && st.st_dev == nst.st_dev &&
 			      st.st_ino == nst.st_ino))
-		    addlinknode(ign, bslashquote(n, NULL, 0));
+		    addlinknode(ign, quotestring(n, NULL, QT_BACKSLASH));
 	    }
 	}
     }
Index: Src/Zle/zle_tricky.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_tricky.c,v
retrieving revision 1.76
diff -u -r1.76 zle_tricky.c
--- Src/Zle/zle_tricky.c	15 Sep 2006 15:38:39 -0000	1.76
+++ Src/Zle/zle_tricky.c	5 Oct 2006 21:02:15 -0000
@@ -402,15 +402,22 @@
 /**/
 mod_export Param keypm;
 
-/* 1 if we are completing in a quoted string (or inside `...`) */
+/*
+ * instring takes one of the QT_* values defined in zsh.h.
+ * It's never QT_TICK, instead we use inbackt.
+ * TODO: can we combine the two?
+ */
 
 /**/
 mod_export int instring, inbackt;
 
-/* Convenience macro for calling bslashquote() (formerly quotename()). *
- * This uses the instring variable above.                              */
+/*
+ * Convenience macro for calling quotestring (formerly bslashquote() (formerly
+ * quotename())).
+ * This uses the instring variable above.
+ */
 
-#define quotename(s, e) bslashquote(s, e, instring)
+#define quotename(s, e) quotestring(s, e, instring)
 
 /* Check if the given string is the name of a parameter and if this *
  * parameter is one worth expanding.                                */
@@ -891,7 +898,7 @@
 	zlemetaline[zlemetacs] == ';' || zlemetaline[zlemetacs] == '|' ||
 	zlemetaline[zlemetacs] == '&' ||
 	zlemetaline[zlemetacs] == '>' || zlemetaline[zlemetacs] == '<' ||
-	(instring && (zlemetaline[zlemetacs] == '"' ||
+	(instring != QT_NONE && (zlemetaline[zlemetacs] == '"' ||
 		      zlemetaline[zlemetacs] == '\'')) ||
 	(addspace = (comppref && !iblank(zlemetaline[zlemetacs])))) {
 	*ptmp = zlemetaline;
@@ -1032,7 +1039,18 @@
 get_comp_string(void)
 {
     int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins, ia, parct, varq = 0;
-    int ona = noaliases, qsub;
+    int ona = noaliases;
+    /*
+     * qsub fixes up the offset into the current completion word
+     * for changes made by the lexer.  That currently means the
+     * effect of RCQUOTES on embedded pairs of single quotes.
+     * zlemetacs_qsub takes account of the effect of this offset
+     * on the cursor position; it's only needed when using the
+     * word we got from the lexer, which we only do sometimes because
+     * otherwise it would be too easy.  If looking at zlemetaline we
+     * still use zlemetacs.
+     */
+    int qsub, zlemetacs_qsub = 0;
     char *s = NULL, *tmp, *p, *tt = NULL, rdop[20];
     char *linptr, *u;
 
@@ -1070,7 +1088,7 @@
 	    u++;
     }
     inbackt = (i & 1);
-    instring = 0;
+    instring = QT_NONE;
     addx(&tmp);
     linptr = zlemetaline;
     pushheap();
@@ -1235,9 +1253,11 @@
 	    clwords[i][--sl] = '\0';
 	/* If this is the word the cursor is in and we added a `x', *
 	 * remove it.                                               */
-	if (clwpos == i++ && addedx)
-	    chuck(&clwords[i - 1][((zlemetacs - wb - qsub) >= sl) ?
-				 (sl - 1) : (zlemetacs - wb - qsub)]);
+	if (clwpos == i++ && addedx) {
+	    zlemetacs_qsub = zlemetacs - qsub;
+	    chuck(&clwords[i - 1][((zlemetacs_qsub - wb) >= sl) ?
+				 (sl - 1) : (zlemetacs_qsub - wb)]);
+	}
     } while (tok != LEXERR && tok != ENDINPUT &&
 	     (tok != SEPER || (zleparse && !tt0)));
     /* Calculate the number of words stored in the clwords array. */
@@ -1299,7 +1319,8 @@
 	*s = sav;
         if (*s == '+')
             s++;
-	if (skipparens(Inbrack, Outbrack, &s) > 0 || s > tt + zlemetacs - wb) {
+	if (skipparens(Inbrack, Outbrack, &s) > 0 || s > tt +
+	    zlemetacs_qsub - wb) {
 	    s = NULL;
 	    inwhat = IN_MATH;
 	    if ((keypm = (Param) paramtab->getnode(paramtab, varname)) &&
@@ -1308,7 +1329,7 @@
 	    else
 		insubscr = 1;
 	} else if (*s == '=') {
-            if (zlemetacs > wb + (s - tt)) {
+            if (zlemetacs_qsub > wb + (s - tt)) {
                 s++;
                 wb += s - tt;
                 s = ztrdup(s);
@@ -1365,7 +1386,7 @@
 	    nnb = s + MB_METACHARLEN(s);
 	else
 	    nnb = s;
-	for (tt = s; tt < s + zlemetacs - wb;) {
+	for (tt = s; tt < s + zlemetacs_qsub - wb;) {
 	    if (*tt == Inbrack) {
 		i++;
 		nb = nnb;
@@ -1504,21 +1525,46 @@
                 level--;
         }
     }
-    if ((*s == Snull || *s == Dnull) && !has_real_token(s + 1)) {
-	char *q = (*s == Snull ? "'" : "\""), *n = tricat(qipre, q, "");
+    if ((*s == Snull || *s == Dnull ||
+	((*s == String || *s == Qstring) && s[1] == Snull))
+	&& !has_real_token(s + 1)) {
 	int sl = strlen(s);
+	char *q, *qtptr = s, *n;
+
+	switch (*s) {
+	case Snull:
+	    q = "'";
+	    instring = QT_SINGLE;
+	    break;
+
+	case Dnull:
+	    q = "\"";
+	    instring = QT_DOUBLE;
+	    break;
+
+	default:
+	    q = "$'";
+	    instring = QT_DOLLARS;
+	    qtptr++;
+	    sl--;
+	    break;
+	}
 
-	instring = (*s == Snull ? 1 : 2);
+	n = tricat(qipre, q, "");
 	zsfree(qipre);
 	qipre = n;
-	if (sl > 1 && s[sl - 1] == *s) {
+	if (sl > 1 && qtptr[sl - 1] == *qtptr) {
 	    n = tricat(q, qisuf, "");
 	    zsfree(qisuf);
 	    qisuf = n;
 	}
 	autoq = ztrdup(q);
 
-        if (instring == 2) {
+	/*
+	 * \! in double quotes is extracted by the history code before normal
+	 * parsing, so sanitize it here, too.
+	 */
+        if (instring == QT_DOUBLE) {
             for (q = s; *q; q++)
                 if (*q == '\\' && q[1] == '!')
                     *q = Bnull;
@@ -1651,11 +1697,11 @@
 
 			new->next = NULL;
 			new->str = dupstrpfx(bbeg, len);
-			new->str = ztrdup(bslashquote(new->str, NULL, instring));
+			new->str = ztrdup(quotename(new->str, NULL));
 			untokenize(new->str);
 			new->pos = begi;
 			*dbeg = '\0';
-			new->qpos = strlen(bslashquote(predup, NULL, instring));
+			new->qpos = strlen(quotename(predup, NULL));
 			*dbeg = '{';
 			i -= len;
 			boffs -= len;
@@ -1700,11 +1746,11 @@
 			lastbrbeg = new;
 
 			new->str = dupstrpfx(bbeg, len);
-			new->str = ztrdup(bslashquote(new->str, NULL, instring));
+			new->str = ztrdup(quotename(new->str, NULL));
 			untokenize(new->str);
 			new->pos = begi;
 			*dbeg = '\0';
-			new->qpos = strlen(bslashquote(predup, NULL, instring));
+			new->qpos = strlen(quotename(predup, NULL));
 			*dbeg = '{';
 			i -= len;
 			boffs -= len;
@@ -1737,7 +1783,7 @@
 		    brend = new;
 
 		    new->str = dupstrpfx(bbeg, len);
-		    new->str = ztrdup(bslashquote(new->str, NULL, instring));
+		    new->str = ztrdup(quotename(new->str, NULL));
 		    untokenize(new->str);
 		    new->pos = dp - predup - len + 1;
 		    new->qpos = len;
@@ -1766,11 +1812,11 @@
 		lastbrbeg = new;
 
 		new->str = dupstrpfx(bbeg, len);
-		new->str = ztrdup(bslashquote(new->str, NULL, instring));
+		new->str = ztrdup(quotename(new->str, NULL));
 		untokenize(new->str);
 		new->pos = begi;
 		*dbeg = '\0';
-		new->qpos = strlen(bslashquote(predup, NULL, instring));
+		new->qpos = strlen(quotename(predup, NULL));
 		*dbeg = '{';
 		boffs -= len;
 		strcpy(dbeg, dbeg + len);
@@ -1785,7 +1831,7 @@
 		    p = bp->pos;
 		    l = bp->qpos;
 		    bp->pos = strlen(predup + p + l);
-		    bp->qpos = strlen(bslashquote(predup + p + l, NULL, instring));
+		    bp->qpos = strlen(quotename(predup + p + l, NULL));
 		    strcpy(predup + p, predup + p + l);
 		}
 	    }

-- 
Peter Stephenson <p.w.stephenson@xxxxxxxxxxxx>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/



Messages sorted by: Reverse Date, Date, Thread, Author