about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2005-03-21 18:58:28 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2005-03-21 18:58:28 +0000
commit2a59e22f73bb2ba0a96daf0e84b6da4fe48c1280 (patch)
tree956194a7343220bcf692c6a15efc81aeaeb177ac
parente222689ea14bcf037ef5085d85216c8bd2ddd3d0 (diff)
downloadzsh-2a59e22f73bb2ba0a96daf0e84b6da4fe48c1280.tar.gz
zsh-2a59e22f73bb2ba0a96daf0e84b6da4fe48c1280.tar.xz
zsh-2a59e22f73bb2ba0a96daf0e84b6da4fe48c1280.zip
21045: fix some uses of Meta characters in completion
-rw-r--r--ChangeLog5
-rw-r--r--Src/Zle/compcore.c840
-rw-r--r--Src/Zle/complete.c402
3 files changed, 891 insertions, 356 deletions
diff --git a/ChangeLog b/ChangeLog
index fb3be9e25..55901aa0d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2005-03-21  Peter Stephenson  <pws@csr.com>
+
+	* 21045: Src/Zle/compcore.c, Src/Zle/complete.c: more places
+	where completion didn't handled Meta characters.
+
 2005-03-16  Peter Stephenson  <pws@csr.com>
 
 	* 20983: Test/D02glob.ztst, Src/glob.c: fixed string segments
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index 48189ab31..084895ae1 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -39,16 +39,23 @@ static Widget lastcompwidget;
 /**/
 int useexact, useline, uselist, forcelist, startauto;
 
+/**/
+mod_export int iforcemenu;
+
 /* Non-zero if we should go back to the last prompt. */
 
 /**/
-int dolastprompt;
+mod_export int dolastprompt;
 
 /* Non-zero if we should keep an old list. */
 
 /**/
-mod_export
-int oldlist, oldins;
+mod_export int oldlist, oldins;
+
+/* Original prefix/suffix lengths. Flag saying if they changed. */
+
+/**/
+int origlpre, origlsuf, lenchanged;
 
 /* This is used to decide when the cursor should be moved to the end of    *
  * the inserted word: 0 - never, 1 - only when a single match is inserted, *
@@ -60,7 +67,12 @@ int movetoend;
 /* The match and group number to insert when starting menucompletion.   */
 
 /**/
-mod_export int insmnum, insgnum, insgroup, insspace;
+mod_export int insmnum, insspace;
+
+#if 0
+/* group-numbers in compstate[insert] */
+int insgnum, insgroup; /* mod_export */
+#endif
 
 /* Information about menucompletion. */
 
@@ -131,6 +143,11 @@ mod_export Cmgroup lastmatches, pmatches, amatches, lmatches, lastlmatches;
 /**/
 mod_export int hasoldlist, hasperm;
 
+/* Non-zero if we have a match representing all other matches. */
+
+/**/
+int hasallmatch;
+
 /* Non-zero if we have newly added matches. */
 
 /**/
@@ -148,6 +165,16 @@ mod_export int nmatches;
 /**/
 mod_export int smatches;
 
+/* != 0 if more than one match and at least two different matches */
+
+/**/
+mod_export int diffmatches;
+
+/* The number of messages. */
+
+/**/
+mod_export int nmessages;
+
 /* != 0 if only explanation strings should be printed */
 
 /**/
@@ -250,7 +277,7 @@ int fromcomp;
 /* This holds the end-position of the last string inserted into the line. */
 
 /**/
-int lastend;
+mod_export int lastend;
 
 #define inststr(X) inststrlen((X),1,-1)
 
@@ -258,9 +285,9 @@ int lastend;
 
 /**/
 int
-do_completion(Hookdef dummy, Compldat dat)
+do_completion(UNUSED(Hookdef dummy), Compldat dat)
 {
-    int ret = 0, lst = dat->lst, incmd = dat->incmd;
+    int ret = 0, lst = dat->lst, incmd = dat->incmd, osl = showinglist;
     char *s = dat->s;
     char *opm;
     LinkNode n;
@@ -278,7 +305,7 @@ do_completion(Hookdef dummy, Compldat dat)
 	compqstack[0] = '\'';
 
     hasunqu = 0;
-    useline = (lst != COMP_LIST_COMPLETE);
+    useline = (wouldinstab ? -1 : (lst != COMP_LIST_COMPLETE));
     useexact = isset(RECEXACT);
     zsfree(compexactstr);
     compexactstr = ztrdup("");
@@ -307,6 +334,9 @@ do_completion(Hookdef dummy, Compldat dat)
     hasmatched = hasunmatched = 0;
     minmlen = 1000000;
     maxmlen = -1;
+    compignored = 0;
+    nmessages = 0;
+    hasallmatch = 0;
 
     /* Make sure we have the completion list and compctl. */
     if (makecomplist(s, incmd, lst)) {
@@ -318,6 +348,8 @@ do_completion(Hookdef dummy, Compldat dat)
 	clearlist = 1;
 	ret = 1;
 	minfo.cur = NULL;
+	if (useline < 0)
+	    ret = selfinsert(zlenoargs);
 	goto compend;
     }
     zsfree(lastprebr);
@@ -326,7 +358,13 @@ do_completion(Hookdef dummy, Compldat dat)
 
     if (comppatmatch && *comppatmatch && comppatmatch != opm)
 	haspattern = 1;
-    if (!useline && uselist) {
+    if (iforcemenu) {
+	if (nmatches)
+            do_ambig_menu();
+	ret = !nmatches;
+    } else if (useline < 0)
+	ret = selfinsert(zlenoargs);
+    else if (!useline && uselist) {
 	/* All this and the guy only wants to see the list, sigh. */
 	cs = 0;
 	foredel(ll);
@@ -334,41 +372,8 @@ do_completion(Hookdef dummy, Compldat dat)
 	cs = origcs;
 	showinglist = -2;
     } else if (useline == 2 && nmatches > 1) {
-	int first = 1, nm = nmatches;
-	Cmatch *mc;
-
-	menucmp = 1;
-	menuacc = 0;
-
-	for (minfo.group = amatches;
-	     minfo.group && !(minfo.group)->mcount;
-	     minfo.group = (minfo.group)->next);
+	do_allmatches(1);
 
-	mc = (minfo.group)->matches;
-
-	while (1) {
-	    if (!first)
-		accept_last();
-	    first = 0;
-
-	    if (!--nm)
-		menucmp = 0;
-
-	    do_single(*mc);
-	    minfo.cur = mc;
-
-	    if (!*++(minfo.cur)) {
-		do {
-		    if (!(minfo.group = (minfo.group)->next))
-			break;
-		} while (!(minfo.group)->mcount);
-		if (!minfo.group)
-		    break;
-		minfo.cur = minfo.group->matches;
-	    }
-	    mc = minfo.cur;
-	}
-	menucmp = 0;
 	minfo.cur = NULL;
 
 	if (forcelist)
@@ -377,10 +382,13 @@ do_completion(Hookdef dummy, Compldat dat)
 	    invalidatelist();
     } else if (useline) {
 	/* We have matches. */
-	if (nmatches > 1) {
+	if (nmatches > 1 && diffmatches) {
 	    /* There is more than one match. */
 	    ret = do_ambiguous();
-	} else if (nmatches == 1) {
+
+	    if (!showinglist && uselist && listshown && (usemenu == 2 || oldlist))
+		showinglist = osl;
+	} else if (nmatches == 1 || (nmatches > 1 && !diffmatches)) {
 	    /* Only one match. */
 	    Cmgroup m = amatches;
 
@@ -396,6 +404,11 @@ do_completion(Hookdef dummy, Compldat dat)
 		    clearlist = 1;
 	    } else
 		invalidatelist();
+	} else if (nmessages && forcelist) {
+	    if (uselist)
+		showinglist = -2;
+	    else
+		clearlist = 1;
 	}
     } else {
 	invalidatelist();
@@ -407,9 +420,10 @@ do_completion(Hookdef dummy, Compldat dat)
 	cs = origcs;
     }
     /* Print the explanation strings if needed. */
-    if (!showinglist && validlist && usemenu != 2 && nmatches != 1 &&
-	useline != 2 && (!oldlist || !listshown)) {
-	onlyexpl = 1;
+    if (!showinglist && validlist && usemenu != 2 && uselist &&
+	(nmatches != 1 || diffmatches) &&
+	useline >= 0 && useline != 2 && (!oldlist || !listshown)) {
+	onlyexpl = 3;
 	showinglist = -2;
     }
  compend:
@@ -430,7 +444,7 @@ static int oldmenucmp;
 
 /**/
 int
-before_complete(Hookdef dummy, int *lst)
+before_complete(UNUSED(Hookdef dummy), int *lst)
 {
     oldmenucmp = menucmp;
 
@@ -469,22 +483,40 @@ before_complete(Hookdef dummy, int *lst)
 
 /**/
 int
-after_complete(Hookdef dummy, Compldat dat)
+after_complete(UNUSED(Hookdef dummy), int *dat)
 {
     if (menucmp && !oldmenucmp) {
-	struct chdata dat;
-
-	dat.matches = amatches;
-	dat.num = nmatches;
-	dat.cur = NULL;
-	if (runhookdef(MENUSTARTHOOK, (void *) &dat))
+	struct chdata cdat;
+	int ret;
+
+	cdat.matches = amatches;
+	cdat.num = nmatches;
+	cdat.nmesg = nmessages;
+	cdat.cur = NULL;
+	if ((ret = runhookdef(MENUSTARTHOOK, (void *) &cdat))) {
+	    dat[1] = 0;
 	    menucmp = menuacc = 0;
+	    minfo.cur = NULL;
+	    if (ret >= 2) {
+		fixsuffix();
+		cs = 0;
+		foredel(ll);
+		inststr(origline);
+		cs = origcs;
+		if (ret == 2) {
+		    clearlist = 1;
+		    invalidatelist();
+		}
+	    }
+	}
     }
     return 0;
 }
 
 /* This calls the given completion widget function. */
 
+static int parwb, parwe, paroffs;
+
 /**/
 static void
 callcompfunc(char *s, char *fn)
@@ -521,6 +553,8 @@ callcompfunc(char *s, char *fn)
 	compparameter = compredirect = "";
 	if (ispar)
 	    compcontext = (ispar == 2 ? "brace_parameter" : "parameter");
+        else if (linwhat == IN_PAR)
+            compcontext = "assign_parameter";
 	else if (linwhat == IN_MATH) {
 	    if (insubscr) {
 		compcontext = "subscript";
@@ -584,7 +618,14 @@ callcompfunc(char *s, char *fn)
 		untokenize(*q = ztrdup(*p));
 	    *q = NULL;
 	} else
-	    compwords = (char **) zcalloc(sizeof(char *));
+	    compwords = (char **) zshcalloc(sizeof(char *));
+
+	if (compredirs)
+	    freearray(compredirs);
+        if (rdstrs)
+            compredirs = bld_list_array(rdstrs);
+        else
+            compredirs = (char **) zshcalloc(sizeof(char *));
 
 	compparameter = ztrdup(compparameter);
 	compredirect = ztrdup(compredirect);
@@ -610,7 +651,7 @@ callcompfunc(char *s, char *fn)
 	zsfree(compprefix);
 	zsfree(compsuffix);
 	if (unset(COMPLETEINWORD)) {
-	    tmp = multiquote(s, 0);
+	    tmp = (linwhat == IN_MATH ? dupstring(s) : multiquote(s, 0));
 	    untokenize(tmp);
 	    compprefix = ztrdup(tmp);
 	    compsuffix = ztrdup("");
@@ -621,22 +662,46 @@ callcompfunc(char *s, char *fn)
 
 	    sav = *ss;
 	    *ss = '\0';
-	    tmp = multiquote(s, 0);
+	    tmp = (linwhat == IN_MATH ? dupstring(s) : multiquote(s, 0));
 	    untokenize(tmp);
 	    compprefix = ztrdup(tmp);
 	    *ss = sav;
-	    ss = multiquote(ss, 0);
+	    ss = (linwhat == IN_MATH ? dupstring(ss) : multiquote(ss, 0));
 	    untokenize(ss);
 	    compsuffix = ztrdup(ss);
 	}
+        zsfree(complastprefix);
+        zsfree(complastsuffix);
+        complastprefix = ztrdup(compprefix);
+        complastsuffix = ztrdup(compsuffix);
 	zsfree(compiprefix);
-	compiprefix = ztrdup("");
 	zsfree(compisuffix);
-	compisuffix = ztrdup("");
+	if (parwb < 0) {
+	    compiprefix = ztrdup("");
+	    compisuffix = ztrdup("");
+	} else {
+	    int l;
+
+	    compiprefix = (char *) zalloc((l = wb - parwb) + 1);
+	    memcpy(compiprefix, line + parwb, l);
+	    compiprefix[l] = '\0';
+	    compisuffix = (char *) zalloc((l = parwe - we) + 1);
+	    memcpy(compisuffix, line + we, l);
+	    compisuffix[l] = '\0';
+
+	    wb = parwb;
+	    we = parwe;
+	    offs = paroffs;
+	}
 	zsfree(compqiprefix);
 	compqiprefix = ztrdup(qipre ? qipre : "");
 	zsfree(compqisuffix);
 	compqisuffix = ztrdup(qisuf ? qisuf : "");
+	origlpre = (strlen(compqiprefix) + strlen(compiprefix) +
+		    strlen(compprefix));
+	origlsuf = (strlen(compqisuffix) + strlen(compisuffix) +
+		    strlen(compsuffix));
+	lenchanged = 0;
 	compcurrent = (usea ? (clwpos + 1 - aadd) : 0);
 
 	zsfree(complist);
@@ -667,7 +732,9 @@ callcompfunc(char *s, char *fn)
 	    compinsert = "";
 	    kset &= ~CP_INSERT;
 	}
-	compinsert = ztrdup(compinsert);
+	compinsert = (useline < 0 ? tricat("tab ", "", compinsert) :
+		      ztrdup(compinsert));
+	zsfree(compexact);
 	if (useexact)
 	    compexact = ztrdup("accept");
 	else {
@@ -737,35 +804,59 @@ callcompfunc(char *s, char *fn)
 	else
 	    uselist = 0;
 	forcelist = (complist && strstr(complist, "force"));
-	onlyexpl = (complist && strstr(complist, "expl"));
+	onlyexpl = (complist ? ((strstr(complist, "expl") ? 1 : 0) |
+				(strstr(complist, "messages") ? 2 : 0)) : 0);
 
 	if (!compinsert)
 	    useline = 0;
+	else if (strstr(compinsert, "tab"))
+	    useline = -1;
 	else if (!strcmp(compinsert, "unambig") ||
 		 !strcmp(compinsert, "unambiguous") ||
 		 !strcmp(compinsert, "automenu-unambiguous"))
 	    useline = 1, usemenu = 0;
-	else if (!strcmp(compinsert, "menu"))
-	    useline = 1, usemenu = 1;
-	else if (!strcmp(compinsert, "auto") ||
-		 !strcmp(compinsert, "automenu"))
-	    useline = 1, usemenu = 2;
 	else if (!strcmp(compinsert, "all"))
 	    useline = 2, usemenu = 0;
 	else if (idigit(*compinsert)) {
+#if 0
+	    /* group-numbers in compstate[insert] */
 	    char *m;
-
+#endif
 	    useline = 1; usemenu = 3;
 	    insmnum = atoi(compinsert);
+#if 0
+	    /* group-numbers in compstate[insert] */
 	    if ((m = strchr(compinsert, ':'))) {
 		insgroup = 1;
 		insgnum = atoi(m + 1);
 	    }
+#endif
 	    insspace = (compinsert[strlen(compinsert) - 1] == ' ');
-	} else
-	    useline = usemenu = 0;
-	startauto = (compinsert &&
-		     !strcmp(compinsert, "automenu-unambiguous"));
+	} else {
+	    char *p;
+
+	    if (strpfx("menu", compinsert))
+		useline = 1, usemenu = 1;
+	    else if (strpfx("auto", compinsert))
+		useline = 1, usemenu = 2;
+	    else
+		useline = usemenu = 0;
+
+	    if (useline && (p = strchr(compinsert, ':'))) {
+		insmnum = atoi(++p);
+#if 0
+		/* group-numbers in compstate[insert] */
+		if ((p = strchr(p, ':'))) {
+		    insgroup = 1;
+		    insgnum = atoi(p + 1);
+		}
+#endif
+	    }
+	}
+	startauto = ((compinsert &&
+		      !strcmp(compinsert, "automenu-unambiguous")) ||
+		     (bashlistfirst && isset(AUTOMENU) &&
+                      (!compinsert || !*compinsert)));
 	useexact = (compexact && !strcmp(compexact, "accept"));
 
 	if (!comptoend || !*comptoend)
@@ -802,16 +893,22 @@ static int
 makecomplist(char *s, int incmd, int lst)
 {
     char *p;
+    int owb = wb, owe = we, ooffs = offs;
 
     /* Inside $... ? */
-    if (compfunc && (p = check_param(s, 0, 0)))
+    if (compfunc && (p = check_param(s, 0, 0))) {
 	s = p;
+	parwb = owb;
+	parwe = owe;
+	paroffs = ooffs;
+    } else
+	parwb = -1;
 
     linwhat = inwhat;
 
     if (compfunc) {
 	char *os = s;
-	int onm = nmatches, osi = movefd(0);
+	int onm = nmatches, odm = diffmatches, osi = movefd(0);
 
 	bmatchers = NULL;
 	mstack = NULL;
@@ -827,8 +924,13 @@ makecomplist(char *s, int incmd, int lst)
 	mnum = 0;
 	unambig_mnum = -1;
 	isuf = NULL;
-	insmnum = insgnum = 1;
-	insgroup = oldlist = oldins = 0;
+	insmnum = 1;
+#if 0
+	/* group-numbers in compstate[insert] */
+	insgnum = 1;
+	insgroup = 0;
+#endif
+	oldlist = oldins = 0;
 	begcmgroup("default", 0);
 	menucmp = menuacc = newmatches = onlyexpl = 0;
 
@@ -841,11 +943,12 @@ makecomplist(char *s, int incmd, int lst)
 
 	if (oldlist) {
 	    nmatches = onm;
+	    diffmatches = odm;
 	    validlist = 1;
 	    amatches = lastmatches;
 	    lmatches = lastlmatches;
 	    if (pmatches) {
-		freematches(pmatches);
+		freematches(pmatches, 1);
 		pmatches = NULL;
 		hasperm = 0;
 	    }
@@ -854,7 +957,7 @@ makecomplist(char *s, int incmd, int lst)
 	    return 0;
 	}
 	if (lastmatches) {
-	    freematches(lastmatches);
+	    freematches(lastmatches, 1);
 	    lastmatches = NULL;
 	}
 	permmatches(1);
@@ -868,7 +971,7 @@ makecomplist(char *s, int incmd, int lst)
 	hasperm = 0;
 	hasoldlist = 1;
 
-	if (nmatches && !errflag) {
+	if ((nmatches || nmessages) && !errflag) {
 	    validlist = 1;
 
 	    redup(osi, 0);
@@ -1041,7 +1144,7 @@ check_param(char *s, int set, int test)
 	    }
 	    /* Save the prefix. */
 	    if (compfunc) {
-		parflags = (br >= 2 ? CMF_PARBR : 0);
+		parflags = (br >= 2 ? CMF_PARBR | (nest ? CMF_PARNEST : 0) : 0);
 		sav = *b;
 		*b = '\0';
 		untokenize(parpre = ztrdup(s));
@@ -1078,6 +1181,30 @@ rembslash(char *s)
     return t;
 }
 
+/* Remove one of every pair of single quotes, without copying. Return
+ * the number of removed quotes. */
+
+/**/
+mod_export int
+remsquote(char *s)
+{
+    int ret = 0, qa = (isset(RCQUOTES) ? 1 : 3);
+    char *t = s;
+
+    while (*s)
+	if (qa == 1 ?
+            (s[0] == '\'' && s[1] == '\'') :
+            (s[0] == '\'' && s[1] == '\\' && s[2] == '\'' && s[3] == '\'')) {
+            ret += qa;
+            *t++ = '\'';
+            s += qa + 1;
+	} else
+	    *t++ = *s++;
+    *t = '\0';
+
+    return ret;
+}
+
 /* This should probably be moved into tokenize(). */
 
 /**/
@@ -1147,8 +1274,9 @@ set_comp_sep(void)
     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, remq;
-    int ois = instring, oib = inbackt, noffs = lp;
+    int tl, got = 0, i = 0, j, cur = -1, oll = ll, 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 = (char *) line, sav, *qp, *qs, *ts, qc = '\0';
 
     s += lip;
@@ -1169,8 +1297,37 @@ set_comp_sep(void)
     memcpy(tmp + 1, s, noffs);
     tmp[(scs = cs = 1 + noffs)] = 'x';
     strcpy(tmp + 2 + noffs, s + noffs);
-    if ((remq = (*compqstack == '\\')))
+
+    switch (*compqstack) {
+    case '\\':
+        remq = 1;
 	tmp = rembslash(tmp);
+        break;
+    case '\'':
+        issq = 1;
+        if (isset(RCQUOTES))
+            qa = 1;
+        else
+            qa = 3;
+
+        sq = remsquote(tmp);
+
+        break;
+    case '"':
+        for (j = 0, p = tmp; *p; p++, j++)
+            if (*p == '\\' && p[1] == '\\') {
+                dq++;
+                chuck(p);
+                if (j > cs) {
+                    cs++;
+                    css++;
+                }
+                if (!*p)
+                    break;
+            }
+    }
+    odq = dq;
+    osq = sq;
     inpush(dupstrspace(tmp), 0, NULL);
     line = (unsigned char *) tmp;
     ll = tl - 1;
@@ -1183,9 +1340,10 @@ set_comp_sep(void)
 
 	    if (!tokstr)
 		break;
-	    for (j = 0, p = tokstr; *p; p++)
+	    for (j = 0, p = tokstr; *p; p++) {
 		if (*p == Snull || *p == Dnull)
 		    j++;
+            }
 	    if (j & 1) {
 		tok = STRING;
 		if (p > tokstr && p[-1] == ' ')
@@ -1194,23 +1352,44 @@ set_comp_sep(void)
 	}
 	if (tok == ENDINPUT || tok == LEXERR)
 	    break;
-	if (tokstr && *tokstr)
+	if (tokstr && *tokstr) {
+            for (p = tokstr; dq && *p; p++) {
+                if (*p == Bnull) {
+                    dq--;
+                    if (p[1] == '\\')
+                        dq--;
+                }
+            }
+            if (issq) {
+                for (p = tokstr, lsq = 0; *p; p++) {
+                    if (sq && *p == Snull)
+                        sq -= qa;
+                    if (*p == '\'') {
+                        sq -= qa;
+                        lsq += qa;
+                    }
+                }
+            }
+            else
+                lsq = 0;
 	    addlinknode(foo, (p = ztrdup(tokstr)));
+        }
 	else
 	    p = NULL;
 	if (!got && !zleparse) {
 	    DPUTS(!p, "no current word in substr");
 	    got = 1;
 	    cur = i;
-	    swb = wb - 1;
-	    swe = we - 1;
-	    soffs = cs - swb;
+	    swb = wb - 1 - dq - sq;
+	    swe = we - 1 - dq - sq;
+            sqq = lsq;
+	    soffs = cs - swb - css;
 	    chuck(p + soffs);
 	    ns = dupstring(p);
 	}
 	i++;
     } while (tok != ENDINPUT && tok != LEXERR);
-    noaliases = 0;
+    noaliases = ona;
     strinend();
     inpop();
     errflag = zleparse = 0;
@@ -1255,8 +1434,15 @@ set_comp_sep(void)
     for (p = ns, i = swb; *p; p++, i++) {
 	if (INULL(*p)) {
 	    if (i < scs) {
-		if (remq && *p == Bnull && p[1])
-		    swb -= 2;
+		if (*p == Bnull) {
+                    if (p[1] && remq)
+                        swb -= 2;
+                    if (odq) {
+                        swb--;
+                        if (p[1] == '\\')
+                            swb--;
+                    }
+                }
 	    }
 	    if (p[1] || *p != Bnull) {
 		if (*p == Bnull) {
@@ -1281,9 +1467,9 @@ set_comp_sep(void)
 	if (ql > rl)
 	    swb -= ql - rl;
     }
-    sav = s[(i = swb - 1)];
+    sav = s[(i = swb - 1 - sqq)];
     s[i] = '\0';
-    qp = rembslash(s);
+    qp = (issq ? dupstring(s) : rembslash(s));
     s[i] = sav;
     if (swe < swb)
 	swe = swb;
@@ -1291,14 +1477,17 @@ set_comp_sep(void)
     sl = strlen(s);
     if (swe > sl) {
 	swe = sl;
-	if (strlen(ns) > swe - swb + 1)
+	if ((int)strlen(ns) > swe - swb + 1)
 	    ns[swe - swb + 1] = '\0';
     }
-    qs = rembslash(s + swe);
+    qs = (issq ? dupstring(s + swe) : rembslash(s + swe));
     sl = strlen(ns);
     if (soffs > sl)
 	soffs = sl;
-
+    if (issq) {
+        remsquote(qp);
+        remsquote(qs);
+    }
     {
 	int set = CP_QUOTE | CP_QUOTING, unset = 0;
 
@@ -1343,6 +1532,10 @@ set_comp_sep(void)
 	    untokenize(ss);
 	    compsuffix = ztrdup(ss);
 	}
+        if ((i = strlen(compprefix)) > 1 && compprefix[i - 1] == '\\' &&
+	    compprefix[i - 2] != '\\' && compprefix[i - 2] != Meta)
+            compprefix[i - 1] = '\0';
+        
 	tmp = tricat(compqiprefix, compiprefix, multiquote(qp, 1));
 	zsfree(compqiprefix);
 	compqiprefix = tmp;
@@ -1369,11 +1562,11 @@ set_comp_sep(void)
     return 0;
 }
 
-/* This stores the strings from the list in an array. */
+/* This builds an array from a list of strings. */
 
 /**/
-mod_export void
-set_list_array(char *name, LinkList l)
+mod_export char **
+bld_list_array(LinkList l)
 {
     char **a, **p;
     LinkNode n;
@@ -1383,7 +1576,16 @@ set_list_array(char *name, LinkList l)
 	*p++ = ztrdup((char *) getdata(n));
     *p = NULL;
 
-    setaparam(name, a);
+    return a;
+}
+
+/* This stores the strings from the list in an array. */
+
+/**/
+mod_export void
+set_list_array(char *name, LinkList l)
+{
+    setaparam(name, bld_list_array(l));
 }
 
 /* Get the words from a variable or a (list of words). */
@@ -1437,18 +1639,72 @@ get_user_var(char *nam)
 	/* Otherwise it should be a parameter name. */
 	char **arr = NULL, *val;
 
+	queue_signals();
 	if ((arr = getaparam(nam)) || (arr = gethparam(nam)))
-	    return (incompfunc ? arrdup(arr) : arr);
-
-	if ((val = getsparam(nam))) {
+	    arr = (incompfunc ? arrdup(arr) : arr);
+	else if ((val = getsparam(nam))) {
 	    arr = (char **) zhalloc(2*sizeof(char *));
 	    arr[0] = (incompfunc ? dupstring(val) : val);
 	    arr[1] = NULL;
 	}
+	unqueue_signals();
 	return arr;
     }
 }
 
+static char **
+get_data_arr(char *name, int keys)
+{
+    struct value vbuf;
+    char **ret;
+    Value v;
+
+    queue_signals();
+    if (!(v = fetchvalue(&vbuf, &name, 1,
+			 (keys ? SCANPM_WANTKEYS : SCANPM_WANTVALS) |
+			 SCANPM_MATCHMANY)))
+	ret = NULL;
+    else
+	ret = getarrvalue(v);
+    unqueue_signals();
+
+    return ret;
+}
+
+static void
+addmatch(char *str, int flags, char ***dispp, int line)
+{
+    Cmatch cm = (Cmatch) zhalloc(sizeof(struct cmatch));
+    char **disp = *dispp;
+
+    memset(cm, 0, sizeof(struct cmatch));
+    cm->str = dupstring(str);
+    cm->flags = (flags |
+                 (complist ?
+                  ((strstr(complist, "packed") ? CMF_PACKED : 0) |
+                   (strstr(complist, "rows")   ? CMF_ROWS   : 0)) : 0));
+    if (disp) {
+        if (!*++disp)
+            disp = NULL;
+        if (disp)
+            cm->disp = dupstring(*disp);
+    } else if (line) {
+        cm->disp = dupstring("");
+        cm->flags |= CMF_DISPLINE;
+    }
+    mnum++;
+    ainfo->count++;
+    if (curexpl)
+        curexpl->count++;
+
+    addlinknode(matches, cm);
+
+    newmatches = 1;
+    mgroup->new = 1;
+
+    *dispp = disp;
+}
+
 /* This is used by compadd to add a couple of matches. The arguments are
  * the strings given via options. The last argument is the array with
  * the matches. */
@@ -1460,10 +1716,11 @@ 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 *oqp = qipre, *oqs = qisuf, qc, **disp = NULL, *ibuf = NULL;
+    char **arrays = NULL;
     int lpl, lsl, pl, sl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
-    int ppl = 0, psl = 0;
+    int ppl = 0, psl = 0, ilen = 0;
     int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern;
-    int oisalt = 0, isalt, isexact, doadd, ois = instring, oib = inbackt;
+    int isexact, doadd, ois = instring, oib = inbackt;
     Cline lc = NULL, pline = NULL, sline = NULL;
     Cmatch cm;
     struct cmlist mst;
@@ -1471,24 +1728,38 @@ addmatches(Cadata dat, char **argv)
     Patprog cp = NULL, *pign = NULL;
     LinkList aparl = NULL, oparl = NULL, dparl = NULL;
     Brinfo bp, bpl = brbeg, obpl, bsl = brend, obsl;
-
-    if (!*argv) {
-	SWITCHHEAPS(compheap) {
-	    /* Select the group in which to store the matches. */
-	    gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT  : 0) |
-		      ((dat->aflags & CAF_UNIQALL) ? CGF_UNIQALL : 0) |
-		      ((dat->aflags & CAF_UNIQCON) ? CGF_UNIQCON : 0));
-	    if (dat->group) {
-		endcmgroup(NULL);
-		begcmgroup(dat->group, gflags);
-	    } else {
-		endcmgroup(NULL);
-		begcmgroup("default", 0);
-	    }
-	} SWITCHBACKHEAPS;
-
+    Heap oldheap;
+
+    SWITCHHEAPS(oldheap, compheap) {
+        if (dat->dummies)
+            dat->aflags = ((dat->aflags | CAF_NOSORT | CAF_UNIQCON) &
+                           ~CAF_UNIQALL);
+
+        /* Select the group in which to store the matches. */
+        gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT  : 0) |
+                  ((dat->aflags & CAF_UNIQALL) ? CGF_UNIQALL : 0) |
+                  ((dat->aflags & CAF_UNIQCON) ? CGF_UNIQCON : 0));
+        if (dat->group) {
+            endcmgroup(NULL);
+            begcmgroup(dat->group, gflags);
+        } else {
+            endcmgroup(NULL);
+            begcmgroup("default", 0);
+        }
+        if (dat->mesg || dat->exp) {
+            curexpl = (Cexpl) zhalloc(sizeof(struct cexpl));
+            curexpl->always = !!dat->mesg;
+            curexpl->count = curexpl->fcount = 0;
+            curexpl->str = dupstring(dat->mesg ? dat->mesg : dat->exp);
+            if (dat->mesg)
+                addexpl(1);
+        } else
+            curexpl = NULL;
+    } SWITCHBACKHEAPS(oldheap);
+
+    if (!*argv && !dat->dummies && !(dat->aflags & CAF_ALL))
 	return 1;
-    }
+
     for (bp = brbeg; bp; bp = bp->next)
 	bp->curpos = ((dat->aflags & CAF_QUOTE) ? bp->pos : bp->qpos);
     for (bp = brend; bp; bp = bp->next)
@@ -1521,7 +1792,7 @@ addmatches(Cadata dat, char **argv)
 
     /* Switch back to the heap that was used when the completion widget
      * was invoked. */
-    SWITCHHEAPS(compheap) {
+    SWITCHHEAPS(oldheap, compheap) {
 	if ((doadd = (!dat->apar && !dat->opar && !dat->dpar))) {
 	    if (dat->aflags & CAF_MATCH)
 		hasmatched = 1;
@@ -1539,13 +1810,6 @@ addmatches(Cadata dat, char **argv)
 		dparr = NULL;
 	    dparl = newlinklist();
 	}
-	if (dat->exp) {
-	    curexpl = (Cexpl) zhalloc(sizeof(struct cexpl));
-	    curexpl->count = curexpl->fcount = 0;
-	    curexpl->str = dupstring(dat->exp);
-	} else
-	    curexpl = NULL;
-
 	/* Store the matcher in our stack of matchers. */
 	if (dat->match) {
 	    mst.next = mstack;
@@ -1597,6 +1861,11 @@ addmatches(Cadata dat, char **argv)
 	    lsuf = dupstring(compsuffix);
 	    llpl = strlen(lpre);
 	    llsl = strlen(lsuf);
+
+	    if (llpl + (int)strlen(compqiprefix) + (int)strlen(lipre) != origlpre
+	     || llsl + (int)strlen(compqisuffix) + (int)strlen(lisuf) != origlsuf)
+		lenchanged = 1;
+
 	    /* Test if there is an existing -P prefix. */
 	    if (dat->pre && *dat->pre) {
 		pl = pfxlen(dat->pre, lpre);
@@ -1649,55 +1918,57 @@ addmatches(Cadata dat, char **argv)
 		    llpl -= gfl;
 		}
 	    }
-	    s = dat->ppre ? dat->ppre : "";
-	    if ((ml = match_str(lpre, s, &bpl, 0, NULL, 0, 0, 1)) >= 0) {
-		if (matchsubs) {
-		    Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
-
-		    tmp->prefix = matchsubs;
-		    if (matchlastpart)
-			matchlastpart->next = tmp;
+	    if ((s = dat->ppre)) {
+		if ((ml = match_str(lpre, s, &bpl, 0, NULL, 0, 0, 1)) >= 0) {
+		    if (matchsubs) {
+			Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
+
+			tmp->prefix = matchsubs;
+			if (matchlastpart)
+			    matchlastpart->next = tmp;
+			else
+			    matchparts = tmp;
+		    }
+		    pline = matchparts;
+		    lpre += ml;
+		    llpl -= ml;
+		    bcp = ml;
+		    bpadd = strlen(s) - ml;
+		} else {
+		    if (llpl <= lpl && strpfx(lpre, s))
+			lpre = dupstring("");
+		    else if (llpl > lpl && strpfx(s, lpre))
+			lpre += lpl;
 		    else
-			matchparts = tmp;
+			*argv = NULL;
+		    bcp = lpl;
 		}
-		pline = matchparts;
-		lpre += ml;
-		llpl -= ml;
-		bcp = ml;
-		bpadd = strlen(s) - ml;
-	    } else {
-		if (llpl <= lpl && strpfx(lpre, s))
-		    lpre = "";
-		else if (llpl > lpl && strpfx(s, lpre))
-		    lpre += lpl;
-		else
-		    *argv = NULL;
-		bcp = lpl;
 	    }
-	    s = dat->psuf ? dat->psuf : "";
-	    if ((ml = match_str(lsuf, s, &bsl, 0, NULL, 1, 0, 1)) >= 0) {
-		if (matchsubs) {
-		    Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, CLF_SUF);
-
-		    tmp->suffix = matchsubs;
-		    if (matchlastpart)
-			matchlastpart->next = tmp;
+	    if ((s = dat->psuf)) {
+		if ((ml = match_str(lsuf, s, &bsl, 0, NULL, 1, 0, 1)) >= 0) {
+		    if (matchsubs) {
+			Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, CLF_SUF);
+
+			tmp->suffix = matchsubs;
+			if (matchlastpart)
+			    matchlastpart->next = tmp;
+			else
+			    matchparts = tmp;
+		    }
+		    sline = revert_cline(matchparts);
+		    lsuf[llsl - ml] = '\0';
+		    llsl -= ml;
+		    bcs = ml;
+		    bsadd = strlen(s) - ml;
+		} else {
+		    if (llsl <= lsl && strsfx(lsuf, s))
+			lsuf = dupstring("");
+		    else if (llsl > lsl && strsfx(s, lsuf))
+			lsuf[llsl - lsl] = '\0';
 		    else
-			matchparts = tmp;
+			*argv = NULL;
+		    bcs = lsl;
 		}
-		sline = revert_cline(matchparts);
-		lsuf[llsl - ml] = '\0';
-		llsl -= ml;
-		bcs = ml;
-		bsadd = strlen(s) - ml;
-	    } else {
-		if (llsl <= lsl && strsfx(lsuf, s))
-		    lsuf = "";
-		else if (llsl > lsl && strsfx(s, lsuf))
-		    lsuf[llsl - lsl] = '\0';
-		else
-		    *argv = NULL;
-		bcs = lsl;
 	    }
 	    if (comppatmatch && *comppatmatch) {
 		int is = (*comppatmatch == '*');
@@ -1721,17 +1992,6 @@ addmatches(Cadata dat, char **argv)
 		}
 	    }
 	}
-	/* Select the group in which to store the matches. */
-	gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT  : 0) |
-		  ((dat->aflags & CAF_UNIQALL) ? CGF_UNIQALL : 0) |
-		  ((dat->aflags & CAF_UNIQCON) ? CGF_UNIQCON : 0));
-	if (dat->group) {
-	    endcmgroup(NULL);
-	    begcmgroup(dat->group, gflags);
-	} else {
-	    endcmgroup(NULL);
-	    begcmgroup("default", 0);
-	}
 	if (*argv) {
 	    if (dat->pre)
 		dat->pre = dupstring(dat->pre);
@@ -1743,7 +2003,6 @@ addmatches(Cadata dat, char **argv)
 	    } else
 		dat->prpre = dupstring(dat->prpre);
 	    /* Select the set of matches. */
-	    oisalt = (dat->aflags & CAF_ALT);
 
 	    if (dat->remf) {
 		dat->remf = dupstring(dat->remf);
@@ -1761,17 +2020,28 @@ addmatches(Cadata dat, char **argv)
 	/* Walk through the matches given. */
 	obpl = bpl;
 	obsl = bsl;
-	if (!oisalt && (aign || pign)) {
-	    int max = 0;
-	    char **ap = argv;
-
-	    ppl = (dat->ppre ? strlen(dat->ppre) : 0);
-	    while ((s = *ap++))
-		if ((sl = strlen(s)) > max)
-		    max = sl;
-	    psl = (dat->psuf ? strlen(dat->psuf) : 0);
-	    ibuf = (char *) zhalloc(1 + ppl + max + psl);
+	if (dat->aflags & CAF_ARRAYS) {
+	    Heap oldheap2;
+
+	    SWITCHHEAPS(oldheap2, oldheap) {
+		arrays = argv;
+		argv = NULL;
+		while (*arrays &&
+		       (!(argv = get_data_arr(*arrays,
+					      (dat->aflags & CAF_KEYS))) ||
+			!*argv))
+		    arrays++;
+		arrays++;
+		if (!argv) {
+		    ms = NULL;
+		    argv = &ms;
+		}
+	    } SWITCHBACKHEAPS(oldheap2);
 	}
+	if (dat->ppre)
+	    ppl = strlen(dat->ppre);
+	if (dat->psuf)
+	    psl = strlen(dat->psuf);
 	for (; (s = *argv); argv++) {
 	    bpl = obpl;
 	    bsl = obsl;
@@ -1780,9 +2050,11 @@ addmatches(Cadata dat, char **argv)
 		    disp = NULL;
 	    }
 	    sl = strlen(s);
-	    isalt = oisalt;
-	    if (!isalt && (aign || pign)) {
-		int il = ppl + sl + psl;
+	    if (aign || pign) {
+		int il = ppl + sl + psl, addit = 1;
+
+		if (il + 1> ilen)
+		    ibuf = (char *) zhalloc((ilen = il) + 2);
 
 		if (ppl)
 		    memcpy(ibuf, dat->ppre, ppl);
@@ -1796,15 +2068,21 @@ addmatches(Cadata dat, char **argv)
 		    char **pt = aign;
 		    int filell;
 
-		    for (isalt = 0; !isalt && *pt; pt++)
-			isalt = ((filell = strlen(*pt)) < il &&
-				 !strcmp(*pt, ibuf + il - filell));
+		    for (; addit && *pt; pt++)
+			addit = !((filell = strlen(*pt)) < il &&
+				  !strcmp(*pt, ibuf + il - filell));
 		}
-		if (!isalt && pign) {
+		if (addit && pign) {
 		    Patprog *pt = pign;
 
-		    for (isalt = 0; !isalt && *pt; pt++)
-			isalt = pattry(*pt, ibuf);
+		    for (; addit && *pt; pt++)
+			addit = !pattry(*pt, ibuf);
+		}
+		if (!addit) {
+		    compignored++;
+		    if (dparr && !*++dparr)
+			dparr = NULL;
+		    goto next_array;
 		}
 	    }
 	    if (!(dat->aflags & CAF_MATCH)) {
@@ -1822,7 +2100,7 @@ addmatches(Cadata dat, char **argv)
 					 &isexact))) {
 		if (dparr && !*++dparr)
 		    dparr = NULL;
-		continue;
+		goto next_array;
 	    }
 	    if (doadd) {
 		Brinfo bp;
@@ -1832,7 +2110,7 @@ addmatches(Cadata dat, char **argv)
 		for (bp = obsl; bp; bp = bp->next)
 		    bp->curpos += bsadd;
 
-		if ((cm = add_match_data(isalt, ms, lc, dat->ipre, NULL,
+		if ((cm = add_match_data(0, ms, s, lc, dat->ipre, NULL,
 					 dat->isuf, dat->pre, dat->prpre,
 					 dat->ppre, pline,
 					 dat->psuf, sline,
@@ -1854,6 +2132,25 @@ addmatches(Cadata dat, char **argv)
 		}
 		free_cline(lc);
 	    }
+	next_array:
+	    if ((dat->aflags & CAF_ARRAYS) && !argv[1]) {
+		Heap oldheap2;
+
+		SWITCHHEAPS(oldheap2, oldheap) {
+		    argv = NULL;
+		    while (*arrays &&
+			   (!(argv = get_data_arr(*arrays,
+						  (dat->aflags & CAF_KEYS))) ||
+			    !*argv))
+			arrays++;
+		    arrays++;
+		    if (!argv) {
+			ms = NULL;
+			argv = &ms;
+		    }
+		    argv--;
+		} SWITCHBACKHEAPS(oldheap2);
+	    }
 	}
 	if (dat->apar)
 	    set_list_array(dat->apar, aparl);
@@ -1862,8 +2159,15 @@ addmatches(Cadata dat, char **argv)
 	if (dat->dpar)
 	    set_list_array(dat->dpar, dparl);
 	if (dat->exp)
-	    addexpl();
-    } SWITCHBACKHEAPS;
+	    addexpl(0);
+	if (!hasallmatch && (dat->aflags & CAF_ALL)) {
+            addmatch("<all>", dat->flags | CMF_ALL, &disp, 1);
+	    hasallmatch = 1;
+	}
+        while (dat->dummies--)
+            addmatch("", dat->flags | CMF_DUMMY, &disp, 0);
+
+    } SWITCHBACKHEAPS(oldheap);
 
     /* We switched back to the current heap, now restore the stack of
      * matchers. */
@@ -1887,7 +2191,7 @@ addmatches(Cadata dat, char **argv)
 
 /**/
 mod_export Cmatch
-add_match_data(int alt, char *str, Cline line,
+add_match_data(int alt, char *str, char *orig, Cline line,
 	       char *ipre, char *ripre, char *isuf,
 	       char *pre, char *prpre,
 	       char *ppre, Cline pline,
@@ -1977,7 +2281,7 @@ add_match_data(int alt, char *str, Cline line,
 	    sl = tsl;
 	}
 	if (qisl) {
-	    Cline qsl = bld_parts(qisuf, qisl, qisl, NULL);
+	    Cline qsl = bld_parts(dupstring(qisuf), qisl, qisl, NULL);
 
 	    qsl->flags |= CLF_SUF;
 	    qsl->suffix = qsl->prefix;
@@ -2060,7 +2364,7 @@ add_match_data(int alt, char *str, Cline line,
 	    line = p;
 	}
 	if (qipl) {
-	    Cline lp, p = bld_parts(qipre, qipl, qipl, &lp);
+	    Cline lp, p = bld_parts(dupstring(qipre), qipl, qipl, &lp);
 
 	    lp->next = line;
 	    line = p;
@@ -2115,6 +2419,7 @@ add_match_data(int alt, char *str, Cline line,
     /* Allocate and fill the match structure. */
     cm = (Cmatch) zhalloc(sizeof(struct cmatch));
     cm->str = str;
+    cm->orig = dupstring(orig);
     cm->ppre = (ppre && *ppre ? ppre : NULL);
     cm->psuf = (psuf && *psuf ? psuf : NULL);
     cm->prpre = ((flags & CMF_FILE) && prpre && *prpre ? prpre : NULL);
@@ -2133,7 +2438,22 @@ add_match_data(int alt, char *str, Cline line,
 		 (complist ?
 		  ((strstr(complist, "packed") ? CMF_PACKED : 0) |
 		   (strstr(complist, "rows")   ? CMF_ROWS   : 0)) : 0));
-
+    cm->mode = 0;
+    cm->modec = '\0';
+    if ((flags & CMF_FILE) && orig[0] && orig[strlen(orig) - 1] != '/') {
+        struct stat buf;
+        char *pb;
+
+        pb = (char *) zhalloc((cm->prpre ? strlen(cm->prpre) : 0) +
+                              3 + strlen(orig));
+        sprintf(pb, "%s%s", (cm->prpre ? cm->prpre : "./"), orig);
+
+        if (!ztat(pb, &buf, 1)) {
+            cm->mode = buf.st_mode;
+            if ((cm->modec = file_type(buf.st_mode)) == ' ')
+                cm->modec = '\0';
+        }
+    }
     if ((*compqstack == '\\' && compqstack[1]) ||
 	(autoq && *compqstack && compqstack[1] == '\\'))
 	cm->flags |= CMF_NOSPACE;
@@ -2175,6 +2495,8 @@ add_match_data(int alt, char *str, Cline line,
 
     newmatches = 1;
     mgroup->new = 1;
+    if (alt)
+	compignored++;
 
     if (!complastprompt || !*complastprompt)
 	dolastprompt = 0;
@@ -2221,7 +2543,7 @@ add_match_data(int alt, char *str, Cline line,
 		comp_setunset(0, 0, CP_EXACTSTR, 0);
 	    }
 	    ai->exactm = cm;
-	} else if (useexact) {
+	} else if (useexact && !matcheq(cm, ai->exactm)) {
 	    ai->exact = 2;
 	    ai->exactm = NULL;
 	    if (incompfunc)
@@ -2258,13 +2580,14 @@ begcmgroup(char *n, int flags)
     }
     mgroup = (Cmgroup) zhalloc(sizeof(struct cmgroup));
     mgroup->name = dupstring(n);
-    mgroup->lcount = mgroup->llcount = mgroup->mcount = 0;
+    mgroup->lcount = mgroup->llcount = mgroup->mcount = mgroup->ecount = 
+	mgroup->ccount = 0;
     mgroup->flags = flags;
     mgroup->matches = NULL;
     mgroup->ylist = NULL;
     mgroup->expls = NULL;
     mgroup->perm = NULL;
-    mgroup->new = 0;
+    mgroup->new = mgroup->num = mgroup->nbrbeg = mgroup->nbrend = 0;
 
     mgroup->lexpls = expls = newlinklist();
     mgroup->lmatches = matches = newlinklist();
@@ -2272,7 +2595,9 @@ begcmgroup(char *n, int flags)
 
     mgroup->lallccs = allccs = ((flags & CGF_NOSORT) ? NULL : newlinklist());
 
-    mgroup->next = amatches;
+    if ((mgroup->next = amatches))
+	amatches->prev = mgroup;
+    mgroup->prev = NULL;
     amatches = mgroup;
 }
 
@@ -2289,7 +2614,7 @@ endcmgroup(char **ylist)
 
 /**/
 mod_export void
-addexpl(void)
+addexpl(int always)
 {
     LinkNode n;
     Cexpl e;
@@ -2299,12 +2624,21 @@ addexpl(void)
 	if (!strcmp(curexpl->str, e->str)) {
 	    e->count += curexpl->count;
 	    e->fcount += curexpl->fcount;
-
+            if (always) {
+                e->always = 1;
+                nmessages++;
+                newmatches = 1;
+                mgroup->new = 1;
+            }
 	    return;
 	}
     }
     addlinknode(expls, curexpl);
     newmatches = 1;
+    if (always) {
+        mgroup->new = 1;
+        nmessages++;
+    }
 }
 
 /* The comparison function for matches (used for sorting). */
@@ -2425,6 +2759,8 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
 	    }
 	} else {
 	    if (!(flags & CGF_UNIQALL) && !(flags & CGF_UNIQCON)) {
+                int dup;
+
 		for (ap = rp; *ap; ap++) {
 		    for (bp = cp = ap + 1; *bp; bp++) {
 			if (!matcheq(*ap, *bp))
@@ -2433,6 +2769,17 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
 			    n--;
 		    }
 		    *cp = NULL;
+                    if (!(*ap)->disp) {
+                        for (dup = 0, bp = ap + 1; *bp; bp++)
+                            if (!(*bp)->disp &&
+                                !((*bp)->flags & CMF_MULT) &&
+                                !strcmp((*ap)->str, (*bp)->str)) {
+                                (*bp)->flags |= CMF_MULT;
+                                dup = 1;
+                            }
+                        if (dup)
+                            (*ap)->flags |= CMF_FMULT;
+                    }
 		}
 	    } else if (!(flags & CGF_UNIQCON)) {
 		int dup;
@@ -2476,9 +2823,10 @@ dupmatch(Cmatch m, int nbeg, int nend)
 {
     Cmatch r;
 
-    r = (Cmatch) zcalloc(sizeof(struct cmatch));
+    r = (Cmatch) zshcalloc(sizeof(struct cmatch));
 
     r->str = ztrdup(m->str);
+    r->orig = ztrdup(m->orig);
     r->ipre = ztrdup(m->ipre);
     r->ripre = ztrdup(m->ripre);
     r->isuf = ztrdup(m->isuf);
@@ -2488,7 +2836,7 @@ dupmatch(Cmatch m, int nbeg, int nend)
     r->pre = ztrdup(m->pre);
     r->suf = ztrdup(m->suf);
     r->flags = m->flags;
-    if (nbeg) {
+    if (m->brpl) {
 	int *p, *q, i;
 
 	r->brpl = (int *) zalloc(nbeg * sizeof(int));
@@ -2497,7 +2845,7 @@ dupmatch(Cmatch m, int nbeg, int nend)
 	    *p = *q;
     } else
 	r->brpl = NULL;
-    if (nend) {
+    if (m->brsl) {
 	int *p, *q, i;
 
 	r->brsl = (int *) zalloc(nend * sizeof(int));
@@ -2512,6 +2860,8 @@ dupmatch(Cmatch m, int nbeg, int nend)
     r->qipl = m->qipl;
     r->qisl = m->qisl;
     r->disp = ztrdup(m->disp);
+    r->mode = m->mode;
+    r->modec = m->modec;
 
     return r;
 }
@@ -2538,7 +2888,7 @@ permmatches(int last)
 
     opm = pmatches;
     pmatches = lmatches = NULL;
-    nmatches = smatches = 0;
+    nmatches = smatches = diffmatches = 0;
 
     if (!ainfo->count) {
 	if (last)
@@ -2570,11 +2920,14 @@ permmatches(int last)
 	    nmatches += g->mcount;
 	    smatches += g->lcount;
 
-	    n = (Cmgroup) zcalloc(sizeof(struct cmgroup));
+	    if (g->mcount > 1)
+		diffmatches = 1;
+
+	    n = (Cmgroup) zshcalloc(sizeof(struct cmgroup));
 
 	    if (g->perm) {
 		g->perm->next = NULL;
-		freematches(g->perm);
+		freematches(g->perm, 0);
 	    }
 	    g->perm = n;
 
@@ -2588,7 +2941,7 @@ permmatches(int last)
 	    n->num = gn++;
 	    n->flags = g->flags;
 	    n->mcount = g->mcount;
-	    n->matches = p = (Cmatch *) zcalloc((n->mcount + 1) * sizeof(Cmatch));
+	    n->matches = p = (Cmatch *) zshcalloc((n->mcount + 1) * sizeof(Cmatch));
 	    n->name = ztrdup(g->name);
 	    for (q = g->matches; *q; q++, p++)
 		*p = dupmatch(*q, nbrbeg, nbrend);
@@ -2602,10 +2955,12 @@ permmatches(int last)
 		n->ylist = NULL;
 
 	    if ((n->ecount = g->ecount)) {
-		n->expls = ep = (Cexpl *) zcalloc((n->ecount + 1) * sizeof(Cexpl));
+		n->expls = ep = (Cexpl *) zshcalloc((n->ecount + 1) * sizeof(Cexpl));
 		for (eq = g->expls; (o = *eq); eq++, ep++) {
-		    *ep = e = (Cexpl) zcalloc(sizeof(struct cexpl));
+		    *ep = e = (Cexpl) zshcalloc(sizeof(struct cexpl));
 		    e->count = (fi ? o->fcount : o->count);
+                    e->always = o->always;
+		    e->fcount = 0;
 		    e->str = ztrdup(o->str);
 		}
 		*ep = NULL;
@@ -2624,18 +2979,29 @@ permmatches(int last)
 
 	    nmatches += g->mcount;
 	    smatches += g->lcount;
+
+	    if (g->mcount > 1)
+		diffmatches = 1;
+
 	    g->num = gn++;
 	}
 	g->new = 0;
 	g = g->next;
     }
-    for (g = pmatches; g; g = g->next) {
+    for (g = pmatches, p = NULL; g; g = g->next) {
 	g->nbrbeg = nbrbeg;
 	g->nbrend = nbrend;
 	for (rn = 1, q = g->matches; *q; q++) {
 	    (*q)->rnum = rn++;
 	    (*q)->gnum = mn++;
 	}
+	if (!diffmatches && *g->matches) {
+	    if (p) {
+		if (!matcheq(*g->matches, *p))
+		    diffmatches = 1;
+	    } else
+		p = g->matches;
+	}
     }
     hasperm = 1;
     permmnum = mn - 1;
@@ -2654,6 +3020,7 @@ freematch(Cmatch m, int nbeg, int nend)
     if (!m) return;
 
     zsfree(m->str);
+    zsfree(m->orig);
     zsfree(m->ipre);
     zsfree(m->ripre);
     zsfree(m->isuf);
@@ -2666,8 +3033,10 @@ freematch(Cmatch m, int nbeg, int nend)
     zsfree(m->remf);
     zsfree(m->disp);
     zsfree(m->autoq);
-    zfree(m->brpl, nbeg * sizeof(int));
-    zfree(m->brsl, nend * sizeof(int));
+    if (m->brpl)
+	zfree(m->brpl, nbeg * sizeof(int));
+    if (m->brsl)
+	zfree(m->brsl, nend * sizeof(int));
 
     zfree(m, sizeof(m));
 }
@@ -2676,7 +3045,7 @@ freematch(Cmatch m, int nbeg, int nend)
 
 /**/
 mod_export void
-freematches(Cmgroup g)
+freematches(Cmgroup g, int cm)
 {
     Cmgroup n;
     Cmatch *m;
@@ -2687,6 +3056,7 @@ freematches(Cmgroup g)
 
 	for (m = g->matches; *m; m++)
 	    freematch(*m, g->nbrbeg, g->nbrend);
+	free(g->matches);
 
 	if (g->ylist)
 	    freearray(g->ylist);
@@ -2704,4 +3074,6 @@ freematches(Cmgroup g)
 
 	g = n;
     }
+    if (cm)
+	minfo.cur = NULL;
 }
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index f625c34cd..d1f3366e7 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -33,16 +33,21 @@
 /* global variables for shell parameters in new style completion */
 
 /**/
-mod_export zlong compcurrent;
+mod_export
+zlong compcurrent,
+      complistmax;
 /**/
-zlong complistmax,
-      complistlines;
+zlong complistlines,
+      compignored;
 
 /**/
 mod_export
 char **compwords,
+     **compredirs,
      *compprefix,
      *compsuffix,
+     *complastprefix,
+     *complastsuffix,
      *compisuffix,
      *compqiprefix,
      *compqisuffix,
@@ -181,66 +186,99 @@ parse_cmatcher(char *name, char *s)
 {
     Cmatcher ret = NULL, r = NULL, n;
     Cpattern line, word, left, right;
-    int fl, ll, wl, lal, ral, err;
+    int fl, fl2, ll, wl, lal, ral, err, both;
 
     if (!*s)
 	return NULL;
 
     while (*s) {
+	lal = ral = both = fl2 = 0;
+	left = right = NULL;
+
 	while (*s && inblank(*s)) s++;
 
 	if (!*s) break;
 
 	switch (*s) {
+	case 'b': fl2 = CMF_INTER;
 	case 'l': fl = CMF_LEFT; break;
+	case 'e': fl2 = CMF_INTER;
 	case 'r': fl = CMF_RIGHT; break;
 	case 'm': fl = 0; break;
+	case 'B': fl2 = CMF_INTER;
 	case 'L': fl = CMF_LEFT | CMF_LINE; break;
+	case 'E': fl2 = CMF_INTER;
 	case 'R': fl = CMF_RIGHT | CMF_LINE; break;
 	case 'M': fl = CMF_LINE; break;
 	default:
-	    zwarnnam(name, "unknown match specification character `%c'", NULL, *s);
+	    if (name)
+		zwarnnam(name, "unknown match specification character `%c'",
+			 NULL, *s);
 	    return pcm_err;
 	}
 	if (s[1] != ':') {
-	    zwarnnam(name, "missing `:'", NULL, 0);
+	    if (name)
+		zwarnnam(name, "missing `:'", NULL, 0);
 	    return pcm_err;
 	}
 	s += 2;
 	if (!*s) {
-	    zwarnnam(name, "missing patterns", NULL, 0);
+	    if (name)
+		zwarnnam(name, "missing patterns", NULL, 0);
 	    return pcm_err;
 	}
-	if (fl & CMF_LEFT) {
+	if ((fl & CMF_LEFT) && !fl2) {
 	    left = parse_pattern(name, &s, &lal, '|', &err);
 	    if (err)
 		return pcm_err;
+
+	    if ((both = (*s && s[1] == '|')))
+		s++;
+
 	    if (!*s || !*++s) {
-		zwarnnam(name, "missing line pattern", NULL, 0);
+		if (name)
+		    zwarnnam(name, "missing line pattern", NULL, 0);
 		return pcm_err;
 	    }
 	} else
 	    left = NULL;
 
-	line = parse_pattern(name, &s, &ll, ((fl & CMF_RIGHT) ? '|' : '='),
+	line = parse_pattern(name, &s, &ll,
+			     (((fl & CMF_RIGHT) && !fl2) ? '|' : '='),
 			     &err);
 	if (err)
 	    return pcm_err;
-	if ((fl & CMF_RIGHT) && (!*s || !*++s)) {
-	    zwarnnam(name, "missing right anchor", NULL, 0);
-	} else if (!(fl & CMF_RIGHT)) {
+	if (both) {
+	    right = line;
+	    ral = ll;
+	    line = NULL;
+	    ll = 0;
+	}
+	if ((fl & CMF_RIGHT) && !fl2 && (!*s || !*++s)) {
+	    if (name)
+		zwarnnam(name, "missing right anchor", NULL, 0);
+	} else if (!(fl & CMF_RIGHT) || fl2) {
 	    if (!*s) {
-		zwarnnam(name, "missing word pattern", NULL, 0);
+		if (name)
+		    zwarnnam(name, "missing word pattern", NULL, 0);
 		return pcm_err;
 	    }
 	    s++;
 	}
-	if (fl & CMF_RIGHT) {
+	if ((fl & CMF_RIGHT) && !fl2) {
+	    if (*s == '|') {
+		left = line;
+		lal = ll;
+		line = NULL;
+		ll = 0;
+		s++;
+	    }
 	    right = parse_pattern(name, &s, &ral, '=', &err);
 	    if (err)
 		return pcm_err;
 	    if (!*s) {
-		zwarnnam(name, "missing word pattern", NULL, 0);
+		if (name)
+		    zwarnnam(name, "missing word pattern", NULL, 0);
 		return pcm_err;
 	    }
 	    s++;
@@ -249,7 +287,8 @@ parse_cmatcher(char *name, char *s)
 
 	if (*s == '*') {
 	    if (!(fl & (CMF_LEFT | CMF_RIGHT))) {
-		zwarnnam(name, "need anchor for `*'", NULL, 0);
+		if (name)
+		    zwarnnam(name, "need anchor for `*'", NULL, 0);
 		return pcm_err;
 	    }
 	    word = NULL;
@@ -262,16 +301,18 @@ parse_cmatcher(char *name, char *s)
 	    word = parse_pattern(name, &s, &wl, 0, &err);
 
 	    if (!word && !line) {
-		zwarnnam(name, "need non-empty word or line pattern", NULL, 0);
+		if (name)
+		    zwarnnam(name, "need non-empty word or line pattern",
+			     NULL, 0);
 		return pcm_err;
 	    }
 	}
 	if (err)
 	    return pcm_err;
 
-	n = (Cmatcher) zcalloc(sizeof(*ret));
+	n = (Cmatcher) hcalloc(sizeof(*ret));
 	n->next = NULL;
-	n->flags = fl;
+	n->flags = fl | fl2;
 	n->line = line;
 	n->llen = ll;
 	n->word = word;
@@ -365,7 +406,7 @@ parse_class(Cpattern p, unsigned char *s, unsigned char e)
 
     n = !n;
     while (*s && (k || *s != e)) {
-	if (s[1] == '-' && s[2] != e) {
+	if (s[1] == '-' && s[2] && s[2] != e) {
 	    /* a run of characters */
 	    for (j = (int) *s; j <= (int) s[2]; j++)
 		p->tab[j] = (eq ? i++ : n);
@@ -380,7 +421,7 @@ parse_class(Cpattern p, unsigned char *s, unsigned char e)
 
 /**/
 static int
-bin_compadd(char *name, char **argv, char *ops, int func)
+bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 {
     struct cadata dat;
     char *p, **sp, *e, *m = NULL, *mstr = NULL;
@@ -391,12 +432,13 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 	zwarnnam(name, "can only be called from completion function", NULL, 0);
 	return 1;
     }
-    dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre =
+    dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre = dat.mesg =
 	dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = 
 	dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL;
     dat.match = NULL;
     dat.flags = 0;
     dat.aflags = CAF_MATCH;
+    dat.dummies = 0;
 
     for (; *argv && **argv ==  '-'; argv++) {
 	if (!(*argv)[1]) {
@@ -414,12 +456,21 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 	    case 'Q':
 		dat.aflags |= CAF_QUOTE;
 		break;
+	    case 'C':
+		dat.aflags |= CAF_ALL;
+		break;
 	    case 'f':
 		dat.flags |= CMF_FILE;
 		break;
 	    case 'e':
 		dat.flags |= CMF_ISPAR;
 		break;
+	    case 'a':
+		dat.aflags |= CAF_ARRAYS;
+		break;
+	    case 'k':
+		dat.aflags |= CAF_ARRAYS|CAF_KEYS;
+		break;
 	    case 'F':
 		sp = &(dat.ign);
 		e = "string expected after -%c";
@@ -476,9 +527,6 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		sp = &(dat.prpre);
 		e = "string expected after -%c";
 		break;
-	    case 'a':
-		dat.aflags |= CAF_ALT;
-		break;
 	    case 'M':
 		sp = &m;
 		e = "matching specification expected after -%c";
@@ -488,6 +536,10 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		sp = &(dat.exp);
 		e = "string expected after -%c";
 		break;
+	    case 'x':
+		sp = &(dat.mesg);
+		e = "string expected after -%c";
+		break;
 	    case 'r':
 		dat.flags |= CMF_REMOVE;
 		sp = &(dat.rems);
@@ -517,6 +569,23 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 	    case 'l':
 		dat.flags |= CMF_DISPLINE;
 		break;
+	    case 'E':
+                if (p[1]) {
+                    dat.dummies = atoi(p + 1);
+                    p = "" - 1;
+                } else if (argv[1]) {
+                    argv++;
+                    dat.dummies = atoi(*argv);
+                    p = "" - 1;
+                } else {
+                    zwarnnam(name, "number expected after -%c", NULL, *p);
+                    return 1;
+                }
+                if (dat.dummies < 0) {
+                    zwarnnam(name, "invalid number: %d", NULL, dat.dummies);
+                    return 1;
+                }
+		break;
 	    case '-':
 		argv++;
 		goto ca_args;
@@ -539,25 +608,28 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		    return 1;
 		}
 		if (dm) {
-		    if (mstr)
-			mstr = tricat(mstr, " ", m);
-		    else
+		    if (mstr) {
+			char *tmp = tricat(mstr, " ", m);
+			zsfree(mstr);
+			mstr = tmp;
+		    } else
 			mstr = ztrdup(m);
 		    m = NULL;
 		}
 	    }
 	}
     }
+
+ ca_args:
+
     if (mstr && (match = parse_cmatcher(name, mstr)) == pcm_err) {
 	zsfree(mstr);
 	return 1;
     }
     zsfree(mstr);
 
- ca_args:
-
-    if (!*argv && !dat.group &&
-	!(dat.aflags & (CAF_NOSORT|CAF_UNIQALL|CAF_UNIQCON)))
+    if (!*argv && !dat.group && !dat.mesg &&
+	!(dat.aflags & (CAF_NOSORT|CAF_UNIQALL|CAF_UNIQCON|CAF_ALL)))
 	return 1;
 
     dat.match = match = cpcmatcher(match);
@@ -635,7 +707,7 @@ restrict_range(int b, int e)
 	    e = wl;
 
 	i = e - b + 1;
-	p = (char **) zcalloc((i + 1) * sizeof(char *));
+	p = (char **) zshcalloc((i + 1) * sizeof(char *));
 
 	for (q = p, pp = compwords + b; i; i--, q++, pp++)
 	    *q = ztrdup(*pp);
@@ -716,7 +788,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
 	if (!na)
 	    return 1;
 	if (na > 0 &&
-	    strlen(test == CVT_PRENUM ? compprefix : compsuffix) >= na) {
+	    (int)strlen(test == CVT_PRENUM ? compprefix : compsuffix) >= na) {
 	    if (mod) {
 		if (test == CVT_PRENUM)
 		    ignore_prefix(na);
@@ -742,25 +814,39 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
 		char *p, sav;
 
 		if (!(l = strlen(compprefix)))
-		    return 0;
+		    return ((na == 1 || na == -1) && pattry(pp, compprefix));
 		if (na < 0) {
 		    p = compprefix + l;
 		    na = -na;
 		    add = -1;
 		} else {
 		    p = compprefix + 1;
+		    if (*p == Meta)
+			p++;
 		    add = 1;
 		}
-		for (; l; l--, p += add) {
+		for (;;) {
 		    sav = *p;
 		    *p = '\0';
 		    test = pattry(pp, compprefix);
 		    *p = sav;
 		    if (test && !--na)
 			break;
+		    if (add > 0) {
+			if (p == compprefix + l)
+			    return 0;
+			if (*p == Meta)
+			    p += 2;
+			else
+			    p++;
+		    } else {
+			if (p == compprefix)
+			    return 0;
+			p--;
+			if (p > compprefix && p[-1] == Meta)
+			    p--;
+		    }
 		}
-		if (!l)
-		    return 0;
 		if (mod)
 		    ignore_prefix(p - compprefix);
 	    } else {
@@ -768,21 +854,37 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
 		char *p;
 
 		if (!(ol = l = strlen(compsuffix)))
-		    return 0;
+		    return ((na == 1 || na == -1) && pattry(pp, compsuffix));
 		if (na < 0) {
 		    p = compsuffix;
 		    na = -na;
 		    add = 1;
 		} else {
 		    p = compsuffix + l - 1;
+		    if (p > compsuffix && p[-1] == Meta)
+			p--;
 		    add = -1;
 		}
-		for (; l; l--, p += add)
+		for (;;) {
 		    if (pattry(pp, p) && !--na)
 			break;
 
-		if (!l)
-		    return 0;
+		    if (add > 0) {
+			if (p == compsuffix + l)
+			    return 0;
+			if (*p == Meta)
+			    p += 2;
+			else
+			    p++;
+		    } else {
+			if (p == compsuffix)
+			    return 0;
+			p--;
+			if (p > compsuffix && p[-1] == Meta)
+			    p--;
+		    }
+		}
+
 		if (mod)
 		    ignore_suffix(ol - (p - compsuffix));
 	    }
@@ -794,7 +896,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
 
 /**/
 static int
-bin_compset(char *name, char **argv, char *ops, int func)
+bin_compset(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 {
     int test = 0, na = 0, nb = 0;
     char *sa = NULL, *sb = NULL;
@@ -871,51 +973,82 @@ bin_compset(char *name, char **argv, char *ops, int func)
  * order of the CP_* bits in comp.h */
 
 #define VAL(X) ((void *) (&(X)))
+#define GSU(X) ((GsuScalar)(void *) (&(X)))
 struct compparam {
     char *name;
     int type;
-    void *var, *set, *get;
+    void *var;
+    GsuScalar gsu;
 };
 
+static const struct gsu_scalar compvarscalar_gsu =
+{ strvargetfn, strvarsetfn, compunsetfn };
+static const struct gsu_scalar complist_gsu =
+{ get_complist, set_complist, compunsetfn };
+static const struct gsu_scalar unambig_gsu =
+{ get_unambig, nullstrsetfn, compunsetfn };
+static const struct gsu_scalar unambig_pos_gsu =
+{ get_unambig_pos, nullstrsetfn, compunsetfn };
+static const struct gsu_scalar insert_pos_gsu =
+{ get_insert_pos, nullstrsetfn, compunsetfn };
+
+static const struct gsu_integer compvarinteger_gsu =
+{ intvargetfn, intvarsetfn, compunsetfn };
+static const struct gsu_integer nmatches_gsu =
+{ get_nmatches, NULL, compunsetfn };
+static const struct gsu_integer unambig_curs_gsu =
+{ get_unambig_curs, NULL, compunsetfn };
+static const struct gsu_integer listlines_gsu =
+{ get_listlines, NULL, compunsetfn };
+
+static const struct gsu_array compvararray_gsu =
+{ arrvargetfn, arrvarsetfn, compunsetfn };
+
+
 static struct compparam comprparams[] = {
-    { "words", PM_ARRAY, VAL(compwords), NULL, NULL },
-    { "CURRENT", PM_INTEGER, VAL(compcurrent), NULL, NULL },
-    { "PREFIX", PM_SCALAR, VAL(compprefix), NULL, NULL },
-    { "SUFFIX", PM_SCALAR, VAL(compsuffix), NULL, NULL },
-    { "IPREFIX", PM_SCALAR, VAL(compiprefix), NULL, NULL },
-    { "ISUFFIX", PM_SCALAR, VAL(compisuffix), NULL, NULL },
-    { "QIPREFIX", PM_SCALAR | PM_READONLY, VAL(compqiprefix), NULL, NULL },
-    { "QISUFFIX", PM_SCALAR | PM_READONLY, VAL(compqisuffix), NULL, NULL },
-    { NULL, 0, NULL, NULL, NULL }
+    { "words", PM_ARRAY, VAL(compwords), NULL },
+    { "redirections", PM_ARRAY, VAL(compredirs), NULL },
+    { "CURRENT", PM_INTEGER, VAL(compcurrent), NULL },
+    { "PREFIX", PM_SCALAR, VAL(compprefix), NULL },
+    { "SUFFIX", PM_SCALAR, VAL(compsuffix), NULL },
+    { "IPREFIX", PM_SCALAR, VAL(compiprefix), NULL },
+    { "ISUFFIX", PM_SCALAR, VAL(compisuffix), NULL },
+    { "QIPREFIX", PM_SCALAR | PM_READONLY, VAL(compqiprefix), NULL },
+    { "QISUFFIX", PM_SCALAR | PM_READONLY, VAL(compqisuffix), NULL },
+    { NULL, 0, NULL, NULL }
 };
 
 static struct compparam compkparams[] = {
-    { "nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_nmatches) },
-    { "context", PM_SCALAR, VAL(compcontext), NULL, NULL },
-    { "parameter", PM_SCALAR, VAL(compparameter), NULL, NULL },
-    { "redirect", PM_SCALAR, VAL(compredirect), NULL, NULL },
-    { "quote", PM_SCALAR | PM_READONLY, VAL(compquote), NULL, NULL },
-    { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL, NULL },
-    { "restore", PM_SCALAR, VAL(comprestore), NULL, NULL },
-    { "list", PM_SCALAR, NULL, VAL(set_complist), VAL(get_complist) },
-    { "insert", PM_SCALAR, VAL(compinsert), NULL, NULL },
-    { "exact", PM_SCALAR, VAL(compexact), NULL, NULL },
-    { "exact_string", PM_SCALAR, VAL(compexactstr), NULL, NULL },
-    { "pattern_match", PM_SCALAR, VAL(comppatmatch), NULL, NULL },
-    { "pattern_insert", PM_SCALAR, VAL(comppatinsert), NULL, NULL },
-    { "unambiguous", PM_SCALAR | PM_READONLY, NULL, NULL, VAL(get_unambig) },
-    { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL, NULL,
-      VAL(get_unambig_curs) },
-    { "list_max", PM_INTEGER, VAL(complistmax), NULL, NULL },
-    { "last_prompt", PM_SCALAR, VAL(complastprompt), NULL, NULL },
-    { "to_end", PM_SCALAR, VAL(comptoend), NULL, NULL },
-    { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL },
-    { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL },
-    { "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 }
+    { "nmatches", PM_INTEGER | PM_READONLY, NULL, GSU(nmatches_gsu) },
+    { "context", PM_SCALAR, VAL(compcontext), NULL },
+    { "parameter", PM_SCALAR, VAL(compparameter), NULL },
+    { "redirect", PM_SCALAR, VAL(compredirect), NULL },
+    { "quote", PM_SCALAR | PM_READONLY, VAL(compquote), NULL },
+    { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL },
+    { "restore", PM_SCALAR, VAL(comprestore), NULL },
+    { "list", PM_SCALAR, NULL, GSU(complist_gsu) },
+    { "insert", PM_SCALAR, VAL(compinsert), NULL },
+    { "exact", PM_SCALAR, VAL(compexact), NULL },
+    { "exact_string", PM_SCALAR, VAL(compexactstr), NULL },
+    { "pattern_match", PM_SCALAR, VAL(comppatmatch), NULL },
+    { "pattern_insert", PM_SCALAR, VAL(comppatinsert), NULL },
+    { "unambiguous", PM_SCALAR | PM_READONLY, NULL, GSU(unambig_gsu) },
+    { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL,
+      GSU(unambig_curs_gsu) },
+    { "unambiguous_positions", PM_SCALAR | PM_READONLY, NULL,
+      GSU(unambig_pos_gsu) },
+    { "insert_positions", PM_SCALAR | PM_READONLY, NULL,
+      GSU(insert_pos_gsu) },
+    { "list_max", PM_INTEGER, VAL(complistmax), NULL },
+    { "last_prompt", PM_SCALAR, VAL(complastprompt), NULL },
+    { "to_end", PM_SCALAR, VAL(comptoend), NULL },
+    { "old_list", PM_SCALAR, VAL(compoldlist), NULL },
+    { "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 },
+    { "ignored", PM_INTEGER | PM_READONLY, VAL(compignored), NULL },
+    { NULL, 0, NULL, NULL }
 };
 
 #define COMPSTATENAME "compstate"
@@ -935,27 +1068,25 @@ addcompparams(struct compparam *cp, Param *pp)
 	if ((pm->u.data = cp->var)) {
 	    switch(PM_TYPE(cp->type)) {
 	    case PM_SCALAR:
-		pm->sets.cfn = strvarsetfn;
-		pm->gets.cfn = strvargetfn;
+		pm->gsu.s = &compvarscalar_gsu;
 		break;
 	    case PM_INTEGER:
-		pm->sets.ifn = intvarsetfn;
-		pm->gets.ifn = intvargetfn;
-		pm->ct = 10;
+		pm->gsu.i = &compvarinteger_gsu;
+		pm->base = 10;
 		break;
 	    case PM_ARRAY:
-		pm->sets.afn = arrvarsetfn;
-		pm->gets.afn = arrvargetfn;
+		pm->gsu.a = &compvararray_gsu;
 		break;
 	    }
 	} else {
-	    pm->sets.cfn = (void (*) _((Param, char *))) cp->set;
-	    pm->gets.cfn = (char *(*) _((Param))) cp->get;
+	    pm->gsu.s = cp->gsu;
 	}
-	pm->unsetfn = compunsetfn;
     }
 }
 
+static const struct gsu_hash compstate_gsu =
+{ get_compstate, set_compstate, compunsetfn };
+
 /**/
 void
 makecompparams(void)
@@ -973,9 +1104,7 @@ makecompparams(void)
     comprpms[CPN_COMPSTATE] = cpm;
     tht = paramtab;
     cpm->level = locallevel + 1;
-    cpm->gets.hfn = get_compstate;
-    cpm->sets.hfn = set_compstate;
-    cpm->unsetfn = compunsetfn;
+    cpm->gsu.h = &compstate_gsu;
     cpm->u.hash = paramtab = newparamtable(31, COMPSTATENAME);
     addcompparams(compkparams, compkpms);
     paramtab = tht;
@@ -990,7 +1119,7 @@ get_compstate(Param pm)
 
 /**/
 static void
-set_compstate(Param pm, HashTable ht)
+set_compstate(UNUSED(Param pm), HashTable ht)
 {
     struct compparam *cp;
     Param *pp;
@@ -999,13 +1128,16 @@ set_compstate(Param pm, HashTable ht)
     struct value v;
     char *str;
 
+    if (!ht)
+        return;
+
     for (i = 0; i < ht->hsize; i++)
 	for (hn = ht->nodes[i]; hn; hn = hn->next)
 	    for (cp = compkparams,
 		 pp = compkpms; cp->name; cp++, pp++)
 		if (!strcmp(hn->nam, cp->name)) {
-		    v.isarr = v.inv = v.a = 0;
-		    v.b = -1;
+		    v.isarr = v.inv = v.start = 0;
+		    v.end = -1;
 		    v.arr = NULL;
 		    v.pm = (Param) hn;
 		    if (cp->type == PM_INTEGER)
@@ -1023,58 +1155,73 @@ set_compstate(Param pm, HashTable ht)
 
 /**/
 static zlong
-get_nmatches(Param pm)
+get_nmatches(UNUSED(Param pm))
 {
-    return num_matches(1);
+    return (permmatches(0) ? 0 : nmatches);
 }
 
 /**/
 static zlong
-get_anmatches(Param pm)
-{
-    return num_matches(0);
-}
-
-/**/
-static zlong
-get_listlines(Param pm)
+get_listlines(UNUSED(Param pm))
 {
     return list_lines();
 }
 
 /**/
 static void
-set_complist(Param pm, char *v)
+set_complist(UNUSED(Param pm), char *v)
 {
     comp_list(v);
 }
 
 /**/
 static char *
-get_complist(Param pm)
+get_complist(UNUSED(Param pm))
 {
     return complist;
 }
 
 /**/
 static char *
-get_unambig(Param pm)
+get_unambig(UNUSED(Param pm))
 {
-    return unambig_data(NULL);
+    return unambig_data(NULL, NULL, NULL);
 }
 
 /**/
 static zlong
-get_unambig_curs(Param pm)
+get_unambig_curs(UNUSED(Param pm))
 {
     int c;
 
-    unambig_data(&c);
+    unambig_data(&c, NULL, NULL);
 
     return c;
 }
 
 /**/
+static char *
+get_unambig_pos(UNUSED(Param pm))
+{
+    char *p;
+
+    unambig_data(NULL, &p, NULL);
+
+    return p;
+}
+
+/**/
+static char *
+get_insert_pos(UNUSED(Param pm))
+{
+    char *p;
+
+    unambig_data(NULL, NULL, &p);
+
+    return p;
+}
+
+/**/
 static void
 compunsetfn(Param pm, int exp)
 {
@@ -1085,7 +1232,7 @@ compunsetfn(Param pm, int exp)
 		*((char **) pm->u.data) = ztrdup("");
 	    } else if (PM_TYPE(pm->flags) == PM_ARRAY) {
 		freearray(*((char ***) pm->u.data));
-		*((char ***) pm->u.data) = zcalloc(sizeof(char *));
+		*((char ***) pm->u.data) = zshcalloc(sizeof(char *));
 	    } else if (PM_TYPE(pm->flags) == PM_HASHED) {
 		deleteparamtable(pm->u.hash);
 		pm->u.hash = NULL;
@@ -1148,13 +1295,13 @@ comp_wrapper(Eprog prog, FuncWrap w, char *name)
     if (incompfunc != 1)
 	return 1;
     else {
-	char *orest, *opre, *osuf, *oipre, *oisuf, **owords;
+	char *orest, *opre, *osuf, *oipre, *oisuf, **owords, **oredirs;
 	char *oqipre, *oqisuf, *oq, *oqi, *oqs, *oaq;
 	zlong ocur;
 	unsigned int runset = 0, kunset = 0, m, sm;
 	Param *pp;
 
-	m = CP_WORDS | CP_CURRENT | CP_PREFIX | CP_SUFFIX | 
+	m = CP_WORDS | CP_REDIRS | CP_CURRENT | CP_PREFIX | CP_SUFFIX | 
 	    CP_IPREFIX | CP_ISUFFIX | CP_QIPREFIX | CP_QISUFFIX;
 	for (pp = comprpms, sm = 1; m; pp++, m >>= 1, sm <<= 1) {
 	    if ((m & 1) && ((*pp)->flags & PM_UNSET))
@@ -1176,6 +1323,7 @@ comp_wrapper(Eprog prog, FuncWrap w, char *name)
 	oqs = ztrdup(compqstack);
 	oaq = ztrdup(autoq);
 	owords = zarrdup(compwords);
+	oredirs = zarrdup(compredirs);
 
 	runshfunc(prog, w, name);
 
@@ -1202,11 +1350,14 @@ comp_wrapper(Eprog prog, FuncWrap w, char *name)
 	    zsfree(autoq);
 	    autoq = oaq;
 	    freearray(compwords);
+	    freearray(compredirs);
 	    compwords = owords;
+            compredirs = oredirs;
 	    comp_setunset(CP_COMPSTATE |
-			  (~runset & (CP_WORDS | CP_CURRENT | CP_PREFIX |
-				     CP_SUFFIX | CP_IPREFIX | CP_ISUFFIX |
-				     CP_QIPREFIX | CP_QISUFFIX)),
+			  (~runset & (CP_WORDS | CP_REDIRS |
+                                      CP_CURRENT | CP_PREFIX |
+                                      CP_SUFFIX | CP_IPREFIX | CP_ISUFFIX |
+                                      CP_QIPREFIX | CP_QISUFFIX)),
 			  (runset & CP_ALLREALS),
 			  (~kunset & CP_RESTORE), (kunset & CP_ALLKEYS));
 	} else {
@@ -1223,6 +1374,7 @@ comp_wrapper(Eprog prog, FuncWrap w, char *name)
 	    zsfree(oqs);
 	    zsfree(oaq);
 	    freearray(owords);
+	    freearray(oredirs);
 	}
 	zsfree(comprestore);
 	comprestore = orest;
@@ -1294,12 +1446,12 @@ struct hookdef comphooks[] = {
 
 /**/
 int
-setup_(Module m)
+setup_(UNUSED(Module m))
 {
     hasperm = 0;
 
     comprpms = compkpms = NULL;
-    compwords = NULL;
+    compwords = compredirs = NULL;
     compprefix = compsuffix = compiprefix = compisuffix = 
 	compqiprefix = compqisuffix =
 	compcontext = compparameter = compredirect = compquote =
@@ -1307,7 +1459,9 @@ setup_(Module m)
 	compexact = compexactstr = comppatmatch = comppatinsert =
 	complastprompt = comptoend = compoldlist = compoldins =
 	compvared = compqstack = NULL;
-
+    complastprefix = ztrdup("");
+    complastsuffix = ztrdup("");
+    complistmax = 0;
     hascompmod = 1;
 
     return 0;
@@ -1352,12 +1506,16 @@ cleanup_(Module m)
 
 /**/
 int
-finish_(Module m)
+finish_(UNUSED(Module m))
 {
     if (compwords)
 	freearray(compwords);
+    if (compredirs)
+	freearray(compredirs);
     zsfree(compprefix);
     zsfree(compsuffix);
+    zsfree(complastprefix);
+    zsfree(complastsuffix);
     zsfree(compiprefix);
     zsfree(compisuffix);
     zsfree(compqiprefix);