about summary refs log tree commit diff
path: root/Src/Zle/compcore.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Zle/compcore.c')
-rw-r--r--Src/Zle/compcore.c2530
1 files changed, 2530 insertions, 0 deletions
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
new file mode 100644
index 000000000..ea9ff5b20
--- /dev/null
+++ b/Src/Zle/compcore.c
@@ -0,0 +1,2530 @@
+/*
+ * compcore.c - the complete module, completion core code
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1999 Sven Wischnowsky
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Sven Wischnowsky and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "complete.mdh"
+#define GLOBAL_PROTOTYPES
+#include "zle_tricky.pro"
+#undef GLOBAL_PROTOTYPES
+#include "compcore.pro"
+
+/* The last completion widget called. */
+
+static Widget lastcompwidget;
+
+/* Flags saying what we have to do with the result. */
+
+/**/
+int useexact, useline, uselist;
+
+/* Non-zero if we should keep an old list. */
+
+/**/
+int oldlist, oldins;
+
+/* 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, *
+ * 2 - when a full match is inserted (single or menu), 3 - always.         */
+
+/**/
+int movetoend;
+
+/* The match and group number to insert when starting menucompletion.   */
+
+/**/
+int insmnum, insgnum, insgroup, insspace;
+
+/* Information about menucompletion. */
+
+/**/
+struct menuinfo minfo;
+
+/* Number of matches accepted with accept-and-menu-complete */
+
+/**/
+int menuacc;
+
+/* Brace insertion stuff. */
+
+/**/
+int hasunqu, useqbr, brpcs, brscs;
+
+/* Flags saying in what kind of string we are. */
+
+/**/
+int ispar, linwhat;
+
+/* A parameter expansion prefix (like ${). */
+
+/**/
+char *parpre;
+
+/* Flags for parameter expansions for new style completion. */
+
+/**/
+int parflags;
+
+/* Match flags for all matches in this group. */
+
+/**/
+int mflags;
+
+/* Flags saying how the parameter expression we are in is quoted. */
+
+/**/
+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                           */
+
+/**/
+char *ipre, *ripre, *isuf;
+
+/* The list of matches.  fmatches contains the matches we first ignore *
+ * because of fignore.                                                 */
+
+/**/
+LinkList matches, fmatches;
+
+/* This holds the list of matches-groups. lastmatches holds the last list of 
+ * permanently allocated matches, pmatches is the same for the list
+ * currently built, amatches is the heap allocated stuff during completion
+ * (after all matches have been generated it is an alias for pmatches), and
+ * lmatches/lastlmatches is a pointer to the last element in the lists. */
+
+/**/
+Cmgroup lastmatches, pmatches, amatches, lmatches, lastlmatches;
+
+/* Non-zero if we have permanently allocated matches (old and new). */
+
+/**/
+int hasoldlist, hasperm;
+
+/* Non-zero if we have newly added matches. */
+
+/**/
+int newmatches;
+
+/* Number of permanently allocated matches and groups. */
+
+/**/
+int permmnum, permgnum, lastpermmnum, lastpermgnum;
+
+/* The total number of matches and the number of matches to be listed. */
+
+/**/
+int nmatches, smatches;
+
+/* != 0 if only explanation strings should be printed */
+
+/**/
+int onlyexpl;
+
+/* Information about the matches for listing. */
+
+/**/
+struct cldata listdat;
+
+/* This flag is non-zero if we are completing a pattern (with globcomplete) */
+
+/**/
+int ispattern, haspattern;
+
+/* Non-zero if at least one match was added without -U. */
+
+/**/
+int hasmatched;
+
+/* The current group of matches. */
+
+/**/
+Cmgroup mgroup;
+
+/* Match counter: all matches. */
+
+/**/
+int mnum;
+
+/* The match counter when unambig_data() was called. */
+
+/**/
+int unambig_mnum;
+
+/* Length of longest/shortest match. */
+
+/**/
+int maxmlen, minmlen;
+
+/* This holds the explanation strings we have to print in this group and *
+ * a pointer to the current cexpl structure. */
+
+/**/
+LinkList expls;
+
+/**/
+Cexpl curexpl;
+
+/* A stack of completion matchers to be used. */
+
+/**/
+Cmlist mstack;
+
+/* The completion matchers used when building new stuff for the line. */
+
+/**/
+Cmlist bmatchers;
+
+/* A list with references to all matchers we used. */
+
+/**/
+LinkList matchers;
+
+/* A heap of free Cline structures. */
+
+/**/
+Cline freecl;
+
+/* Ambiguous information. */
+
+/**/
+Aminfo ainfo, fainfo;
+
+/* The memory heap to use for new style completion generation. */
+
+/**/
+Heap compheap;
+
+/* A list of some data.
+ *
+ * Well, actually, it's the list of all compctls used so far, but since
+ * conceptually we don't know anything about compctls here... */
+
+/**/
+LinkList allccs;
+
+/* This says what of the state the line is in when completion is started *
+ * came from a previous completion. If the FC_LINE bit is set, the       *
+ * string was inserted. If FC_INWORD is set, the last completion moved   *
+ * the cursor into the word although it was at the end of it when the    *
+ * last completion was invoked.                                          *
+ * This is used to detect if the string should be taken as an exact      *
+ * match (see do_ambiguous()) and if the cursor has to be moved to the   *
+ * end of the word before generating the completions.                    */
+
+/**/
+int fromcomp;
+
+/* This holds the end-position of the last string inserted into the line. */
+
+/**/
+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. */
+
+/**/
+int
+do_completion(Hookdef dummy, Compldat dat)
+{
+    int ret = 0, lst = dat->lst, incmd = dat->incmd;
+    char *s = dat->s;
+
+    HEAPALLOC {
+	char *opm;
+	LinkNode n;
+
+	pushheap();
+
+	ainfo = fainfo = NULL;
+	matchers = newlinklist();
+
+	hasunqu = 0;
+	useline = (lst != COMP_LIST_COMPLETE);
+	useexact = isset(RECEXACT);
+	uselist = (useline ?
+		   ((isset(AUTOLIST) && !isset(BASHAUTOLIST)) ? 
+		    (isset(LISTAMBIGUOUS) ? 3 : 2) : 0) : 1);
+	zsfree(comppatmatch);
+	opm = comppatmatch = ztrdup(useglob ? "*" : "");
+	zsfree(comppatinsert);
+	comppatinsert = ztrdup("menu");
+	zsfree(compforcelist);
+	compforcelist = ztrdup("");
+	haspattern = 0;
+	complistmax = getiparam("LISTMAX");
+	zsfree(complastprompt);
+	complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) ||
+				(unset(ALWAYSLASTPROMPT) && zmult != 1)) ?
+				"yes" : "");
+	movetoend = ((cs == we || isset(ALWAYSTOEND)) ? 2 : 1);
+	showinglist = 0;
+	hasmatched = 0;
+	minmlen = 1000000;
+	maxmlen = -1;
+
+	/* Make sure we have the completion list and compctl. */
+	if (makecomplist(s, incmd, lst)) {
+	    /* Error condition: feeeeeeeeeeeeep(). */
+	    cs = 0;
+	    foredel(ll);
+	    inststr(origline);
+	    cs = origcs;
+	    clearlist = 1;
+	    ret = 1;
+	    minfo.cur = NULL;
+	    goto compend;
+	}
+	zsfree(lastprebr);
+	zsfree(lastpostbr);
+	lastprebr = lastpostbr = NULL;
+
+	if (comppatmatch && *comppatmatch && comppatmatch != opm)
+	    haspattern = 1;
+	if (!useline && uselist) {
+	    /* All this and the guy only wants to see the list, sigh. */
+	    cs = 0;
+	    foredel(ll);
+	    inststr(origline);
+	    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);
+
+	    mc = (minfo.group)->matches;
+
+	    while (1) {
+		if (!first)
+		    acceptlast();
+		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 (compforcelist && *compforcelist && uselist)
+		showinglist = -2;
+	    else
+		invalidatelist();
+	} else if (useline) {
+	    /* We have matches. */
+	    if (nmatches > 1) {
+		/* There is more than one match. */
+		ret = do_ambiguous();
+	    } else if (nmatches == 1) {
+		/* Only one match. */
+		Cmgroup m = amatches;
+
+		while (!m->mcount)
+		    m = m->next;
+		minfo.cur = NULL;
+		minfo.asked = 0;
+		do_single(m->matches[0]);
+		if (compforcelist && *compforcelist) {
+		    if (uselist)
+			showinglist = -2;
+		    else
+			clearlist = 1;
+		} else
+		    invalidatelist();
+	    }
+	} else {
+	    invalidatelist();
+	    if (compforcelist && *compforcelist)
+		clearlist = 1;
+	    cs = 0;
+	    foredel(ll);
+	    inststr(origline);
+	    cs = origcs;
+	}
+	/* Print the explanation strings if needed. */
+	if (!showinglist && validlist && usemenu != 2 && nmatches != 1 &&
+	    useline != 2 && (!oldlist || !listshown)) {
+	    onlyexpl = 1;
+	    showinglist = -2;
+	}
+      compend:
+	for (n = firstnode(matchers); n; incnode(n))
+	    freecmatcher((Cmatcher) getdata(n));
+
+	ll = strlen((char *)line);
+	if (cs > ll)
+	    cs = ll;
+	popheap();
+    } LASTALLOC;
+
+    return ret;
+}
+
+/* Before and after hooks called by zle. */
+
+static int oldmenucmp;
+
+/**/
+int
+before_complete(Hookdef dummy, int *lst)
+{
+    oldmenucmp = menucmp;
+
+    if (showagain && validlist)
+	showinglist = -2;
+    showagain = 0;
+
+    /* If we are doing a menu-completion... */
+
+    if (menucmp && *lst != COMP_LIST_EXPAND && 
+	(!compwidget || compwidget == lastcompwidget)) {
+	do_menucmp(*lst);
+	return 1;
+    }
+    if (menucmp && validlist && *lst == COMP_LIST_COMPLETE) {
+	showinglist = -2;
+	onlyexpl = listdat.valid = 0;
+	return 1;
+    }
+    lastcompwidget = compwidget;
+
+    /* We may have to reset the cursor to its position after the   *
+     * string inserted by the last completion. */
+
+    if (fromcomp & FC_INWORD)
+	if ((cs = lastend) > ll)
+	    cs = ll;
+
+    /* Check if we have to start a menu-completion (via automenu). */
+
+    if (isset(AUTOMENU) && lastambig &&
+	(!isset(BASHAUTOLIST) || lastambig == 2))
+	usemenu = 2;
+
+    return 0;
+}
+
+/**/
+int
+after_complete(Hookdef dummy, Compldat dat)
+{
+    if (menucmp && !oldmenucmp) {
+	struct chdata dat;
+
+	dat.matches = amatches;
+	dat.num = nmatches;
+	dat.cur = NULL;
+	if (runhookdef(MENUSTARTHOOK, (void *) &dat))
+	    menucmp = menuacc = 0;
+    }
+    return 0;
+}
+
+/* This calls the given completion widget function. */
+
+/**/
+static void
+callcompfunc(char *s, char *fn)
+{
+    List list;
+    int lv = lastval;
+    char buf[20];
+
+    if ((list = getshfunc(fn)) != &dummy_list) {
+	char **p, *tmp;
+	int aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext;
+	unsigned int rset, kset;
+	Param *ocrpms = comprpms, *ockpms = compkpms;
+
+	comprpms = (Param *) zalloc(CP_REALPARAMS * sizeof(Param));
+	compkpms = (Param *) zalloc(CP_KEYPARAMS * sizeof(Param));
+
+	rset = CP_ALLREALS;
+	kset = CP_ALLKEYS &
+	    ~(CP_PARAMETER | CP_REDIRECT | CP_QUOTE | CP_QUOTING |
+	      CP_EXACTSTR | CP_FORCELIST | CP_OLDLIST | CP_OLDINS |
+	      (useglob ? 0 : CP_PATMATCH));
+	zsfree(compvared);
+	if (varedarg) {
+	    compvared = ztrdup(varedarg);
+	    kset |= CP_VARED;
+	} else
+	    compvared = ztrdup("");
+	if (!*complastprompt)
+	    kset &= ~CP_LASTPROMPT;
+	zsfree(compcontext);
+	zsfree(compparameter);
+	zsfree(compredirect);
+	compparameter = compredirect = "";
+	if (ispar)
+	    compcontext = (ispar == 2 ? "brace_parameter" : "parameter");
+	else if (linwhat == IN_MATH) {
+	    if (insubscr) {
+		compcontext = "subscript";
+		if (varname) {
+		    compparameter = varname;
+		    kset |= CP_PARAMETER;
+		}
+	    } else
+		compcontext = "math";
+	    usea = 0;
+	} else if (lincmd) {
+	    if (insubscr) {
+		compcontext = "subscript";
+		kset |= CP_PARAMETER;
+	    } else
+		compcontext = "command";
+	} else if (linredir) {
+	    compcontext = "redirect";
+	    if (rdstr)
+		compredirect = rdstr;
+	    kset |= CP_REDIRECT;
+	} else
+	    switch (linwhat) {
+	    case IN_ENV:
+		compcontext = (linarr ? "array_value" : "value");
+		compparameter = varname;
+		kset |= CP_PARAMETER;
+		if (!clwpos) {
+		    clwpos = 1;
+		    clwnum = 2;
+		    zsfree(clwords[1]);
+		    clwords[1] = ztrdup(s);
+		    zsfree(clwords[2]);
+		    clwords[2] = NULL;
+		}
+		aadd = 1;
+		break;
+	    case IN_COND:
+		compcontext = "condition";
+		break;
+	    default:
+		if (cmdstr)
+		    compcontext = "command";
+		else {
+		    compcontext = "value";
+		    kset |= CP_PARAMETER;
+		    if (clwords[0])
+			compparameter = clwords[0];
+		    aadd = 1;
+		}
+	    }
+	compcontext = ztrdup(compcontext);
+	if (compwords)
+	    freearray(compwords);
+	if (usea && (!aadd || clwords[0])) {
+	    char **q;
+
+	    PERMALLOC {
+		q = compwords = (char **)
+		    zalloc((clwnum + 1) * sizeof(char *));
+		for (p = clwords + aadd; *p; p++, q++) {
+		    tmp = dupstring(*p);
+		    untokenize(tmp);
+		    *q = ztrdup(tmp);
+		}
+		*q = NULL;
+	    } LASTALLOC;
+	} else
+	    compwords = (char **) zcalloc(sizeof(char *));
+
+	compparameter = ztrdup(compparameter);
+	compredirect = ztrdup(compredirect);
+	zsfree(compquote);
+	zsfree(compquoting);
+	if (instring) {
+	    if (instring == 1) {
+		compquote = ztrdup("\'");
+		compquoting = ztrdup("single");
+	    } else {
+		compquote = ztrdup("\"");
+		compquoting = ztrdup("double");
+	    }
+	    kset |= CP_QUOTE | CP_QUOTING;
+	} else if (inbackt) {
+	    compquote = ztrdup("`");
+	    compquoting = ztrdup("backtick");
+	    kset |= CP_QUOTE | CP_QUOTING;
+	} else {
+	    compquote = ztrdup("");
+	    compquoting = ztrdup("");
+	}
+	zsfree(compprefix);
+	zsfree(compsuffix);
+	if (unset(COMPLETEINWORD)) {
+	    tmp = quotename(s, NULL);
+	    untokenize(tmp);
+	    compprefix = ztrdup(tmp);
+	    compsuffix = ztrdup("");
+	} else {
+	    char *ss, sav;
+	    
+	    ss = s + offs;
+
+	    sav = *ss;
+	    *ss = '\0';
+	    tmp = quotename(s, NULL);
+	    untokenize(tmp);
+	    compprefix = ztrdup(tmp);
+	    *ss = sav;
+	    ss = quotename(ss, NULL);
+	    untokenize(ss);
+	    compsuffix = ztrdup(ss);
+	}
+	zsfree(compiprefix);
+	compiprefix = ztrdup("");
+	zsfree(compisuffix);
+	compisuffix = ztrdup("");
+	zsfree(compqiprefix);
+	compqiprefix = ztrdup(qipre ? qipre : "");
+	zsfree(compqisuffix);
+	compqisuffix = ztrdup(qisuf ? qisuf : "");
+	compcurrent = (usea ? (clwpos + 1 - aadd) : 0);
+
+	zsfree(complist);
+	switch (uselist) {
+	case 0: complist = ""; kset &= ~CP_LIST; break;
+	case 1: complist = "list"; break;
+	case 2: complist = "autolist"; break;
+	case 3: complist = "ambiguous"; break;
+	}
+	complist = ztrdup(complist);
+	zsfree(compinsert);
+	if (useline) {
+	    switch (usemenu) {
+	    case 0: compinsert = "unambiguous"; break;
+	    case 1: compinsert = "menu"; break;
+	    case 2: compinsert = "automenu"; break;
+	    }
+	} else {
+	    compinsert = "";
+	    kset &= ~CP_INSERT;
+	}
+	compinsert = ztrdup(compinsert);
+	if (useexact)
+	    compexact = ztrdup("accept");
+	else {
+	    compexact = ztrdup("");
+	    kset &= ~CP_EXACT;
+	}
+	zsfree(comptoend);
+	if (movetoend == 1)
+	    comptoend = ztrdup("single");
+	else
+	    comptoend = ztrdup("match");
+	zsfree(compoldlist);
+	zsfree(compoldins);
+	if (hasoldlist && lastpermmnum) {
+	    if (listshown)
+		compoldlist = "shown";
+	    else
+		compoldlist = "yes";
+	    kset |= CP_OLDLIST;
+	    if (minfo.cur) {
+		sprintf(buf, "%d", (*(minfo.cur))->gnum);
+		compoldins = buf;
+		kset |= CP_OLDINS;
+	    } else
+		compoldins = "";
+	} else
+	    compoldlist = compoldins = "";
+	compoldlist = ztrdup(compoldlist);
+	compoldins = ztrdup(compoldins);
+
+	incompfunc = 1;
+	startparamscope();
+	makecompparams();
+	comp_setunset(rset, (~rset & CP_ALLREALS),
+		      kset, (~kset & CP_ALLKEYS));
+	makezleparams(1);
+	sfcontext = SFC_CWIDGET;
+	NEWHEAPS(compheap) {
+	    LinkList largs = NULL;
+	    int olv = lastval;
+
+	    if (*cfargs) {
+		char **p = cfargs;
+
+		largs = newlinklist();
+		addlinknode(largs, dupstring(fn));
+		while (*p)
+		    addlinknode(largs, dupstring(*p++));
+	    }
+	    doshfunc(fn, list, largs, 0, 0);
+	    cfret = lastval;
+	    lastval = olv;
+	} OLDHEAPS;
+	sfcontext = osc;
+	endparamscope();
+	lastcmd = 0;
+	incompfunc = icf;
+
+	if (!complist)
+	    uselist = 0;
+	else if (!strncmp(complist, "list", 4))
+	    uselist = 1;
+	else if (!strncmp(complist, "auto", 4))
+	    uselist = 2;
+	else if (!strncmp(complist, "ambig", 5))
+	    uselist = 3;
+	else
+	    uselist = 0;
+
+	onlyexpl = (complist && strstr(complist, "expl"));
+
+	if (!compinsert)
+	    useline = 0;
+	else if (!strcmp(compinsert, "unambig") ||
+		 !strcmp(compinsert, "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)) {
+	    char *m;
+
+	    useline = 1; usemenu = 3;
+	    insmnum = atoi(compinsert);
+	    if ((m = strchr(compinsert, ':'))) {
+		insgroup = 1;
+		insgnum = atoi(m + 1);
+	    }
+	    insspace = (compinsert[strlen(compinsert) - 1] == ' ');
+	} else
+	    useline = usemenu = 0;
+	useexact = (compexact && !strcmp(compexact, "accept"));
+
+	if (!comptoend || !*comptoend)
+	    movetoend = 0;
+	else if (!strcmp(comptoend, "single"))
+	    movetoend = 1;
+	else if (!strcmp(comptoend, "always"))
+	    movetoend = 3;
+	else
+	    movetoend = 2;
+
+	oldlist = (hasoldlist && compoldlist && !strcmp(compoldlist, "keep"));
+	oldins = (hasoldlist && minfo.cur &&
+		  compoldins && !strcmp(compoldins, "keep"));
+
+	zfree(comprpms, CP_REALPARAMS * sizeof(Param));
+	zfree(compkpms, CP_KEYPARAMS * sizeof(Param));
+	comprpms = ocrpms;
+	compkpms = ockpms;
+    }
+    lastval = lv;
+}
+
+/* Create the completion list.  This is called whenever some bit of   *
+ * completion code needs the list.                                    *
+ * Along with the list is maintained the prefixes/suffixes etc.  When *
+ * any of this becomes invalid -- e.g. if some text is changed on the *
+ * command line -- invalidatelist() should be called, to set          *
+ * validlist to zero and free up the memory used.  This function      *
+ * returns non-zero on error.                                         */
+
+/**/
+static int
+makecomplist(char *s, int incmd, int lst)
+{
+    struct cmlist ms;
+    Cmlist m;
+    char *p, *os = s;
+    int onm = nmatches, osi = movefd(0);
+
+    /* Inside $... ? */
+    if (compfunc && (p = check_param(s, 0, 0)))
+	os = s = p;
+
+    /* We build a copy of the list of matchers to use to make sure that this
+     * works even if a shell function called from the completion code changes
+     * the global matchers. */
+
+    if ((m = cmatcher)) {
+	Cmlist mm, *mp = &mm;
+	int n;
+
+	for (n = 0; m; m = m->next, n++) {
+	    if (m->matcher) {
+		*mp = (Cmlist) zhalloc(sizeof(struct cmlist));
+		(*mp)->matcher = m->matcher;
+		(*mp)->next = NULL;
+		(*mp)->str = dupstring(m->str);
+		mp = &((*mp)->next);
+		addlinknode(matchers, m->matcher);
+		m->matcher->refc++;
+	    }
+	}
+	m = mm;
+	compmatcher = 1;
+	compmatchertot = n;
+    } else
+	compmatcher = 0;
+
+    linwhat = inwhat;
+
+    /* Walk through the global matchers. */
+    for (;;) {
+	bmatchers = NULL;
+	zsfree(compmatcherstr);
+	if (m) {
+	    ms.next = NULL;
+	    ms.matcher = m->matcher;
+	    mstack = &ms;
+
+	    /* Store the matchers used in the bmatchers list which is used
+	     * when building new parts for the string to insert into the 
+	     * line. */
+	    add_bmatchers(m->matcher);
+	    compmatcherstr = ztrdup(m->str);
+	} else {
+	    mstack = NULL;
+	    compmatcherstr = ztrdup("");
+	}
+	ainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
+	fainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
+
+	freecl = NULL;
+
+	if (!validlist)
+	    lastambig = 0;
+	amatches = NULL;
+	mnum = 0;
+	unambig_mnum = -1;
+	isuf = NULL;
+	insmnum = insgnum = 1;
+	insgroup = oldlist = oldins = 0;
+	begcmgroup("default", 0);
+	menucmp = menuacc = newmatches = onlyexpl = 0;
+
+	runhookdef(COMPCTLBEFOREHOOK, NULL);
+
+	s = dupstring(os);
+	if (compfunc)
+	    callcompfunc(s, compfunc);
+	else {
+	    struct ccmakedat dat;
+
+	    dat.str = s;
+	    dat.incmd = incmd;
+	    dat.lst = lst;
+	    runhookdef(COMPCTLMAKEHOOK, (void *) &dat);
+	}
+	endcmgroup(NULL);
+
+	runhookdef(COMPCTLAFTERHOOK,
+		   (void *) ((amatches && !oldlist) ? 1L : 0L));
+
+	if (oldlist) {
+	    nmatches = onm;
+	    validlist = 1;
+	    amatches = lastmatches;
+	    lmatches = lastlmatches;
+	    if (pmatches) {
+		freematches(pmatches);
+		pmatches = NULL;
+		hasperm = 0;
+	    }
+	    redup(osi, 0);
+
+	    return 0;
+	}
+	PERMALLOC {
+	    if (lastmatches) {
+		freematches(lastmatches);
+		lastmatches = NULL;
+	    }
+	    permmatches(1);
+	    amatches = pmatches;
+	    lastpermmnum = permmnum;
+	    lastpermgnum = permgnum;
+	} LASTALLOC;
+
+	lastmatches = pmatches;
+	lastlmatches = lmatches;
+	pmatches = NULL;
+	hasperm = 0;
+	hasoldlist = 1;
+
+	if (nmatches && !errflag) {
+	    validlist = 1;
+
+	    redup(osi, 0);
+
+	    return 0;
+	}
+	if (!m || !(m = m->next))
+	    break;
+
+	errflag = 0;
+	compmatcher++;
+    }
+    redup(osi, 0);
+    return 1;
+}
+
+/* Check if we have to complete a parameter name. */
+
+/**/
+char *
+check_param(char *s, int set, int test)
+{
+    char *p;
+
+    zsfree(parpre);
+    parpre = NULL;
+
+    if (!test)
+	ispar = parq = eparq = 0;
+    /* Try to find a `$'. */
+    for (p = s + offs; p > s && *p != String && *p != Qstring; p--);
+    if (*p == String || *p == Qstring) {
+	/* Handle $$'s */
+	while (p > s && (p[-1] == String || p[-1] == Qstring))
+	    p--;
+	while ((p[1] == String || p[1] == Qstring) &&
+	       (p[2] == String || p[2] == Qstring))
+	    p += 2;
+    }
+    if ((*p == String || *p == Qstring) && p[1] != Inpar && p[1] != Inbrack) {
+	/* This is really a parameter expression (not $(...) or $[...]). */
+	char *b = p + 1, *e = b;
+	int n = 0, br = 1, nest = 0;
+
+	if (*b == Inbrace) {
+	    char *tb = b;
+
+	    /* If this is a ${...}, see if we are before the '}'. */
+	    if (!skipparens(Inbrace, Outbrace, &tb))
+		return NULL;
+
+	    /* Ignore the possible (...) flags. */
+	    b++, br++;
+	    n = skipparens(Inpar, Outpar, &b);
+
+	    for (tb = p - 1; tb > s && *tb != Outbrace && *tb != Inbrace; tb--);
+	    if (tb > s && *tb == Inbrace && (tb[-1] == String || *tb == Qstring))
+		nest = 1;
+	}
+
+	/* Ignore the stuff before the parameter name. */
+	for (; *b; b++)
+	    if (*b != '^' && *b != Hat &&
+		*b != '=' && *b != Equals &&
+		*b != '~' && *b != Tilde)
+		break;
+	if (*b == '#' || *b == Pound || *b == '+')
+	    b++;
+
+	e = b;
+	if (br) {
+	    while (*e == (test ? Dnull : '"'))
+		e++, parq++;
+	    if (!test)
+		b = e;
+	}
+	/* Find the end of the name. */
+	if (*e == Quest || *e == Star || *e == String || *e == Qstring ||
+	    *e == '?'   || *e == '*'  || *e == '$'    ||
+	    *e == '-'   || *e == '!'  || *e == '@')
+	    e++;
+	else if (idigit(*e))
+	    while (idigit(*e))
+		e++;
+	else if (iident(*e))
+	    while (iident(*e) ||
+		   (comppatmatch && *comppatmatch && (*e == Star || *e == Quest)))
+		e++;
+
+	/* Now make sure that the cursor is inside the name. */
+	if (offs <= e - s && offs >= b - s && n <= 0) {
+	    char sav;
+
+	    if (br) {
+		p = e;
+		while (*p == (test ? Dnull : '"'))
+		    p++, parq--, eparq++;
+	    }
+	    /* It is. */
+	    if (test)
+		return b;
+	    /* If we were called from makecomplistflags(), we have to set the
+	     * global variables. */
+
+	    if (set) {
+		if (br >= 2) {
+		    mflags |= CMF_PARBR;
+		    if (nest)
+			mflags |= CMF_PARNEST;
+		}
+		/* Get the prefix (anything up to the character before the name). */
+		isuf = dupstring(e);
+		untokenize(isuf);
+		sav = *b;
+		*b = *e = '\0';
+		ripre = dyncat((ripre ? ripre : ""), s);
+		ipre = dyncat((ipre ? ipre : ""), s);
+		*b = sav;
+
+		untokenize(ipre);
+	    }
+	    /* Save the prefix. */
+	    if (compfunc) {
+		parflags = (br >= 2 ? CMF_PARBR : 0);
+		sav = *b;
+		*b = '\0';
+		untokenize(parpre = ztrdup(s));
+		*b = sav;
+	    }
+	    /* And adjust wb, we, and offs again. */
+	    offs -= b - s;
+	    wb = cs - offs;
+	    we = wb + e - b;
+	    ispar = (br >= 2 ? 2 : 1);
+	    b[we-wb] = '\0';
+	    return b;
+	}
+    }
+    return NULL;
+}
+
+/* Copy the given string and remove backslashes from the copy and return it. */
+
+/**/
+char *
+rembslash(char *s)
+{
+    char *t = s = dupstring(s);
+
+    while (*s)
+	if (*s == '\\') {
+	    chuck(s);
+	    if (*s)
+		s++;
+	} else
+	    s++;
+
+    return t;
+}
+
+/* This should probably be moved into tokenize(). */
+
+/**/
+char *
+ctokenize(char *p)
+{
+    char *r = p;
+    int bslash = 0;
+
+    tokenize(p);
+
+    for (p = r; *p; p++) {
+	if (*p == '\\')
+	    bslash = 1;
+	else {
+	    if (*p == '$' || *p == '{' || *p == '}') {
+		if (bslash)
+		    p[-1] = Bnull;
+		else
+		    *p = (*p == '$' ? String :
+			  (*p == '{' ? Inbrace : Outbrace));
+	    }
+	    bslash = 0;
+	}
+    }
+    return r;
+}
+
+/**/
+char *
+comp_str(int *ipl, int *pl, int untok)
+{
+    char *p = dupstring(compprefix);
+    char *s = dupstring(compsuffix);
+    char *ip = dupstring(compiprefix);
+    char *str;
+    int lp, ls, lip;
+
+    if (!untok) {
+	ctokenize(p);
+	remnulargs(p);
+	ctokenize(s);
+	remnulargs(s);
+    }
+    lp = strlen(p);
+    ls = strlen(s);
+    lip = strlen(ip);
+    str = zhalloc(lip + lp + ls + 1);
+    strcpy(str, ip);
+    strcat(str, p);
+    strcat(str, s);
+
+    if (ipl)
+	*ipl = lip;
+    if (pl)
+	*pl = lp;
+
+    return str;
+}
+
+/* This is for compset -q. */
+
+/**/
+int
+set_comp_sep(void)
+{
+    int lip, lp;
+    char *s = comp_str(&lip, &lp, 0);
+    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 ois = instring, oib = inbackt, noffs = lip + lp;
+    char *tmp, *p, *ns, *ol = (char *) line, sav, oaq = autoq, *qp, *qs;
+
+    if (compisuffix)
+	s = dyncat(s, compisuffix);
+    untokenize(s);
+
+    swb = swe = soffs = 0;
+    ns = NULL;
+
+    /* Put the string in the lexer buffer and call the lexer to *
+     * get the words we have to expand.                        */
+    zleparse = 1;
+    addedx = 1;
+    noerrs = 1;
+    lexsave();
+    tmp = (char *) zhalloc(tl = 3 + strlen(s));
+    tmp[0] = ' ';
+    memcpy(tmp + 1, s, noffs);
+    tmp[(scs = cs = 1 + noffs)] = 'x';
+    strcpy(tmp + 2 + noffs, s + noffs);
+    tmp = rembslash(tmp);
+    inpush(dupstrspace(tmp), 0, NULL);
+    line = (unsigned char *) tmp;
+    ll = tl - 1;
+    strinbeg(0);
+    noaliases = 1;
+    do {
+	ctxtlex();
+	if (tok == LEXERR) {
+	    int j;
+
+	    if (!tokstr)
+		break;
+	    for (j = 0, p = tokstr; *p; p++)
+		if (*p == Snull || *p == Dnull)
+		    j++;
+	    if (j & 1) {
+		tok = STRING;
+		if (p > tokstr && p[-1] == ' ')
+		    p[-1] = '\0';
+	    }
+	}
+	if (tok == ENDINPUT || tok == LEXERR)
+	    break;
+	if (tokstr && *tokstr)
+	    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;
+	    chuck(p + soffs);
+	    ns = dupstring(p);
+	}
+	i++;
+    } while (tok != ENDINPUT && tok != LEXERR);
+    noaliases = 0;
+    strinend();
+    inpop();
+    errflag = zleparse = 0;
+    noerrs = ne;
+    lexrestore();
+    wb = owb;
+    we = owe;
+    cs = ocs;
+    line = (unsigned char *) ol;
+    ll = oll;
+    if (cur < 0 || i < 1)
+	return 1;
+    owb = offs;
+    offs = soffs;
+    if ((p = check_param(ns, 0, 1))) {
+	for (p = ns; *p; p++)
+	    if (*p == Dnull)
+		*p = '"';
+	    else if (*p == Snull)
+		*p = '\'';
+    }
+    offs = owb;
+    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 ? '\'' : '"');
+    } else {
+	instring = 0;
+	autoq = '\0';
+    }
+    for (p = ns, i = swb; *p; p++, i++) {
+	if (INULL(*p)) {
+	    if (i < scs)
+		soffs--;
+	    if (p[1] || *p != Bnull) {
+		if (*p == Bnull) {
+		    if (scs == i + 1)
+			scs++, soffs++;
+		} else {
+		    if (scs > i--)
+			scs--;
+		}
+	    } else {
+		if (scs == swe)
+		    scs--;
+	    }
+	    chuck(p--);
+	}
+    }
+    sav = s[(i = swb - 1)];
+    s[i] = '\0';
+    qp = tricat(qipre, 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, "");
+    sl = strlen(ns);
+    if (soffs > sl)
+	soffs = sl;
+
+    {
+	int set = CP_QUOTE | CP_QUOTING, unset = 0;
+
+	zsfree(compquote);
+	zsfree(compquoting);
+	if (instring == 2) {
+	    compquote = "\"";
+	    compquoting = "double";
+	} else if (instring == 1) {
+	    compquote = "'";
+	    compquoting = "single";
+	} else {
+	    compquote = compquoting = "";
+	    unset = set;
+	    set = 0;
+	}
+	compquote = ztrdup(compquote);
+	compquoting = ztrdup(compquoting);
+	comp_setunset(0, 0, set, unset);
+
+	if (unset(COMPLETEINWORD)) {
+	    untokenize(ns);
+	    zsfree(compprefix);
+	    compprefix = ztrdup(ns);
+	    zsfree(compsuffix);
+	    compsuffix = ztrdup("");
+	} else {
+	    char *ss, sav;
+	    
+	    ss = ns + soffs;
+
+	    sav = *ss;
+	    *ss = '\0';
+	    untokenize(ns);
+	    compprefix = ztrdup(ns);
+	    *ss = sav;
+	    untokenize(ss);
+	    compsuffix = ztrdup(ss);
+	}
+	zsfree(compiprefix);
+	compiprefix = ztrdup("");
+	zsfree(compisuffix);
+	compisuffix = ztrdup("");
+	zsfree(compqiprefix);
+	zsfree(compqisuffix);
+	if (ois) {
+	    compqiprefix = qp;
+	    compqisuffix = qs;
+	} else {
+	    compqiprefix = ztrdup(quotename(qp, NULL));
+	    zsfree(qp);
+	    compqisuffix = ztrdup(quotename(qs, NULL));
+	    zsfree(qs);
+	}
+	freearray(compwords);
+	i = countlinknodes(foo);
+	compwords = (char **) zalloc((i + 1) * sizeof(char *));
+	for (n = firstnode(foo), i = 0; n; incnode(n), i++) {
+	    p = compwords[i] = (char *) getdata(n);
+	    untokenize(p);
+	}
+	compcurrent = cur + 1;
+	compwords[i] = NULL;
+    }
+    autoq = oaq;
+    instring = ois;
+    inbackt = oib;
+
+    return 0;
+}
+
+/* This stores the strings from the list in an array. */
+
+/**/
+void
+set_list_array(char *name, LinkList l)
+{
+    char **a, **p;
+    LinkNode n;
+
+    a = (char **) zalloc((countlinknodes(l) + 1) * sizeof(char *));
+    for (p = a, n = firstnode(l); n; incnode(n))
+	*p++ = ztrdup((char *) getdata(n));
+    *p = NULL;
+
+    setaparam(name, a);
+}
+
+/* Get the words from a variable or a (list of words). */
+
+/**/
+char **
+get_user_var(char *nam)
+{
+    if (!nam)
+	return NULL;
+    else if (*nam == '(') {
+	/* It's a (...) list, not a parameter name. */
+	char *ptr, *s, **uarr, **aptr;
+	int count = 0, notempty = 0, brk = 0;
+	LinkList arrlist = newlinklist();
+
+	ptr = dupstring(nam);
+	s = ptr + 1;
+	while (*++ptr) {
+	    if (*ptr == '\\' && ptr[1])
+		chuck(ptr), notempty = 1;
+	    else if (*ptr == ',' || inblank(*ptr) || *ptr == ')') {
+		if (*ptr == ')')
+		    brk++;
+		if (notempty) {
+		    *ptr = '\0';
+		    count++;
+		    if (*s == '\n')
+			s++;
+		    addlinknode(arrlist, s);
+		}
+		s = ptr + 1;
+		notempty = 0;
+	    } else {
+		notempty = 1;
+		if (*ptr == Meta)
+		    ptr++;
+	    }
+	    if (brk)
+		break;
+	}
+	if (!brk || !count)
+	    return NULL;
+	*ptr = '\0';
+	aptr = uarr = (char **) zhalloc(sizeof(char *) * (count + 1));
+
+	while ((*aptr++ = (char *)ugetnode(arrlist)));
+	uarr[count] = NULL;
+	return uarr;
+    } else {
+	/* Otherwise it should be a parameter name. */
+	char **arr = NULL, *val;
+
+	if ((arr = getaparam(nam)) || (arr = gethparam(nam)))
+	    return (incompfunc ? arrdup(arr) : arr);
+
+	if ((val = getsparam(nam))) {
+	    arr = (char **) zhalloc(2*sizeof(char *));
+	    arr[0] = (incompfunc ? dupstring(val) : val);
+	    arr[1] = NULL;
+	}
+	return arr;
+    }
+}
+
+/* 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. */
+
+/**/
+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 *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;
+    int oisalt = 0, isalt, isexact, doadd, ois = instring, oib = inbackt;
+    Cline lc = NULL, pline = NULL, sline = NULL;
+    Cmatch cm;
+    struct cmlist mst;
+    Cmlist oms = mstack;
+    Patprog cp = NULL;
+    LinkList aparl = NULL, oparl = NULL, dparl = NULL;
+    Brinfo bp, bpl = brbeg, obpl, bsl = brend, obsl;
+
+    for (bp = brbeg; bp; bp = bp->next)
+	bp->curpos = ((dat->aflags & CAF_QUOTE) ? bp->pos : bp->qpos);
+    for (bp = brend; bp; bp = bp->next)
+	bp->curpos = ((dat->aflags & CAF_QUOTE) ? bp->pos : bp->qpos);
+
+    if (dat->flags & CMF_ISPAR)
+	dat->flags |= parflags;
+    if (compquote && (qc = *compquote)) {
+	if (qc == '`') {
+	    instring = 0;
+	    inbackt = 0;
+	    autoq = '\0';
+	} else {
+	    instring = (qc == '\'' ? 1 : 2);
+	    inbackt = 0;
+	    autoq = qc;
+	}
+    } else {
+	instring = inbackt = 0;
+	autoq = '\0';
+    }
+    qipre = ztrdup(compqiprefix ? compqiprefix : "");
+    qisuf = ztrdup(compqisuffix ? compqisuffix : "");
+
+    /* Switch back to the heap that was used when the completion widget
+     * was invoked. */
+    SWITCHHEAPS(compheap) {
+	HEAPALLOC {
+	    if ((doadd = (!dat->apar && !dat->opar && !dat->dpar)) &&
+		(dat->aflags & CAF_MATCH))
+		hasmatched = 1;
+	    if (dat->apar)
+		aparl = newlinklist();
+	    if (dat->opar)
+		oparl = newlinklist();
+	    if (dat->dpar) {
+		if (*(dat->dpar) == '(')
+		    dparr = NULL;
+		else if ((dparr = get_user_var(dat->dpar)) && !*dparr)
+		    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;
+		mst.matcher = dat->match;
+		mstack = &mst;
+
+		if (!mnum)
+		    add_bmatchers(dat->match);
+
+		addlinknode(matchers, dat->match);
+		dat->match->refc++;
+	    }
+	    if (mnum && (mstack || bmatchers))
+		update_bmatchers();
+
+	    /* Get the suffixes to ignore. */
+	    if (dat->ign)
+		aign = get_user_var(dat->ign);
+	    /* Get the display strings. */
+	    if (dat->disp)
+		if ((disp = get_user_var(dat->disp)))
+		    disp--;
+	    /* Get the contents of the completion variables if we have
+	     * to perform matching. */
+	    if (dat->aflags & CAF_MATCH) {
+		lipre = dupstring(compiprefix);
+		lisuf = dupstring(compisuffix);
+		lpre = dupstring(compprefix);
+		lsuf = dupstring(compsuffix);
+		llpl = strlen(lpre);
+		llsl = strlen(lsuf);
+		/* Test if there is an existing -P prefix. */
+		if (dat->pre && *dat->pre) {
+		    char *dp = rembslash(dat->pre);
+
+		    pl = pfxlen(dp, lpre);
+		    llpl -= pl;
+		    lpre += pl;
+		}
+	    }
+	    /* Now duplicate the strings we have from the command line. */
+	    if (dat->ipre)
+		dat->ipre = (lipre ? dyncat(lipre, dat->ipre) :
+			     dupstring(dat->ipre));
+	    else if (lipre)
+		dat->ipre = lipre;
+	    if (dat->isuf)
+		dat->isuf = (lisuf ? dyncat(lisuf, dat->isuf) :
+			     dupstring(dat->isuf));
+	    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);
+		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);
+		lsl = strlen(dat->psuf);
+	    } else
+		lsl = 0;
+	    if (dat->aflags & CAF_MATCH) {
+		int ml;
+
+		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;
+			else
+			    matchparts = tmp;
+		    }
+		    pline = matchparts;
+		    lpre += 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;
+			else
+			    matchparts = tmp;
+		    }
+		    sline = revert_cline(matchparts);
+		    lsuf[llsl - ml] = '\0';
+		    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 == '*');
+		    char *tmp = (char *) zhalloc(2 + llpl + llsl);
+
+		    strcpy(tmp, lpre);
+		    tmp[llpl] = 'x';
+		    strcpy(tmp + llpl + is, lsuf);
+
+		    tokenize(tmp);
+		    remnulargs(tmp);
+		    if (haswilds(tmp)) {
+			if (is)
+			    tmp[llpl] = Star;
+			if ((cp = patcompile(tmp, 0, NULL)))
+			    haspattern = 1;
+		    }
+		}
+	    }
+	    if (*argv) {
+		if (dat->pre)
+		    dat->pre = dupstring(dat->pre);
+		if (dat->suf)
+		    dat->suf = dupstring(dat->suf);
+		if (!dat->prpre && (dat->prpre = oppre)) {
+		    singsub(&(dat->prpre));
+		    untokenize(dat->prpre);
+		} else
+		    dat->prpre = dupstring(dat->prpre);
+		/* 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);
+		}
+		/* Select the set of matches. */
+		oisalt = (dat->aflags & CAF_ALT);
+
+		if (dat->remf) {
+		    dat->remf = dupstring(dat->remf);
+		    dat->rems = NULL;
+		} else if (dat->rems)
+		    dat->rems = dupstring(dat->rems);
+	    }
+	    /* Walk through the matches given. */
+	    obpl = bpl;
+	    obsl = bsl;
+	    for (; (s = *argv); argv++) {
+		bpl = obpl;
+		bsl = obsl;
+		if (disp) {
+		    if (!*++disp)
+			disp = NULL;
+		}
+		sl = strlen(s);
+		isalt = oisalt;
+		if ((!dat->psuf || !*(dat->psuf)) && aign) {
+		    /* Do the suffix-test. If the match has one of the
+		     * suffixes from ign, we put it in the alternate set. */
+		    char **pt = aign;
+		    int filell;
+
+		    for (isalt = 0; !isalt && *pt; pt++)
+			if ((filell = strlen(*pt)) < sl
+			    && !strcmp(*pt, s + sl - filell))
+			    isalt = 1;
+
+		    if (isalt && !doadd) {
+			if (dparr && !*++dparr)
+			    dparr = NULL;
+			continue;
+		    }
+		}
+		if (!(dat->aflags & CAF_MATCH)) {
+		    if (dat->aflags & CAF_QUOTE)
+			ms = dupstring(s);
+		    else
+			sl = strlen(ms = quotename(s, NULL));
+		    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->flags & CMF_FILE) ? 1 : 2) : 0),
+					     &bpl, bcp, &bsl, bcs,
+					     &isexact))) {
+		    if (dparr && !*++dparr)
+			dparr = NULL;
+		    continue;
+		}
+		if (doadd) {
+		    Brinfo bp;
+
+		    for (bp = obpl; bp; bp = bp->next)
+			bp->curpos += bpadd;
+		    for (bp = obsl; bp; bp = bp->next)
+			bp->curpos += bsadd;
+
+		    if ((cm = add_match_data(isalt, ms, lc, dat->ipre, NULL,
+					     dat->isuf, dat->pre, dat->prpre,
+					     dat->ppre, pline,
+					     dat->psuf, sline,
+					     dat->suf, dat->flags, isexact))) {
+			cm->rems = dat->rems;
+			cm->remf = dat->remf;
+			if (disp)
+			    cm->disp = dupstring(*disp);
+		    }
+		} else {
+		    if (dat->apar)
+			addlinknode(aparl, ms);
+		    if (dat->opar)
+			addlinknode(oparl, s);
+		    if (dat->dpar && dparr) {
+			addlinknode(dparl, *dparr);
+			if (!*++dparr)
+			    dparr = NULL;
+		    }
+		    free_cline(lc);
+		}
+	    }
+	    if (dat->apar)
+		set_list_array(dat->apar, aparl);
+	    if (dat->opar)
+		set_list_array(dat->opar, oparl);
+	    if (dat->dpar)
+		set_list_array(dat->dpar, dparl);
+	    if (dat->exp)
+		addexpl();
+	} LASTALLOC;
+    } SWITCHBACKHEAPS;
+
+    /* We switched back to the current heap, now restore the stack of
+     * matchers. */
+    mstack = oms;
+
+    instring = ois;
+    inbackt = oib;
+    autoq = oaq;
+    zsfree(qipre);
+    zsfree(qisuf);
+    qipre = oqp;
+    qisuf = oqs;
+
+    if (mnum == nm)
+	haspattern = ohp;
+
+    return (mnum == nm);
+}
+
+/* This adds all the data we have for a match. */
+
+/**/
+Cmatch
+add_match_data(int alt, char *str, Cline line,
+	       char *ipre, char *ripre, char *isuf,
+	       char *pre, char *prpre,
+	       char *ppre, Cline pline,
+	       char *psuf, Cline sline,
+	       char *suf, int flags, int exact)
+{
+    Cmatch cm;
+    Aminfo ai = (alt ? fainfo : ainfo);
+    int palen, salen, qipl, ipl, pl, ppl, qisl, isl, psl;
+    int sl, lpl, lsl, ml;
+
+    palen = salen = qipl = ipl = pl = ppl = qisl = isl = psl = 0;
+
+    DPUTS(!line, "BUG: add_match_data() without cline");
+
+    cline_matched(line);
+    if (pline)
+	cline_matched(pline);
+    if (sline)
+	cline_matched(sline);
+
+    /* If there is a path suffix, we build a cline list for it and
+     * append it to the list for the match itself. */
+    if (!sline && psuf)
+	salen = (psl = strlen(psuf));
+    if (isuf)
+	salen += (isl = strlen(isuf));
+    if (qisuf)
+	salen += (qisl = strlen(qisuf));
+
+    if (salen) {
+	char *asuf = (char *) zhalloc(salen);
+	Cline pp, p, s, sl = NULL;
+	
+
+	if (psl)
+	    memcpy(asuf, psuf, psl);
+	if (isl)
+	    memcpy(asuf + psl, isuf, isl);
+	if (qisl)
+	    memcpy(asuf + psl + isl, qisuf, qisl);
+
+	for (pp = NULL, p = line; p->next; pp = p, p = p->next);
+
+	if (salen > qisl) {
+	    s = bld_parts(asuf, salen - qisl, salen - qisl, &sl);
+
+	    if (sline) {
+		Cline sp;
+
+		sline = cp_cline(sline, 1);
+
+		for (sp = sline; sp->next; sp = sp->next);
+		sp->next = s;
+		s = sline;
+	    }
+	    if (!(p->flags & (CLF_SUF | CLF_MID)) &&
+		!p->llen && !p->wlen && !p->olen) {
+		if (p->prefix) {
+		    Cline q;
+
+		    for (q = p->prefix; q->next; q = q->next);
+		    q->next = s->prefix;
+		    s->prefix = p->prefix;
+		    p->prefix = NULL;
+		}
+		s->flags |= (p->flags & CLF_MATCHED);
+		free_cline(p);
+		if (pp)
+		    pp->next = s;
+		else
+		    line = s;
+	    } else
+		p->next = s;
+	}
+	if (qisl) {
+	    Cline qsl = bld_parts(asuf + psl + isl, qisl, qisl, NULL);
+
+	    qsl->flags |= CLF_SUF;
+	    qsl->suffix = qsl->prefix;
+	    qsl->prefix = NULL;
+	    if (sl)
+		sl->next = qsl;
+	    else if (sline) {
+		Cline sp;
+
+		sline = cp_cline(sline, 1);
+
+		for (sp = sline; sp->next; sp = sp->next);
+		sp->next = qsl;
+		p->next = sline;
+	    } else
+		p->next = qsl;
+	}
+    } else if (sline) {
+	Cline p;
+
+	for (p = line; p->next; p = p->next);
+	p->next = cp_cline(sline, 1);
+    }
+    /* The prefix is handled differently because the completion code
+     * is much more eager to insert the -P prefix than it is to insert
+     * the -S suffix. */
+    if (qipre)
+	palen = (qipl = strlen(qipre));
+    if (ipre)
+	palen += (ipl = strlen(ipre));
+    if (pre)
+	palen += (pl = strlen(pre));
+    if (!pline && ppre)
+	palen += (ppl = strlen(ppre));
+
+    if (pl) {
+	if (ppl || pline) {
+	    Cline lp, p;
+
+	    if (pline)
+		for (p = cp_cline(pline, 1), lp = p; lp->next; lp = lp->next);
+	    else
+		p = bld_parts(ppre, ppl, ppl, &lp);
+
+	    if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID)) &&
+		!p->llen && !p->wlen && !p->olen) {
+		Cline lpp;
+
+		for (lpp = lp->prefix; lpp->next; lpp = lpp->next);
+
+		lpp->next = line->prefix;
+		line->prefix = lp->prefix;
+		lp->prefix = NULL;
+
+		free_cline(lp);
+
+		if (p != lp) {
+		    Cline q;
+
+		    for (q = p; q->next != lp; q = q->next);
+
+		    q->next = line;
+		    line = p;
+		}
+	    } else {
+		lp->next = line;
+		line = p;
+	    }
+	}
+	if (pl) {
+	    Cline lp, p = bld_parts(pre, pl, pl, &lp);
+
+	    lp->next = line;
+	    line = p;
+	}
+	if (ipl) {
+	    Cline lp, p = bld_parts(ipre, ipl, ipl, &lp);
+
+	    lp->next = line;
+	    line = p;
+	}
+	if (qipl) {
+	    Cline lp, p = bld_parts(qipre, qipl, qipl, &lp);
+
+	    lp->next = line;
+	    line = p;
+	}
+    } else if (palen || pline) {
+	Cline p, lp;
+
+	if (palen) {
+	    char *apre = (char *) zhalloc(palen);
+
+	    if (qipl)
+		memcpy(apre, qipre, qipl);
+	    if (ipl)
+		memcpy(apre + qipl, ipre, ipl);
+	    if (pl)
+		memcpy(apre + qipl + ipl, pre, pl);
+	    if (ppl)
+		memcpy(apre + qipl + ipl + pl, ppre, ppl);
+
+	    p = bld_parts(apre, palen, palen, &lp);
+
+	    if (pline)
+		for (lp->next = cp_cline(pline, 1); lp->next; lp = lp->next);
+	} else
+	    for (p = lp = cp_cline(pline, 1); lp->next; lp = lp->next);
+
+	if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID)) &&
+	    !p->llen && !p->wlen && !p->olen) {
+	    Cline lpp;
+
+	    for (lpp = lp->prefix; lpp->next; lpp = lpp->next);
+
+	    lpp->next = line->prefix;
+	    line->prefix = lp->prefix;
+	    lp->prefix = NULL;
+
+	    free_cline(lp);
+
+	    if (p != lp) {
+		Cline q;
+
+		for (q = p; q->next != lp; q = q->next);
+
+		q->next = line;
+		line = p;
+	    }
+	} else {
+	    lp->next = line;
+	    line = p;
+	}
+    }
+    /* Allocate and fill the match structure. */
+    cm = (Cmatch) zhalloc(sizeof(struct cmatch));
+    cm->str = str;
+    cm->ppre = (ppre && *ppre ? ppre : NULL);
+    cm->psuf = (psuf && *psuf ? psuf : NULL);
+    cm->prpre = ((flags & CMF_FILE) && prpre && *prpre ? prpre : NULL);
+    if (qipre && *qipre)
+	cm->ipre = (ipre && *ipre ? dyncat(qipre, ipre) : dupstring(qipre));
+    else
+	cm->ipre = (ipre && *ipre ? ipre : NULL);
+    cm->ripre = (ripre && *ripre ? ripre : NULL);
+    if (qisuf && *qisuf)
+	cm->isuf = (isuf && *isuf ? dyncat(isuf, qisuf) : dupstring(qisuf));
+    else
+	cm->isuf = (isuf && *isuf ? isuf : NULL);
+    cm->pre = pre;
+    cm->suf = suf;
+    cm->flags = flags;
+    if (nbrbeg) {
+	int *p;
+	Brinfo bp;
+
+	cm->brpl = (int *) zhalloc(nbrbeg * sizeof(int));
+
+	for (p = cm->brpl, bp = brbeg; bp; p++, bp = bp->next)
+	    *p = bp->curpos;
+    } else
+	cm->brpl = NULL;
+    if (nbrend) {
+	int *p;
+	Brinfo bp;
+
+	cm->brsl = (int *) zhalloc(nbrend * sizeof(int));
+
+	for (p = cm->brsl, bp = brend; bp; p++, bp = bp->next)
+	    *p = bp->curpos;
+    } else
+	cm->brsl = NULL;
+    cm->qipl = qipl;
+    cm->qisl = qisl;
+    cm->autoq = (autoq ? autoq : (inbackt ? '`' : '\0'));
+    cm->rems = cm->remf = cm->disp = NULL;
+
+    if ((lastprebr || lastpostbr) && !hasbrpsfx(cm, lastprebr, lastpostbr))
+	return NULL;
+
+    /* Then build the unambiguous cline list. */
+    ai->line = join_clines(ai->line, line);
+
+    mnum++;
+    ai->count++;
+
+    addlinknode((alt ? fmatches : matches), cm);
+
+    newmatches = 1;
+
+    /* One more match for this explanation. */
+    if (curexpl) {
+	if (alt)
+	    curexpl->fcount++;
+	else
+	    curexpl->count++;
+    }
+    if (!ai->firstm)
+	ai->firstm = cm;
+
+    sl = strlen(str);
+    lpl = (cm->ppre ? strlen(cm->ppre) : 0);
+    lsl = (cm->psuf ? strlen(cm->psuf) : 0);
+    ml = sl + lpl + lsl;
+
+    if (ml < minmlen)
+	minmlen = ml;
+    if (ml > maxmlen)
+	maxmlen = ml;
+
+    /* Do we have an exact match? More than one? */
+    if (exact) {
+	if (!ai->exact) {
+	    ai->exact = 1;
+	    if (incompfunc) {
+		/* If a completion widget is active, we make the exact
+		 * string available in `compstate'. */
+
+		char *e;
+
+		zsfree(compexactstr);
+		compexactstr = e = (char *) zalloc(ml + 1);
+		if (cm->ppre) {
+		    strcpy(e, cm->ppre);
+		    e += lpl;
+		}
+		strcpy(e, str);
+		e += sl;
+		if (cm->psuf)
+		    strcpy(e, cm->psuf);
+		comp_setunset(0, 0, CP_EXACTSTR, 0);
+	    }
+	    ai->exactm = cm;
+	} else {
+	    ai->exact = 2;
+	    ai->exactm = NULL;
+	    if (incompfunc)
+		comp_setunset(0, 0, 0, CP_EXACTSTR);
+	}
+    }
+    return cm;
+}
+
+/* This begins a new group of matches. */
+
+/**/
+void
+begcmgroup(char *n, int flags)
+{
+    if (n) {
+	Cmgroup p = amatches;
+
+	while (p) {
+	    if (p->name &&
+		flags == (p->flags & (CGF_NOSORT|CGF_UNIQALL|CGF_UNIQCON)) &&
+		!strcmp(n, p->name)) {
+		mgroup = p;
+
+		expls = p->lexpls;
+		matches = p->lmatches;
+		fmatches = p->lfmatches;
+		allccs = p->lallccs;
+
+		return;
+	    }
+	    p = p->next;
+	}
+    }
+    mgroup = (Cmgroup) zhalloc(sizeof(struct cmgroup));
+    mgroup->name = dupstring(n);
+    mgroup->lcount = mgroup->llcount = mgroup->mcount = 0;
+    mgroup->flags = flags;
+    mgroup->matches = NULL;
+    mgroup->ylist = NULL;
+    mgroup->expls = NULL;
+
+    mgroup->lexpls = expls = newlinklist();
+    mgroup->lmatches = matches = newlinklist();
+    mgroup->lfmatches = fmatches = newlinklist();
+
+    mgroup->lallccs = allccs = ((flags & CGF_NOSORT) ? NULL : newlinklist());
+
+    mgroup->next = amatches;
+    amatches = mgroup;
+}
+
+/* End the current group for now. */
+
+/**/
+void
+endcmgroup(char **ylist)
+{
+    mgroup->ylist = ylist;
+}
+
+/* Add an explanation string to the current group, joining duplicates. */
+
+/**/
+void
+addexpl(void)
+{
+    LinkNode n;
+    Cexpl e;
+
+    for (n = firstnode(expls); n; incnode(n)) {
+	e = (Cexpl) getdata(n);
+	if (!strcmp(curexpl->str, e->str)) {
+	    e->count += curexpl->count;
+	    e->fcount += curexpl->fcount;
+
+	    return;
+	}
+    }
+    addlinknode(expls, curexpl);
+    newmatches = 1;
+}
+
+/* The comparison function for matches (used for sorting). */
+
+/**/
+static int
+matchcmp(Cmatch *a, Cmatch *b)
+{
+    if ((*a)->disp) {
+	if ((*b)->disp) {
+	    if ((*a)->flags & CMF_DISPLINE) {
+		if ((*b)->flags & CMF_DISPLINE)
+		    return strcmp((*a)->disp, (*b)->disp);
+		else
+		    return -1;
+	    } else {
+		if ((*b)->flags & CMF_DISPLINE)
+		    return 1;
+		else
+		    return strcmp((*a)->disp, (*b)->disp);
+	    }
+	}
+	return -1;
+    }
+    if ((*b)->disp)
+	return 1;
+
+    return strbpcmp(&((*a)->str), &((*b)->str));
+}
+
+/* This tests whether two matches are equal (would produce the same
+ * strings on the command line). */
+
+#define matchstreq(a, b) ((!(a) && !(b)) || ((a) && (b) && !strcmp((a), (b))))
+
+/**/
+static int
+matcheq(Cmatch a, Cmatch b)
+{
+    return matchstreq(a->ipre, b->ipre) &&
+	matchstreq(a->pre, b->pre) &&
+	matchstreq(a->ppre, b->ppre) &&
+	matchstreq(a->psuf, b->psuf) &&
+	matchstreq(a->suf, b->suf) &&
+	((!a->disp && !b->disp && matchstreq(a->str, b->str)) ||
+	 (a->disp && b->disp && !strcmp(a->disp, b->disp) &&
+	  matchstreq(a->str, b->str)));
+}
+
+/* Make an array from a linked list. The second argument says whether *
+ * the array should be sorted. The third argument is used to return   *
+ * the number of elements in the resulting array. The fourth argument *
+ * is used to return the number of NOLIST elements. */
+
+/**/
+static Cmatch *
+makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
+{
+    Cmatch *ap, *bp, *cp, *rp;
+    LinkNode nod;
+    int n, nl = 0, ll = 0;
+
+    /* Build an array for the matches. */
+    rp = ap = (Cmatch *) ncalloc(((n = countlinknodes(l)) + 1) *
+				 sizeof(Cmatch));
+
+    /* And copy them into it. */
+    for (nod = firstnode(l); nod; incnode(nod))
+	*ap++ = (Cmatch) getdata(nod);
+    *ap = NULL;
+
+    if (!type) {
+	if (flags) {
+	    char **ap, **bp, **cp;
+
+	    /* Now sort the array (it contains strings). */
+	    qsort((void *) rp, n, sizeof(char *),
+		  (int (*) _((const void *, const void *)))strbpcmp);
+
+	    /* And delete the ones that occur more than once. */
+	    for (ap = cp = (char **) rp; *ap; ap++) {
+		*cp++ = *ap;
+		for (bp = ap; bp[1] && !strcmp(*ap, bp[1]); bp++, n--);
+		ap = bp;
+	    }
+	    *cp = NULL;
+	}
+    } else {
+	if (!(flags & CGF_NOSORT)) {
+	    /* Now sort the array (it contains matches). */
+	    qsort((void *) rp, n, sizeof(Cmatch),
+		  (int (*) _((const void *, const void *)))matchcmp);
+
+	    if (!(flags & CGF_UNIQCON)) {
+		/* And delete the ones that occur more than once. */
+		for (ap = cp = rp; *ap; ap++) {
+		    *cp++ = *ap;
+		    for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--);
+		    ap = bp;
+		    /* Mark those, that would show the same string in the list. */
+		    for (; bp[1] && !(*ap)->disp && !(bp[1])->disp &&
+			     !strcmp((*ap)->str, (bp[1])->str); bp++)
+			(bp[1])->flags |= CMF_NOLIST;
+		}
+		*cp = NULL;
+	    }
+	    for (ap = rp; *ap; ap++) {
+		if ((*ap)->disp && ((*ap)->flags & CMF_DISPLINE))
+		    ll++;
+		if ((*ap)->flags & CMF_NOLIST)
+		    nl++;
+	    }
+	} else {
+	    if (!(flags & CGF_UNIQALL) && !(flags & CGF_UNIQCON)) {
+		for (ap = rp; *ap; ap++) {
+		    for (bp = cp = ap + 1; *bp; bp++) {
+			if (!matcheq(*ap, *bp))
+			    *cp++ = *bp;
+			else
+			    n--;
+		    }
+		    *cp = NULL;
+		}
+	    } else if (!(flags & CGF_UNIQCON)) {
+		for (ap = cp = rp; *ap; ap++) {
+		    *cp++ = *ap;
+		    for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--);
+		    ap = bp;
+		    for (; bp[1] && !(*ap)->disp && !(bp[1])->disp &&
+			     !strcmp((*ap)->str, (bp[1])->str); bp++)
+			(bp[1])->flags |= CMF_NOLIST;
+		}
+		*cp = NULL;
+	    }
+	    for (ap = rp; *ap; ap++) {
+		if ((*ap)->disp && ((*ap)->flags & CMF_DISPLINE))
+		    ll++;
+		if ((*ap)->flags & CMF_NOLIST)
+		    nl++;
+	    }
+	}
+    }
+    if (np)
+	*np = n;
+    if (nlp)
+	*nlp = nl;
+    if (llp)
+	*llp = ll;
+    return rp;
+}
+
+/* This duplicates one match. */
+
+/**/
+static Cmatch
+dupmatch(Cmatch m, int nbeg, int nend)
+{
+    Cmatch r;
+
+    r = (Cmatch) ncalloc(sizeof(struct cmatch));
+
+    r->str = ztrdup(m->str);
+    r->ipre = ztrdup(m->ipre);
+    r->ripre = ztrdup(m->ripre);
+    r->isuf = ztrdup(m->isuf);
+    r->ppre = ztrdup(m->ppre);
+    r->psuf = ztrdup(m->psuf);
+    r->prpre = ztrdup(m->prpre);
+    r->pre = ztrdup(m->pre);
+    r->suf = ztrdup(m->suf);
+    r->flags = m->flags;
+    if (nbeg) {
+	int *p, *q, i;
+
+	r->brpl = (int *) zalloc(nbeg * sizeof(int));
+
+	for (p = r->brpl, q = m->brpl, i = nbeg; i--; p++, q++)
+	    *p = *q;
+    } else
+	r->brpl = NULL;
+    if (nend) {
+	int *p, *q, i;
+
+	r->brsl = (int *) zalloc(nend * sizeof(int));
+
+	for (p = r->brsl, q = m->brsl, i = nend; i--; p++, q++)
+	    *p = *q;
+    } else
+	r->brsl = NULL;
+    r->rems = ztrdup(m->rems);
+    r->remf = ztrdup(m->remf);
+    r->autoq = m->autoq;
+    r->qipl = m->qipl;
+    r->qisl = m->qisl;
+    r->disp = dupstring(m->disp);
+
+    return r;
+}
+
+/* This duplicates all groups of matches. */
+
+/**/
+int
+permmatches(int last)
+{
+    Cmgroup g = amatches, n;
+    Cmatch *p, *q;
+    Cexpl *ep, *eq, e, o;
+    LinkList mlist;
+    static int fi = 0;
+    int nn, nl, ll, gn = 1, mn = 1, rn;
+
+    if (pmatches && !newmatches)
+	return fi;
+
+    newmatches = fi = 0;
+
+    if (pmatches)
+	freematches(pmatches);
+
+    pmatches = lmatches = NULL;
+    nmatches = smatches = 0;
+
+    if (!ainfo->count) {
+	if (last)
+	    ainfo = fainfo;
+	fi = 1;
+    }
+    while (g) {
+	HEAPALLOC {
+	    if (empty(g->lmatches))
+		/* We have no matches, try ignoring fignore. */
+		mlist = g->lfmatches;
+	    else
+		mlist = g->lmatches;
+
+	    g->matches = makearray(mlist, 1, g->flags, &nn, &nl, &ll);
+	    g->mcount = nn;
+	    if ((g->lcount = nn - nl) < 0)
+		g->lcount = 0;
+	    g->llcount = ll;
+	    if (g->ylist) {
+		g->lcount = arrlen(g->ylist);
+		smatches = 2;
+	    }
+	    g->expls = (Cexpl *) makearray(g->lexpls, 0, 0, &(g->ecount),
+					   NULL, NULL);
+
+	    g->ccount = 0;
+	} LASTALLOC;
+
+	nmatches += g->mcount;
+	smatches += g->lcount;
+
+	n = (Cmgroup) ncalloc(sizeof(struct cmgroup));
+
+	if (!lmatches)
+	    lmatches = n;
+	if (pmatches)
+	    pmatches->prev = n;
+	n->next = pmatches;
+	pmatches = n;
+	n->prev = 0;
+	n->num = gn++;
+
+	n->flags = g->flags;
+	n->mcount = g->mcount;
+	n->matches = p = (Cmatch *) ncalloc((n->mcount + 1) *
+					    sizeof(Cmatch));
+	for (q = g->matches; *q; q++, p++)
+	    *p = dupmatch(*q, nbrbeg, nbrend);
+	*p = NULL;
+
+	n->lcount = g->lcount;
+	n->llcount = g->llcount;
+	if (g->ylist)
+	    n->ylist = arrdup(g->ylist);
+	else
+	    n->ylist = NULL;
+
+	if ((n->ecount = g->ecount)) {
+	    n->expls = ep = (Cexpl *) ncalloc((n->ecount + 1) *
+					      sizeof(Cexpl));
+	    for (eq = g->expls; (o = *eq); eq++, ep++) {
+		*ep = e = (Cexpl) ncalloc(sizeof(struct cexpl));
+		e->count = (fi ? o->fcount : o->count);
+		e->str = ztrdup(o->str);
+	    }
+	    *ep = NULL;
+	} else
+	    n->expls = NULL;
+
+	n->widths = NULL;
+
+	g = g->next;
+    }
+    for (g = pmatches; g; g = g->next) {
+	g->nbrbeg = nbrbeg;
+	g->nbrend = nbrend;
+	for (rn = 1, q = g->matches; *q; q++) {
+	    (*q)->rnum = rn++;
+	    (*q)->gnum = mn++;
+	}
+    }
+    hasperm = 1;
+    permmnum = mn - 1;
+    permgnum = gn - 1;
+    listdat.valid = 0;
+
+    return fi;
+}
+
+/* This frees one match. */
+
+/**/
+static void
+freematch(Cmatch m, int nbeg, int nend)
+{
+    if (!m) return;
+
+    zsfree(m->str);
+    zsfree(m->ipre);
+    zsfree(m->ripre);
+    zsfree(m->isuf);
+    zsfree(m->ppre);
+    zsfree(m->psuf);
+    zsfree(m->pre);
+    zsfree(m->suf);
+    zsfree(m->prpre);
+    zsfree(m->rems);
+    zsfree(m->remf);
+    zsfree(m->disp);
+    zfree(m->brpl, nbeg * sizeof(int));
+    zfree(m->brsl, nend * sizeof(int));
+
+    zfree(m, sizeof(m));
+}
+
+/* This frees the groups of matches. */
+
+/**/
+void
+freematches(Cmgroup g)
+{
+    Cmgroup n;
+    Cmatch *m;
+    Cexpl *e;
+
+    while (g) {
+	n = g->next;
+	
+	for (m = g->matches; *m; m++)
+	    freematch(*m, g->nbrbeg, g->nbrend);
+
+	if (g->ylist)
+	    freearray(g->ylist);
+
+	if ((e = g->expls)) {
+	    while (*e) {
+		zsfree((*e)->str);
+		free(*e);
+		e++;
+	    }
+	    free(g->expls);
+	}
+	free(g);
+
+	g = n;
+    }
+}