about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Completion/Core/_path_files29
-rw-r--r--Doc/Zsh/compwid.yo11
-rw-r--r--Doc/Zsh/mod_computil.yo17
-rw-r--r--Src/Zle/comp.h9
-rw-r--r--Src/Zle/compcore.c171
-rw-r--r--Src/Zle/compctl.c67
-rw-r--r--Src/Zle/complete.c13
-rw-r--r--Src/Zle/compmatch.c14
-rw-r--r--Src/Zle/compresult.c15
-rw-r--r--Src/Zle/computil.c51
-rw-r--r--Src/Zle/computil.mdd2
-rw-r--r--Src/Zle/zle_tricky.c23
-rw-r--r--Src/params.c6
13 files changed, 312 insertions, 116 deletions
diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files
index 077c2608b..019fafbcb 100644
--- a/Completion/Core/_path_files
+++ b/Completion/Core/_path_files
@@ -233,7 +233,9 @@ for prepath in "$prepaths[@]"; do
  	if [[ "$tmp2[1]" = */* ]]; then
 	  tmp2=( "${(@)tmp2#${prepath}${realpath}}" )
 	  if [[ "$tmp2[1]" = */* ]]; then
-	    exppaths=( "$exppaths[@]" ${^tmp2:h:q}/${tpre}${tsuf} )
+	    tmp2=( "${(@)tmp2:h}" )
+	    compquote tmp2
+	    exppaths=( "$exppaths[@]" ${^tmp2}/${tpre}${tsuf} )
           else
 	    exppaths=( "$exppaths[@]" ${tpre}${tsuf} )
 	  fi
@@ -329,36 +331,39 @@ for prepath in "$prepaths[@]"; do
 	SUFFIX="${tsuf}"
       fi
 
+tmp4="$testpath"
+compquote tmp1 tmp4
+
       if [[ -n $menu || "$compconfig[path_expand]" != *suffix* ]]; then
         [[ -n "$compconfig[path_cursor]" ]] && compstate[to_end]=''
         if [[ "$tmp3" = */* ]]; then
-	  compadd -Qf -p "$linepath${testpath:q}" -s "/${tmp3#*/}" \
+	  compadd -Qf -p "$linepath$tmp4" -s "/${tmp3#*/}" \
 	          -W "$prepath$realpath$testpath" "$ignore[@]" \
 		  "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \
                   -M "r:|/=* r:|=* $match" "$group[@]" "$expl[@]" \
-		  - "${(@)${(@)tmp1%%/*}:q}"
+		  - "${(@)tmp1%%/*}"
 	else
-	  compadd -Qf -p "$linepath${testpath:q}" \
+	  compadd -Qf -p "$linepath$tmp4" \
 	          -W "$prepath$realpath$testpath" "$ignore[@]" \
 		   "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \
                    -M "r:|/=* r:|=* $match" "$group[@]" "$expl[@]" \
-		   - "${(@)tmp1:q}"
+		   - "$tmp1[@]"
 	fi
       else
         if [[ "$tmp3" = */* ]]; then
           for i in "$tmp1[@]"; do
-	    compadd -Qf -p "$linepath${testpath:q}" -s "/${${i#*/}:q}" \
+	    compadd -Qf -p "$linepath$tmp4" -s "/${i#*/}" \
 		    -W "$prepath$realpath$testpath" "$ignore[@]" \
 		    "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \
                     -M "r:|/=* r:|=* $match" "$group[@]" "$expl[@]" \
-		    - "${${i%%/*}:q}"
+		    - "${i%%/*}"
 	  done
         else
-	  compadd -Qf -p "$linepath${testpath:q}" \
+	  compadd -Qf -p "$linepath$tmp4" \
 		  -W "$prepath$realpath$testpath" "$ignore[@]" \
 		  "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \
                   -M "r:|/=* r:|=* $match" "$group[@]" "$expl[@]" \
-		  - "${(@)tmp1:q}"
+		  - "$tmp1[@]"
         fi
       fi
       tmp4=-
@@ -397,11 +402,13 @@ for prepath in "$prepaths[@]"; do
   if [[ -z "$tmp4" ]]; then
     PREFIX="${opre}${osuf}"
     SUFFIX=""
-    compadd -Qf -p "$linepath${testpath:q}" \
+    tmp4="$testpath"
+    compquote tmp4 tmp1
+    compadd -Qf -p "$linepath$tmp4" \
 	    -W "$prepath$realpath$testpath" "$ignore[@]" \
 	    "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \
             -M "r:|/=* r:|=* $match" "$group[@]" "$expl[@]" \
-	    - "${(@)tmp1:q}"
+	    - "$tmp1[@]"
   fi
 done
 
diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo
index fc2285e40..b5ebb4f8e 100644
--- a/Doc/Zsh/compwid.yo
+++ b/Doc/Zsh/compwid.yo
@@ -184,6 +184,17 @@ When completing inside quotes, this contains the quotation character
 (i.e. either a single quote, a double quote, or a backtick).  Otherwise it
 is unset.
 )
+item(tt(all_quotes))(
+The tt(-q) option of the tt(compset) builtin command (see below)
+allows breaking a quoted string into separate words and completing one 
+of these words. This key allows to test which types of quoted strings
+are currently broken into parts this way. 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 innermost quoting
+level.
+)
 item(tt(nmatches))(
 The number of matches generated and accepted by the completion code so
 far, excluding those matches that are only accepted by ignoring the
diff --git a/Doc/Zsh/mod_computil.yo b/Doc/Zsh/mod_computil.yo
index cc0abc38e..541f81605 100644
--- a/Doc/Zsh/mod_computil.yo
+++ b/Doc/Zsh/mod_computil.yo
@@ -1,16 +1,29 @@
 texinode(The computil Module)(The deltochar Module)(The complist Module)(Zsh Modules)
 sect(The computil Module)
 cindex(completion, utility)
-The tt(computil) module adds four builtin commands that are used by
+The tt(computil) module adds several builtin commands that are used by
 some of the completion functions in the shell function based
 completions system (see
 ifzman(zmanref(zshcompsys))\
 ifnzman(noderef(Completion System))
-). Except for tt(compdisplay) these builtin commands are very
+). Except for tt(compquote) and tt(compdisplay) these builtin
+commands are very
 specialised and thus not very interesting when writing your own
 completion functions. In short, these builtin commands are:
 
 startitem()
+item(tt(compquote) var(names) ...)(
+There may be reasons to write completion functions that have to add
+the matches using the tt(-Q) option to tt(compadd) and do the quoting
+themselves. Instead of interpreting the first character of the
+tt(all_quotes) key of the tt(compstate) special association and using
+the tt(q) flag for parameter expansions, one can use this builtin
+command. The arguements are the names of scalar or array parameters
+and the values of these parameters are quoted as needed for the
+innermost quoting level.
+
+The return value is non-zero in case of an error and zero otherwise.
+)
 item(tt(compdisplay) var(name) var(string) var(defs) ...)(
 The var(defs) are strings which should be of the form
 `var(str)tt(:)var(descr)' (the intended use is that the var(descr)
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 59701608e..6f3b2cd16 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -95,7 +95,7 @@ struct cmatch {
     char *pre;			/* prefix string from -P */
     char *suf;			/* suffix string from -S */
     char *disp;			/* string to display (compadd -d) */
-    char autoq;			/* closing quote to add automatically */
+    char *autoq;		/* closing quote to add automatically */
     int flags;			/* see CMF_* below */
     int *brpl;			/* places where to put the brace prefixes */
     int *brsl;			/* ...and the suffixes */
@@ -115,6 +115,7 @@ struct cmatch {
 #define CMF_NOLIST    32	/* should not be listed */
 #define CMF_DISPLINE  64	/* display strings one per line */
 #define CMF_HIDE     128	/* temporarily hide this one */
+#define CMF_NOSPACE  256	/* don't add a space */
 
 /* Stuff for completion matcher control. */
 
@@ -353,9 +354,11 @@ typedef void (*CLPrintFunc)(Cmgroup, Cmatch *, int, int, int, int,
 #define CP_ANMATCHES   (1 << CPN_ANMATCHES)
 #define CPN_LISTLINES  26
 #define CP_LISTLINES   (1 << CPN_LISTLINES)
+#define CPN_QUOTES     27
+#define CP_QUOTES      (1 << CPN_QUOTES)
 
-#define CP_KEYPARAMS   27
-#define CP_ALLKEYS     ((unsigned int) 0x7ffffff)
+#define CP_KEYPARAMS   28
+#define CP_ALLKEYS     ((unsigned int) 0xfffffff)
 
 /* Hooks. */
 
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index b6e957e99..036da0c2f 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -101,8 +101,7 @@ int parq, eparq;
 
 /* We store the following prefixes/suffixes:                               *
  * ipre,ripre  -- the ignored prefix (quoted and unquoted)                 *
- * isuf        -- the ignored suffix                                       *
- * autoq       -- quotes to automatically insert                           */
+ * isuf        -- the ignored suffix                                       */
 
 /**/
 char *ipre, *ripre, *isuf;
@@ -246,10 +245,6 @@ int fromcomp;
 /**/
 int lastend;
 
-/* Convenience macro for calling bslashquote() (formerly quotename()). */
-
-#define quotename(s, e) bslashquote(s, e, instring)
-
 #define inststr(X) inststrlen((X),1,-1)
 
 /* Main completion entry point, called from zle. */
@@ -270,6 +265,13 @@ do_completion(Hookdef dummy, Compldat dat)
 	ainfo = fainfo = NULL;
 	matchers = newlinklist();
 
+	zsfree(compqstack);
+	compqstack = ztrdup("\\");
+	if (instring == 2)
+	    compqstack[0] = '"';
+	else if (instring)
+	    compqstack[0] = '\'';
+
 	hasunqu = 0;
 	useline = (lst != COMP_LIST_COMPLETE);
 	useexact = isset(RECEXACT);
@@ -603,7 +605,7 @@ callcompfunc(char *s, char *fn)
 	zsfree(compprefix);
 	zsfree(compsuffix);
 	if (unset(COMPLETEINWORD)) {
-	    tmp = quotename(s, NULL);
+	    tmp = multiquote(s, 0);
 	    untokenize(tmp);
 	    compprefix = ztrdup(tmp);
 	    compsuffix = ztrdup("");
@@ -614,11 +616,11 @@ callcompfunc(char *s, char *fn)
 
 	    sav = *ss;
 	    *ss = '\0';
-	    tmp = quotename(s, NULL);
+	    tmp = multiquote(s, 0);
 	    untokenize(tmp);
 	    compprefix = ztrdup(tmp);
 	    *ss = sav;
-	    ss = quotename(ss, NULL);
+	    ss = multiquote(ss, 0);
 	    untokenize(ss);
 	    compsuffix = ztrdup(ss);
 	}
@@ -917,6 +919,40 @@ makecomplist(char *s, int incmd, int lst)
     return 1;
 }
 
+/**/
+char *
+multiquote(char *s, int ign)
+{
+    char *os = s, *p = compqstack;
+
+    if (p && *p && (ign < 1 || p[ign])) {
+	if (ign > 0)
+	    p += ign;
+	while (*p) {
+	    if (ign >= 0 || p[1])
+		s = bslashquote(s, NULL,
+				(*p == '\'' ? 1 : (*p == '"' ? 2 : 0)));
+	    p++;
+	}
+    }
+    return (s == os ? dupstring(s) : s);
+}
+
+/**/
+char *
+tildequote(char *s, int ign)
+{
+    int tilde;
+
+    if ((tilde = (*s == '~')))
+	*s = 'x';
+    s = multiquote(s, ign);
+    if (tilde)
+	*s = '~';
+
+    return s;
+}
+
 /* Check if we have to complete a parameter name. */
 
 /**/
@@ -1121,20 +1157,18 @@ comp_str(int *ipl, int *pl, int untok)
     return str;
 }
 
-/* This is for compset -q. */
-
 /**/
 int
 set_comp_sep(void)
 {
     int lip, lp;
-    char *s = comp_str(&lip, &lp, 0);
+    char *s = comp_str(&lip, &lp, 1);
     LinkList foo = newlinklist();
     LinkNode n;
     int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs;
-    int tl, got = 0, i = 0, cur = -1, oll = ll, sl;
+    int tl, got = 0, i = 0, cur = -1, oll = ll, sl, remq;
     int ois = instring, oib = inbackt, noffs = lip + lp;
-    char *tmp, *p, *ns, *ol = (char *) line, sav, oaq = autoq, *qp, *qs;
+    char *tmp, *p, *ns, *ol = (char *) line, sav, *qp, *qs, *ts, qc = '\0';
 
     if (compisuffix)
 	s = dyncat(s, compisuffix);
@@ -1154,7 +1188,8 @@ set_comp_sep(void)
     memcpy(tmp + 1, s, noffs);
     tmp[(scs = cs = 1 + noffs)] = 'x';
     strcpy(tmp + 2 + noffs, s + noffs);
-    tmp = rembslash(tmp);
+    if ((remq = (*compqstack == '\\')))
+	tmp = rembslash(tmp);
     inpush(dupstrspace(tmp), 0, NULL);
     line = (unsigned char *) tmp;
     ll = tl - 1;
@@ -1217,21 +1252,31 @@ set_comp_sep(void)
 		*p = '\'';
     }
     offs = owb;
+
+    untokenize(ts = dupstring(ns));
+
     if (*ns == Snull || *ns == Dnull) {
 	instring = (*ns == Snull ? 1 : 2);
 	inbackt = 0;
 	swb++;
 	if (ns[strlen(ns) - 1] == *ns && ns[1])
 	    swe--;
-	autoq = (*ns == Snull ? '\'' : '"');
+	zsfree(autoq);
+	autoq = ztrdup(compqstack[1] ? "" :
+		       multiquote(*ns == Snull ? "'" : "\"", 1));
+	qc = (*ns == Snull ? '\'' : '"');
+	ts++;
     } else {
 	instring = 0;
-	autoq = '\0';
+	zsfree(autoq);
+	autoq = NULL;
     }
     for (p = ns, i = swb; *p; p++, i++) {
 	if (INULL(*p)) {
-	    if (i < scs)
-		soffs--;
+	    if (i < scs) {
+		if (remq && *p == Bnull && p[1])
+		    swb -= 2;
+	    }
 	    if (p[1] || *p != Bnull) {
 		if (*p == Bnull) {
 		    if (scs == i + 1)
@@ -1247,17 +1292,28 @@ set_comp_sep(void)
 	    chuck(p--);
 	}
     }
+    ns = ts;
+
+    if (instring && strchr(compqstack, '\\')) {
+	int rl = strlen(ns), ql = strlen(multiquote(ns, !!compqstack[1]));
+
+	if (ql > rl)
+	    swb -= ql - rl;
+    }
     sav = s[(i = swb - 1)];
     s[i] = '\0';
-    qp = tricat(qipre, rembslash(s), "");
+    qp = rembslash(s);
     s[i] = sav;
     if (swe < swb)
 	swe = swb;
     swe--;
     sl = strlen(s);
-    if (swe > sl)
-	swe = sl, ns[swe - swb + 1] = '\0';
-    qs = tricat(rembslash(s + swe), qisuf, "");
+    if (swe > sl) {
+	swe = sl;
+	if (strlen(ns) > swe - swb + 1)
+	    ns[swe - swb + 1] = '\0';
+    }
+    qs = rembslash(s + swe);
     sl = strlen(ns);
     if (soffs > sl)
 	soffs = sl;
@@ -1265,6 +1321,11 @@ set_comp_sep(void)
     {
 	int set = CP_QUOTE | CP_QUOTING, unset = 0;
 
+	p = tricat((instring ? (instring == 1 ? "'" : "\"") : "\\"),
+		   compqstack, "");
+	zsfree(compqstack);
+	compqstack = p;
+
 	zsfree(compquote);
 	zsfree(compquoting);
 	if (instring == 2) {
@@ -1282,11 +1343,11 @@ set_comp_sep(void)
 	compquoting = ztrdup(compquoting);
 	comp_setunset(0, 0, set, unset);
 
+	zsfree(compprefix);
+	zsfree(compsuffix);
 	if (unset(COMPLETEINWORD)) {
 	    untokenize(ns);
-	    zsfree(compprefix);
 	    compprefix = ztrdup(ns);
-	    zsfree(compsuffix);
 	    compsuffix = ztrdup("");
 	} else {
 	    char *ss, sav;
@@ -1305,17 +1366,12 @@ set_comp_sep(void)
 	compiprefix = ztrdup("");
 	zsfree(compisuffix);
 	compisuffix = ztrdup("");
+	tmp = tricat(compqiprefix, "", multiquote(qp, 1));
 	zsfree(compqiprefix);
+	compqiprefix = tmp;
+	tmp = tricat(multiquote(qs, 1), "", compqisuffix);
 	zsfree(compqisuffix);
-	if (ois) {
-	    compqiprefix = qp;
-	    compqisuffix = qs;
-	} else {
-	    compqiprefix = ztrdup(quotename(qp, NULL));
-	    zsfree(qp);
-	    compqisuffix = ztrdup(quotename(qs, NULL));
-	    zsfree(qs);
-	}
+	compqisuffix = tmp;
 	freearray(compwords);
 	i = countlinknodes(foo);
 	compwords = (char **) zalloc((i + 1) * sizeof(char *));
@@ -1326,7 +1382,6 @@ set_comp_sep(void)
 	compcurrent = cur + 1;
 	compwords[i] = NULL;
     }
-    autoq = oaq;
     instring = ois;
     inbackt = oib;
 
@@ -1422,7 +1477,7 @@ int
 addmatches(Cadata dat, char **argv)
 {
     char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL;
-    char **aign = NULL, **dparr = NULL, oaq = autoq, *oppre = dat->ppre;
+    char **aign = NULL, **dparr = NULL, *oaq = autoq, *oppre = dat->ppre;
     char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL;
     int lpl, lsl, pl, sl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
     int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern;
@@ -1446,15 +1501,19 @@ addmatches(Cadata dat, char **argv)
 	if (qc == '`') {
 	    instring = 0;
 	    inbackt = 0;
-	    autoq = '\0';
+	    autoq = "";
 	} else {
+	    char buf[2];
+
 	    instring = (qc == '\'' ? 1 : 2);
 	    inbackt = 0;
-	    autoq = qc;
+	    buf[0] = qc;
+	    buf[1] = '\0';
+	    autoq = multiquote(buf, 1);
 	}
     } else {
 	instring = inbackt = 0;
-	autoq = '\0';
+	autoq = NULL;
     }
     qipre = ztrdup(compqiprefix ? compqiprefix : "");
     qisuf = ztrdup(compqisuffix ? compqisuffix : "");
@@ -1536,21 +1595,16 @@ addmatches(Cadata dat, char **argv)
 	    else if (lisuf)
 		dat->isuf = lisuf;
 	    if (dat->ppre) {
-		if (!(dat->aflags & CAF_QUOTE)) {
-		    dat->ppre = quotename(dat->ppre, NULL);
-		    if ((dat->flags & CMF_FILE) &&
-			dat->ppre[0] == '\\' && dat->ppre[1] == '~')
-			chuck(dat->ppre);
-		} else
-		    dat->ppre = dupstring(dat->ppre);
+		dat->ppre = ((dat->flags & CMF_FILE) ?
+			     tildequote(dat->ppre,
+					!!(dat->aflags & CAF_QUOTE)) :
+			     multiquote(dat->ppre,
+					!!(dat->aflags & CAF_QUOTE)));
 		lpl = strlen(dat->ppre);
 	    } else
 		lpl = 0;
 	    if (dat->psuf) {
-		if (!(dat->aflags & CAF_QUOTE))
-		    dat->psuf = quotename(dat->psuf, NULL);
-		else
-		    dat->psuf = dupstring(dat->psuf);
+		dat->psuf = multiquote(dat->psuf, !!(dat->aflags & CAF_QUOTE));
 		lsl = strlen(dat->psuf);
 	    } else
 		lsl = 0;
@@ -1653,6 +1707,11 @@ addmatches(Cadata dat, char **argv)
 		    dat->rems = NULL;
 		} else if (dat->rems)
 		    dat->rems = dupstring(dat->rems);
+
+		lpre = ((!(dat->aflags & CAF_QUOTE) &&
+			 (!dat->ppre && (dat->flags & CMF_FILE))) ?
+			tildequote(lpre, 1) : multiquote(lpre, 1));
+		lsuf = multiquote(lsuf, 1);
 	    }
 	    /* Walk through the matches given. */
 	    obpl = bpl;
@@ -1687,12 +1746,12 @@ addmatches(Cadata dat, char **argv)
 		    if (dat->aflags & CAF_QUOTE)
 			ms = dupstring(s);
 		    else
-			sl = strlen(ms = quotename(s, NULL));
+			sl = strlen(ms = multiquote(s, 0));
 		    lc = bld_parts(ms, sl, -1, NULL);
 		    isexact = 0;
 		} else if (!(ms = comp_match(lpre, lsuf, s, cp, &lc,
 					     (!(dat->aflags & CAF_QUOTE) ?
-					      ((dat->ppre && dat->ppre) ||
+					      (dat->ppre ||
 					       !(dat->flags & CMF_FILE) ? 1 : 2) : 0),
 					     &bpl, bcp, &bsl, bcs,
 					     &isexact))) {
@@ -1994,6 +2053,9 @@ add_match_data(int alt, char *str, Cline line,
     cm->pre = pre;
     cm->suf = suf;
     cm->flags = flags;
+    if (*compqstack == '\\' ||
+	(autoq && *compqstack && compqstack[1] == '\\'))
+	cm->flags |= CMF_NOSPACE;
     if (nbrbeg) {
 	int *p;
 	Brinfo bp;
@@ -2016,7 +2078,7 @@ add_match_data(int alt, char *str, Cline line,
 	cm->brsl = NULL;
     cm->qipl = qipl;
     cm->qisl = qisl;
-    cm->autoq = (autoq ? autoq : (inbackt ? '`' : '\0'));
+    cm->autoq = dupstring(autoq ? autoq : (inbackt ? "`" : NULL));
     cm->rems = cm->remf = cm->disp = NULL;
 
     if ((lastprebr || lastpostbr) && !hasbrpsfx(cm, lastprebr, lastpostbr))
@@ -2348,7 +2410,7 @@ dupmatch(Cmatch m, int nbeg, int nend)
 	r->brsl = NULL;
     r->rems = ztrdup(m->rems);
     r->remf = ztrdup(m->remf);
-    r->autoq = m->autoq;
+    r->autoq = ztrdup(m->autoq);
     r->qipl = m->qipl;
     r->qisl = m->qisl;
     r->disp = dupstring(m->disp);
@@ -2489,6 +2551,7 @@ freematch(Cmatch m, int nbeg, int nend)
     zsfree(m->rems);
     zsfree(m->remf);
     zsfree(m->disp);
+    zsfree(m->autoq);
     zfree(m->brpl, nbeg * sizeof(int));
     zfree(m->brsl, nend * sizeof(int));
 
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index d00e317c2..cab0f55a4 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1811,10 +1811,12 @@ addmatch(char *s, char *t)
 		    isalt = 1;
 	}
 	ms = ((addwhat == CC_FILES || addwhat == -6 ||
-	       addwhat == -5 || addwhat == -8) ? 
-	      comp_match(qfpre, qfsuf, s, filecomp, &lc, (ppre && *ppre ? 1 : 2),
+	       addwhat == -5 || addwhat == -8) ?
+	      comp_match(tildequote(qfpre, 1), multiquote(qfsuf, 1),
+			 s, filecomp, &lc, (ppre && *ppre ? 1 : 2),
 			 &bpl, ppl ,&bsl, psl, &isexact) :
-	      comp_match(fpre, fsuf, s, filecomp, &lc, 0,
+	      comp_match(multiquote(fpre, 1), multiquote(fsuf, 1),
+			 s, filecomp, &lc, 0,
 			 &bpl, ppl, &bsl, psl, &isexact));
 	if (!ms)
 	    return;
@@ -1855,6 +1857,8 @@ addmatch(char *s, char *t)
 	    p1 = qlpre; s1 = qlsuf;
 	    p2 = lpre;  s2 = lsuf;
 	}
+	p1 = multiquote(p1, 1); s1 = multiquote(s1, 1);
+	p2 = multiquote(p2, 1); s2 = multiquote(s2, 1);
 	bpt = bpl;
 	bst = bsl;
 
@@ -2128,21 +2132,24 @@ makecomplistctl(int flags)
 	    char *str = comp_str(&lip, &lp, 0), *t;
 	    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 *oisuf = isuf, *oqp = qipre, *oqs = qisuf, *oaq = autoq;
+	    char buf[2];
 
 	    if (compquote && (qc = *compquote)) {
 		if (qc == '`') {
 		    instring = 0;
 		    inbackt = 0;
-		    autoq = '\0';
+		    autoq = "";
 		} else {
+		    buf[0] = qc;
+		    buf[1] = '\0';
 		    instring = (qc == '\'' ? 1 : 2);
 		    inbackt = 0;
-		    autoq = qc;
+		    autoq = buf;
 		}
 	    } else {
 		instring = inbackt = 0;
-		autoq = '\0';
+		autoq = "";
 	    }
 	    qipre = ztrdup(compqiprefix ? compqiprefix : "");
 	    qisuf = ztrdup(compqisuffix ? compqisuffix : "");
@@ -2593,9 +2600,10 @@ sep_comp_string(char *ss, char *s, int noffs)
     LinkList foo = newlinklist();
     LinkNode n;
     int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs;
-    int sl = strlen(ss), tl, got = 0, i = 0, cur = -1, oll = ll;
+    int sl = strlen(ss), tl, got = 0, i = 0, cur = -1, oll = ll, remq;
     int ois = instring, oib = inbackt;
-    char *tmp, *p, *ns, *ol = (char *) line, sav, oaq = autoq, *qp, *qs;
+    char *tmp, *p, *ns, *ol = (char *) line, sav, *oaq = autoq, *qp, *qs;
+    char *ts, qc = '\0';
 
     swb = swe = soffs = 0;
     ns = NULL;
@@ -2612,6 +2620,8 @@ sep_comp_string(char *ss, char *s, int noffs)
     memcpy(tmp + sl + 1, s, noffs);
     tmp[(scs = cs = sl + 1 + noffs)] = 'x';
     strcpy(tmp + sl + 2 + noffs, s + noffs);
+    if ((remq = (*compqstack == '\\')))
+	tmp = rembslash(tmp);
     inpush(dupstrspace(tmp), 0, NULL);
     line = (unsigned char *) tmp;
     ll = tl - 1;
@@ -2674,21 +2684,29 @@ sep_comp_string(char *ss, char *s, int noffs)
 		*p = '\'';
     }
     offs = owb;
+
+    untokenize(ts = dupstring(ns));
+
     if (*ns == Snull || *ns == Dnull) {
 	instring = (*ns == Snull ? 1 : 2);
 	inbackt = 0;
 	swb++;
 	if (ns[strlen(ns) - 1] == *ns && ns[1])
 	    swe--;
-	autoq = (*ns == Snull ? '\'' : '"');
+	autoq = compqstack[1] ? "" : multiquote(*ns == Snull ? "'" : "\"", 1);
+	qc = (*ns == Snull ? '\'' : '"');
+	ts++;
     } else {
 	instring = 0;
-	autoq = '\0';
+	autoq = "";
     }
     for (p = ns, i = swb; *p; p++, i++) {
 	if (INULL(*p)) {
-	    if (i < scs)
+	    if (i < scs) {
 		soffs--;
+		if (remq && *p == Bnull && p[1])
+		    swb -= 2;
+	    }
 	    if (p[1] || *p != Bnull) {
 		if (*p == Bnull) {
 		    if (scs == i + 1)
@@ -2704,27 +2722,42 @@ sep_comp_string(char *ss, char *s, int noffs)
 	    chuck(p--);
 	}
     }
+    ns = ts;
+
+    if (instring && strchr(compqstack, '\\')) {
+	int rl = strlen(ns), ql = strlen(multiquote(ns, !!compqstack[1]));
+
+	if (ql > rl)
+	    swb -= ql - rl;
+    }
     sav = s[(i = swb - sl - 1)];
     s[i] = '\0';
-    qp = tricat(qipre, s, "");
+    qp = tricat(qipre, multiquote(s, 0), "");
     s[i] = sav;
     if (swe < swb)
 	swe = swb;
     swe -= sl + 1;
     sl = strlen(s);
-    if (swe > sl)
-	swe = sl, ns[swe - swb + 1] = '\0';
-    qs = tricat(s + swe, qisuf, "");
+    if (swe > sl) {
+	swe = sl;
+	if (strlen(ns) > swe - swb + 1)
+	    ns[swe - swb + 1] = '\0';
+    }
+    qs = tricat(multiquote(s + swe, 0), qisuf, "");
     sl = strlen(ns);
     if (soffs > sl)
 	soffs = sl;
 
     {
 	char **ow = clwords, *os = cmdstr, *oqp = qipre, *oqs = qisuf;
+	char *oqst = compqstack;
 	int olws = clwsize, olwn = clwnum, olwp = clwpos;
 	int obr = brange, oer = erange, oof = offs;
 	unsigned long occ = ccont;
 
+	compqstack = tricat((instring ? (instring == 1 ? "'" : "\"") : "\\"),
+			    compqstack, "");
+
 	clwsize = clwnum = countlinknodes(foo);
 	clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
 	for (n = firstnode(foo), i = 0; n; incnode(n), i++) {
@@ -2756,6 +2789,8 @@ sep_comp_string(char *ss, char *s, int noffs)
 	qipre = oqp;
 	zsfree(qisuf);
 	qisuf = oqs;
+	zsfree(compqstack);
+	compqstack = oqst;
     }
     autoq = oaq;
     instring = ois;
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index b4d32a34c..bf558f352 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -61,6 +61,7 @@ char **compwords,
      *compredirect,
      *compquote,
      *compquoting,
+     *compqstack,
      *comprestore,
      *complist,
      *compforcelist,
@@ -962,6 +963,7 @@ static struct compparam compkparams[] = {
     { "vared", PM_SCALAR, VAL(compvared), NULL, NULL },
     { "alternate_nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_anmatches) },
     { "list_lines", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_listlines) },
+    { "all_quotes", PM_SCALAR | PM_READONLY, VAL(compqstack), NULL, NULL },
     { NULL, 0, NULL, NULL, NULL }
 };
 
@@ -1169,7 +1171,7 @@ comp_wrapper(List list, FuncWrap w, char *name)
 	return 1;
     else {
 	char *orest, *opre, *osuf, *oipre, *oisuf, **owords;
-	char *oqipre, *oqisuf, *oq, *oqi;
+	char *oqipre, *oqisuf, *oq, *oqi, *oqs, *oaq;
 	zlong ocur;
 	unsigned int runset = 0, kunset = 0, m, sm;
 	Param *pp;
@@ -1193,6 +1195,8 @@ comp_wrapper(List list, FuncWrap w, char *name)
 	oqisuf = dupstring(compqisuffix);
 	oq = dupstring(compquote);
 	oqi = dupstring(compquoting);
+	oqs = dupstring(compqstack);
+	oaq = dupstring(autoq);
 
 	HEAPALLOC {
 	    owords = arrdup(compwords);
@@ -1218,6 +1222,10 @@ comp_wrapper(List list, FuncWrap w, char *name)
 	    compquote = ztrdup(oq);
 	    zsfree(compquoting);
 	    compquoting = ztrdup(oqi);
+	    zsfree(compqstack);
+	    compqstack = ztrdup(oqs);
+	    zsfree(autoq);
+	    autoq = ztrdup(oaq);
 	    freearray(compwords);
 	    PERMALLOC {
 		compwords = arrdup(owords);
@@ -1354,7 +1362,7 @@ setup_complete(Module m)
 	compquoting = comprestore = complist = compinsert =
 	compexact = compexactstr = comppatmatch = comppatinsert =
 	compforcelist = complastprompt = comptoend = 
-	compoldlist = compoldins = compvared = NULL;
+	compoldlist = compoldins = compvared = compqstack = NULL;
 
     hascompmod = 1;
 
@@ -1418,6 +1426,7 @@ finish_complete(Module m)
     zsfree(compparameter);
     zsfree(compredirect);
     zsfree(compquote);
+    zsfree(compqstack);
     zsfree(compquoting);
     zsfree(comprestore);
     zsfree(complist);
diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c
index 344aef161..c3b4c9eae 100644
--- a/Src/Zle/compmatch.c
+++ b/Src/Zle/compmatch.c
@@ -33,10 +33,6 @@
 #undef GLOBAL_PROTOTYPES
 #include "compmatch.pro"
 
-/* Convenience macro for calling bslashquote() (formerly quotename()). */
-
-#define quotename(s, e) bslashquote(s, e, instring)
-
 /* This compares two cpattern lists and returns non-zero if they are
  * equal. */
 
@@ -853,9 +849,8 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu,
 	if (!pattry(cp, r))
 	    return NULL;
     
-	r = (qu ? quotename(r, NULL) : dupstring(r));
-	if (qu == 2 && r[0] == '\\' && r[1] == '~')
-	    chuck(r);
+	r = (qu == 2 ? tildequote(r, 0) : multiquote(r, !qu));
+
 	/* We still break it into parts here, trying to build a sensible
 	 * cline list for these matches, too. */
 	w = dupstring(w);
@@ -866,10 +861,7 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu,
 	Cline pli, plil;
 	int mpl, rpl, wl;
 
-	w = (qu ? quotename(w, NULL) : dupstring(w));
-	if (qu == 2 && w[0] == '\\' && w[1] == '~')
-	    chuck(w);
-
+	w = (qu == 2 ? tildequote(w, 0) : multiquote(w, !qu));
 	wl = strlen(w);
 
 	/* Always try to match the prefix. */
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
index 5b807684e..b3c8bdd4f 100644
--- a/Src/Zle/compresult.c
+++ b/Src/Zle/compresult.c
@@ -33,11 +33,6 @@
 #undef GLOBAL_PROTOTYPES
 #include "compresult.pro"
 
-/* Convenience macro for calling bslashquote() (formerly quotename()). *
- * This uses the instring variable above.                              */
-
-#define quotename(s, e) bslashquote(s, e, instring)
-
 #define inststr(X) inststrlen((X),1,-1)
 
 /* This cuts the cline list before the stuff that isn't worth
@@ -857,11 +852,13 @@ do_single(Cmatch m)
 	/* If we didn't add a suffix, add a space, unless we are *
 	 * doing menu completion or we are completing files and  *
 	 * the string doesn't name an existing file.             */
-	if (m->autoq && (!m->isuf || m->isuf[0] != m->autoq)) {
-	    inststrlen(&(m->autoq), 1, 1);
-	    minfo.insc++;
+	if (m->autoq && (!m->isuf || !strpfx(m->autoq, m->isuf))) {
+	    int al = strlen(m->autoq);
+	    inststrlen(m->autoq, 1, al);
+	    minfo.insc += al;
 	}
-	if (!menucmp && (usemenu != 3 || insspace)) {
+	if (!menucmp && !(m->flags & CMF_NOSPACE) &&
+	    (usemenu != 3 || insspace)) {
 	    inststrlen(" ", 1, 1);
 	    minfo.insc++;
 	    if (minfo.we)
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 0f8188762..22a8f7656 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -1887,11 +1887,62 @@ bin_compvalues(char *nam, char **args, char *ops, int func)
 }
 
 
+static int
+bin_compquote(char *nam, char **args, char *ops, int func)
+{
+    char *name;
+    Value v;
+
+    if (!compqstack || !*compqstack)
+	return 0;
+
+    while ((name = *args++)) {
+	name = dupstring(name);
+	if ((v = getvalue(&name, 0))) {
+	    switch (PM_TYPE(v->pm->flags)) {
+	    case PM_SCALAR:
+		{
+		    char *val = getstrvalue(v);
+
+		    val = bslashquote(val, NULL,
+				      (*compqstack == '\'' ? 1 :
+				       (*compqstack == '"' ? 2 : 0)));
+
+		    setstrvalue(v, ztrdup(val));
+		}
+		break;
+	    case PM_ARRAY:
+		{
+		    char **val = v->pm->gets.afn(v->pm);
+		    char **new = (char **) zalloc((arrlen(val) + 1) *
+						  sizeof(char *));
+		    char **p = new;
+
+		    for (; *val; val++, p++)
+			*p = ztrdup(bslashquote(*val, NULL,
+						(*compqstack == '\'' ? 1 :
+						 (*compqstack == '"' ? 2 :
+						  0))));
+		    *p = NULL;
+
+		    setarrvalue(v, new);
+		}
+		break;
+	    default:
+		zwarnnam(nam, "invalid parameter type: %s", args[-1], 0);
+	    }
+	} else
+	    zwarnnam(nam, "unknown parameter: %s", args[-1], 0);
+    }
+    return 0;
+}
+
 static struct builtin bintab[] = {
     BUILTIN("compdisplay", 0, bin_compdisplay, 2, -1, 0, NULL, NULL),
     BUILTIN("compdescribe", 0, bin_compdescribe, 3, -1, 0, NULL, NULL),
     BUILTIN("comparguments", 0, bin_comparguments, 1, -1, 0, NULL, NULL),
     BUILTIN("compvalues", 0, bin_compvalues, 1, -1, 0, NULL, NULL),
+    BUILTIN("compquote", 0, bin_compquote, 1, -1, 0, NULL, NULL),
 };
 
 
diff --git a/Src/Zle/computil.mdd b/Src/Zle/computil.mdd
index 56f633414..4789280a9 100644
--- a/Src/Zle/computil.mdd
+++ b/Src/Zle/computil.mdd
@@ -2,4 +2,4 @@ moddeps="complete"
 
 objects="computil.o"
 
-autobins="compdisplay compdescribe comparguments compvalues"
+autobins="compdisplay compdescribe comparguments compvalues compquote"
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 6a17c46d0..04b841411 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -112,7 +112,7 @@ static char *qword;
  * closing quote. */
 
 /**/
-char *qipre, *qisuf, autoq;
+char *qipre, *qisuf, *autoq;
 
 /* This contains the name of the function to call if this is for a new  *
  * style completion. */
@@ -547,7 +547,8 @@ docomplete(int lst)
     qipre = ztrdup("");
     zsfree(qisuf);
     qisuf = ztrdup("");
-    autoq = '\0';
+    zsfree(autoq);
+    autoq = NULL;
     /* Get the word to complete. */
     noerrs = 1;
     s = get_comp_string();
@@ -882,6 +883,20 @@ dupbrinfo(Brinfo p, Brinfo *last)
     return ret;
 }
 
+/* This is a bit like has_token(), but ignores nulls. */
+
+static int
+has_real_token(const char *s)
+{
+    while (*s) {
+	if (itok(*s) && !INULL(*s))
+	    return 1;
+	s++;
+    }
+    return 0;
+}
+
+
 /* Lasciate ogni speranza.                                                  *
  * This function is a nightmare.  It works, but I'm sure that nobody really *
  * understands why.  The problem is: to make it cleaner we would need       *
@@ -1264,7 +1279,7 @@ get_comp_string(void)
 		else if (*p == Snull)
 		    *p = '\'';
 	}
-	if ((*s == Snull || *s == Dnull) && !has_token(s + 1)) {
+	if ((*s == Snull || *s == Dnull) && !has_real_token(s + 1)) {
 	    char *q = (*s == Snull ? "'" : "\""), *n = tricat(qipre, q, "");
 	    int sl = strlen(s);
 
@@ -1276,7 +1291,7 @@ get_comp_string(void)
 		zsfree(qisuf);
 		qisuf = n;
 	    }
-	    autoq = *q;
+	    autoq = ztrdup(q);
 	}
 	/* While building the quoted form, we also clean up the command line. */
 	for (p = s, tt = qword, i = wb; *p; p++, tt++, i++)
diff --git a/Src/params.c b/Src/params.c
index 4c4f89978..b41ef6b37 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -1417,7 +1417,7 @@ getnumvalue(Value v)
 }
 
 /**/
-static void
+void
 setstrvalue(Value v, char *val)
 {
     char buf[(sizeof(zlong) * 8) + 4];
@@ -1515,7 +1515,7 @@ setstrvalue(Value v, char *val)
 }
 
 /**/
-static void
+void
 setnumvalue(Value v, mnumber val)
 {
     char buf[DIGBUFSIZE], *p;
@@ -1552,7 +1552,7 @@ setnumvalue(Value v, mnumber val)
 }
 
 /**/
-static void
+void
 setarrvalue(Value v, char **val)
 {
     if (v->pm->flags & PM_READONLY) {