about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-11-01 09:37:12 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-11-01 09:37:12 +0000
commit00464365490f4c6f77ba5e16d7992cb6bbefc693 (patch)
tree6baa4a083ce2197c5f66451e4c98941f4dfaee8c /Src
parent8f35520cb3ef271407d73404a59d51a6b815e8f7 (diff)
downloadzsh-00464365490f4c6f77ba5e16d7992cb6bbefc693.tar.gz
zsh-00464365490f4c6f77ba5e16d7992cb6bbefc693.tar.xz
zsh-00464365490f4c6f77ba5e16d7992cb6bbefc693.zip
manual/8478
Diffstat (limited to 'Src')
-rw-r--r--Src/Zle/comp.h59
-rw-r--r--Src/Zle/compcore.c2530
-rw-r--r--Src/Zle/complete.c131
-rw-r--r--Src/Zle/complete.mdd4
-rw-r--r--Src/Zle/complist.c8
-rw-r--r--Src/Zle/compmatch.c1906
-rw-r--r--Src/Zle/compresult.c1940
-rw-r--r--Src/Zle/zle.h54
-rw-r--r--Src/Zle/zle.mdd2
-rw-r--r--Src/Zle/zle_main.c108
-rw-r--r--Src/Zle/zle_misc.c51
-rw-r--r--Src/Zle/zle_thingy.c2
-rw-r--r--Src/Zle/zle_tricky.c6373
13 files changed, 6858 insertions, 6310 deletions
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 68575cff4..6097f256c 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -238,7 +238,6 @@ struct cadata {
     char *pre;			/* prefix to insert (-P) */
     char *suf;			/* suffix to insert (-S) */
     char *group;		/* name of the group (-[JV]) */
-    char *ylist;		/* display list (-y) */
     char *rems;			/* remove suffix on chars... (-r) */
     char *remf;			/* function to remove suffix (-R) */
     char *ign;			/* ignored suffixes (-F) */
@@ -270,28 +269,10 @@ struct cldata {
 typedef void (*CLPrintFunc)(Cmgroup, Cmatch *, int, int, int, int,
 			    char *, struct stat *);
 
-/* Information about one brace run. */
+/* Flags for fromcomp. */
 
-typedef struct brinfo *Brinfo;
-
-struct brinfo {
-    Brinfo next;		/* next in list */
-    Brinfo prev;		/* previous (only for closing braces) */
-    char *str;			/* the string to insert */
-    int pos;			/* original position */
-    int qpos;			/* original position, with quoting */
-    int curpos;			/* position for current match */
-};
-
-/* Data given to hooks. */
-
-typedef struct chdata *Chdata;
-
-struct chdata {
-    Cmgroup matches;		/* the matches generated */
-    int num;			/* the number of matches */
-    Cmatch cur;			/* current match or NULL */
-};
+#define FC_LINE   1
+#define FC_INWORD 2
 
 /* Flags for special parameters. */
 
@@ -376,12 +357,30 @@ struct chdata {
 #define CP_KEYPARAMS   27
 #define CP_ALLKEYS     ((unsigned int) 0x7ffffff)
 
-/* Types of completion. */
+/* Hooks. */
+
+#define INSERTMATCHHOOK     (comphooks + 0)
+#define MENUSTARTHOOK       (comphooks + 1)
+#define COMPCTLMAKEHOOK     (comphooks + 2)
+#define COMPCTLBEFOREHOOK   (comphooks + 3)
+#define COMPCTLAFTERHOOK    (comphooks + 4)
+#define COMPLISTMATCHESHOOK (comphooks + 5)
+
+/* compctl hook data struct */
+
+struct ccmakedat {
+    char *str;
+    int incmd;
+    int lst;
+};
+
+/* Data given to offered hooks. */
+
+typedef struct chdata *Chdata;
+
+struct chdata {
+    Cmgroup matches;		/* the matches generated */
+    int num;			/* the number of matches */
+    Cmatch cur;			/* current match or NULL */
+};
 
-#define COMP_COMPLETE        0
-#define COMP_LIST_COMPLETE   1
-#define COMP_SPELL           2
-#define COMP_EXPAND          3
-#define COMP_EXPAND_COMPLETE 4
-#define COMP_LIST_EXPAND     5
-#define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND)
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;
+    }
+}
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index 283b8de62..b4d32a34c 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -1,5 +1,5 @@
 /*
- * complete.c - the complete module
+ * complete.c - the complete module, interface part
  *
  * This file is part of zsh, the Z shell.
  *
@@ -28,10 +28,55 @@
  */
 
 #include "complete.mdh"
-#include "complete.pro"
 #define GLOBAL_PROTOTYPES
 #include "zle_tricky.pro"
 #undef GLOBAL_PROTOTYPES
+#include "complete.pro"
+
+/* Global matcher. */
+
+/**/
+Cmlist cmatcher;
+
+/* global variables for shell parameters in new style completion */
+
+/**/
+zlong compcurrent,
+      compmatcher,
+      compmatchertot,
+      complistmax,
+      complistlines;
+
+/**/
+char **compwords,
+     *compprefix,
+     *compsuffix,
+     *compiprefix,
+     *compisuffix,
+     *compqiprefix,
+     *compqisuffix,
+     *compmatcherstr,
+     *compcontext,
+     *compparameter,
+     *compredirect,
+     *compquote,
+     *compquoting,
+     *comprestore,
+     *complist,
+     *compforcelist,
+     *compinsert,
+     *compexact,
+     *compexactstr,
+     *comppatmatch,
+     *comppatinsert,
+     *complastprompt,
+     *comptoend,
+     *compoldlist,
+     *compoldins,
+     *compvared;
+
+/**/
+Param *comprpms, *compkpms;
 
 /**/
 void
@@ -88,6 +133,7 @@ freecpattern(Cpattern p)
 
 /* Copy a list of completion matchers. */
 
+/**/
 static Cmlist
 cpcmlist(Cmlist l)
 {
@@ -399,7 +445,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
     }
     dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre =
 	dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = 
-	dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = dat.ylist = NULL;
+	dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL;
     dat.match = NULL;
     dat.flags = 0;
     dat.aflags = CAF_MATCH;
@@ -462,10 +508,6 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		if (!(dat.aflags & CAF_UNIQALL))
 		    dat.aflags |= CAF_UNIQCON;
 		break;
-	    case 'y':
-		sp = &(dat.ylist);
-		e = "string expected after -%c";
-		break;
 	    case 'i':
 		sp = &(dat.ipre);
 		e = "string expected after -%c";
@@ -644,6 +686,7 @@ restrict_range(int b, int e)
     }
 }
 
+/**/
 static int
 do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
 {
@@ -1284,12 +1327,36 @@ static struct paramdef patab[] = {
     PARAMDEF("compmatchers", PM_ARRAY|PM_SPECIAL, NULL, cmsetfn, cmgetfn, cmunsetfn)
 };
 
+/* The order of the entries in this table has to match the *HOOK
+ * macros in comp.h */
+
+/**/
+struct hookdef comphooks[] = {
+    HOOKDEF("insert_match", NULL, HOOKF_ALL),
+    HOOKDEF("menu_start", NULL, HOOKF_ALL),
+    HOOKDEF("compctl_make", NULL, 0),
+    HOOKDEF("compctl_before", NULL, 0),
+    HOOKDEF("compctl_after", NULL, 0),
+    HOOKDEF("comp_list_matches", ilistmatches, 0),
+};
+
 /**/
 int
 setup_complete(Module m)
 {
-    makecompparamsptr = makecompparams;
-    comp_setunsetptr = comp_setunset;
+    hasperm = 0;
+
+    comprpms = compkpms = NULL;
+    compwords = NULL;
+    compprefix = compsuffix = compiprefix = compisuffix = 
+	compqiprefix = compqisuffix = compmatcherstr = 
+	compcontext = compparameter = compredirect = compquote =
+	compquoting = comprestore = complist = compinsert =
+	compexact = compexactstr = comppatmatch = comppatinsert =
+	compforcelist = complastprompt = comptoend = 
+	compoldlist = compoldins = compvared = NULL;
+
+    hascompmod = 1;
 
     return 0;
 }
@@ -1298,6 +1365,14 @@ setup_complete(Module m)
 int
 boot_complete(Module m)
 {
+    addhookfunc("complete", (Hookfn) do_completion);
+    addhookfunc("before_complete", (Hookfn) before_complete);
+    addhookfunc("after_complete", (Hookfn) after_complete);
+    addhookfunc("accept_completion", (Hookfn) accept_last);
+    addhookfunc("reverse_menu", (Hookfn) reverse_menu);
+    addhookfunc("list_matches", (Hookfn) list_matches);
+    addhookfunc("invalidate_list", (Hookfn) invalidate_list);
+    addhookdefs(m->nam, comphooks, sizeof(comphooks)/sizeof(*comphooks));
     if (!(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
 	  addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) |
 	  addparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)) |
@@ -1312,6 +1387,14 @@ boot_complete(Module m)
 int
 cleanup_complete(Module m)
 {
+    deletehookfunc("complete", (Hookfn) do_completion);
+    deletehookfunc("before_complete", (Hookfn) before_complete);
+    deletehookfunc("after_complete", (Hookfn) after_complete);
+    deletehookfunc("accept_completion", (Hookfn) accept_last);
+    deletehookfunc("reverse_menu", (Hookfn) reverse_menu);
+    deletehookfunc("list_matches", (Hookfn) list_matches);
+    deletehookfunc("invalidate_list", (Hookfn) invalidate_list);
+    deletehookdefs(m->nam, comphooks, sizeof(comphooks)/sizeof(*comphooks));
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
     deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab));
@@ -1323,8 +1406,34 @@ cleanup_complete(Module m)
 int
 finish_complete(Module m)
 {
-    makecompparamsptr = NULL;
-    comp_setunsetptr = NULL;
+    freearray(compwords);
+    zsfree(compprefix);
+    zsfree(compsuffix);
+    zsfree(compiprefix);
+    zsfree(compisuffix);
+    zsfree(compqiprefix);
+    zsfree(compqisuffix);
+    zsfree(compmatcherstr);
+    zsfree(compcontext);
+    zsfree(compparameter);
+    zsfree(compredirect);
+    zsfree(compquote);
+    zsfree(compquoting);
+    zsfree(comprestore);
+    zsfree(complist);
+    zsfree(compforcelist);
+    zsfree(compinsert);
+    zsfree(compexact);
+    zsfree(compexactstr);
+    zsfree(comppatmatch);
+    zsfree(comppatinsert);
+    zsfree(complastprompt);
+    zsfree(comptoend);
+    zsfree(compoldlist);
+    zsfree(compoldins);
+    zsfree(compvared);
+
+    hascompmod = 0;
 
     return 0;
 }
diff --git a/Src/Zle/complete.mdd b/Src/Zle/complete.mdd
index 628058e2a..3ea802065 100644
--- a/Src/Zle/complete.mdd
+++ b/Src/Zle/complete.mdd
@@ -8,4 +8,6 @@ autoprefixconds="prefix suffix between after"
 
 autoparams="compmatchers"
 
-objects="complete.o"
+headers="comp.h"
+
+objects="complete.o compcore.o compmatch.o compresult.o"
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index cc2ac7517..405f3a6d2 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -622,7 +622,7 @@ domenuselect(Hookdef dummy, Chdata dat)
 		fixsuffix();
 		validlist = 0;
 		amatches = pmatches = lastmatches = NULL;
-		invalidatelist();
+		invalidate_list();
 		PERMALLOC {
 		    menucomplete(zlenoargs);
 		} LASTALLOC;
@@ -653,7 +653,7 @@ domenuselect(Hookdef dummy, Chdata dat)
 		s->nbrbeg = nbrbeg;
 		s->nbrend = nbrend;
 		s->nmatches = nmatches;
-		acceptlast();
+		accept_last();
 		do_menucmp(0);
 		mselect = (*(minfo.cur))->gnum;
 		setwish = 1;
@@ -913,7 +913,7 @@ boot_complist(Module m)
 		 NULL, 0);
 	return -1;
     }
-    addhookfunc("list_matches", (Hookfn) complistmatches);
+    addhookfunc("comp_list_matches", (Hookfn) complistmatches);
     addhookfunc("menu_start", (Hookfn) domenuselect);
     mskeymap = newkeymap(NULL, "menuselect");
     linkkeymap(mskeymap, "menuselect", 1);
@@ -941,7 +941,7 @@ cleanup_complist(Module m)
     free(mgtab);
 
     deletezlefunction(w_menuselect);
-    deletehookfunc("list_matches", (Hookfn) complistmatches);
+    deletehookfunc("comp_list_matches", (Hookfn) complistmatches);
     deletehookfunc("menu_start", (Hookfn) domenuselect);
     unlinkkeymap("menuselect", 1);
     return 0;
diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c
new file mode 100644
index 000000000..32e0c3a68
--- /dev/null
+++ b/Src/Zle/compmatch.c
@@ -0,0 +1,1906 @@
+/*
+ * compmatch.c - the complete module, completion matching 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 "compmatch.pro"
+
+/* Convenience macro for calling bslashquote() (formerly quotename()). */
+
+#define quotename(s, e) bslashquote(s, e, instring)
+
+/* This compares two cpattern lists and returns non-zero if they are
+ * equal. */
+
+/**/
+static int
+cmp_cpatterns(Cpattern a, Cpattern b)
+{
+    while (a) {
+	if (a->equiv != b->equiv || memcmp(a->tab, b->tab, 256))
+	    return 0;
+	a = a->next;
+	b = b->next;
+    }
+    return 1;
+}
+
+/* This compares two cmatchers and returns non-zero if they are equal. */
+
+/**/
+static int
+cmp_cmatchers(Cmatcher a, Cmatcher b)
+{
+    return (a == b ||
+	    (a->flags == b->flags &&
+	     a->llen == b->llen && a->wlen == b->wlen &&
+	     (!a->llen || cmp_cpatterns(a->line, b->line)) &&
+	     (a->wlen <= 0 || cmp_cpatterns(a->word, b->word)) &&
+	     (!(a->flags & CMF_LEFT) ||
+	      (a->lalen == b->lalen &&
+	       (!a->lalen || cmp_cpatterns(a->left, b->left)))) &&
+	     (!(a->flags & CMF_RIGHT) ||
+	      (a->ralen == b->ralen &&
+	       (!a->ralen || cmp_cpatterns(a->right, b->right))))));
+}
+
+/* Add the given matchers to the bmatcher list. */
+
+/**/
+void
+add_bmatchers(Cmatcher m)
+{
+    Cmlist old = bmatchers, *q = &bmatchers, n;
+
+    for (; m; m = m->next) {
+	if ((!m->flags && m->wlen > 0 && m->llen > 0) ||
+	    (m->flags == CMF_RIGHT && m->wlen == -1 && !m->llen)) {
+	    *q = n = (Cmlist) zhalloc(sizeof(struct cmlist));
+	    n->matcher = m;
+	    q = &(n->next);
+	}
+    }
+    *q = old;
+}
+
+/* This is called when the matchers in the mstack have changed to
+ * ensure that the bmatchers list contains no matchers not in mstack. */
+
+/**/
+void
+update_bmatchers(void)
+{
+    Cmlist p = bmatchers, q = NULL, ms;
+    Cmatcher mp;
+    int t;
+
+    while (p) {
+	t = 0;
+	for (ms = mstack; ms && !t; ms = ms->next)
+	    for (mp = ms->matcher; mp && !t; mp = mp->next)
+		t = cmp_cmatchers(mp, p->matcher);
+
+	p = p->next;
+	if (!t) {
+	    if (q)
+		q->next = p;
+	    else
+		bmatchers = p;
+	}
+    }
+}
+
+/* This returns a new Cline structure. */
+
+/**/
+Cline
+get_cline(char *l, int ll, char *w, int wl, char *o, int ol, int fl)
+{
+    Cline r;
+
+    /* Prefer to take it from the buffer list (freecl), if there
+     * is none, allocate a new one. */
+
+    if ((r = freecl))
+	freecl = r->next;
+    else
+	r = (Cline) zhalloc(sizeof(*r));
+
+    r->next = NULL;
+    r->line = l; r->llen = ll;
+    r->word = w; r->wlen = wl;
+    r->orig = o; r->olen = ol;
+    r->slen = 0;
+    r->flags = fl;
+    r->prefix = r->suffix = NULL;
+    return r;
+}
+
+/* This frees a cline list. */
+
+/**/
+void
+free_cline(Cline l)
+{
+    Cline n;
+
+    while (l) {
+	n = l->next;
+	l->next = freecl;
+	freecl = l;
+	free_cline(l->prefix);
+	free_cline(l->suffix);
+	l = n;
+    }
+}
+
+/* Copy a cline list. */
+
+/**/
+Cline
+cp_cline(Cline l, int deep)
+{
+    Cline r = NULL, *p = &r, t, lp = NULL;
+
+    while (l) {
+	if ((t = freecl))
+	    freecl = t->next;
+	else
+	    t = (Cline) zhalloc(sizeof(*t));
+	memcpy(t, l, sizeof(*t));
+	if (deep) {
+	    if (t->prefix)
+		t->prefix = cp_cline(t->prefix, 0);
+	    if (t->suffix)
+		t->suffix = cp_cline(t->suffix, 0);
+	}
+	*p = lp = t;
+	p = &(t->next);
+	l = l->next;
+    }
+    *p = NULL;
+
+    return r;
+}
+
+/* Calculate the length of a cline and its sub-lists. */
+
+/**/
+int
+cline_sublen(Cline l)
+{
+    int len = ((l->flags & CLF_LINE) ? l->llen : l->wlen);
+
+    if (l->olen && !((l->flags & CLF_SUF) ? l->suffix : l->prefix))
+	len += l->olen;
+    else {
+	Cline p;
+
+	for (p = l->prefix; p; p = p->next)
+	    len += ((p->flags & CLF_LINE) ? p->llen : p->wlen);
+	for (p = l->suffix; p; p = p->next)
+	    len += ((p->flags & CLF_LINE) ? p->llen : p->wlen);
+    }
+    return len;
+}
+
+/* Set the lengths in the cline lists. */
+
+/**/
+void
+cline_setlens(Cline l, int both)
+{
+    while (l) {
+	l->max = cline_sublen(l);
+	if (both)
+	    l->min = l->max;
+	l = l->next;
+    }
+}
+
+/* This sets the CLF_MATCHED flag in the given clines. */
+
+/**/
+void
+cline_matched(Cline p)
+{
+    while (p) {
+	p->flags |= CLF_MATCHED;
+	cline_matched(p->prefix);
+	cline_matched(p->suffix);
+
+	p = p->next;
+    }
+}
+
+/* This reverts the order of the elements of the given cline list and
+ * returns a pointer to the new head. */
+
+/**/
+Cline
+revert_cline(Cline p)
+{
+    Cline r = NULL, n;
+
+    while (p) {
+	n = p->next;
+	p->next = r;
+	r = p;
+	p = n;
+    }
+    return r;
+}
+
+/* Global variables used during matching: a char-buffer for the string to
+ * use for the match, and two cline lists for the two levels we use. */
+
+/**/
+char *matchbuf = NULL;
+/**/
+int matchbuflen = 0, matchbufadded;
+
+/**/
+Cline matchparts, matchlastpart;
+/**/
+Cline matchsubs, matchlastsub;
+
+/* This initialises the variables above. */
+
+/**/
+static void
+start_match(void)
+{
+    if (matchbuf)
+	*matchbuf = '\0';
+    matchbufadded = 0;
+    matchparts = matchlastpart = matchsubs = matchlastsub = NULL;
+}
+
+/* This aborts a matching, freeing the cline lists build. */
+
+/**/
+static void
+abort_match(void)
+{
+    free_cline(matchparts);
+    free_cline(matchsubs);
+    matchparts = matchsubs = NULL;
+}
+
+/* This adds a new string in the static char buffer. The arguments are
+ * the matcher used (if any), the strings from the line and the word
+ * and the length of the string from the word. The last argument is
+ * non-zero if we are matching a suffix (where the given string has to 
+ * be prepended to the contents of the buffer). */
+
+/**/
+static void
+add_match_str(Cmatcher m, char *l, char *w, int wl, int sfx)
+{
+    /* Get the string and length to insert: either from the line 
+     * or from the match. */
+    if (m && (m->flags & CMF_LINE)) {
+	wl = m->llen; w = l;
+    }
+    if (wl) {
+	/* Probably resize the buffer. */
+	if (matchbuflen - matchbufadded <= wl) {
+	    int blen = matchbuflen + wl + 20;
+	    char *buf;
+
+	    buf = (char *) zalloc(blen);
+	    memcpy(buf, matchbuf, matchbuflen);
+	    zfree(matchbuf, matchbuflen);
+	    matchbuf = buf;
+	    matchbuflen = blen;
+	}
+	/* Insert the string. */
+	if (sfx) {
+	    memmove(matchbuf + wl, matchbuf, matchbufadded + 1);
+	    memcpy(matchbuf, w, wl);
+	} else
+	    memcpy(matchbuf + matchbufadded, w, wl);
+	matchbufadded += wl;
+	matchbuf[matchbufadded] = '\0';
+    }
+}
+
+/* This adds a cline for a word-part during matching. Arguments are the
+ * matcher used, pointers to the line and word strings for the anchor,
+ * a pointer to the original line string for the whole part, the string
+ * before (or after) the anchor that has not yet been added, the length
+ * of the line-string for that, and a flag saying if we are matching a 
+ * suffix. */
+
+/**/
+static void
+add_match_part(Cmatcher m, char *l, char *w, int wl,
+	       char *o, int ol, char *s, int sl, int osl, int sfx)
+{
+    Cline p, lp;
+
+    /* If the anchors are equal, we keep only one. */
+
+    if (!strncmp(l, w, wl))
+	l = NULL;
+
+    /* Split the new part into parts and turn the last one into a
+     * `suffix' if we have a left anchor. */
+
+    p = bld_parts(s, sl, osl, &lp);
+
+    p->flags &= ~CLF_NEW;
+    if (m && (m->flags & CMF_LEFT)) {
+	lp->flags |= CLF_SUF;
+	lp->suffix = lp->prefix;
+	lp->prefix = NULL;
+    }
+    /* cline lists for suffixes are sorted from back to front, so we have
+     * to revert the list we got. */
+    if (sfx)
+	p = revert_cline(lp = p);
+    /* Now add the sub-clines we already had. */
+    if (matchsubs) {
+	if (sfx) {
+	    Cline q;
+
+	    if ((q = lp->prefix)) {
+		while (q->next)
+		    q = q->next;
+		q->next = matchsubs;
+	    } else
+		lp->prefix = matchsubs;
+
+	    matchlastsub->next = NULL;
+	} else {
+	    matchlastsub->next = p->prefix;
+	    p->prefix = matchsubs;
+	}
+	matchsubs = matchlastsub = NULL;
+    }
+    /* Store the arguments in the last part-cline. */
+    lp->line = l; lp->llen = wl;
+    lp->word = w; lp->wlen = wl;
+    lp->orig = o; lp->olen = ol;
+    lp->flags &= ~CLF_NEW;
+
+    /* Finally, put the new parts on the list. */
+    if (matchlastpart)
+	matchlastpart->next = p;
+    else
+	matchparts = p;
+    matchlastpart = lp;
+}
+
+/* This adds a new sub-cline. Arguments are the matcher and the strings from
+ * the line and the word. */
+
+/**/
+static void
+add_match_sub(Cmatcher m, char *l, int ll, char *w, int wl)
+{
+    int flags;
+    Cline n;
+
+    /* Check if we are interested only in the string from the line. */
+    if (m && (m->flags & CMF_LINE)) {
+	w = NULL; wl = 0;
+	flags = CLF_LINE;
+    } else
+	flags = 0;
+
+    /* And add the cline. */
+    if (wl || ll) {
+	n = get_cline(l, ll, w, wl, NULL, 0, flags);
+	if (matchlastsub)
+	    matchlastsub->next = n;
+	else
+	    matchsubs = n;
+	matchlastsub = n;
+    }
+}
+
+/* This tests if the string from the line l matches the word w. In bp
+ * the offset for the brace is returned, in rwlp the length of the
+ * matched prefix or suffix, not including the stuff before or after
+ * the last anchor is given. When sfx is non-zero matching is done from
+ * the ends of the strings backward, if test is zero, the global variables
+ * above are used to build the string for the match and the cline. If
+ * part is non-zero, we are satisfied if only a part of the line-string
+ * is used (and return the length used). */
+
+/**/
+int
+match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
+	  int sfx, int test, int part)
+{
+    int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw;
+    int il = 0, iw = 0, t, ind, add, he = 0, bpc, obc = bc;
+    VARARR(unsigned char, ea, ll + 1);
+    char *ow;
+    Cmlist ms;
+    Cmatcher mp, lm = NULL;
+    Brinfo bp = NULL;
+
+    if (!test) {
+	start_match();
+	bp = *bpp;
+    }
+    /* Adjust the pointers and get the values for subscripting and
+     * incrementing. */
+
+    if (sfx) {
+	l += ll; w += lw;
+	ind = -1; add = -1;
+    } else {
+	ind = 0; add = 1;
+    }
+    /* ow will always point to the beginning (or end) of that sub-string
+     * in w that wasn't put in the match-variables yet. */
+
+    ow = w;
+
+    /* If the brace is at the beginning, we have to treat it now. */
+
+    if (!test && bp && bc >= bp->pos) {
+	bp->curpos = bc;
+	bp = bp->next;
+    }
+    while (ll && lw) {
+	/* First try the matchers. */
+	for (mp = NULL, ms = mstack; !mp && ms; ms = ms->next) {
+	    for (mp = ms->matcher; mp; mp = mp->next) {
+		t = 1;
+		if ((lm && lm == mp) ||
+		    ((oll == ll || olw == lw) &&
+		     (test == 1 || (test && !mp->left && !mp->right)) &&
+		     mp->wlen < 0))
+		    /* If we were called recursively, don't use `*' patterns
+		     * at the beginning (avoiding infinite recursion). */
+		    continue;
+
+		if (mp->wlen < 0) {
+		    int both, loff, aoff, llen, alen, zoff, moff, ct, ict;
+		    char *tp, savl = '\0', savw;
+		    Cpattern ap;
+
+		    /* This is for `*' patterns, first initialise some
+		     * local variables. */
+		    llen = mp->llen;
+		    alen = (mp->flags & CMF_LEFT ? mp->lalen : mp->ralen);
+
+		    /* Give up if we don't have enough characters for the
+		     * line-string and the anchor. */
+		    if (ll < llen + alen || lw < alen)
+			continue;
+
+		    if (mp->flags & CMF_LEFT) {
+			ap = mp->left; zoff = 0; moff = alen;
+			if (sfx) {
+			    both = 0; loff = -llen; aoff = -(llen + alen);
+			} else {
+			    both = 1; loff = alen; aoff = 0;
+			}
+		    } else {
+			ap = mp->right; zoff = alen; moff = 0;
+			if (sfx) {
+			    both = 1; loff = -(llen + alen); aoff = -alen;
+			} else {
+			    both = 0; loff = 0; aoff = llen;
+			}
+		    }
+		    /* Try to match the line pattern and the anchor. */
+		    if (!pattern_match(mp->line, l + loff, NULL, NULL))
+			continue;
+		    if (ap) {
+			if (!pattern_match(ap, l + aoff, NULL, NULL) ||
+			    (both && (!pattern_match(ap, w + aoff, NULL, NULL) ||
+				      !match_parts(l + aoff, w + aoff, alen,
+						   part))))
+				continue;
+		    } else if (!both || il || iw)
+			continue;
+
+		    /* Fine, now we call ourselves recursively to find the
+		     * string matched by the `*'. */
+		    if (sfx) {
+			savl = l[-(llen + zoff)];
+			l[-(llen + zoff)] = '\0';
+		    }
+		    for (t = 0, tp = w, ct = 0, ict = lw - alen + 1;
+			 ict;
+			 tp += add, ct++, ict--) {
+			if ((both &&
+			     (!ap || !test ||
+			      !pattern_match(ap, tp + aoff, NULL, NULL))) ||
+			    (!both &&
+			     pattern_match(ap, tp - moff, NULL, NULL) &&
+			     match_parts(l + aoff , tp - moff, alen, part))) {
+			    if (sfx) {
+				savw = tp[-zoff];
+				tp[-zoff] = '\0';
+				t = match_str(l - ll, w - lw,
+					      NULL, 0, NULL, 1, 2, part);
+				tp[-zoff] = savw;
+			    } else
+				t = match_str(l + llen + moff, tp + moff,
+					      NULL, 0, NULL, 0, 1, part);
+			    if (t || !both)
+				break;
+			}
+		    }
+		    ict = ct;
+		    if (sfx)
+			l[-(llen + zoff)] = savl;
+
+		    /* Have we found a position in w where the rest of l
+		     * matches? */
+		    if (!t)
+			continue;
+
+		    /* Yes, add the strings and clines if this is a 
+		     * top-level call. */
+		    if (!test && (!he || (llen + alen))) {
+			char *op, *lp, *map, *wap, *wmp;
+			int ol;
+
+			if (sfx) {
+			    op = w; ol = ow - w; lp = l - (llen + alen);
+			    map = tp - alen;
+			    if (mp->flags & CMF_LEFT) {
+				wap = tp - alen; wmp = tp;
+			    } else {
+				wap = w - alen; wmp = tp - alen;
+			    }
+			} else {
+			    op = ow; ol = w - ow; lp = l;
+			    map = ow;
+			    if (mp->flags & CMF_LEFT) {
+				wap = w; wmp = w + alen;
+			    } else {
+				wap = tp; wmp = ow;
+			    }
+			}
+			/* If the matcher says that we are only interested
+			 * in the line pattern, we just add that and the
+			 * anchor and the string not added yet. Otherwise
+			 * we add a new part. */
+			if (mp->flags & CMF_LINE) {
+			    add_match_str(NULL, NULL, op, ol, sfx);
+			    add_match_str(NULL, NULL, lp, llen + alen, sfx);
+			    add_match_sub(NULL, NULL, ol, op, ol);
+			    add_match_sub(NULL, NULL, llen + alen,
+					  lp, llen + alen);
+			} else if (sfx) {
+			    add_match_str(NULL, NULL,
+					  map, ct + ol + alen, sfx);
+			    add_match_part(mp, l + aoff, wap, alen,
+					   l + loff, llen, op, ol, ol, sfx);
+			    add_match_sub(NULL, NULL, 0, wmp, ct);
+			} else {
+			    add_match_str(NULL, NULL,
+					  map, ct + ol + alen, sfx);
+			    if (both) {
+				add_match_sub(NULL, NULL, ol, op, ol);
+				ol = -1;
+			    } else
+				ct += ol;
+			    add_match_part(mp, l + aoff, wap, alen,
+					   l + loff, llen, wmp, ct, ol, sfx);
+			}
+		    }
+		    /* Now skip over the matched portion and the anchor. */
+		    llen += alen; alen += ict;
+		    if (sfx) {
+			l -= llen; w -= alen;
+		    } else {
+			l += llen; w += alen;
+		    }
+		    ll -= llen; il += llen;
+		    lw -= alen; iw += alen;
+		    bc += llen;
+
+		    if (!test)
+			while (bp &&
+			       bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
+			    bp->curpos = matchbufadded + bpc - bc + obc;
+			    bp = bp->next;
+			}
+		    ow = w;
+
+		    if (!llen && !alen) {
+			lm = mp;
+			if (he)
+			    mp = NULL;
+			else
+			    he = 1;
+		    } else {
+			lm = NULL; he = 0;
+		    }
+		    break;
+		} else if (ll >= mp->llen && lw >= mp->wlen) {
+		    /* Non-`*'-pattern. */
+		    char *tl, *tw;
+		    int tll, tlw, til, tiw;
+
+		    /* We do this only if the line- and word-substrings
+		     * are not equal. */
+		    if (!(mp->flags & (CMF_LEFT | CMF_RIGHT)) &&
+			mp->llen == mp->wlen &&
+			!(sfx ? strncmp(l - mp->llen, w - mp->wlen, mp->llen) :
+			  strncmp(l, w, mp->llen)))
+			continue;
+
+		    /* Using local variables to make the following
+		     * independent of whether we match a prefix or a
+		     * suffix. */
+		    if (sfx) {
+			tl = l - mp->llen; tw = w - mp->wlen;
+			til = ll - mp->llen; tiw = lw - mp->wlen;
+			tll = il + mp->llen; tlw = iw + mp->wlen;
+		    } else {
+			tl = l; tw = w;
+			til = il; tiw = iw;
+			tll = ll; tlw = lw;
+		    }
+		    if (mp->flags & CMF_LEFT) {
+			/* Try to match the left anchor, if any. */
+			if (til < mp->lalen || tiw < mp->lalen)
+			    continue;
+			else if (mp->left)
+			    t = pattern_match(mp->left, tl - mp->lalen,
+					      NULL, NULL) &&
+				pattern_match(mp->left, tw - mp->lalen,
+					      NULL, NULL);
+			else
+			    t = (!sfx && !il && !iw);
+		    }
+		    if (mp->flags & CMF_RIGHT) {
+			/* Try to match the right anchor, if any. */
+			if (tll < mp->llen + mp->ralen ||
+			    tlw < mp->wlen + mp->ralen)
+			    continue;
+			else if (mp->left)
+			    t = pattern_match(mp->right,
+					      tl + mp->llen - mp->ralen,
+					      NULL, NULL) &&
+				pattern_match(mp->right,
+					      tw + mp->wlen - mp->ralen,
+					      NULL, NULL);
+			else
+			    t = (sfx && !il && !iw);
+		    }
+		    /* Now try to match the line and word patterns. */
+		    if (!t ||
+			!pattern_match(mp->line, tl, NULL, ea) ||
+			!pattern_match(mp->word, tw, ea, NULL))
+			continue;
+
+		    /* Probably add the matched strings. */
+		    if (!test) {
+			if (sfx)
+			    add_match_str(NULL, NULL, w, ow - w, 0);
+			else
+			    add_match_str(NULL, NULL, ow, w - ow, 0);
+			add_match_str(mp, tl, tw, mp->wlen, 0);
+			if (sfx)
+			    add_match_sub(NULL, NULL, 0, w, ow - w);
+			else
+			    add_match_sub(NULL, NULL, 0, ow, w - ow);
+
+			add_match_sub(mp, tl, mp->llen, tw, mp->wlen);
+		    }
+		    if (sfx) {
+			l = tl;	w = tw;
+		    } else {
+			l += mp->llen; w += mp->wlen;
+		    }
+		    il += mp->llen; iw += mp->wlen;
+		    ll -= mp->llen; lw -= mp->wlen;
+		    bc += mp->llen;
+
+		    if (!test)
+			while (bp &&
+			       bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
+			    bp->curpos = matchbufadded + bpc - bc + obc;
+			    bp = bp->next;
+			}
+		    ow = w;
+		    lm = NULL;
+		    he = 0;
+		    break;
+		}
+	    }
+	}
+	if (mp)
+	    continue;
+
+	if (l[ind] == w[ind]) {
+	    /* No matcher could be used, but the strings have the same
+	     * character here, skip over it. */
+	    l += add; w += add;
+	    il++; iw++;
+	    ll--; lw--;
+	    bc++;
+	    if (!test)
+		while (bp && bc >= (useqbr ? bp->qpos : bp->pos)) {
+		    bp->curpos = matchbufadded + (sfx ? (ow - w) : (w - ow)) + obc;
+		    bp = bp->next;
+		}
+	    lm = NULL;
+	    he = 0;
+	} else {
+	    /* No matcher and different characters: l does not match w. */
+	    if (test)
+		return 0;
+
+	    abort_match();
+
+	    return -1;
+	}
+    }
+    /* If this is a recursive call, we just return if l matched w or not. */
+    if (test)
+	return (part || !ll);
+
+    /* In top-level calls, if ll is non-zero (unmatched portion in l),
+     * we have to free the collected clines. */
+    if (!part && ll) {
+	abort_match();
+
+	return -1;
+    }
+    if (rwlp)
+	*rwlp = iw - (sfx ? ow - w : w - ow);
+
+    /* If we matched a suffix, the anchors stored in the top-clines
+     * will be in the wrong clines: shifted by one. Adjust this. */
+    if (sfx && matchparts) {
+	Cline t, tn, s;
+
+	if (matchparts->prefix || matchparts->suffix) {
+	    t = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
+	    t->next = matchparts;
+	    if (matchparts->prefix)
+		t->prefix = (Cline) 1;
+	    else
+		t->suffix = (Cline) 1;
+	    matchparts = t;
+	}
+	for (t = matchparts; (tn = t->next); t = tn) {
+	    s = (tn->prefix ? tn->prefix : tn->suffix);
+	    if (t->suffix)
+		t->suffix = s;
+	    else
+		t->prefix = s;
+	}
+	t->prefix = t->suffix = NULL;
+    }
+    /* Finally, return the number of matched characters. */
+
+    *bpp = bp;
+    return (part ? il : iw);
+}
+
+/* Wrapper for match_str(), only for a certain length and only doing
+ * the test. */
+
+/**/
+static int
+match_parts(char *l, char *w, int n, int part)
+{
+    char lsav = l[n], wsav = w[n];
+    int ret;
+
+    l[n] = w[n] = '\0';
+    ret = match_str(l, w, NULL, 0, NULL, 0, 1, part);
+    l[n] = lsav;
+    w[n] = wsav;
+
+    return ret;
+}
+
+/* Check if the word w is matched by the strings in pfx and sfx (the prefix
+ * and the suffix from the line) or the pattern cp. In clp a cline list for
+ * w is returned.
+ * qu is non-zero if the words has to be quoted before processed any further.
+ * bpl and bsl are used to report the positions where the brace-strings in
+ * the prefix and the suffix have to be re-inserted if this match is inserted
+ * in the line.
+ * The return value is the string to use as a completion or NULL if the prefix
+ * and the suffix don't match the word w. */
+
+/**/
+char *
+comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu,
+	   Brinfo *bpl, int bcp, Brinfo *bsl, int bcs, int *exact)
+{
+    char *r = NULL;
+
+    if (cp) {
+	/* We have a globcomplete-like pattern, just use that. */
+	int wl;
+
+	r = w;
+	if (!pattry(cp, r))
+	    return NULL;
+    
+	r = (qu ? quotename(r, NULL) : dupstring(r));
+	if (qu == 2 && r[0] == '\\' && r[1] == '~')
+	    chuck(r);
+	/* We still break it into parts here, trying to build a sensible
+	 * cline list for these matches, too. */
+	w = dupstring(w);
+	wl = strlen(w);
+	*clp = bld_parts(w, wl, wl, NULL);
+	*exact = 0;
+    } else {
+	Cline pli, plil;
+	int mpl, rpl, wl;
+
+	w = (qu ? quotename(w, NULL) : dupstring(w));
+	if (qu == 2 && w[0] == '\\' && w[1] == '~')
+	    chuck(w);
+
+	wl = strlen(w);
+
+	/* Always try to match the prefix. */
+
+	useqbr = qu;
+	if ((mpl = match_str(pfx, w, bpl, bcp, &rpl, 0, 0, 0)) < 0)
+	    return NULL;
+
+	if (sfx && *sfx) {
+	    int wpl = matchbufadded, msl, rsl;
+	    VARARR(char, wpfx, wpl);
+	    Cline mli, mlil;
+
+	    /* We also have a suffix to match, so first save the
+	     * contents of the global matching variables. */
+	    memcpy(wpfx, matchbuf, wpl);
+	    if (matchsubs) {
+		Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
+
+		tmp->prefix = matchsubs;
+		if (matchlastpart)
+		    matchlastpart->next = tmp;
+		else
+		    matchparts = tmp;
+	    }
+	    pli = matchparts;
+	    plil = matchlastpart;
+
+	    /* The try to match the suffix. */
+
+	    if ((msl = match_str(sfx, w + mpl, bsl, bcs, &rsl, 1, 0, 0)) < 0) {
+		free_cline(pli);
+
+		return NULL;
+	    }
+	    /* Matched, so add the string in the middle and the saved
+	     * string for the prefix, and build a combined cline list
+	     * for the prefix and the suffix. */
+	    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;
+	    }
+	    add_match_str(NULL, NULL, w + rpl, wl - rpl - rsl, 1);
+	    add_match_str(NULL, NULL, wpfx, wpl, 1);
+
+	    mli = bld_parts(w + rpl, wl - rpl - rsl,
+			    (mpl - rpl) + (msl - rsl), &mlil);
+	    mlil->flags |= CLF_MID;
+	    mlil->slen = msl - rsl;
+	    mlil->next = revert_cline(matchparts);
+
+	    if (plil)
+		plil->next = mli;
+	    else
+		pli = mli;
+	} else {
+	    /* Only a prefix, add the string and a part-cline for it. */
+	    add_match_str(NULL, NULL, w + rpl, wl - rpl, 0);
+
+	    add_match_part(NULL, NULL, NULL, 0, NULL, 0, w + rpl, wl - rpl,
+			   mpl - rpl, 0);
+	    pli = matchparts;
+	}
+	r = dupstring(matchbuf ? matchbuf : "");
+
+	*clp = pli;
+
+	/* Test if the string built is equal to the one from the line. */
+	if (sfx && *sfx) {
+	    int pl = strlen(pfx);
+
+	    *exact = (!strncmp(pfx, w, pl) && !strcmp(sfx, w + pl));
+	} else
+	    *exact = !strcmp(pfx, w);
+    }
+    if (!qu)
+	hasunqu = 1;
+
+    return r;
+}
+
+/* Check if the given pattern matches the given string.             *
+ * `in' and `out' are used for {...} classes. In `out' we store the *
+ * character number that was matched. In the word pattern this is   *
+ * given in `in' so that we can easily test if we found the         *
+ * corresponding character. */
+
+/**/
+static int
+pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out)
+{
+    unsigned char c;
+
+    while (p) {
+	c = *((unsigned char *) s);
+
+	if (out)
+	    *out = 0;
+
+	if (p->equiv) {
+	    if (in) {
+		c = p->tab[c];
+		if ((*in && *in != c) || (!*in && !c))
+		    return 0;
+	    } else if (out) {
+		if (!(*out = p->tab[c]))
+		    return 0;
+	    } else if (!p->tab[c])
+		return 0;
+
+	    if (in && *in)
+		in++;
+	    if (out)
+		out++;
+	} else if (!p->tab[c])
+	    return 0;
+
+	s++;
+	p = p->next;
+    }
+    return 1;
+}
+
+/* This splits the given string into a list of cline structs, separated
+ * at those places where one of the anchors of an `*' pattern was found.
+ * plen gives the number of characters on the line that matched this
+ * string. In lp we return a pointer to the last cline struct we build. */
+
+/**/
+Cline
+bld_parts(char *str, int len, int plen, Cline *lp)
+{
+    Cline ret = NULL, *q = &ret, n;
+    Cmlist ms;
+    Cmatcher mp;
+    int t, op = plen;
+    char *p = str;
+
+    while (len) {
+	for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+	    mp = ms->matcher;
+	    if (mp->flags == CMF_RIGHT && mp->wlen == -1 &&
+		!mp->llen && len >= mp->ralen && mp->ralen &&
+		pattern_match(mp->right, str, NULL, NULL)) {
+		int olen = str - p, llen;
+
+		/* We found an anchor, create a new cline. The NEW flag
+		 * is set if the characters before the anchor were not
+		 * on the line. */
+		*q = n = get_cline(NULL, mp->ralen, str, mp->ralen, NULL, 0,
+				   ((plen < 0) ? CLF_NEW : 0));
+
+		/* If there were any characters before the anchor, add
+		 * them as a cline struct. */
+
+		if (p != str) {
+		    llen = (op < 0 ? 0 : op);
+
+		    if (llen > olen)
+			llen = olen;
+		    n->prefix = get_cline(NULL, llen, p, olen, NULL, 0, 0);
+		}
+		q = &(n->next);
+		str += mp->ralen; len -= mp->ralen;
+		plen -= mp->ralen;
+		op -= olen;
+		p = str;
+		t = 1;
+	    }
+	}
+	if (!t) {
+	    /* No anchor was found here, skip. */
+	    str++; len--;
+	    plen--;
+	}
+    }
+    /* This is the cline struct for the remaining string at the end. */
+
+    *q = n = get_cline(NULL, 0, NULL, 0, NULL, 0, (plen < 0 ? CLF_NEW : 0));
+    if (p != str) {
+	int olen = str - p, llen = (op < 0 ? 0 : op);
+
+	if (llen > olen)
+	    llen = olen;
+	n->prefix = get_cline(NULL, llen, p, olen, NULL, 0, 0);
+    }
+    n->next = NULL;
+
+    if (lp)
+	*lp = n;
+
+    return ret;
+}
+
+/* This builds all the possible line patterns for the pattern pat in the
+ * buffer line. Initially line is the same as lp, but during recursive
+ * calls lp is incremented for storing successive characters. Whenever
+ * a full possible string is build, we test if this line matches the
+ * string given by wlen and word. The in argument contains the characters
+ * to use for the correspondence classes, it was filled by a call to 
+ * pattern_match() in the calling function.
+ * The return value is the length of the string matched in the word, it
+ * is zero if we couldn't build a line that matches the word. */
+
+/**/
+static int
+bld_line(Cpattern pat, char *line, char *lp,
+	 char *word, int wlen, unsigned char *in, int sfx)
+{
+    if (pat) {
+	/* Still working on the pattern. */
+
+	int i, l;
+	unsigned char c = 0;
+
+	/* Get the number of the character for a correspondence class
+	 * if it has a correxponding class. */
+	if (pat->equiv)
+	    if ((c = *in))
+		in++;
+
+	/* Walk through the table in the pattern and try the characters
+	 * that may appear in the current position. */
+	for (i = 0; i < 256; i++)
+	    if ((pat->equiv && c) ? (c == pat->tab[i]) : pat->tab[i]) {
+		*lp = i;
+		/* We stored the character, now call ourselves to build
+		 * the rest. */
+		if ((l = bld_line(pat->next, line, lp + 1, word, wlen,
+				  in, sfx)))
+		    return l;
+	    }
+    } else {
+	/* We reached the end, i.e. the line string is fully build, now
+	 * see if it matches the given word. */
+
+	Cmlist ms;
+	Cmatcher mp;
+	int l = lp - line, t, rl = 0, ind, add;
+	VARARR(unsigned char, ea, l + 1);
+
+	/* Quick test if the strings are exactly the same. */
+	if (l == wlen && !strncmp(line, word, l))
+	    return l;
+
+	if (sfx) {
+	    line = lp; word += wlen;
+	    ind = -1; add = -1;
+	} else {
+	    ind = 0; add = 1;
+	}
+	/* We loop through the whole line string built. */
+	while (l && wlen) {
+	    if (word[ind] == line[ind]) {
+		/* The same character in both strings, skip over. */
+		line += add; word += add;
+		l--; wlen--; rl++;
+	    } else {
+		t = 0;
+		for (ms = bmatchers; ms && !t; ms = ms->next) {
+		    mp = ms->matcher;
+		    if (!mp->flags && mp->wlen <= wlen && mp->llen <= l &&
+			pattern_match(mp->line, (sfx ? line - mp->llen : line),
+				      NULL, ea) &&
+			pattern_match(mp->word, (sfx ? word - mp->wlen : word),
+				      ea, NULL)) {
+			/* Both the line and the word pattern matched,
+			 * now skip over the matched portions. */
+			if (sfx) {
+			    line -= mp->llen; word -= mp->wlen;
+			} else {
+			    line += mp->llen; word += mp->wlen;
+			}
+			l -= mp->llen; wlen -= mp->wlen; rl += mp->wlen;
+			t = 1;
+		    }
+		}
+		if (!t)
+		    /* Didn't match, give up. */
+		    return 0;
+	    }
+	}
+	if (!l)
+	    /* Unmatched portion in the line built, return matched length. */
+	    return rl;
+    }
+    return 0;
+}
+
+/* This builds a string that may be put on the line that fully matches the
+ * given strings. The return value is NULL if no such string could be built
+ * or that string in local static memory, dup it. */
+
+/**/
+static char *
+join_strs(int la, char *sa, int lb, char *sb)
+{
+    static char *rs = NULL;
+    static int rl = 0;
+
+    VARARR(unsigned char, ea, (la > lb ? la : lb) + 1);
+    Cmlist ms;
+    Cmatcher mp;
+    int t, bl, rr = rl;
+    char *rp = rs;
+
+    while (la && lb) {
+	if (*sa != *sb) {
+	    /* Different characters, try the matchers. */
+	    for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+		mp = ms->matcher;
+		if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
+		    mp->wlen <= la && mp->wlen <= lb) {
+		    /* The pattern has no anchors and the word
+		     * pattern fits, try it. */
+		    if ((t = pattern_match(mp->word, sa, NULL, ea)) ||
+			pattern_match(mp->word, sb, NULL, ea)) {
+			/* It matched one of the strings, t says which one. */
+			VARARR(char, line, mp->llen + 1);
+			char **ap, **bp;
+			int *alp, *blp;
+
+			if (t) {
+			    ap = &sa; alp = &la;
+			    bp = &sb; blp = &lb;
+			} else {
+			    ap = &sb; alp = &lb;
+			    bp = &sa; blp = &la;
+			}
+			/* Now try to build a string that matches the other
+			 * string. */
+			if ((bl = bld_line(mp->line, line, line,
+					   *bp, *blp, ea, 0))) {
+			    /* Found one, put it into the return string. */
+			    line[mp->llen] = '\0';
+			    if (rr <= mp->llen) {
+				char *or = rs;
+
+				rs = realloc(rs, (rl += 20));
+				rr += 20;
+				rp += rs - or;
+			    }
+			    memcpy(rp, line, mp->llen);
+			    rp += mp->llen; rr -= mp->llen;
+			    *ap += mp->wlen; *alp -= mp->wlen;
+			    *bp += bl; *blp -= bl;
+			    t = 1;
+			} else
+			    t = 0;
+		    }
+		}
+	    }
+	    if (!t)
+		break;
+	} else {
+	    /* Same character, just take it. */
+	    if (rr <= 1) {
+		char *or = rs;
+
+		rs = realloc(rs, (rl += 20));
+		rr += 20;
+		rp += rs - or;
+	    }
+	    *rp++ = *sa; rr--;
+	    sa++; sb++;
+	    la--; lb--;
+	}
+    }
+    if (la || lb)
+	return NULL;
+
+    *rp = '\0';
+
+    return rs;
+}
+
+/* This compares the anchors stored in two top-level clines. */
+
+/**/
+static int
+cmp_anchors(Cline o, Cline n, int join)
+{
+    int line = 0;
+    char *j;
+
+    /* First try the exact strings. */
+    if ((!(o->flags & CLF_LINE) && o->wlen == n->wlen &&
+	 (!o->word || !strncmp(o->word, n->word, o->wlen))) ||
+	(line = ((!o->line && !n->line && !o->wlen && !n->wlen) ||
+		 (o->llen == n->llen && o->line && n->line &&
+		  !strncmp(o->line, n->line, o->llen))))) {
+	if (line) {
+	    o->flags |= CLF_LINE;
+	    o->word = NULL;
+	    n->wlen = 0;
+	}
+	return 1;
+    }
+    /* Didn't work, try to build a string matching both anchors. */
+    if (join && !(o->flags & CLF_JOIN) && o->word && n->word &&
+	(j = join_strs(o->wlen, o->word, n->wlen, n->word))) {
+	o->flags |= CLF_JOIN;
+	o->wlen = strlen(j);
+	o->word = dupstring(j);
+
+	return 2;
+    }
+    return 0;
+}
+
+/* Below is the code to join two cline lists. This struct is used to walk
+ * through a sub-list. */
+
+typedef struct cmdata *Cmdata;
+
+struct cmdata {
+    Cline cl, pcl;
+    char *str, *astr;
+    int len, alen, olen, line;
+};
+
+/* This is used to ensure that a cmdata struct contains usable data.
+ * The return value is non-zero if we reached the end. */
+
+static int
+check_cmdata(Cmdata md, int sfx)
+{
+    /* We will use the str and len fields to contain the next sub-string
+     * in the list. If len is zero, we have to use the next cline. */
+    if (!md->len) {
+	/* If there is none, we reached the end. */
+	if (!md->cl)
+	    return 1;
+
+	/* Otherwise, get the string. Only the line-string or both.
+	 * We also have to adjust the pointer if this is for a suffix. */
+	if (md->cl->flags & CLF_LINE) {
+	    md->line = 1;
+	    md->len = md->cl->llen;
+	    md->str = md->cl->line;
+	} else {
+	    md->line = 0;
+	    md->len = md->olen = md->cl->wlen;
+	    if ((md->str = md->cl->word) && sfx)
+		md->str += md->len;
+	    md->alen = md->cl->llen;
+	    if ((md->astr = md->cl->line) && sfx)
+		md->astr += md->alen;
+	}
+	md->pcl = md->cl;
+	md->cl = md->cl->next;
+    }
+    return 0;
+}
+
+/* This puts the not-yet-matched portion back into the last cline and 
+ * returns that. */
+
+static Cline
+undo_cmdata(Cmdata md, int sfx)
+{
+    Cline r = md->pcl;
+
+    if (md->line) {
+	r->word = NULL;
+	r->wlen = 0;
+	r->flags |= CLF_LINE;
+	r->llen = md->len;
+	r->line = md->str - (sfx ? md->len : 0);
+    } else if (md->len != md->olen) {
+	r->wlen = md->len;
+	r->word = md->str - (sfx ? md->len : 0);
+    }
+    return r;
+}
+
+/* This tries to build a string matching a sub-string in a sub-cline
+ * that could not be matched otherwise. */
+
+static Cline
+join_sub(Cmdata md, char *str, int len, int *mlen, int sfx, int join)
+{
+    if (!check_cmdata(md, sfx)) {
+	char *ow = str, *nw = md->str;
+	int ol = len, nl = md->len;
+	Cmlist ms;
+	Cmatcher mp;
+	VARARR(unsigned char, ea, (ol > nl ? ol : nl) + 1);
+	int t;
+
+	if (sfx) {
+	    ow += ol; nw += nl;
+	}
+	for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+	    mp = ms->matcher;
+	    /* We use only those patterns that match a non-empty
+	     * string in both the line and the word and that have
+	     * no anchors. */
+	    if (!mp->flags && mp->wlen > 0 && mp->llen > 0) {
+		/* We first test, if the old string matches already the
+		 * new one. */
+		if (mp->llen <= ol && mp->wlen <= nl &&
+		    pattern_match(mp->line, ow - (sfx ? mp->llen : 0),
+				  NULL, ea) &&
+		    pattern_match(mp->word, nw - (sfx ? mp->wlen : 0),
+				  ea, NULL)) {
+		    /* It did, update the contents of the cmdata struct
+		     * and return a cline for the matched part. */
+		    if (sfx)
+			md->str -= mp->wlen;
+		    else
+			md->str += mp->wlen;
+		    md->len -= mp->wlen;
+		    *mlen = mp->llen;
+
+		    return get_cline(NULL, 0, ow - (sfx ? mp->llen : 0),
+				     mp->llen, NULL, 0, 0);
+		}
+		/* Otherwise we will try to build a string that matches
+		 * both strings. But try the pattern only if the word-
+		 * pattern matches one of the strings. */
+		if (join && mp->wlen <= ol && mp->wlen <= nl &&
+		    ((t = pattern_match(mp->word, ow - (sfx ? mp->wlen : 0),
+				       NULL, ea)) ||
+		     pattern_match(mp->word, nw - (sfx ? mp->wlen : 0),
+				   NULL, ea))) {
+		    VARARR(char, line, mp->llen + 1);
+		    int bl;
+
+		    /* Then build all the possible lines and see
+		     * if one of them matches the other string. */
+		    if ((bl = bld_line(mp->line, line, line,
+				       (t ? nw : ow), (t ? nl : ol),
+				       ea, sfx))) {
+			/* Yep, one of the lines matched the other
+			 * string. */
+			line[mp->llen] = '\0';
+
+			if (t) {
+			    ol = mp->wlen; nl = bl;
+			} else {
+			    ol = bl; nl = mp->wlen;
+			}
+			if (sfx)
+			    md->str -= nl;
+			else
+			    md->str += nl;
+			md->len -= nl;
+			*mlen = ol;
+
+			return get_cline(NULL, 0, dupstring(line), mp->llen,
+					 NULL, 0, CLF_JOIN);
+		    }
+		}
+	    }
+	}
+    }
+    return NULL;
+}
+
+/* This is used to match a sub-string in a sub-cline. The length of the
+ * matched portion is returned. This tests only for exact equality. */
+
+static int
+sub_match(Cmdata md, char *str, int len, int sfx)
+{
+    int ret = 0, l, ind, add;
+    char *p, *q;
+
+    if (sfx) {
+	str += len;
+	ind = -1; add = -1;
+    } else {
+	ind = 0; add = 1;
+    }
+    /* str and len describe the old string, in md we have the new one. */
+    while (len) {
+	if (check_cmdata(md, sfx))
+	    return ret;
+
+	for (l = 0, p = str, q = md->str;
+	     l < len && l < md->len && p[ind] == q[ind];
+	     l++, p += add, q += add);
+
+	if (l) {
+	    /* There was a common prefix, use it. */
+	    md->len -= l; len -= l;
+	    if (sfx) {
+		md->str -= l; str -= l;
+	    } else {
+		md->str += l; str += l;
+	    }
+	    ret += l;
+	} else if (md->line || md->len != md->olen || !md->astr)
+	    return ret;
+	else {
+	    /* We still have the line string to try. */
+	    md->line = 1;
+	    md->len = md->alen;
+	    md->str = md->astr;
+	}
+    }
+    return ret;
+}
+
+/* This is used to build a common prefix or suffix sub-list. If requested
+ * it returns the unmatched cline lists in orest and nrest. */
+
+/**/
+static void
+join_psfx(Cline ot, Cline nt, Cline *orest, Cline *nrest, int sfx)
+{
+    Cline p = NULL, o, n;
+    struct cmdata md, omd;
+    char **sstr = NULL;
+    int len, join = 0, line = 0, *slen = NULL;
+
+    if (sfx) {
+	o = ot->suffix; n = nt->suffix;
+    } else {
+	o = ot->prefix;	n = nt->prefix;
+    }
+    if (!o) {
+	if (orest)
+	    *orest = NULL;
+	if (nrest)
+	    *nrest = n;
+
+	return;
+    }
+    if (!n) {
+	if (sfx)
+	    ot->suffix = NULL;
+	else
+	    ot->prefix = NULL;
+
+	if (orest)
+	    *orest = o;
+	else
+	    free_cline(o);
+	if (nrest)
+	    *nrest = NULL;
+	return;
+    }
+    md.cl = n;
+    md.len = 0;
+
+    /* Walk through the old list. */
+    while (o) {
+	join = 0;
+	memcpy(&omd, &md, sizeof(struct cmdata));
+
+	/* We first get the length of the prefix equal in both strings. */
+	if (o->flags & CLF_LINE) {
+	    if ((len = sub_match(&md, o->line, o->llen, sfx)) != o->llen) {
+		join = 1; line = 1; slen = &(o->llen); sstr = &(o->line);
+	    }
+	} else if ((len = sub_match(&md, o->word, o->wlen, sfx)) != o->wlen) {
+	    if (o->line) {
+		memcpy(&md, &omd, sizeof(struct cmdata));
+		o->flags |= CLF_LINE | CLF_DIFF;
+
+		continue;
+	    }
+	    join = 1; line = 0; slen = &(o->wlen); sstr = &(o->word);
+	}
+	if (join) {
+	    /* There is a rest that is different in the two lists,
+	     * we try to build a new cline matching both strings. */
+	    Cline joinl;
+	    int jlen;
+
+	    if ((joinl = join_sub(&md, *sstr + len, *slen - len,
+				  &jlen, sfx, !(o->flags & CLF_JOIN)))) {
+		/* We have one, insert it into the list. */
+		joinl->flags |= CLF_DIFF;
+		if (len + jlen != *slen) {
+		    Cline rest;
+
+		    rest = get_cline(NULL, 0, *sstr + (sfx ? 0 : len + jlen),
+				     *slen - len - jlen, NULL, 0, 0);
+
+		    rest->next = o->next;
+		    joinl->next = rest;
+		} else
+		    joinl->next = o->next;
+
+		if (len) {
+		    if (sfx)
+			*sstr += *slen - len;
+		    *slen = len;
+		    o->next = joinl;
+		} else {
+		    o->next = NULL;
+		    free_cline(o);
+		    if (p)
+			p->next = joinl;
+		    else if (sfx)
+			ot->suffix = joinl;
+		    else
+			ot->prefix = joinl;
+		}
+		o = joinl;
+		join = 0;
+	    }
+	}
+	if (join) {
+	    /* We couldn't build a cline for a common string, so we
+	     * cut the list here. */
+	    if (len) {
+		Cline r;
+
+		if (orest) {
+		    if (line)
+			r = get_cline(o->line + len, *slen - len,
+				      NULL, 0, NULL, 0, o->flags);
+		    else
+			r = get_cline(NULL, 0, o->word + len, *slen - len,
+				      NULL, 0, o->flags);
+
+		    r->next = o->next;
+		    *orest = r;
+
+		    *slen = len;
+		    o->next = NULL;
+		} else {
+		    if (sfx)
+			*sstr += *slen - len;
+		    *slen = len;
+		    free_cline(o->next);
+		    o->next = NULL;
+		}
+	    } else {
+		if (p)
+		    p->next = NULL;
+		else if (sfx)
+		    ot->suffix = NULL;
+		else
+		    ot->prefix = NULL;
+
+		if (orest)
+		    *orest = o;
+		else
+		    free_cline(o);
+	    }
+	    if (!orest || !nrest)
+		ot->flags |= CLF_MISS;
+
+	    if (nrest)
+		*nrest = undo_cmdata(&md, sfx);
+
+	    return;
+	}
+	p = o;
+	o = o->next;
+    }
+    if (md.len || md.cl)
+	ot->flags |= CLF_MISS;
+    if (orest)
+	*orest = NULL;
+    if (nrest)
+	*nrest = undo_cmdata(&md, sfx);
+}
+
+/* This builds the common prefix and suffix for a mid-cline -- the one
+ * describing the place where the prefix and the suffix meet. */
+
+/**/
+static void
+join_mid(Cline o, Cline n)
+{
+    if (o->flags & CLF_JOIN) {
+	/* The JOIN flag is set in the old cline struct if it was
+	 * already joined with another one. In this case the suffix
+	 * field contains the suffix from previous calls. */
+	Cline nr;
+
+	join_psfx(o, n, NULL, &nr, 0);
+
+	n->suffix = revert_cline(nr);
+
+	join_psfx(o, n, NULL, NULL, 1);
+    } else {
+	/* This is the first time for both structs, so the prefix field
+	 * contains the whole sub-list. */
+	Cline or, nr;
+
+	o->flags |= CLF_JOIN;
+
+	/* We let us give both rests and use them as the suffixes. */
+	join_psfx(o, n, &or, &nr, 0);
+
+	if (or)
+	    or->llen = (o->slen > or->wlen ? or->wlen : o->slen);
+	o->suffix = revert_cline(or);
+	n->suffix = revert_cline(nr);
+
+	join_psfx(o, n, NULL, NULL, 1);
+    }
+    n->suffix = NULL;
+}
+
+/* This turns the sequence of anchor cline structs from b to e into a
+ * prefix sequence, puts it before the prefix of e and then tries to
+ * join that with the prefix of a.
+ * This is needed if some matches had a anchor match spec and others
+ * didn't. */
+
+/**/
+static void
+sub_join(Cline a, Cline b, Cline e, int anew)
+{
+    if (!e->suffix && a->prefix) {
+	Cline op = e->prefix, n = NULL, *p = &n, t, ca;
+	int min = 0, max = 0;
+
+	for (; b != e; b = b->next) {
+	    if ((*p = t = b->prefix)) {
+		while (t->next)
+		    t = t->next;
+		p = &(t->next);
+	    }
+	    b->suffix = b->prefix = NULL;
+	    b->flags &= ~CLF_SUF;
+	    min += b->min;
+	    max += b->max;
+	    *p = b;
+	    p = &(b->next);
+	}
+	*p = e->prefix;
+	ca = a->prefix;
+
+	while (n != op) {
+	    e->prefix = cp_cline(n, 0);
+	    a->prefix = cp_cline(ca, 0);
+
+	    if (anew) {
+		join_psfx(e, a, NULL, NULL, 0);
+		if (e->prefix) {
+		    e->min += min;
+		    e->max += max;
+		    break;
+		}
+	    } else {
+		join_psfx(e, a, NULL, NULL, 0);
+		if (a->prefix) {
+		    a->min += min;
+		    a->max += max;
+		    break;
+		}
+	    }
+	    min -= n->min;
+	    max -= n->max;
+
+	    n = n->next;
+	}
+    }
+}
+
+/* This simplifies the cline list given as the first argument so that
+ * it also matches the second list. */
+
+/**/
+Cline
+join_clines(Cline o, Cline n)
+{
+    cline_setlens(n, 1);
+
+    /* First time called, just return the new list. On further invocations
+     * we will get it as the first argument. */
+    if (!o)
+	return n;
+    else {
+	Cline oo = o, nn = n, po = NULL, pn = NULL;
+
+	/* Walk through the lists. */
+	while (o && n) {
+	    /* If one of them describes a new part and the other one does
+	     * not, synchronise them by searching an old part in the
+	     * other list. */
+	    if ((o->flags & CLF_NEW) && !(n->flags & CLF_NEW)) {
+		Cline t, tn;
+
+		for (t = o; (tn = t->next) && (tn->flags & CLF_NEW); t = tn);
+		if (tn && cmp_anchors(tn, n, 0)) {
+		    sub_join(n, o, tn, 1);
+
+		    if (po)
+			po->next = tn;
+		    else
+			oo = tn;
+		    t->next = NULL;
+		    free_cline(o);
+		    o = tn;
+		    o->flags |= CLF_MISS;
+		    continue;
+		}
+	    }
+	    if (!(o->flags & CLF_NEW) && (n->flags & CLF_NEW)) {
+		Cline t, tn;
+
+		for (t = n; (tn = t->next) && (tn->flags & CLF_NEW); t = tn);
+		if (tn && cmp_anchors(o, tn, 0)) {
+		    sub_join(o, n, tn, 0);
+
+		    n = tn;
+		    o->flags |= CLF_MISS;
+		    continue;
+		}
+	    }
+	    /* Almost the same as above, but for the case that they
+	     * describe different types of parts (prefix, suffix, or mid). */
+	    if ((o->flags & (CLF_SUF | CLF_MID)) !=
+		(n->flags & (CLF_SUF | CLF_MID))) {
+		Cline t, tn;
+
+		for (t = n;
+		     (tn = t->next) &&
+			 (tn->flags & (CLF_SUF | CLF_MID)) !=
+			 (o->flags  & (CLF_SUF | CLF_MID));
+		     t = tn);
+		if (tn && cmp_anchors(o, tn, 1)) {
+		    sub_join(o, n, tn, 0);
+
+		    n = tn;
+		    continue;
+		}
+		for (t = o;
+		     (tn = t->next) &&
+			 (tn->flags & (CLF_SUF | CLF_MID)) !=
+			 (n->flags  & (CLF_SUF | CLF_MID));
+		     t = tn);
+		if (tn && cmp_anchors(tn, n, 1)) {
+		    sub_join(n, o, tn, 1);
+		    if (po)
+			po->next = tn;
+		    else
+			oo = tn;
+		    t->next = NULL;
+		    free_cline(o);
+		    o = tn;
+		    continue;
+		}
+		if (o->flags & CLF_MID) {
+		    o->flags = (o->flags & ~CLF_MID) | (n->flags & CLF_SUF);
+		    if (n->flags & CLF_SUF) {
+			free_cline(o->prefix);
+			o->prefix = NULL;
+		    } else {
+			free_cline(o->suffix);
+			o->suffix = NULL;
+		    }
+		}
+		break;
+	    }
+	    /* Now see if they have matching anchors. If not, cut the list. */
+	    if (!(o->flags & CLF_MID) && !cmp_anchors(o, n, 1)) {
+		Cline t, tn;
+
+		for (t = n; (tn = t->next) && !cmp_anchors(o, tn, 1); t = tn);
+
+		if (tn) {
+		    sub_join(o, n, tn, 0);
+
+		    n = tn;
+		    o->flags |= CLF_MISS;
+		    continue;
+		} else {
+		    for (t = o; (tn = t->next) && !cmp_anchors(n, tn, 1);
+			 t = tn);
+
+		    if (tn) {
+			sub_join(n, o, tn, 1);
+
+			if (po)
+			    po->next = tn;
+			else
+			    oo = tn;
+			o = tn;
+			o->flags |= CLF_MISS;
+			continue;
+		    } else {
+			if (o->flags & CLF_SUF)
+			    break;
+
+			o->word = o->line = o->orig = NULL;
+			o->wlen = 0;
+			free_cline(o->next);
+			o->next = NULL;
+			o->flags |= CLF_MISS;
+		    }
+		}
+	    }
+	    /* Ok, they are equal, now copy the information about the
+             * original string if needed, calculate minimum and maximum
+	     * lengths, and join the sub-lists. */
+	    if (!o->orig && !o->olen) {
+		o->orig = n->orig;
+		o->olen = n->olen;
+	    }
+	    if (n->min < o->min)
+		o->min = n->min;
+	    if (n->max > o->max)
+		o->max = n->max;
+	    if (o->flags & CLF_MID)
+		join_mid(o, n);
+	    else
+		join_psfx(o, n, NULL, NULL, (o->flags & CLF_SUF));
+
+	    po = o;
+	    o = o->next;
+	    pn = n;
+	    n = n->next;
+	}
+	/* Free the rest of the old list. */
+	if (o) {
+	    if (po)
+		po->next = NULL;
+	    else
+		oo = NULL;
+
+	    free_cline(o);
+	}
+	free_cline(nn);
+
+	return oo;
+    }
+}
+
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
new file mode 100644
index 000000000..fe997b12b
--- /dev/null
+++ b/Src/Zle/compresult.c
@@ -0,0 +1,1940 @@
+/*
+ * compresult.c - the complete module, completion result handling
+ *
+ * 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 "compresult.pro"
+
+/* Convenience macro for calling bslashquote() (formerly quotename()). *
+ * This uses the instring variable above.                              */
+
+#define quotename(s, e) bslashquote(s, e, instring)
+
+#define inststr(X) inststrlen((X),1,-1)
+
+/* This cuts the cline list before the stuff that isn't worth
+ * inserting in the line. */
+
+/**/
+static Cline
+cut_cline(Cline l)
+{
+    Cline q, p, e = NULL, maxp = NULL;
+    int sum = 0, max = 0, tmp, ls = 0;
+
+    /* If no match was added with matching, we don't really know
+     * which parts of the unambiguous string are worth keeping,
+     * so for now we keep everything (in the hope that this
+     * produces a string containing at least everything that was 
+     * originally on the line). */
+
+    if (!hasmatched) {
+	cline_setlens(l, 0);
+	return l;
+    }
+    e = l = cp_cline(l, 0);
+
+    /* First, search the last struct for which we have something on
+     * the line. Anything before that is kept. */
+
+    for (q = NULL, p = l; p; p = p->next) {
+	if (p->orig || p->olen || !(p->flags & CLF_NEW))
+	    e = p->next;
+	if (!p->suffix && (p->wlen || p->llen || p->prefix))
+	    q = p;
+    }
+    if (!e && q && !q->orig && !q->olen && (q->flags & CLF_MISS) &&
+	!(q->flags & CLF_MATCHED) && (q->word ? q->wlen : q->llen) < 3) {
+	q->word = q->line = NULL;
+	q->wlen = q->llen = 0;
+    }
+    /* Then keep all structs without missing characters. */
+
+    while (e && !(e->flags & CLF_MISS))
+	e = e->next;
+
+    if (e) {
+	/* Then we see if there is another struct with missing
+	 * characters. If not, we keep the whole list. */
+
+	for (p = e->next; p && !(p->flags & CLF_MISS); p = p->next);
+
+	if (p) {
+	    for (p = e; p; p = p->next) {
+		if (!(p->flags & CLF_MISS))
+		    sum += p->max;
+		else {
+		    tmp = cline_sublen(p);
+		    if (tmp > 2 && tmp > ((p->max + p->min) >> 1))
+			sum += tmp - (p->max - tmp);
+		    else if (tmp < p->min)
+			sum -= (((p->max + p->min) >> 1) - tmp) << (tmp < 2);
+		}
+		if (sum > max) {
+		    max = sum;
+		    maxp = p;
+		}
+	    }
+	    if (max)
+		e = maxp;
+	    else {
+		int len = 0;
+
+		cline_setlens(l, 0);
+		ls = 1;
+
+		for (p = e; p; p = p->next)
+		    len += p->max;
+
+		if (len > ((minmlen << 1) / 3))
+		    return l;
+	    }
+	    e->line = e->word = NULL;
+	    e->llen = e->wlen = e->olen = 0;
+	    e->next = NULL;
+	}
+    }
+    if (!ls)
+	cline_setlens(l, 0);
+
+    return l;
+}
+
+/* This builds the unambiguous string. If ins is non-zero, it is
+ * immediatly inserted in the line. Otherwise csp is used to return
+ * the relative cursor position in the string returned. */
+
+/**/
+static char *
+cline_str(Cline l, int ins, int *csp)
+{
+    Cline s;
+    int ocs = cs, ncs, pcs, scs, pm, pmax, pmm, sm, smax, smm, d, dm, mid;
+    int i, j, li = 0, cbr;
+    Brinfo brp, brs;
+
+    l = cut_cline(l);
+
+    pmm = smm = dm = 0;
+    pm = pmax = sm = smax = d = mid = cbr = -1;
+
+    /* Get the information about the brace beginning and end we have
+     * to re-insert. */
+    if (ins) {
+	Brinfo bp;
+	int olen = we - wb;
+
+	if ((brp = brbeg)) {
+	    for (bp = brbeg; bp; bp = bp->next) {
+		bp->curpos = (hasunqu ? bp->pos : bp->qpos);
+		olen -= strlen(bp->str);
+	    }
+	}
+	if ((brs = lastbrend)) {
+	    for (bp = brend; bp; bp = bp->next)
+		olen -= strlen(bp->str);
+
+	    for (bp = brend; bp; bp = bp->next)
+		bp->curpos = olen - (hasunqu ? bp->pos : bp->qpos);
+	}
+	while (brp && !brp->curpos) {
+	    inststrlen(brp->str, 1, -1);
+	    brp = brp->next;
+	}
+	while (brs && !brs->curpos) {
+	    if (cbr < 0)
+		cbr = cs;
+	    inststrlen(brs->str, 1, -1);
+	    brs = brs->prev;
+	}
+    }
+    /* Walk through the top-level cline list. */
+    while (l) {
+	/* Insert the original string if no prefix. */
+	if (l->olen && !(l->flags & CLF_SUF) && !l->prefix) {
+	    pcs = cs + l->olen;
+	    inststrlen(l->orig, 1, l->olen);
+	} else {
+	    /* Otherwise insert the prefix. */
+	    for (s = l->prefix; s; s = s->next) {
+		pcs = cs + s->llen;
+		if (s->flags & CLF_LINE)
+		    inststrlen(s->line, 1, s->llen);
+		else
+		    inststrlen(s->word, 1, s->wlen);
+		scs = cs;
+
+		if ((s->flags & CLF_DIFF) && (!dm || (s->flags & CLF_MATCHED))) {
+		    d = cs; dm = s->flags & CLF_MATCHED;
+		}
+		li += s->llen;
+	    }
+	}
+	if (ins) {
+	    int ocs, bl;
+
+	    while (brp && li >= brp->curpos) {
+		ocs = cs;
+		bl = strlen(brp->str);
+		cs = pcs - (li - brp->curpos);
+		inststrlen(brp->str, 1, bl);
+		cs = ocs + bl;
+		pcs += bl;
+		scs += bl;
+		brp = brp->next;
+	    }
+	}
+	/* Remember the position if this is the first prefix with
+	 * missing characters. */
+	if ((l->flags & CLF_MISS) && !(l->flags & CLF_SUF) &&
+	    ((pmax < (l->min - l->max) && (!pmm || (l->flags & CLF_MATCHED))) ||
+	     ((l->flags & CLF_MATCHED) && !pmm))) {
+	    pm = cs; pmax = l->min - l->max; pmm = l->flags & CLF_MATCHED;
+	}
+	if (ins) {
+	    int ocs, bl;
+
+	    while (brs && li >= brs->curpos) {
+		ocs = cs;
+		bl = strlen(brs->str);
+		cs = scs - (li - brs->curpos);
+		if (cbr < 0)
+		    cbr = cs;
+		inststrlen(brs->str, 1, bl);
+		cs = ocs + bl;
+		pcs += bl;
+		brs = brs->prev;
+	    }
+	}
+	pcs = cs;
+	/* Insert the anchor. */
+	if (l->flags & CLF_LINE)
+	    inststrlen(l->line, 1, l->llen);
+	else
+	    inststrlen(l->word, 1, l->wlen);
+	scs = cs;
+	if (ins) {
+	    int ocs, bl;
+
+	    li += l->llen;
+
+	    while (brp && li >= brp->curpos) {
+		ocs = cs;
+		bl = strlen(brp->str);
+		cs = pcs + l->llen - (li - brp->curpos);
+		inststrlen(brp->str, 1, bl);
+		cs = ocs + bl;
+		pcs += bl;
+		scs += bl;
+		brp = brp->next;
+	    }
+	}
+	/* Remember the cursor position for suffixes and mids. */
+	if (l->flags & CLF_MISS) {
+	    if (l->flags & CLF_MID)
+		mid = cs;
+	    else if ((l->flags & CLF_SUF) && 
+		     ((smax < (l->min - l->max) &&
+		       (!smm || (l->flags & CLF_MATCHED))) ||
+		      ((l->flags & CLF_MATCHED) && !smm))) {
+		sm = cs; smax = l->min - l->max; smm = l->flags & CLF_MATCHED;
+	    }
+	}
+	if (ins) {
+	    int ocs, bl;
+
+	    while (brs && li >= brs->curpos) {
+		ocs = cs;
+		bl = strlen(brs->str);
+		cs = scs - (li - brs->curpos);
+		if (cbr < 0)
+		    cbr = cs;
+		inststrlen(brs->str, 1, bl);
+		cs = ocs + bl;
+		pcs += bl;
+		brs = brs->prev;
+	    }
+	}
+	/* And now insert the suffix or the original string. */
+	if (l->olen && (l->flags & CLF_SUF) && !l->suffix) {
+	    pcs = cs;
+	    inststrlen(l->orig, 1, l->olen);
+	    if (ins) {
+		int ocs, bl;
+
+		li += l->olen;
+
+		while (brp && li >= brp->curpos) {
+		    ocs = cs;
+		    bl = strlen(brp->str);
+		    cs = pcs + l->olen - (li - brp->curpos);
+		    inststrlen(brp->str, 1, bl);
+		    cs = ocs + bl;
+		    pcs += bl;
+		    brp = brp->next;
+		}
+		while (brs && li >= brs->curpos) {
+		    ocs = cs;
+		    bl = strlen(brs->str);
+		    cs = pcs + l->olen - (li - brs->curpos);
+		    if (cbr < 0)
+			cbr = cs;
+		    inststrlen(brs->str, 1, bl);
+		    cs = ocs + bl;
+		    pcs += bl;
+		    brs = brs->prev;
+		}
+	    }
+	} else {
+	    Cline js = NULL;
+
+	    for (j = -1, i = 0, s = l->suffix; s; s = s->next) {
+		if (j < 0 && (s->flags & CLF_DIFF))
+		    j = i, js = s;
+		pcs = cs;
+		if (s->flags & CLF_LINE) {
+		    inststrlen(s->line, 0, s->llen);
+		    i += s->llen; scs = cs + s->llen;
+		} else {
+		    inststrlen(s->word, 0, s->wlen);
+		    i += s->wlen; scs = cs + s->wlen;
+		}
+		if (ins) {
+		    int ocs, bl;
+
+		    li += s->llen;
+
+		    while (brp && li >= brp->curpos) {
+			ocs = cs;
+			bl = strlen(brp->str);
+			cs = pcs + (li - brp->curpos);
+			inststrlen(brp->str, 1, bl);
+			cs = ocs + bl;
+			pcs += bl;
+			scs += bl;
+			brp = brp->next;
+		    }
+		    while (brs && li >= brs->curpos) {
+			ocs = cs;
+			bl = strlen(brs->str);
+			cs = scs - (li - brs->curpos);
+			if (cbr < 0)
+			    cbr = cs;
+			inststrlen(brs->str, 1, bl);
+			cs = ocs + bl;
+			pcs += bl;
+			brs = brs->prev;
+		    }
+		}
+	    }
+	    cs += i;
+	    if (j >= 0 && (!dm || (js->flags & CLF_MATCHED))) {
+		d = cs - j; dm = js->flags & CLF_MATCHED;
+	    }
+	}
+	l = l->next;
+    }
+    if (ins) {
+	int ocs = cs;
+
+	for (; brp; brp = brp->next)
+	    inststrlen(brp->str, 1, -1);
+	for (; brs; brs = brs->prev) {
+	    if (cbr < 0)
+		cbr = cs;
+	    inststrlen(brs->str, 1, -1);
+	}
+	if (mid >= ocs)
+	    mid += cs - ocs;
+	if (pm >= ocs)
+	    pm += cs - ocs;
+	if (sm >= ocs)
+	    sm += cs - ocs;
+	if (d >= ocs)
+	    d += cs - ocs;
+    }
+    /* This calculates the new cursor position. If we had a mid cline
+     * with missing characters, we take this, otherwise if we have a
+     * prefix with missing characters, we take that, the same for a
+     * suffix, and finally a place where the matches differ. */
+    ncs = (cbr >= 0 ? cbr :
+	   (mid >= 0 ? mid :
+	    (pm >= 0 ? pm : (sm >= 0 ? sm : (d >= 0 ? d : cs)))));
+
+    if (!ins) {
+	/* We always inserted the string in the line. If that was not
+	 * requested, we copy it and remove from the line. */
+	char *r = zalloc((i = cs - ocs) + 1);
+
+	memcpy(r, (char *) (line + ocs), i);
+	r[i] = '\0';
+	cs = ocs;
+	foredel(i);
+
+	*csp = ncs - ocs;
+
+	return r;
+    }
+    lastend = cs;
+    cs = ncs;
+
+    return NULL;
+}
+
+/* This is a utility function using the function above to allow access
+ * to the unambiguous string and cursor position via compstate. */
+
+/**/
+char *
+unambig_data(int *cp)
+{
+    static char *scache = NULL;
+    static int ccache;
+
+    if (mnum && ainfo) {
+	if (mnum != unambig_mnum) {
+	    zsfree(scache);
+	    scache = cline_str((ainfo->count ? ainfo->line : fainfo->line),
+			       0, &ccache);
+	}
+    } else if (mnum != unambig_mnum || !ainfo || !scache) {
+	zsfree(scache);
+	scache = ztrdup("");
+	ccache = 0;
+    }
+    unambig_mnum = mnum;
+    if (cp)
+	*cp = ccache + 1;
+
+    return scache;
+}
+
+/* Insert the given match. This returns the number of characters inserted.
+ * scs is used to return the position where a automatically created suffix
+ * has to be inserted. */
+
+/**/
+static int
+instmatch(Cmatch m, int *scs)
+{
+    int l, r = 0, ocs, a = cs, brb = 0, bradd, *brpos;
+    Brinfo bp;
+
+    zsfree(lastprebr);
+    zsfree(lastpostbr);
+    lastprebr = lastpostbr = NULL;
+
+    /* Ignored prefix. */
+    if (m->ipre) {
+	char *p = m->ipre + (menuacc ? m->qipl : 0);
+
+	inststrlen(p, 1, (l = strlen(p)));
+	r += l;
+    }
+    /* -P prefix. */
+    if (m->pre) {
+	inststrlen(m->pre, 1, (l = strlen(m->pre)));
+	r += l;
+    }
+    /* Path prefix. */
+    if (m->ppre) {
+	inststrlen(m->ppre, 1, (l = strlen(m->ppre)));
+	r += l;
+    }
+    /* The string itself. */
+    inststrlen(m->str, 1, (l = strlen(m->str)));
+    r += l;
+    ocs = cs;
+    /* Re-insert the brace beginnings, if any. */
+    if (brbeg) {
+	int pcs = cs;
+
+	l = 0;
+	for (bp = brbeg, brpos = m->brpl,
+		 bradd = (m->pre ? strlen(m->pre) : 0);
+	     bp; bp = bp->next, brpos++) {
+	    cs = a + *brpos + bradd;
+	    pcs = cs;
+	    l = strlen(bp->str);
+	    bradd += l;
+	    brpcs = cs;
+	    inststrlen(bp->str, 1, l);
+	    r += l;
+	    ocs += l;
+	}
+	lastprebr = (char *) zalloc(pcs - a + 1);
+	memcpy(lastprebr, (char *) line + a, pcs - a);
+	lastprebr[pcs - a] = '\0';
+	cs = ocs;
+    }
+    /* Path suffix. */
+    if (m->psuf) {
+	inststrlen(m->psuf, 1, (l = strlen(m->psuf)));
+	r += l;
+    }
+    /* Re-insert the brace end. */
+    if (brend) {
+	a = cs;
+	for (bp = brend, brpos = m->brsl, bradd = 0; bp; bp = bp->next, brpos++) {
+	    cs = a - *brpos;
+	    ocs = brscs = cs;
+	    l = strlen(bp->str);
+	    bradd += l;
+	    inststrlen(bp->str, 1, l);
+	    brb = cs;
+	    r += l;
+	}
+	cs = a + bradd;
+	if (scs)
+	    *scs = ocs;
+    } else {
+	brscs = -1;
+
+	if (scs)
+	    *scs = cs;
+    }
+    /* -S suffix */
+    if (m->suf) {
+	inststrlen(m->suf, 1, (l = strlen(m->suf)));
+	r += l;
+    }
+    /* ignored suffix */
+    if (m->isuf) {
+	inststrlen(m->isuf, 1, (l = strlen(m->isuf)));
+	r += l;
+    }
+    if (brend) {
+	lastpostbr = (char *) zalloc(cs - brb + 1);
+	memcpy(lastpostbr, (char *) line + brb, cs - brb);
+	lastpostbr[cs - brb] = '\0';
+    }
+    lastend = cs;
+    cs = ocs;
+
+    return r;
+}
+
+/* Check if the match has the given prefix/suffix before/after the
+ * braces. */
+
+/**/
+int
+hasbrpsfx(Cmatch m, char *pre, char *suf)
+{
+    char *op = lastprebr, *os = lastpostbr;
+    VARARR(char, oline, ll);
+    int oll = ll, ocs = cs, ole = lastend, opcs = brpcs, oscs = brscs, ret;
+
+    memcpy(oline, line, ll);
+
+    lastprebr = lastpostbr = NULL;
+
+    instmatch(m, NULL);
+
+    cs = 0;
+    foredel(ll);
+    spaceinline(oll);
+    memcpy(line, oline, oll);
+    cs = ocs;
+    lastend = ole;
+    brpcs = opcs;
+    brscs = oscs;
+
+    ret = (((!pre && !lastprebr) ||
+	    (pre && lastprebr && !strcmp(pre, lastprebr))) &&
+	   ((!suf && !lastpostbr) ||
+	    (suf && lastpostbr && !strcmp(suf, lastpostbr))));
+
+    zsfree(lastprebr);
+    zsfree(lastpostbr);
+    lastprebr = op;
+    lastpostbr = os;
+
+    return ret;
+}
+
+/* Handle the case were we found more than one match. */
+
+/**/
+int
+do_ambiguous(void)
+{
+    int ret = 0;
+
+    menucmp = menuacc = 0;
+
+    /* If we have to insert the first match, call do_single().  This is *
+     * how REC_EXACT takes effect.  We effectively turn the ambiguous   *
+     * completion into an unambiguous one.                              */
+    if (ainfo && ainfo->exact == 1 && useexact && !(fromcomp & FC_LINE)) {
+	minfo.cur = NULL;
+	do_single(ainfo->exactm);
+	invalidatelist();
+	return ret;
+    }
+    /* Setting lastambig here means that the completion is ambiguous and *
+     * AUTO_MENU might want to start a menu completion next time round,  *
+     * but this might be overridden below if we can complete an          *
+     * unambiguous prefix.                                               */
+    lastambig = 1;
+
+    if (usemenu || (haspattern && comppatinsert &&
+		    !strcmp(comppatinsert, "menu"))) {
+	/* We are in a position to start using menu completion due to one  *
+	 * of the menu completion options, or due to the menu-complete-    *
+	 * word command, or due to using GLOB_COMPLETE which does menu-    *
+	 * style completion regardless of the setting of the normal menu   *
+	 * completion options.                                             */
+	do_ambig_menu();
+    } else if (ainfo) {
+	int atend = (cs == we), la, eq, tcs;
+
+	minfo.cur = NULL;
+	minfo.asked = 0;
+
+	fixsuffix();
+
+	/* First remove the old string from the line. */
+	cs = wb;
+	foredel(we - wb);
+
+	/* Now get the unambiguous string and insert it into the line. */
+	cline_str(ainfo->line, 1, NULL);
+	if (eparq) {
+	    tcs = cs;
+	    cs = lastend;
+	    for (eq = eparq; eq; eq--)
+		inststrlen("\"", 0, 1);
+	    cs = tcs;
+	}
+	/* la is non-zero if listambiguous may be used. Copying and
+	 * comparing the line looks like BFI but it is the easiest
+	 * solution. Really. */
+	la = (ll != origll || strncmp(origline, (char *) line, ll));
+
+	/* If REC_EXACT and AUTO_MENU are set and what we inserted is an  *
+	 * exact match, we want menu completion the next time round       *
+	 * so we set fromcomp, to ensure that the word on the line is not *
+	 * taken as an exact match. Also we remember if we just moved the *
+	 * cursor into the word.                                          */
+	fromcomp = ((isset(AUTOMENU) ? FC_LINE : 0) |
+		    ((atend && cs != lastend) ? FC_INWORD : 0));
+
+	/* Probably move the cursor to the end. */
+	if (movetoend == 3)
+	    cs = lastend;
+
+	/* If the LIST_AMBIGUOUS option (meaning roughly `show a list only *
+	 * if the completion is completely ambiguous') is set, and some    *
+	 * prefix was inserted, return now, bypassing the list-displaying  *
+	 * code.  On the way, invalidate the list and note that we don't   *
+	 * want to enter an AUTO_MENU imediately.                          */
+	if (uselist == 3 && la) {
+	    int fc = fromcomp;
+
+	    invalidatelist();
+	    fromcomp = fc;
+	    lastambig = 0;
+	    clearlist = 1;
+	    return ret;
+	}
+    } else
+	return ret;
+
+    /* At this point, we might want a completion listing.  Show the listing *
+     * if it is needed.                                                     */
+    if (isset(LISTBEEP))
+	ret = 1;
+
+    if (uselist && (usemenu != 2 || (!listshown && !oldlist)) &&
+	((!showinglist && (!listshown || !oldlist)) ||
+	 (usemenu == 3 && !oldlist)) &&
+	(smatches >= 2 || (compforcelist && *compforcelist)))
+	showinglist = -2;
+
+    return ret;
+}
+
+/* This is a stat that ignores backslashes in the filename.  The `ls' *
+ * parameter says if we have to do lstat() or stat().  I think this   *
+ * should instead be done by use of a general function to expand a    *
+ * filename (stripping backslashes), combined with the actual         *
+ * (l)stat().                                                         */
+
+/**/
+int
+ztat(char *nam, struct stat *buf, int ls)
+{
+    char b[PATH_MAX], *p;
+
+    for (p = b; p < b + sizeof(b) - 1 && *nam; nam++)
+	if (*nam == '\\' && nam[1])
+	    *p++ = *++nam;
+	else
+	    *p++ = *nam;
+    *p = '\0';
+
+    return ls ? lstat(b, buf) : stat(b, buf);
+}
+
+/* Insert a single match in the command line. */
+
+/**/
+void
+do_single(Cmatch m)
+{
+    int l, sr = 0, scs;
+    int havesuff = 0;
+    int partest = (m->ripre || ((m->flags & CMF_ISPAR) && parpre));
+    char *str = m->str, *ppre = m->ppre, *psuf = m->psuf, *prpre = m->prpre;
+
+    if (!prpre) prpre = "";
+    if (!ppre) ppre = "";
+    if (!psuf) psuf = "";
+
+    fixsuffix();
+
+    if (!minfo.cur) {
+	/* We are currently not in a menu-completion, *
+	 * so set the position variables.             */
+	minfo.pos = wb;
+	minfo.we = (movetoend >= 2 || (movetoend == 1 && !menucmp));
+	minfo.end = we;
+    }
+    /* If we are already in a menu-completion or if we have done a *
+     * glob completion, we have to delete some of the stuff on the *
+     * command line.                                               */
+    if (minfo.cur)
+	l = minfo.len + minfo.insc;
+    else
+	l = we - wb;
+
+    minfo.insc = 0;
+    cs = minfo.pos;
+    foredel(l);
+
+    /* And then we insert the new string. */
+    minfo.len = instmatch(m, &scs);
+    minfo.end = cs;
+    cs = minfo.pos + minfo.len;
+
+    if (m->suf) {
+	havesuff = 1;
+	minfo.insc = ztrlen(m->suf);
+	minfo.len -= minfo.insc;
+	if (minfo.we) {
+	    minfo.end += minfo.insc;
+	    if (m->flags & CMF_REMOVE) {
+		makesuffixstr(m->remf, m->rems, minfo.insc);
+		if (minfo.insc == 1)
+		    suffixlen[STOUC(m->suf[0])] = 1;
+	    }
+	}
+    } else {
+	/* There is no user-specified suffix, *
+	 * so generate one automagically.     */
+	cs = scs;
+	if (partest && (m->flags & CMF_PARBR)) {
+	    int pq;
+
+	    /*{{*/
+	    /* Completing a parameter in braces.  Add a removable `}' suffix. */
+	    cs += eparq;
+	    for (pq = parq; pq; pq--)
+		inststrlen("\"", 1, 1);
+	    minfo.insc += parq;
+	    inststrlen("}", 1, 1);
+	    minfo.insc++;
+	    if (minfo.we)
+		minfo.end += minfo.insc;
+	    if (m->flags & CMF_PARNEST)
+		havesuff = 1;
+	}
+	if (((m->flags & CMF_FILE) || (partest && isset(AUTOPARAMSLASH))) &&
+	    cs > 0 && line[cs - 1] != '/') {
+	    /* If we have a filename or we completed a parameter name      *
+	     * and AUTO_PARAM_SLASH is set, lets see if it is a directory. *
+	     * If it is, we append a slash.                                */
+	    struct stat buf;
+	    char *p;
+	    int t = 0;
+
+	    if (m->ipre && m->ipre[0] == '~' && !m->ipre[1])
+		t = 1;
+	    else {
+		/* Build the path name. */
+		if (partest && !*psuf && !(m->flags & CMF_PARNEST)) {
+		    int ne = noerrs;
+
+		    p = (char *) zhalloc(strlen((m->flags & CMF_ISPAR) ?
+						parpre : m->ripre) +
+					 strlen(str) + 2);
+		    sprintf(p, "%s%s%c",
+			    ((m->flags & CMF_ISPAR) ? parpre : m->ripre), str,
+			    ((m->flags & CMF_PARBR) ? Outbrace : '\0'));
+		    noerrs = 1;
+		    parsestr(p);
+		    singsub(&p);
+		    errflag = 0;
+		    noerrs = ne;
+		} else {
+		    p = (char *) zhalloc(strlen(prpre) + strlen(str) +
+				 strlen(psuf) + 3);
+		    sprintf(p, "%s%s%s", ((prpre && *prpre) ?
+					  prpre : "./"), str, psuf);
+		}
+		/* And do the stat. */
+		t = (!(sr = ztat(p, &buf, 0)) && S_ISDIR(buf.st_mode));
+	    }
+	    if (t) {
+		/* It is a directory, so add the slash. */
+		havesuff = 1;
+		inststrlen("/", 1, 1);
+		minfo.insc++;
+		if (minfo.we)
+		    minfo.end++;
+		if (!menucmp || minfo.we) {
+		    if (m->remf || m->rems)
+			makesuffixstr(m->remf, m->rems, 1);
+		    else if (isset(AUTOREMOVESLASH)) {
+			makesuffix(1);
+			suffixlen['/'] = 1;
+		    }
+		}
+	    }
+	}
+	if (!minfo.insc)
+	    cs = minfo.pos + minfo.len - m->qisl;
+    }
+    /* If completing in a brace expansion... */
+    if (brbeg) {
+	if (havesuff) {
+	    /*{{*/
+	    /* If a suffix was added, and is removable, let *
+	     * `,' and `}' remove it.                       */
+	    if (isset(AUTOPARAMKEYS))
+		suffixlen[','] = suffixlen['}'] = suffixlen[256];
+	} else if (!menucmp) {
+	    /*{{*/
+	    /* Otherwise, add a `,' suffix, and let `}' remove it. */
+	    cs = scs;
+	    havesuff = 1;
+	    inststrlen(",", 1, 1);
+	    minfo.insc++;
+	    makesuffix(1);
+	    if ((!menucmp || minfo.we) && isset(AUTOPARAMKEYS))
+		suffixlen[','] = suffixlen['}'] = 1;
+	}
+    } else if (!havesuff && (!(m->flags & CMF_FILE) || !sr)) {
+	/* If we didn't add a suffix, add a space, unless we are *
+	 * doing menu completion or we are completing files and  *
+	 * the string doesn't name an existing file.             */
+	if (m->autoq && (!m->isuf || m->isuf[0] != m->autoq)) {
+	    inststrlen(&(m->autoq), 1, 1);
+	    minfo.insc++;
+	}
+	if (!menucmp && (usemenu != 3 || insspace)) {
+	    inststrlen(" ", 1, 1);
+	    minfo.insc++;
+	    if (minfo.we)
+		makesuffix(1);
+	}
+    }
+    if (minfo.we && partest && isset(AUTOPARAMKEYS))
+	makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), minfo.insc - parq);
+
+    if ((menucmp && !minfo.we) || !movetoend) {
+	cs = minfo.end;
+	if (cs + m->qisl == lastend)
+	    cs += minfo.insc;
+    }
+    {
+	Cmatch *om = minfo.cur;
+	struct chdata dat;
+
+	dat.matches = amatches;
+	dat.num = nmatches;
+	dat.cur = m;
+
+	if (menucmp)
+	    minfo.cur = &m;
+	runhookdef(INSERTMATCHHOOK, (void *) &dat);
+	minfo.cur = om;
+    }
+}
+
+/* Do completion, given that we are in the middle of a menu completion.  We *
+ * don't need to generate a list of matches, because that's already been    *
+ * done by previous commands.  We will either list the completions, or      *
+ * insert the next completion.                                              */
+
+/**/
+void
+do_menucmp(int lst)
+{
+    /* Just list the matches if the list was requested. */
+    if (lst == COMP_LIST_COMPLETE) {
+	showinglist = -2;
+	return;
+    }
+    /* Otherwise go to the next match in the array... */
+    HEAPALLOC {
+	do {
+	    if (!*++(minfo.cur)) {
+		do {
+		    if (!(minfo.group = (minfo.group)->next))
+			minfo.group = amatches;
+		} while (!(minfo.group)->mcount);
+		minfo.cur = minfo.group->matches;
+	    }
+	} while (menuacc &&
+		 !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr));
+	/* ... and insert it into the command line. */
+	metafy_line();
+	do_single(*(minfo.cur));
+	unmetafy_line();
+    } LASTALLOC;
+}
+
+/**/
+int
+reverse_menu(Hookdef dummy, void *dummy2)
+{
+    HEAPALLOC {
+	do {
+	    if (minfo.cur == (minfo.group)->matches) {
+		do {
+		    if (!(minfo.group = (minfo.group)->prev))
+			minfo.group = lmatches;
+		} while (!(minfo.group)->mcount);
+		minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1;
+	    } else
+		minfo.cur--;
+	} while (menuacc &&
+		 !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr));
+	metafy_line();
+	do_single(*(minfo.cur));
+	unmetafy_line();
+    } LASTALLOC;
+
+    return 0;
+}
+
+/* Accepts the current completion and starts a new arg, *
+ * with the next completions. This gives you a way to   *
+ * accept several selections from the list of matches.  */
+
+/**/
+int
+accept_last(void)
+{
+    if (!menuacc) {
+	zsfree(minfo.prebr);
+	minfo.prebr = ztrdup(lastprebr);
+	zsfree(minfo.postbr);
+	minfo.postbr = ztrdup(lastpostbr);
+
+	if (listshown && (lastprebr || lastpostbr)) {
+	    Cmgroup g;
+	    Cmatch *m;
+
+	    for (g = amatches, m = NULL; g && (!m || !*m); g = g->next)
+		for (m = g->matches; *m; m++)
+		    if (!hasbrpsfx(*m, minfo.prebr, minfo.postbr)) {
+			showinglist = -2;
+			break;
+		    }
+	}
+    }
+    menuacc++;
+
+    if (brbeg) {
+	int l;
+
+	iremovesuffix(',', 1);
+
+	l = (brscs >= 0 ? brscs : cs) - brpcs;
+
+	zsfree(lastbrbeg->str);
+	lastbrbeg->str = (char *) zalloc(l + 2);
+	memcpy(lastbrbeg->str, line + brpcs, l);
+	lastbrbeg->str[l] = ',';
+	lastbrbeg->str[l + 1] = '\0';
+    } else {
+	int l;
+
+	cs = minfo.pos + minfo.len + minfo.insc;
+	iremovesuffix(' ', 1);
+	l = cs;
+	cs = minfo.pos + minfo.len + minfo.insc - (*(minfo.cur))->qisl;
+	if (cs < l)
+	    foredel(l - cs);
+	else if (cs > ll)
+	    cs = ll;
+	inststrlen(" ", 1, 1);
+	if (parpre)
+	    inststr(parpre);
+	minfo.insc = minfo.len = 0;
+	minfo.pos = cs;
+	minfo.we = 1;
+    }
+    return 0;
+}
+
+/* This maps the value in v into the range [0,m-1], decrementing v
+ * if it is non-negative and making negative values count backwards. */
+
+/**/
+static int
+comp_mod(int v, int m)
+{
+    if (v >= 0)
+	v--;
+    if (v >= 0)
+	return v % m;
+    else {
+	while (v < 0)
+	    v += m;
+	return v;
+    }
+}
+
+/* This handles the beginning of menu-completion. */
+
+/**/
+static void
+do_ambig_menu(void)
+{
+    Cmatch *mc;
+
+    if (usemenu != 3) {
+	menucmp = 1;
+	menuacc = 0;
+	minfo.cur = NULL;
+    } else {
+	if (oldlist) {
+	    if (oldins && minfo.cur)
+		acceptlast();
+	} else
+	    minfo.cur = NULL;
+    }
+    if (insgroup) {
+	insgnum = comp_mod(insgnum, lastpermgnum);
+	for (minfo.group = amatches;
+	     minfo.group && (minfo.group)->num != insgnum + 1;
+	     minfo.group = (minfo.group)->next);
+	if (!minfo.group || !(minfo.group)->mcount) {
+	    minfo.cur = NULL;
+	    minfo.asked = 0;
+	    return;
+	}
+	insmnum = comp_mod(insmnum, (minfo.group)->mcount);
+    } else {
+	insmnum = comp_mod(insmnum, lastpermmnum);
+	for (minfo.group = amatches;
+	     minfo.group && (minfo.group)->mcount <= insmnum;
+	     minfo.group = (minfo.group)->next)
+	    insmnum -= (minfo.group)->mcount;
+	if (!minfo.group) {
+	    minfo.cur = NULL;
+	    minfo.asked = 0;
+	    return;
+	}
+    }
+    mc = (minfo.group)->matches + insmnum;
+    do_single(*mc);
+    minfo.cur = mc;
+}
+
+/* Return the real number of matches. */
+
+/**/
+zlong
+num_matches(int normal)
+{
+    int alt;
+
+    PERMALLOC {
+	alt = permmatches(0);
+    } LASTALLOC;
+
+    if (normal)
+	return (alt ? 0 : nmatches);
+    else
+	return (alt ? nmatches : 0);
+}
+
+/* Return the number of screen lines needed for the list. */
+
+/**/
+zlong
+list_lines(void)
+{
+    Cmgroup oam;
+
+    PERMALLOC {
+	permmatches(0);
+    } LASTALLOC;
+
+    oam = amatches;
+    amatches = pmatches;
+    listdat.valid = 0;
+    calclist();
+    listdat.valid = 0;
+    amatches = oam;
+
+    return listdat.nlines;
+}
+
+/**/
+void
+comp_list(char *v)
+{
+    zsfree(complist);
+    complist = ztrdup(v);
+
+    onlyexpl = (v && strstr(v, "expl"));
+}
+
+/* This is used to print the explanation string. *
+ * It returns the number of lines printed.       */
+
+/**/
+int
+printfmt(char *fmt, int n, int dopr, int doesc)
+{
+    char *p = fmt, nc[DIGBUFSIZE];
+    int l = 0, cc = 0, b = 0, s = 0, u = 0, m;
+
+    for (; *p; p++) {
+	/* Handle the `%' stuff (%% == %, %n == <number of matches>). */
+	if (doesc && *p == '%') {
+	    if (*++p) {
+		m = 0;
+		switch (*p) {
+		case '%':
+		    if (dopr)
+			putc('%', shout);
+		    cc++;
+		    break;
+		case 'n':
+		    sprintf(nc, "%d", n);
+		    if (dopr)
+			fprintf(shout, nc);
+		    cc += strlen(nc);
+		    break;
+		case 'B':
+		    b = 1;
+		    if (dopr)
+			tcout(TCBOLDFACEBEG);
+		    break;
+		case 'b':
+		    b = 0; m = 1;
+		    if (dopr)
+			tcout(TCALLATTRSOFF);
+		    break;
+		case 'S':
+		    s = 1;
+		    if (dopr)
+			tcout(TCSTANDOUTBEG);
+		    break;
+		case 's':
+		    s = 0; m = 1;
+		    if (dopr)
+			tcout(TCSTANDOUTEND);
+		    break;
+		case 'U':
+		    u = 1;
+		    if (dopr)
+			tcout(TCUNDERLINEBEG);
+		    break;
+		case 'u':
+		    u = 0; m = 1;
+		    if (dopr)
+			tcout(TCUNDERLINEEND);
+		    break;
+		case '{':
+		    for (p++; *p && (*p != '%' || p[1] != '}'); p++, cc++)
+			if (dopr)
+			    putc(*p, shout);
+		    if (*p)
+			p++;
+		    else
+			p--;
+		    break;
+		}
+		if (dopr && m) {
+		    if (b)
+			tcout(TCBOLDFACEBEG);
+		    if (s)
+			tcout(TCSTANDOUTBEG);
+		    if (u)
+			tcout(TCUNDERLINEBEG);
+		}
+	    } else
+		break;
+	} else {
+	    cc++;
+	    if (*p == '\n') {
+		if (dopr) {
+		    if (tccan(TCCLEAREOL))
+			tcout(TCCLEAREOL);
+		    else {
+			int s = columns - 1 - (cc % columns);
+
+			while (s-- > 0)
+			    putc(' ', shout);
+		    }
+		}
+		l += 1 + (cc / columns);
+		cc = 0;
+	    }
+	    if (dopr)
+		putc(*p, shout);
+	}
+    }
+    if (dopr) {
+	if (tccan(TCCLEAREOL))
+	    tcout(TCCLEAREOL);
+	else {
+	    int s = columns - 1 - (cc % columns);
+
+	    while (s-- > 0)
+		putc(' ', shout);
+	}
+    }
+    return l + (cc / columns);
+}
+
+/* This skips over matches that are not to be listed. */
+
+/**/
+Cmatch *
+skipnolist(Cmatch *p)
+{
+    while (*p && (((*p)->flags & (CMF_NOLIST | CMF_HIDE)) ||
+		  ((*p)->disp && ((*p)->flags & (CMF_DISPLINE | CMF_HIDE)))))
+	p++;
+
+    return p;
+}
+
+/**/
+void
+calclist(void)
+{
+    Cmgroup g;
+    Cmatch *p, m;
+    Cexpl *e;
+    int hidden = 0, nlist = 0, nlines = 0, add = 2 + isset(LISTTYPES);
+    int max = 0, i;
+    VARARR(int, mlens, nmatches + 1);
+
+    if (listdat.valid && onlyexpl == listdat.onlyexpl &&
+	menuacc == listdat.menuacc &&
+	lines == listdat.lines && columns == listdat.columns)
+	return;
+
+    for (g = amatches; g; g = g->next) {
+	char **pp = g->ylist;
+	int nl = 0, l, glong = 1, gshort = columns, ndisp = 0, totl = 0;
+
+	if (!onlyexpl && pp) {
+	    /* We have an ylist, lets see, if it contains newlines. */
+	    hidden = 1;
+	    while (!nl && *pp)
+		nl = !!strchr(*pp++, '\n');
+
+	    pp = g->ylist;
+	    if (nl || !pp[1]) {
+		/* Yup, there are newlines, count lines. */
+		char *nlptr, *sptr;
+
+		g->flags |= CGF_LINES;
+		hidden = 1;
+		while ((sptr = *pp)) {
+		    while (sptr && *sptr) {
+			nlines += (nlptr = strchr(sptr, '\n'))
+			    ? 1 + (nlptr-sptr)/columns
+			    : strlen(sptr)/columns;
+			sptr = nlptr ? nlptr+1 : NULL;
+		    }
+		    nlines++;
+		    pp++;
+		}
+		nlines--;
+	    } else {
+		while (*pp) {
+		    l = strlen(*pp);
+		    ndisp++;
+		    if (l > glong)
+			glong = l;
+		    if (l < gshort)
+			gshort = l;
+		    totl += l;
+		    nlist++;
+		    pp++;
+		}
+	    }
+	} else if (!onlyexpl) {
+	    for (p = g->matches; (m = *p); p++) {
+		if (menuacc && !hasbrpsfx(m, minfo.prebr, minfo.postbr)) {
+		    m->flags |= CMF_HIDE;
+		    continue;
+		}
+		m->flags &= ~CMF_HIDE;
+
+		if (m->disp) {
+		    if (m->flags & CMF_DISPLINE) {
+			nlines += 1 + printfmt(m->disp, 0, 0, 0);
+			g->flags |= CGF_HASDL;
+		    } else {
+			l = niceztrlen(m->disp);
+			ndisp++;
+			if (l > glong)
+			    glong = l;
+			if (l < gshort)
+			    gshort = l;
+			totl += l;
+			mlens[m->gnum] = l;
+		    }
+		    nlist++;
+		} else if (!(m->flags & CMF_NOLIST)) {
+		    l = niceztrlen(m->str);
+		    ndisp++;
+		    if (l > glong)
+			glong = l;
+		    if (l < gshort)
+			gshort = l;
+		    totl += l;
+		    mlens[m->gnum] = l;
+		    nlist++;
+		} else
+		    hidden = 1;
+	    }
+	}
+	if ((e = g->expls)) {
+	    while (*e) {
+		if ((*e)->count)
+		    nlines += 1 + printfmt((*e)->str, (*e)->count, 0, 1);
+		e++;
+	    }
+	}
+	g->totl = totl + (ndisp * add);
+	g->dcount = ndisp;
+	g->width = glong + add;
+	g->shortest = gshort + add;
+	if ((g->cols = columns / g->width) > g->dcount)
+	    g->cols = g->dcount;
+	if (g->cols) {
+	    i = g->cols * g->width - add;
+	    if (i > max)
+		max = i;
+	}
+    }
+    if (!onlyexpl) {
+	for (g = amatches; g; g = g->next) {
+	    char **pp;
+	    int glines = 0;
+
+	    zfree(g->widths, 0);
+	    g->widths = NULL;
+
+	    if ((pp = g->ylist)) {
+		if (!(g->flags & CGF_LINES)) {
+		    if (g->cols) {
+			glines += (arrlen(pp) + g->cols - 1) / g->cols;
+			if (g->cols > 1)
+			    g->width += (max - (g->width * g->cols - add)) / g->cols;
+		    } else {
+			g->cols = 1;
+			g->width = 1;
+			
+			while (*pp)
+			    glines += 1 + (strlen(*pp++) / columns);
+		    }
+		}
+	    } else {
+		if (g->cols) {
+		    glines += (g->dcount + g->cols - 1) / g->cols;
+		    if (g->cols > 1)
+			g->width += (max - (g->width * g->cols - add)) / g->cols;
+		} else if (!(g->flags & CGF_LINES)) {
+		    g->cols = 1;
+		    g->width = 0;
+		    
+		    for (p = g->matches; (m = *p); p++)
+			if (!(m->flags & CMF_HIDE)) {
+			    if (m->disp) {
+				if (!(m->flags & CMF_DISPLINE))
+				    glines += 1 + (mlens[m->gnum] / columns);
+			    } else if (!(m->flags & CMF_NOLIST))
+				glines += 1 + ((1 + mlens[m->gnum]) / columns);
+			}
+		}
+	    }
+	    g->lins = glines;
+	    nlines += glines;
+	}
+    }
+    if (!onlyexpl && isset(LISTPACKED)) {
+	char **pp;
+	int *ws, tlines, tline, tcols, maxlen, nth, width;
+
+	for (g = amatches; g; g = g->next) {
+	    ws = g->widths = (int *) zalloc(columns * sizeof(int));
+	    memset(ws, 0, columns * sizeof(int));
+	    tlines = g->lins;
+	    tcols = g->cols;
+	    width = 0;
+
+	    if ((pp = g->ylist)) {
+		if (!(g->flags & CGF_LINES)) {
+		    int yl = arrlen(pp), i;
+		    VARARR(int, ylens, yl);
+
+		    for (i = 0; *pp; i++, pp++)
+			ylens[i] = strlen(*pp) + add;
+
+		    if (isset(LISTROWSFIRST)) {
+			int count, tcol, first, maxlines = 0, llines;
+
+			for (tcols = columns / g->shortest; tcols > g->cols;
+			     tcols--) {
+			    for (nth = first = maxlen = width = maxlines =
+				     llines = tcol = 0,
+				     count = g->dcount;
+				 count > 0; count--) {
+				if (ylens[nth] > maxlen)
+				    maxlen = ylens[nth];
+				nth += tcols;
+				tlines++;
+				if (nth >= g->dcount) {
+				    if ((width += maxlen) >= columns)
+					break;
+				    ws[tcol++] = maxlen;
+				    maxlen = 0;
+				    nth = ++first;
+				    if (llines > maxlines)
+					maxlines = llines;
+				    llines = 0;
+				}
+			    }
+			    if (nth < yl) {
+				ws[tcol++] = maxlen;
+				width += maxlen;
+			    }
+			    if (!count && width < columns)
+				break;
+			}
+			if (tcols > g->cols)
+			    tlines = maxlines;
+		    } else {
+			for (tlines = ((g->totl + columns) / columns);
+			     tlines < g->lins; tlines++) {
+			    for (pp = g->ylist, nth = tline = width =
+				     maxlen = tcols = 0;
+				 *pp; nth++, pp++) {
+				if (ylens[nth] > maxlen)
+				    maxlen = ylens[nth];
+				if (++tline == tlines) {
+				    if ((width += maxlen) >= columns)
+					break;
+				    ws[tcols++] = maxlen;
+				    maxlen = tline = 0;
+				}
+			    }
+			    if (tline) {
+				ws[tcols++] = maxlen;
+				width += maxlen;
+			    }
+			    if (nth == yl && width < columns)
+				break;
+			}
+		    }
+		}
+	    } else if (g->width) {
+		if (isset(LISTROWSFIRST)) {
+		    int addlen, count, tcol, maxlines = 0, llines, i;
+		    Cmatch *first;
+
+		    for (tcols = columns / g->shortest; tcols > g->cols;
+			 tcols--) {
+			p = first = skipnolist(g->matches);
+			for (maxlen = width = maxlines = llines = tcol = 0,
+				 count = g->dcount;
+			     count > 0; count--) {
+			    m = *p;
+			    addlen = mlens[m->gnum] + add;
+			    if (addlen > maxlen)
+				maxlen = addlen;
+			    for (i = tcols; i && *p; i--)
+				p = skipnolist(p + 1);
+
+			    llines++;
+			    if (!*p) {
+				if (llines > maxlines)
+				    maxlines = llines;
+				llines = 0;
+
+				if ((width += maxlen) >= columns)
+				    break;
+				ws[tcol++] = maxlen;
+				maxlen = 0;
+
+				p = first = skipnolist(first + 1);
+			    }
+			}
+			if (tlines) {
+			    ws[tcol++] = maxlen;
+			    width += maxlen;
+			}
+			if (!count && width < columns)
+			    break;
+		    }
+		    if (tcols > g->cols)
+			tlines = maxlines;
+		} else {
+		    int addlen;
+
+		    for (tlines = ((g->totl + columns) / columns);
+			 tlines < g->lins; tlines++) {
+			for (p = g->matches, nth = tline = width =
+				 maxlen = tcols = 0;
+			     (m = *p); p++, nth++) {
+			    if (!(m->flags &
+				  (m->disp ? (CMF_DISPLINE | CMF_HIDE) :
+				   (CMF_NOLIST | CMF_HIDE)))) {
+				addlen = mlens[m->gnum] + add;
+				if (addlen > maxlen)
+				    maxlen = addlen;
+				if (++tline == tlines) {
+				    if ((width += maxlen) >= columns)
+					break;
+				    ws[tcols++] = maxlen;
+				    maxlen = tline = 0;
+				}
+			    }
+			}
+			if (tline) {
+			    ws[tcols++] = maxlen;
+			    width += maxlen;
+			}
+			if (nth == g->dcount && width < columns)
+			    break;
+		    }
+		}
+	    }
+	    if (tlines == g->lins) {
+		zfree(ws, columns * sizeof(int));
+		g->widths = NULL;
+	    } else {
+		nlines += tlines - g->lins;
+		g->lins = tlines;
+		g->cols = tcols;
+		g->totl = width;
+		width -= add;
+		if (width > max)
+		    max = width;
+	    }
+	}
+	for (g = amatches; g; g = g->next) {
+	    if (g->widths) {
+		int *p, a = (max - g->totl + add) / g->cols;
+
+		for (i = g->cols, p = g->widths; i; i--, p++)
+		    *p += a;
+	    } else if (g->width && g->cols > 1)
+		g->width += (max - (g->width * g->cols - add)) / g->cols;
+	}
+    }
+    listdat.valid = 1;
+    listdat.hidden = hidden;
+    listdat.nlist = nlist;
+    listdat.nlines = nlines;
+    listdat.menuacc = menuacc;
+    listdat.onlyexpl = onlyexpl;
+    listdat.columns = columns;
+    listdat.lines = lines;
+}
+
+/**/
+int asklist(void)
+{
+    /* Set the cursor below the prompt. */
+    trashzle();
+    showinglist = listshown = 0;
+
+    clearflag = (isset(USEZLE) && !termflags &&
+		 complastprompt && *complastprompt);
+
+    /* Maybe we have to ask if the user wants to see the list. */
+    if ((!minfo.cur || !minfo.asked) &&
+	((complistmax && listdat.nlist > complistmax) ||
+	 (!complistmax && listdat.nlines >= lines))) {
+	int qup;
+	zsetterm();
+	qup = printfmt("zsh: do you wish to see all %n possibilities? ",
+		       listdat.nlist, 1, 1);
+	fflush(shout);
+	if (getzlequery() != 'y') {
+	    if (clearflag) {
+		putc('\r', shout);
+		tcmultout(TCUP, TCMULTUP, qup);
+		if (tccan(TCCLEAREOD))
+		    tcout(TCCLEAREOD);
+		tcmultout(TCUP, TCMULTUP, nlnct);
+	    } else
+		putc('\n', shout);
+	    if (minfo.cur)
+		minfo.asked = 2;
+	    return 1;
+	}
+	if (clearflag) {
+	    putc('\r', shout);
+	    tcmultout(TCUP, TCMULTUP, qup);
+	    if (tccan(TCCLEAREOD))
+		tcout(TCCLEAREOD);
+	} else
+	    putc('\n', shout);
+	settyinfo(&shttyinfo);
+	if (minfo.cur)
+	    minfo.asked = 1;
+    }
+    return 0;
+}
+
+/**/
+int
+printlist(int over, CLPrintFunc printm)
+{
+    Cmgroup g;
+    Cmatch *p, m;
+    Cexpl *e;
+    int pnl = 0, cl = (over ? listdat.nlines : -1);
+    int mc = 0, ml = 0, printed = 0;
+
+    if (cl < 2) {
+	cl = -1;
+	if (tccan(TCCLEAREOD))
+	    tcout(TCCLEAREOD);
+    }
+    g = amatches;
+    while (g) {
+	char **pp = g->ylist;
+
+	if ((e = g->expls)) {
+	    int l;
+
+	    while (*e) {
+		if ((*e)->count) {
+		    if (pnl) {
+			putc('\n', shout);
+			pnl = 0;
+			ml++;
+			if (cl >= 0 && --cl <= 1) {
+			    cl = -1;
+			    if (tccan(TCCLEAREOD))
+				tcout(TCCLEAREOD);
+			}
+		    }
+		    l = printfmt((*e)->str, (*e)->count, 1, 1);
+		    ml += l;
+		    if (cl >= 0 && (cl -= l) <= 1) {
+			cl = -1;
+			if (tccan(TCCLEAREOD))
+			    tcout(TCCLEAREOD);
+		    }
+		    pnl = 1;
+		}
+		e++;
+	    }
+	}
+	if (!listdat.onlyexpl && pp && *pp) {
+	    if (pnl) {
+		putc('\n', shout);
+		pnl = 0;
+		ml++;
+		if (cl >= 0 && --cl <= 1) {
+		    cl = -1;
+		    if (tccan(TCCLEAREOD))
+			tcout(TCCLEAREOD);
+		}
+	    }
+	    if (g->flags & CGF_LINES) {
+		while (*pp) {
+		    zputs(*pp, shout);
+		    if (*++pp)
+			putc('\n', shout);
+		}
+	    } else {
+		int n = g->lcount, nl, nc, i, a;
+		char **pq;
+
+		nl = nc = g->lins;
+
+		while (n && nl--) {
+		    i = g->cols;
+		    mc = 0;
+		    pq = pp;
+		    while (n && i--) {
+			if (pq - g->ylist >= g->lcount)
+			    break;
+			zputs(*pq, shout);
+			if (i) {
+			    a = (g->widths ? g->widths[mc] : g->width) -
+				strlen(*pq);
+			    while (a--)
+				putc(' ', shout);
+			}
+			pq += (isset(LISTROWSFIRST) ? 1 : nc);
+			mc++;
+			n--;
+		    }
+		    if (n) {
+			putc('\n', shout);
+			ml++;
+			if (cl >= 0 && --cl <= 1) {
+			    cl = -1;
+			    if (tccan(TCCLEAREOD))
+				tcout(TCCLEAREOD);
+			}
+		    }
+		    pp += (isset(LISTROWSFIRST) ? g->cols : 1);
+		}
+	    }
+	} else if (!listdat.onlyexpl && g->lcount) {
+	    int n = g->dcount, nl, nc, i, j, wid;
+	    Cmatch *q;
+
+	    nl = nc = g->lins;
+
+	    if (g->flags & CGF_HASDL) {
+		for (p = g->matches; (m = *p); p++)
+		    if (m->disp && (m->flags & CMF_DISPLINE)) {
+			if (pnl) {
+			    putc('\n', shout);
+			    pnl = 0;
+			    ml++;
+			    if (cl >= 0 && --cl <= 1) {
+				cl = -1;
+				if (tccan(TCCLEAREOD))
+				    tcout(TCCLEAREOD);
+			    }
+			}
+			printed++;
+			printm(g, p, 0, ml, 1, 0, NULL, NULL);
+			pnl = 1;
+		    }
+	    }
+	    if (n && pnl) {
+		putc('\n', shout);
+		pnl = 0;
+		ml++;
+		if (cl >= 0 && --cl <= 1) {
+		    cl = -1;
+		    if (tccan(TCCLEAREOD))
+			tcout(TCCLEAREOD);
+		}
+	    }
+	    for (p = skipnolist(g->matches); n && nl--;) {
+		i = g->cols;
+		mc = 0;
+		q = p;
+		while (n && i--) {
+		    wid = (g->widths ? g->widths[mc] : g->width);
+		    if (!(m = *q)) {
+			printm(g, NULL, mc, ml, (!i), wid, NULL, NULL);
+			break;
+		    }
+		    if (!m->disp && (m->flags & CMF_FILE)) {
+			struct stat buf;
+			char *pb;
+
+			pb = (char *) zhalloc((m->prpre ? strlen(m->prpre) : 0) +
+					     3 + strlen(m->str));
+			sprintf(pb, "%s%s", (m->prpre ? m->prpre : "./"),
+				m->str);
+
+			if (ztat(pb, &buf, 1))
+			    printm(g, q, mc, ml, (!i), wid, NULL, NULL);
+			else
+			    printm(g, q, mc, ml, (!i), wid, pb, &buf);
+		    } else
+			printm(g, q, mc, ml, (!i), wid, NULL, NULL);
+
+		    printed++;
+
+		    if (--n)
+			for (j = (isset(LISTROWSFIRST) ? 1 : nc); j && *q; j--)
+			    q = skipnolist(q + 1);
+		    mc++;
+		}
+		while (i-- > 0)
+		    printm(g, NULL, mc++, ml, (!i),
+			   (g->widths ? g->widths[mc] : g->width), NULL, NULL);
+
+		if (n) {
+		    putc('\n', shout);
+		    ml++;
+		    if (cl >= 0 && --cl <= 1) {
+			cl = -1;
+			if (tccan(TCCLEAREOD))
+			    tcout(TCCLEAREOD);
+		    }
+		    if (nl)
+			for (j = (isset(LISTROWSFIRST) ? g->cols : 1); j && *p; j--)
+			    p = skipnolist(p + 1);
+		}
+	    }
+	}
+	if (g->lcount)
+	    pnl = 1;
+	g = g->next;
+    }
+    if (clearflag) {
+	/* Move the cursor up to the prompt, if always_last_prompt *
+	 * is set and all that...                                  */
+	if ((ml = listdat.nlines + nlnct - 1) < lines) {
+	    tcmultout(TCUP, TCMULTUP, ml);
+	    showinglist = -1;
+	} else
+	    clearflag = 0, putc('\n', shout);
+    } else
+	putc('\n', shout);
+    listshown = (clearflag ? 1 : -1);
+
+    return printed;
+}
+
+/**/
+static void
+iprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
+	char *path, struct stat *buf)
+{
+    Cmatch m;
+    int len = 0;
+
+    if (!mp)
+	return;
+
+    m = *mp;
+    if (m->disp) {
+	if (m->flags & CMF_DISPLINE) {
+	    printfmt(m->disp, 0, 1, 0);
+	    return;
+	}
+	nicezputs(m->disp, shout);
+	len = niceztrlen(m->disp);
+    } else {
+	nicezputs(m->str, shout);
+	len = niceztrlen(m->str);
+
+	if (isset(LISTTYPES)) {
+	    if (buf)
+		putc(file_type(buf->st_mode), shout);
+	    len++;
+	}
+    }
+    if (!lastc) {
+	len = width - len;
+
+	while (len-- > 0)
+	    putc(' ', shout);
+    }
+}
+
+/**/
+int
+ilistmatches(Hookdef dummy, Chdata dat)
+{
+    calclist();
+
+    if (!listdat.nlines) {
+	showinglist = listshown = 0;
+	return 1;
+    }
+    if (asklist())
+	return 0;
+
+    printlist(0, iprintm);
+
+    return 0;
+}
+
+/* List the matches.  Note that the list entries are metafied. */
+
+/**/
+int
+list_matches(Hookdef dummy, void *dummy2)
+{
+    struct chdata dat;
+
+#ifdef DEBUG
+    /* Sanity check */
+    if (!validlist) {
+	showmsg("BUG: listmatches called with bogus list");
+	return 1;
+    }
+#endif
+
+    dat.matches = amatches;
+    dat.num = nmatches;
+    dat.cur = NULL;
+    return runhookdef(COMPLISTMATCHESHOOK, (void *) &dat);
+}
+
+/* Invalidate the completion list. */
+
+/**/
+int
+invalidate_list(void)
+{
+    if (showinglist == -2)
+	listmatches();
+    if (validlist) {
+	freematches(lastmatches);
+	lastmatches = NULL;
+	hasoldlist = 0;
+    }
+    lastambig = menucmp = menuacc = validlist = showinglist = fromcomp = 0;
+    listdat.valid = 0;
+    if (listshown < 0)
+	listshown = 0;
+    minfo.cur = NULL;
+    minfo.asked = 0;
+    zsfree(minfo.prebr);
+    zsfree(minfo.postbr);
+    minfo.postbr = minfo.prebr = NULL;
+    compwidget = NULL;
+
+    return 0;
+}
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 470aa890f..2dfeb6950 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -144,19 +144,53 @@ typedef struct cutbuffer *Cutbuffer;
 
 #define KRINGCT 8   /* number of buffers in the kill ring */
 
+/* Types of completion. */
+
+#define COMP_COMPLETE        0
+#define COMP_LIST_COMPLETE   1
+#define COMP_SPELL           2
+#define COMP_EXPAND          3
+#define COMP_EXPAND_COMPLETE 4
+#define COMP_LIST_EXPAND     5
+#define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND)
+
+/* Information about one brace run. */
+
+typedef struct brinfo *Brinfo;
+
+struct brinfo {
+    Brinfo next;		/* next in list */
+    Brinfo prev;		/* previous (only for closing braces) */
+    char *str;			/* the string to insert */
+    int pos;			/* original position */
+    int qpos;			/* original position, with quoting */
+    int curpos;			/* position for current match */
+};
+
 /* Convenience macros for the hooks */
 
-#define LISTMATCHESHOOK   (zlehooks + 0)
-#define INSERTMATCHHOOK   (zlehooks + 1)
-#define MENUSTARTHOOK     (zlehooks + 2)
-#define COMPCTLMAKEHOOK   (zlehooks + 3)
-#define COMPCTLBEFOREHOOK (zlehooks + 4)
-#define COMPCTLAFTERHOOK  (zlehooks + 5)
+#define LISTMATCHESHOOK    (zlehooks + 0)
+#define COMPLETEHOOK       (zlehooks + 1)
+#define BEFORECOMPLETEHOOK (zlehooks + 2)
+#define AFTERCOMPLETEHOOK  (zlehooks + 3)
+#define ACCEPTCOMPHOOK     (zlehooks + 4)
+#define REVERSEMENUHOOK    (zlehooks + 5)
+#define INVALIDATELISTHOOK (zlehooks + 6)
 
-/* compctl hook data structs */
+/* complete hook data struct */
 
-struct ccmakedat {
-    char *str;
-    int incmd;
+typedef struct compldat *Compldat;
+
+struct compldat {
+    char *s;
     int lst;
+    int incmd;
 };
+
+/* List completion matches. */
+
+#define listmatches() runhookdef(LISTMATCHESHOOK, NULL)
+
+/* Invalidate the completion list. */
+
+#define invalidatelist() runhookdef(INVALIDATELISTHOOK, NULL)
diff --git a/Src/Zle/zle.mdd b/Src/Zle/zle.mdd
index c3d6672a4..3235c88a1 100644
--- a/Src/Zle/zle.mdd
+++ b/Src/Zle/zle.mdd
@@ -6,7 +6,7 @@ objects="zle_bindings.o zle_hist.o zle_keymap.o zle_main.o \
 zle_misc.o zle_move.o zle_params.o zle_refresh.o \
 zle_thingy.o zle_tricky.o zle_utils.o zle_vi.o zle_word.o"
 
-headers="zle.h zle_things.h comp.h"
+headers="zle.h zle_things.h"
 
 :<<\Make
 zle_things.h: thingies.list zle_things.sed
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 56ecd02ba..f29da3e0e 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -30,14 +30,6 @@
 #include "zle.mdh"
 #include "zle_main.pro"
 
-/* Defined by the complete module, called in zle_tricky.c. */
-
-/**/
-void (*makecompparamsptr) _((void));
-
-/**/
-void (*comp_setunsetptr) _((int, int, int, int));
-
 /* != 0 if in a shell function called from completion, such that read -[cl]  *
  * will work (i.e., the line is metafied, and the above word arrays are OK). */
 
@@ -49,50 +41,10 @@ int incompctlfunc;
 /**/
 int incompfunc;
 
-/* Global matcher. */
-
-/**/
-Cmlist cmatcher;
-
-/* global variables for shell parameters in new style completion */
+/* != 0 if completion module is loaded */
 
 /**/
-zlong compcurrent,
-      compmatcher,
-      compmatchertot,
-      complistmax,
-      complistlines;
-
-/**/
-char **compwords,
-     *compprefix,
-     *compsuffix,
-     *compiprefix,
-     *compisuffix,
-     *compqiprefix,
-     *compqisuffix,
-     *compmatcherstr,
-     *compcontext,
-     *compparameter,
-     *compredirect,
-     *compquote,
-     *compquoting,
-     *comprestore,
-     *complist,
-     *compforcelist,
-     *compinsert,
-     *compexact,
-     *compexactstr,
-     *comppatmatch,
-     *comppatinsert,
-     *complastprompt,
-     *comptoend,
-     *compoldlist,
-     *compoldins,
-     *compvared;
-
-/**/
-Param *comprpms, *compkpms;
+int hascompmod;
 
 /* != 0 if we're done editing */
 
@@ -1022,12 +974,13 @@ static struct builtin bintab[] = {
 
 /**/
 struct hookdef zlehooks[] = {
-    HOOKDEF("list_matches", ilistmatches, 0),
-    HOOKDEF("insert_match", NULL, HOOKF_ALL),
-    HOOKDEF("menu_start", NULL, HOOKF_ALL),
-    HOOKDEF("compctl_make", NULL, 0),
-    HOOKDEF("compctl_before", NULL, 0),
-    HOOKDEF("compctl_after", NULL, 0),
+    HOOKDEF("list_matches", NULL, 0),
+    HOOKDEF("complete", NULL, 0),
+    HOOKDEF("before_complete", NULL, 0),
+    HOOKDEF("after_complete", NULL, 0),
+    HOOKDEF("accept_completion", NULL, 0),
+    HOOKDEF("reverse_menu", NULL, 0),
+    HOOKDEF("invalidate_list", NULL, 0),
 };
 
 /**/
@@ -1050,30 +1003,16 @@ setup_zle(Module m)
     /* miscellaneous initialisations */
     stackhist = stackcs = -1;
     kungetbuf = (char *) zalloc(kungetsz = 32);
-    hasperm = 0;
 
     /* initialise the keymap system */
     init_keymaps();
 
     varedarg = NULL;
 
-    incompfunc = incompctlfunc = 0;
-
-    comprpms = compkpms = NULL;
-    compwords = NULL;
-    compprefix = compsuffix = compiprefix = compisuffix = 
-	compqiprefix = compqisuffix = compmatcherstr = 
-	compcontext = compparameter = compredirect = compquote =
-	compquoting = comprestore = complist = compinsert =
-	compexact = compexactstr = comppatmatch = comppatinsert =
-	compforcelist = complastprompt = comptoend = 
-	compoldlist = compoldins = compvared = NULL;
+    incompfunc = incompctlfunc = hascompmod = 0;
 
     clwords = (char **) zcalloc((clwsize = 16) * sizeof(char *));
 
-    makecompparamsptr = NULL;
-    comp_setunsetptr = NULL;
-
     return 0;
 }
 
@@ -1132,33 +1071,6 @@ finish_zle(Module m)
 
     getkeyptr = NULL;
 
-    freearray(compwords);
-    zsfree(compprefix);
-    zsfree(compsuffix);
-    zsfree(compiprefix);
-    zsfree(compisuffix);
-    zsfree(compqiprefix);
-    zsfree(compqisuffix);
-    zsfree(compmatcherstr);
-    zsfree(compcontext);
-    zsfree(compparameter);
-    zsfree(compredirect);
-    zsfree(compquote);
-    zsfree(compquoting);
-    zsfree(comprestore);
-    zsfree(complist);
-    zsfree(compforcelist);
-    zsfree(compinsert);
-    zsfree(compexact);
-    zsfree(compexactstr);
-    zsfree(comppatmatch);
-    zsfree(comppatinsert);
-    zsfree(complastprompt);
-    zsfree(comptoend);
-    zsfree(compoldlist);
-    zsfree(compoldins);
-    zsfree(compvared);
-
     zfree(clwords, clwsize * sizeof(char *));
 
     return 0;
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 136a9d432..18e38ee3a 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -640,7 +640,7 @@ Thingy
 executenamedcommand(char *prmt)
 {
     Thingy cmd;
-    int len, l = strlen(prmt), ols = listshown, feep = 0;
+    int len, l = strlen(prmt), ols = listshown, feep = 0, listed = 0, curlist = 0;
     char *ptr;
     char *okeymap = curkeymapname;
 
@@ -660,14 +660,31 @@ executenamedcommand(char *prmt)
 	    selectkeymap(okeymap, 1);
 	    if ((listshown = ols))
 		showinglist = -2;
-	    else
-		clearlist = 1;
+	    else if (listed)
+		clearlist = listshown = 1;
+
 	    return NULL;
 	}
 	if(cmd == Th(z_clearscreen)) {
 	    clearscreen(zlenoargs);
+	    if (curlist) {
+		int zmultsav = zmult;
+
+		zmult = 1;
+		listlist(cmdll);
+		showinglist = 0;
+		zmult = zmultsav;
+	    }
 	} else if(cmd == Th(z_redisplay)) {
 	    redisplay(zlenoargs);
+	    if (curlist) {
+		int zmultsav = zmult;
+
+		zmult = 1;
+		listlist(cmdll);
+		showinglist = 0;
+		zmult = zmultsav;
+	    }
 	} else if(cmd == Th(z_viquotedinsert)) {
 	    *ptr = '^';
 	    zrefresh();
@@ -675,23 +692,28 @@ executenamedcommand(char *prmt)
 	    if(c == EOF || !c || len == NAMLEN)
 		feep = 1;
 	    else
-		*ptr++ = c, len++;
+		*ptr++ = c, len++, curlist = 0;
 	} else if(cmd == Th(z_quotedinsert)) {
 	    if((c = getkey(0)) == EOF || !c || len == NAMLEN)
 		feep = 1;
 	    else
-		*ptr++ = c, len++;
+		*ptr++ = c, len++, curlist = 0;
 	} else if(cmd == Th(z_backwarddeletechar) ||
 	    	cmd == Th(z_vibackwarddeletechar)) {
 	    if (len)
-		len--, ptr--;
+		len--, ptr--, curlist = 0;
 	} else if(cmd == Th(z_killregion) || cmd == Th(z_backwardkillword) ||
-	    	cmd == Th(z_vibackwardkillword)) {
+		  cmd == Th(z_vibackwardkillword)) {
+	    if (len)
+		curlist = 0;
 	    while (len && (len--, *--ptr != '-'));
 	} else if(cmd == Th(z_killwholeline) || cmd == Th(z_vikillline) ||
 	    	cmd == Th(z_backwardkillline)) {
 	    len = 0;
 	    ptr = cmdbuf;
+	    if (listed)
+		clearlist = listshown = 1;
+	    curlist = 0;
 	} else {
 	    if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode)) {
 		Thingy r;
@@ -704,8 +726,8 @@ executenamedcommand(char *prmt)
 		    selectkeymap(okeymap, 1);
 		    if ((listshown = ols))
 			showinglist = -2;
-		    else
-			clearlist = 1;
+		    else if (listed)
+			clearlist = listshown = 1;
 		    return r;
 		}
 		unrefthingy(r);
@@ -728,15 +750,19 @@ executenamedcommand(char *prmt)
 
 		    scanhashtable(thingytab, 1, 0, DISABLED, scancompcmd, 0);
 		} LASTALLOC;
-		if (empty(cmdll))
+		if (empty(cmdll)) {
 		    feep = 1;
-		else if (cmd == Th(z_listchoices) ||
+		    if (listed)
+			clearlist = listshown = 1;
+		    curlist = 0;
+		} else if (cmd == Th(z_listchoices) ||
 		    cmd == Th(z_deletecharorlist)) {
 		    int zmultsav = zmult;
 		    *ptr = '_';
 		    statusll = l + len + 1;
 		    zmult = 1;
 		    listlist(cmdll);
+		    listed = curlist = 1;
 		    showinglist = 0;
 		    zmult = zmultsav;
 		} else if (!nextnode(firstnode(cmdll))) {
@@ -756,6 +782,7 @@ executenamedcommand(char *prmt)
 			statusll = l + cmdambig + 1;
 			zmult = 1;
 			listlist(cmdll);
+			listed = curlist = 1;
 			showinglist = 0;
 			zmult = zmultsav;
 		    }
@@ -765,7 +792,7 @@ executenamedcommand(char *prmt)
 		if (len == NAMLEN || icntrl(c) || cmd != Th(z_selfinsert))
 		    feep = 1;
 		else
-		    *ptr++ = c, len++;
+		    *ptr++ = c, len++, curlist = 0;
 	    }
 	}
 	if (feep)
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 72a0f120d..a4191a9a4 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -424,7 +424,7 @@ bin_zle_refresh(char *name, char **args, char *ops, char func)
 	} else if (ops['c'])
 	    clearlist = 1;
     } else if (ops['c'])
-	clearlist = 1;
+	clearlist = listshown = 1;
     zrefresh();
 
     clearlist = ocl;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index a5f6dbc88..14c1791d1 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -42,13 +42,14 @@
  * file only are not translated: they remain indexes into the metafied  *
  * line.                                                                */
 
-
 #define inststr(X) inststrlen((X),1,-1)
 
 /* The line before completion was tried. */
 
-static char *origline;
-static int origcs, origll;
+/**/
+char *origline;
+/**/
+int origcs, origll;
 
 /* Words on the command line, for use in completion */
  
@@ -68,54 +69,18 @@ int wb, we;
 /**/
 int offs;
 
-/* We store the following prefixes/suffixes:                               *
- * ipre,ripre  -- the ignored prefix (quoted and unquoted)                 *
- * isuf        -- the ignored suffix                                       *
- * autoq       -- quotes to automatically insert                           */
+/* These control the type of completion that will be done.  They are       *
+ * affected by the choice of ZLE command and by relevant shell options.    *
+ * usemenu is set to 2 if we have to start automenu and 3 if we have to    *
+ * insert a match as if for menucompletion but without really starting it. */
 
 /**/
-char *ipre, *ripre, *isuf, *qipre, *qisuf, autoq;
-
-/* the last completion widget called */
+int usemenu, useglob;
 
-static Widget lastcompwidget;
-
-/* These control the type of completion that will be done.  They are      *
- * affected by the choice of ZLE command and by relevant shell options.   *
- * usemenu is set to 2 if we have to start automenu and 3 if we have to   *
- * insert a match as if for menucompletion but without really stating it. */
+/* != 0 if we are in the middle of a menu completion. */
 
 /**/
-int usemenu, useglob, useexact, useline, uselist;
-
-/* Non-zero if we should keep an old list. */
-
-static int oldlist, oldins;
-
-/* Non-zero if we have to redisplay the list of matches. */
-
-static int showagain = 0;
-
-/* The match and group number to insert when starting menucompletion.   */
-
-static int insmnum, insgnum, insgroup, insspace;
-
-/* 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.         */
-
-static int movetoend;
-
-/* != 0 if we are in the middle of a menu completion and number of matches
-* accepted with accept-and-menu-complete */
-
-/**/
-int menucmp, menuacc;
-
-/* Information about menucompletion. */
-
-/**/
-struct menuinfo minfo;
+int menucmp;
 
 /* Lists of brace-infos before/after cursor (first and last for each). */
 
@@ -125,164 +90,34 @@ Brinfo brbeg, lastbrbeg, brend, lastbrend;
 /**/
 int nbrbeg, nbrend;
 
-static char *lastprebr, *lastpostbr;
-static int hasunqu, useqbr, brpcs, brscs;
-
-/* 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. */
-
-static int permmnum, permgnum, lastpermmnum, lastpermgnum;
-
-/* The total number of matches and the number of matches to be listed. */
-
-/**/
-int nmatches, smatches;
+char *lastprebr, *lastpostbr;
 
 /* !=0 if we have a valid completion list. */
 
 /**/
 int validlist;
 
-/* != 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. */
+/* Non-zero if we have to redisplay the list of matches. */
 
 /**/
-int hasmatched;
-
-/* A parameter expansion prefix (like ${). */
-
-static char *parpre;
-
-/* Flags for parameter expansions for new style completion. */
-
-static int parflags;
+int showagain = 0;
 
 /* This holds the word we are completing in quoted from. */
 
 static char *qword;
 
-/* The current group of matches. */
-
-static Cmgroup mgroup;
-
-/* Match counters: all matches, normal matches (not alternate set). */
-
-/**/
-int mnum;
-
-/* The match counter when unambig_data() was called. */
-
-static int unambig_mnum;
-
-/* Match flags for all matches in this group. */
-
-/**/
-int mflags;
-
-/* Length of longest/shortest match. */
-
-static int maxmlen, minmlen;
-
-/* This holds the explanation strings we have to print in this group and *
- * a pointer to the current cexpl structure. */
-
-static LinkList expls;
+/* The quoted prefix/suffix and a flag saying if we want to add the
+ * closing quote. */
 
 /**/
-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. */
-
-static Cline freecl;
-
-/* Ambiguous information. */
-
-static Aminfo ainfo, fainfo;
+char *qipre, *qisuf, autoq;
 
 /* This contains the name of the function to call if this is for a new  *
  * style completion. */
 
-static char *compfunc = NULL;
-
-/* The memory heap to use for new style completion generation. */
-
-/**/
-Heap compheap;
-
-/* Find out if we have to insert a tab (instead of trying to complete). */
-
-/* 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;
-
-/**/
-static int
-usetab(void)
-{
-    unsigned char *s = line + cs - 1;
-
-    for (; s >= line && *s != '\n'; s--)
-	if (*s != '\t' && *s != ' ')
-	    return 0;
-    return 1;
-}
+char *compfunc = NULL;
 
 /* Non-zero if the last completion done was ambiguous (used to find   *
  * out if AUTOMENU should start).  More precisely, it's nonzero after *
@@ -298,28 +133,26 @@ usetab(void)
 /**/
 int lastambig;
 
-/* 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.                    */
-
-static int fromcomp;
-
-/* This holds the end-position of the last string inserted into the line. */
+/* Arguments for and return value of completion widget. */
 
-static int lastend;
+/**/
+char **cfargs;
+/**/
+int cfret;
 
-#define FC_LINE   1
-#define FC_INWORD 2
+/* Find out if we have to insert a tab (instead of trying to complete). */
 
-/* Arguments for and return value of completion widget. */
+/**/
+static int
+usetab(void)
+{
+    unsigned char *s = line + cs - 1;
 
-static char **cfargs;
-static int cfret;
+    for (; s >= line && *s != '\n'; s--)
+	if (*s != '\t' && *s != ' ')
+	    return 0;
+    return 1;
+}
 
 /**/
 int
@@ -387,21 +220,15 @@ spellword(char **args)
 int
 deletecharorlist(char **args)
 {
-    Cmgroup mg = minfo.group;
-    Cmatch *mc = minfo.cur;
-    int ret;
-
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
+
     if (cs != ll) {
 	fixsuffix();
-	ret = deletechar(args);
-    } else
-	ret = docomplete(COMP_LIST_COMPLETE);
-
-    minfo.cur = mc;
-    minfo.group = mg;
-    return ret;
+	invalidatelist();
+	return deletechar(args);
+    }
+    return docomplete(COMP_LIST_COMPLETE);
 }
 
 /**/
@@ -462,92 +289,17 @@ reversemenucomplete(char **args)
     if (!menucmp)
 	return menucomplete(args);
 
-    HEAPALLOC {
-	do {
-	    if (minfo.cur == (minfo.group)->matches) {
-		do {
-		    if (!(minfo.group = (minfo.group)->prev))
-			minfo.group = lmatches;
-		} while (!(minfo.group)->mcount);
-		minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1;
-	    } else
-		minfo.cur--;
-	} while (menuacc &&
-		 !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr));
-	metafy_line();
-	do_single(*(minfo.cur));
-	unmetafy_line();
-    } LASTALLOC;
+    runhookdef(REVERSEMENUHOOK, NULL);
     return 0;
 }
 
-/* Accepts the current completion and starts a new arg, *
- * with the next completions. This gives you a way to   *
- * accept several selections from the list of matches.  */
-
-/**/
-void
-acceptlast(void)
-{
-    if (!menuacc) {
-	zsfree(minfo.prebr);
-	minfo.prebr = ztrdup(lastprebr);
-	zsfree(minfo.postbr);
-	minfo.postbr = ztrdup(lastpostbr);
-
-	if (listshown && (lastprebr || lastpostbr)) {
-	    Cmgroup g;
-	    Cmatch *m;
-
-	    for (g = amatches, m = NULL; g && (!m || !*m); g = g->next)
-		for (m = g->matches; *m; m++)
-		    if (!hasbrpsfx(*m, minfo.prebr, minfo.postbr)) {
-			showinglist = -2;
-			break;
-		    }
-	}
-    }
-    menuacc++;
-
-    if (brbeg) {
-	int l;
-
-	iremovesuffix(',', 1);
-
-	l = (brscs >= 0 ? brscs : cs) - brpcs;
-
-	zsfree(lastbrbeg->str);
-	lastbrbeg->str = (char *) zalloc(l + 2);
-	memcpy(lastbrbeg->str, line + brpcs, l);
-	lastbrbeg->str[l] = ',';
-	lastbrbeg->str[l + 1] = '\0';
-    } else {
-	int l;
-
-	cs = minfo.pos + minfo.len + minfo.insc;
-	iremovesuffix(' ', 1);
-	l = cs;
-	cs = minfo.pos + minfo.len + minfo.insc - (*(minfo.cur))->qisl;
-	if (cs < l)
-	    foredel(l - cs);
-	else if (cs > ll)
-	    cs = ll;
-	inststrlen(" ", 1, 1);
-	if (parpre)
-	    inststr(parpre);
-	minfo.insc = minfo.len = 0;
-	minfo.pos = cs;
-	minfo.we = 1;
-    }
-}
-
 /**/
 int
 acceptandmenucomplete(char **args)
 {
     if (!menucmp)
 	return 1;
-    acceptlast();
+    runhookdef(ACCEPTCOMPHOOK, NULL);
     return menucomplete(args);
 }
 
@@ -555,11 +307,12 @@ acceptandmenucomplete(char **args)
  * position, in a redirection, or in a parameter expansion.   */
 
 /**/
-int lincmd, linredir, ispar, parq, eparq, linwhat, linarr;
+int lincmd, linredir, linarr;
 
 /* The string for the redirection operator. */
 
-static char *rdstr;
+/**/
+char *rdstr;
 
 /* This holds the name of the current command (used to find the right *
  * compctl).                                                          */
@@ -569,7 +322,8 @@ char *cmdstr;
 
 /* This hold the name of the variable we are working on. */
 
-static char *varname;
+/**/
+char *varname;
 
 /* != 0 if we are in a subscript */
 
@@ -591,25 +345,6 @@ int instring, inbackt;
 
 #define quotename(s, e) bslashquote(s, e, instring)
 
-/* 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;
-}
-
 /* Check if the given string is the name of a parameter and if this *
  * parameter is one worth expanding.                                */
 
@@ -628,7 +363,7 @@ checkparams(char *p)
 		    e = 1;
 	    }
     return (n == 1) ? (getsparam(p) != NULL) :
-	(!menucmp && e && isset(RECEXACT));
+	(!menucmp && e && (!hascompmod || isset(RECEXACT)));
 }
 
 /* Check if the given string has wildcards.  The difficulty is that we *
@@ -692,15 +427,10 @@ cmphaswilds(char *str)
 
 /**/
 char *
-check_param(char *s, int set, int test)
+parambeg(char *s)
 {
     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) {
@@ -743,10 +473,8 @@ check_param(char *s, int set, int test)
 
 	e = b;
 	if (br) {
-	    while (*e == (test ? Dnull : '"'))
-		e++, parq++;
-	    if (!test)
-		b = e;
+	    while (*e == Dnull)
+		e++;
 	}
 	/* Find the end of the name. */
 	if (*e == Quest || *e == Star || *e == String || *e == Qstring ||
@@ -757,60 +485,17 @@ check_param(char *s, int set, int test)
 	    while (idigit(*e))
 		e++;
 	else if (iident(*e))
-	    while (iident(*e) ||
-		   (comppatmatch && *comppatmatch &&
-		    (*e == Star || *e == Quest)))
+	    while (iident(*e))
 		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++;
+		while (*p == Dnull)
+		    p++;
 	    }
 	    /* 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);
-	    }
-	    else
-		parq = eparq = 0;
-
-	    /* 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;
 	}
     }
@@ -824,33 +509,10 @@ static int
 docomplete(int lst)
 {
     char *s, *ol;
-    int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0, omc = menucmp;
+    int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0;
 
-    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);
+    if (runhookdef(BEFORECOMPLETEHOOK, (void *) &lst))
 	return 0;
-    }
-    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;
 
     /* Expand history references before starting completion.  If anything *
      * changed, do no more.                                               */
@@ -930,7 +592,7 @@ docomplete(int lst)
 		/* The word starts with `=', see if we can expand it. */
 		q = s + 1;
 		if (cmdnamtab->getnode(cmdnamtab, q) || hashcmd(q, pathchecked)) {
-		    if (isset(RECEXACT))
+		    if (!hascompmod || isset(RECEXACT))
 			lst = COMP_EXPAND;
 		    else {
 			int t0, n = 0;
@@ -1081,51 +743,11 @@ docomplete(int lst)
     zsfree(qword);
     unmetafy_line();
 
-    if (menucmp && !omc) {
-	struct chdata dat;
+    runhookdef(AFTERCOMPLETEHOOK, (void *) &lst);
 
-	dat.matches = amatches;
-	dat.num = nmatches;
-	dat.cur = NULL;
-	if (runhookdef(MENUSTARTHOOK, (void *) &dat))
-	    menucmp = menuacc = 0;
-    }
     return ret;
 }
 
-/* Do completion, given that we are in the middle of a menu completion.  We *
- * don't need to generate a list of matches, because that's already been    *
- * done by previous commands.  We will either list the completions, or      *
- * insert the next completion.                                              */
-
-/**/
-void
-do_menucmp(int lst)
-{
-    /* Just list the matches if the list was requested. */
-    if (lst == COMP_LIST_COMPLETE) {
-	showinglist = -2;
-	return;
-    }
-    /* Otherwise go to the next match in the array... */
-    HEAPALLOC {
-	do {
-	    if (!*++(minfo.cur)) {
-		do {
-		    if (!(minfo.group = (minfo.group)->next))
-			minfo.group = amatches;
-		} while (!(minfo.group)->mcount);
-		minfo.cur = minfo.group->matches;
-	    }
-	} while (menuacc &&
-		 !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr));
-	/* ... and insert it into the command line. */
-	metafy_line();
-	do_single(*(minfo.cur));
-	unmetafy_line();
-    } LASTALLOC;
-}
-
 /* 1 if we are completing the prefix */
 static int comppref;
 
@@ -1193,7 +815,7 @@ dupstrspace(const char *str)
  * functions (there's one for each direction).                             */
 
 /**/
-static void
+void
 metafy_line(void)
 {
     int len = ll;
@@ -1209,7 +831,7 @@ metafy_line(void)
 }
 
 /**/
-static void
+void
 unmetafy_line(void)
 {
     cs = ztrsub((char *) line + cs, (char *) line);
@@ -1634,7 +1256,7 @@ get_comp_string(void)
 	/* This variable will hold the current word in quoted form. */
 	qword = ztrdup(s);
 	offs = cs - wb;
-	if ((p = check_param(s, 0, 1))) {
+	if ((p = parambeg(s))) {
 	    for (p = s; *p; p++)
 		if (*p == Dnull)
 		    *p = '"';
@@ -1925,6 +1547,26 @@ get_comp_string(void)
     return (char *)s;
 }
 
+/* Insert the given string into the command line.  If move is non-zero, *
+ * the cursor position is changed and len is the length of the string   *
+ * to insert (if it is -1, the length is calculated here).              *
+ * The last argument says if we should quote the string.                */
+
+/**/
+int
+inststrlen(char *str, int move, int len)
+{
+    if (!len || !str)
+	return 0;
+    if (len == -1)
+	len = strlen(str);
+    spaceinline(len);
+    strncpy((char *)(line + cs), str, len);
+    if (move)
+	cs += len;
+    return len;
+}
+
 /* Expand the current word. */
 
 /**/
@@ -1980,12 +1622,6 @@ doexpansion(char *s, int lst, int olst, int explincmd)
 	    untokenize(ss);
 	    ss = quotename(ss, NULL);
 	    inststr(ss);
-#if 0
-	    if (nonempty(vl)) {
-		spaceinline(1);
-		line[cs++] = ' ';
-	    }
-#endif
 	    if (olst != COMP_EXPAND_COMPLETE || nonempty(vl) ||
 		(cs && line[cs-1] != '/')) {
 		spaceinline(1);
@@ -2012,3513 +1648,55 @@ gotword(void)
     }
 }
 
-/* This compares two cpattern lists and returns non-zero if they are
- * equal. */
-
-static int
-cmp_cpatterns(Cpattern a, Cpattern b)
-{
-    while (a) {
-	if (a->equiv != b->equiv || memcmp(a->tab, b->tab, 256))
-	    return 0;
-	a = a->next;
-	b = b->next;
-    }
-    return 1;
-}
-
-/* This compares two cmatchers and returns non-zero if they are equal. */
-
-static int
-cmp_cmatchers(Cmatcher a, Cmatcher b)
-{
-    return (a == b ||
-	    (a->flags == b->flags &&
-	     a->llen == b->llen && a->wlen == b->wlen &&
-	     (!a->llen || cmp_cpatterns(a->line, b->line)) &&
-	     (a->wlen <= 0 || cmp_cpatterns(a->word, b->word)) &&
-	     (!(a->flags & CMF_LEFT) ||
-	      (a->lalen == b->lalen &&
-	       (!a->lalen || cmp_cpatterns(a->left, b->left)))) &&
-	     (!(a->flags & CMF_RIGHT) ||
-	      (a->ralen == b->ralen &&
-	       (!a->ralen || cmp_cpatterns(a->right, b->right))))));
-}
-
-/* Add the given matchers to the bmatcher list. */
-
-static void
-add_bmatchers(Cmatcher m)
-{
-    Cmlist old = bmatchers, *q = &bmatchers, n;
-
-    for (; m; m = m->next) {
-	if ((!m->flags && m->wlen > 0 && m->llen > 0) ||
-	    (m->flags == CMF_RIGHT && m->wlen == -1 && !m->llen)) {
-	    *q = n = (Cmlist) zhalloc(sizeof(struct cmlist));
-	    n->matcher = m;
-	    q = &(n->next);
-	}
-    }
-    *q = old;
-}
-
-/* This is called when the matchers in the mstack have changed to
- * ensure that the bmatchers list contains no matchers not in mstack. */
-
-static void
-update_bmatchers(void)
-{
-    Cmlist p = bmatchers, q = NULL, ms;
-    Cmatcher mp;
-    int t;
-
-    while (p) {
-	t = 0;
-	for (ms = mstack; ms && !t; ms = ms->next)
-	    for (mp = ms->matcher; mp && !t; mp = mp->next)
-		t = cmp_cmatchers(mp, p->matcher);
-
-	p = p->next;
-	if (!t) {
-	    if (q)
-		q->next = p;
-	    else
-		bmatchers = p;
-	}
-    }
-}
-
-/* This returns a new Cline structure. */
-
-static Cline
-get_cline(char *l, int ll, char *w, int wl, char *o, int ol, int fl)
-{
-    Cline r;
-
-    /* Prefer to take it from the buffer list (freecl), if there
-     * is none, allocate a new one. */
-
-    if ((r = freecl))
-	freecl = r->next;
-    else
-	r = (Cline) zhalloc(sizeof(*r));
-
-    r->next = NULL;
-    r->line = l; r->llen = ll;
-    r->word = w; r->wlen = wl;
-    r->orig = o; r->olen = ol;
-    r->slen = 0;
-    r->flags = fl;
-    r->prefix = r->suffix = NULL;
-    return r;
-}
-
-/* This frees a cline list. */
-
-static void
-free_cline(Cline l)
-{
-    Cline n;
-
-    while (l) {
-	n = l->next;
-	l->next = freecl;
-	freecl = l;
-	free_cline(l->prefix);
-	free_cline(l->suffix);
-	l = n;
-    }
-}
-
-/* Copy a cline list. */
-
-static Cline
-cp_cline(Cline l, int deep)
-{
-    Cline r = NULL, *p = &r, t, lp = NULL;
-
-    while (l) {
-	if ((t = freecl))
-	    freecl = t->next;
-	else
-	    t = (Cline) zhalloc(sizeof(*t));
-	memcpy(t, l, sizeof(*t));
-	if (deep) {
-	    if (t->prefix)
-		t->prefix = cp_cline(t->prefix, 0);
-	    if (t->suffix)
-		t->suffix = cp_cline(t->suffix, 0);
-	}
-	*p = lp = t;
-	p = &(t->next);
-	l = l->next;
-    }
-    *p = NULL;
-
-    return r;
-}
-
-/* Calculate the length of a cline and its sub-lists. */
-
-static int
-cline_sublen(Cline l)
-{
-    int len = ((l->flags & CLF_LINE) ? l->llen : l->wlen);
-
-    if (l->olen && !((l->flags & CLF_SUF) ? l->suffix : l->prefix))
-	len += l->olen;
-    else {
-	Cline p;
-
-	for (p = l->prefix; p; p = p->next)
-	    len += ((p->flags & CLF_LINE) ? p->llen : p->wlen);
-	for (p = l->suffix; p; p = p->next)
-	    len += ((p->flags & CLF_LINE) ? p->llen : p->wlen);
-    }
-    return len;
-}
-
-/* Set the lengths in the cline lists. */
-
-static void
-cline_setlens(Cline l, int both)
-{
-    while (l) {
-	l->max = cline_sublen(l);
-	if (both)
-	    l->min = l->max;
-	l = l->next;
-    }
-}
-
-/* This sets the CLF_MATCHED flag in the given clines. */
-
-static void
-cline_matched(Cline p)
-{
-    while (p) {
-	p->flags |= CLF_MATCHED;
-	cline_matched(p->prefix);
-	cline_matched(p->suffix);
-
-	p = p->next;
-    }
-}
-
-/* This reverts the order of the elements of the given cline list and
- * returns a pointer to the new head. */
-
-static Cline
-revert_cline(Cline p)
-{
-    Cline r = NULL, n;
-
-    while (p) {
-	n = p->next;
-	p->next = r;
-	r = p;
-	p = n;
-    }
-    return r;
-}
-
-/* Check if the given pattern matches the given string.             *
- * `in' and `out' are used for {...} classes. In `out' we store the *
- * character number that was matched. In the word pattern this is   *
- * given in `in' so that we can easily test if we found the         *
- * corresponding character. */
-
-/**/
-static int
-pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out)
-{
-    unsigned char c;
-
-    while (p) {
-	c = *((unsigned char *) s);
-
-	if (out)
-	    *out = 0;
-
-	if (p->equiv) {
-	    if (in) {
-		c = p->tab[c];
-		if ((*in && *in != c) || (!*in && !c))
-		    return 0;
-	    } else if (out) {
-		if (!(*out = p->tab[c]))
-		    return 0;
-	    } else if (!p->tab[c])
-		return 0;
-
-	    if (in && *in)
-		in++;
-	    if (out)
-		out++;
-	} else if (!p->tab[c])
-	    return 0;
-
-	s++;
-	p = p->next;
-    }
-    return 1;
-}
-
-/* This splits the given string into a list of cline structs, separated
- * at those places where one of the anchors of an `*' pattern was found.
- * plen gives the number of characters on the line that matched this
- * string. In lp we return a pointer to the last cline struct we build. */
-
-static Cline
-bld_parts(char *str, int len, int plen, Cline *lp)
-{
-    Cline ret = NULL, *q = &ret, n;
-    Cmlist ms;
-    Cmatcher mp;
-    int t, op = plen;
-    char *p = str;
-
-    while (len) {
-	for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
-	    mp = ms->matcher;
-	    if (mp->flags == CMF_RIGHT && mp->wlen == -1 &&
-		!mp->llen && len >= mp->ralen && mp->ralen &&
-		pattern_match(mp->right, str, NULL, NULL)) {
-		int olen = str - p, llen;
-
-		/* We found an anchor, create a new cline. The NEW flag
-		 * is set if the characters before the anchor were not
-		 * on the line. */
-		*q = n = get_cline(NULL, mp->ralen, str, mp->ralen, NULL, 0,
-				   ((plen < 0) ? CLF_NEW : 0));
-
-		/* If there were any characters before the anchor, add
-		 * them as a cline struct. */
-
-		if (p != str) {
-		    llen = (op < 0 ? 0 : op);
-
-		    if (llen > olen)
-			llen = olen;
-		    n->prefix = get_cline(NULL, llen, p, olen, NULL, 0, 0);
-		}
-		q = &(n->next);
-		str += mp->ralen; len -= mp->ralen;
-		plen -= mp->ralen;
-		op -= olen;
-		p = str;
-		t = 1;
-	    }
-	}
-	if (!t) {
-	    /* No anchor was found here, skip. */
-	    str++; len--;
-	    plen--;
-	}
-    }
-    /* This is the cline struct for the remaining string at the end. */
-
-    *q = n = get_cline(NULL, 0, NULL, 0, NULL, 0, (plen < 0 ? CLF_NEW : 0));
-    if (p != str) {
-	int olen = str - p, llen = (op < 0 ? 0 : op);
-
-	if (llen > olen)
-	    llen = olen;
-	n->prefix = get_cline(NULL, llen, p, olen, NULL, 0, 0);
-    }
-    n->next = NULL;
-
-    if (lp)
-	*lp = n;
-
-    return ret;
-}
-
-/* Global variables used during matching: a char-buffer for the string to
- * use for the match, and two cline lists for the two levels we use. */
-
-static char *matchbuf = NULL;
-static int matchbuflen = 0, matchbufadded;
-
-static Cline matchparts, matchlastpart;
-static Cline matchsubs, matchlastsub;
-
-/* This initialises the variables above. */
-
-static void
-start_match(void)
-{
-    if (matchbuf)
-	*matchbuf = '\0';
-    matchbufadded = 0;
-    matchparts = matchlastpart = matchsubs = matchlastsub = NULL;
-}
-
-/* This aborts a matching, freeing the cline lists build. */
-
-static void
-abort_match(void)
-{
-    free_cline(matchparts);
-    free_cline(matchsubs);
-    matchparts = matchsubs = NULL;
-}
-
-/* This adds a new string in the static char buffer. The arguments are
- * the matcher used (if any), the strings from the line and the word
- * and the length of the string from the word. The last argument is
- * non-zero if we are matching a suffix (where the given string has to 
- * be prepended to the contents of the buffer). */
-
-static void
-add_match_str(Cmatcher m, char *l, char *w, int wl, int sfx)
-{
-    /* Get the string and length to insert: either from the line 
-     * or from the match. */
-    if (m && (m->flags & CMF_LINE)) {
-	wl = m->llen; w = l;
-    }
-    if (wl) {
-	/* Probably resize the buffer. */
-	if (matchbuflen - matchbufadded <= wl) {
-	    int blen = matchbuflen + wl + 20;
-	    char *buf;
-
-	    buf = (char *) zalloc(blen);
-	    memcpy(buf, matchbuf, matchbuflen);
-	    zfree(matchbuf, matchbuflen);
-	    matchbuf = buf;
-	    matchbuflen = blen;
-	}
-	/* Insert the string. */
-	if (sfx) {
-	    memmove(matchbuf + wl, matchbuf, matchbufadded + 1);
-	    memcpy(matchbuf, w, wl);
-	} else
-	    memcpy(matchbuf + matchbufadded, w, wl);
-	matchbufadded += wl;
-	matchbuf[matchbufadded] = '\0';
-    }
-}
-
-/* This adds a cline for a word-part during matching. Arguments are the
- * matcher used, pointers to the line and word strings for the anchor,
- * a pointer to the original line string for the whole part, the string
- * before (or after) the anchor that has not yet been added, the length
- * of the line-string for that, and a flag saying if we are matching a 
- * suffix. */
-
-static void
-add_match_part(Cmatcher m, char *l, char *w, int wl,
-	       char *o, int ol, char *s, int sl, int osl, int sfx)
-{
-    Cline p, lp;
-
-    /* If the anchors are equal, we keep only one. */
-
-    if (!strncmp(l, w, wl))
-	l = NULL;
-
-    /* Split the new part into parts and turn the last one into a
-     * `suffix' if we have a left anchor. */
-
-    p = bld_parts(s, sl, osl, &lp);
-
-    p->flags &= ~CLF_NEW;
-    if (m && (m->flags & CMF_LEFT)) {
-	lp->flags |= CLF_SUF;
-	lp->suffix = lp->prefix;
-	lp->prefix = NULL;
-    }
-    /* cline lists for suffixes are sorted from back to front, so we have
-     * to revert the list we got. */
-    if (sfx)
-	p = revert_cline(lp = p);
-    /* Now add the sub-clines we already had. */
-    if (matchsubs) {
-	if (sfx) {
-	    Cline q;
-
-	    if ((q = lp->prefix)) {
-		while (q->next)
-		    q = q->next;
-		q->next = matchsubs;
-	    } else
-		lp->prefix = matchsubs;
-
-	    matchlastsub->next = NULL;
-	} else {
-	    matchlastsub->next = p->prefix;
-	    p->prefix = matchsubs;
-	}
-	matchsubs = matchlastsub = NULL;
-    }
-    /* Store the arguments in the last part-cline. */
-    lp->line = l; lp->llen = wl;
-    lp->word = w; lp->wlen = wl;
-    lp->orig = o; lp->olen = ol;
-    lp->flags &= ~CLF_NEW;
-
-    /* Finally, put the new parts on the list. */
-    if (matchlastpart)
-	matchlastpart->next = p;
-    else
-	matchparts = p;
-    matchlastpart = lp;
-}
-
-/* This adds a new sub-cline. Arguments are the matcher and the strings from
- * the line and the word. */
-
-static void
-add_match_sub(Cmatcher m, char *l, int ll, char *w, int wl)
-{
-    int flags;
-    Cline n;
-
-    /* Check if we are interested only in the string from the line. */
-    if (m && (m->flags & CMF_LINE)) {
-	w = NULL; wl = 0;
-	flags = CLF_LINE;
-    } else
-	flags = 0;
-
-    /* And add the cline. */
-    if (wl || ll) {
-	n = get_cline(l, ll, w, wl, NULL, 0, flags);
-	if (matchlastsub)
-	    matchlastsub->next = n;
-	else
-	    matchsubs = n;
-	matchlastsub = n;
-    }
-}
-
-/* This tests if the string from the line l matches the word w. In bp
- * the offset for the brace is returned, in rwlp the length of the
- * matched prefix or suffix, not including the stuff before or after
- * the last anchor is given. When sfx is non-zero matching is done from
- * the ends of the strings backward, if test is zero, the global variables
- * above are used to build the string for the match and the cline. If
- * part is non-zero, we are satisfied if only a part of the line-string
- * is used (and return the length used). */
-
-static int
-match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
-	  int sfx, int test, int part)
-{
-    int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw;
-    int il = 0, iw = 0, t, ind, add, he = 0, bpc, obc = bc;
-    VARARR(unsigned char, ea, ll + 1);
-    char *ow;
-    Cmlist ms;
-    Cmatcher mp, lm = NULL;
-    Brinfo bp = NULL;
-
-    if (!test) {
-	start_match();
-	bp = *bpp;
-    }
-    /* Adjust the pointers and get the values for subscripting and
-     * incrementing. */
-
-    if (sfx) {
-	l += ll; w += lw;
-	ind = -1; add = -1;
-    } else {
-	ind = 0; add = 1;
-    }
-    /* ow will always point to the beginning (or end) of that sub-string
-     * in w that wasn't put in the match-variables yet. */
-
-    ow = w;
-
-    /* If the brace is at the beginning, we have to treat it now. */
-
-    if (!test && bp && bc >= bp->pos) {
-	bp->curpos = bc;
-	bp = bp->next;
-    }
-    while (ll && lw) {
-	/* First try the matchers. */
-	for (mp = NULL, ms = mstack; !mp && ms; ms = ms->next) {
-	    for (mp = ms->matcher; mp; mp = mp->next) {
-		t = 1;
-		if ((lm && lm == mp) ||
-		    ((oll == ll || olw == lw) &&
-		     (test == 1 || (test && !mp->left && !mp->right)) &&
-		     mp->wlen < 0))
-		    /* If we were called recursively, don't use `*' patterns
-		     * at the beginning (avoiding infinite recursion). */
-		    continue;
-
-		if (mp->wlen < 0) {
-		    int both, loff, aoff, llen, alen, zoff, moff, ct, ict;
-		    char *tp, savl = '\0', savw;
-		    Cpattern ap;
-
-		    /* This is for `*' patterns, first initialise some
-		     * local variables. */
-		    llen = mp->llen;
-		    alen = (mp->flags & CMF_LEFT ? mp->lalen : mp->ralen);
-
-		    /* Give up if we don't have enough characters for the
-		     * line-string and the anchor. */
-		    if (ll < llen + alen || lw < alen)
-			continue;
-
-		    if (mp->flags & CMF_LEFT) {
-			ap = mp->left; zoff = 0; moff = alen;
-			if (sfx) {
-			    both = 0; loff = -llen; aoff = -(llen + alen);
-			} else {
-			    both = 1; loff = alen; aoff = 0;
-			}
-		    } else {
-			ap = mp->right; zoff = alen; moff = 0;
-			if (sfx) {
-			    both = 1; loff = -(llen + alen); aoff = -alen;
-			} else {
-			    both = 0; loff = 0; aoff = llen;
-			}
-		    }
-		    /* Try to match the line pattern and the anchor. */
-		    if (!pattern_match(mp->line, l + loff, NULL, NULL))
-			continue;
-		    if (ap) {
-			if (!pattern_match(ap, l + aoff, NULL, NULL) ||
-			    (both && (!pattern_match(ap, w + aoff, NULL, NULL) ||
-				      !match_parts(l + aoff, w + aoff, alen,
-						   part))))
-				continue;
-		    } else if (!both || il || iw)
-			continue;
-
-		    /* Fine, now we call ourselves recursively to find the
-		     * string matched by the `*'. */
-		    if (sfx) {
-			savl = l[-(llen + zoff)];
-			l[-(llen + zoff)] = '\0';
-		    }
-		    for (t = 0, tp = w, ct = 0, ict = lw - alen + 1;
-			 ict;
-			 tp += add, ct++, ict--) {
-			if ((both &&
-			     (!ap || !test ||
-			      !pattern_match(ap, tp + aoff, NULL, NULL))) ||
-			    (!both &&
-			     pattern_match(ap, tp - moff, NULL, NULL) &&
-			     match_parts(l + aoff , tp - moff, alen, part))) {
-			    if (sfx) {
-				savw = tp[-zoff];
-				tp[-zoff] = '\0';
-				t = match_str(l - ll, w - lw,
-					      NULL, 0, NULL, 1, 2, part);
-				tp[-zoff] = savw;
-			    } else
-				t = match_str(l + llen + moff, tp + moff,
-					      NULL, 0, NULL, 0, 1, part);
-			    if (t || !both)
-				break;
-			}
-		    }
-		    ict = ct;
-		    if (sfx)
-			l[-(llen + zoff)] = savl;
-
-		    /* Have we found a position in w where the rest of l
-		     * matches? */
-		    if (!t)
-			continue;
-
-		    /* Yes, add the strings and clines if this is a 
-		     * top-level call. */
-		    if (!test && (!he || (llen + alen))) {
-			char *op, *lp, *map, *wap, *wmp;
-			int ol;
-
-			if (sfx) {
-			    op = w; ol = ow - w; lp = l - (llen + alen);
-			    map = tp - alen;
-			    if (mp->flags & CMF_LEFT) {
-				wap = tp - alen; wmp = tp;
-			    } else {
-				wap = w - alen; wmp = tp - alen;
-			    }
-			} else {
-			    op = ow; ol = w - ow; lp = l;
-			    map = ow;
-			    if (mp->flags & CMF_LEFT) {
-				wap = w; wmp = w + alen;
-			    } else {
-				wap = tp; wmp = ow;
-			    }
-			}
-			/* If the matcher says that we are only interested
-			 * in the line pattern, we just add that and the
-			 * anchor and the string not added yet. Otherwise
-			 * we add a new part. */
-			if (mp->flags & CMF_LINE) {
-			    add_match_str(NULL, NULL, op, ol, sfx);
-			    add_match_str(NULL, NULL, lp, llen + alen, sfx);
-			    add_match_sub(NULL, NULL, ol, op, ol);
-			    add_match_sub(NULL, NULL, llen + alen,
-					  lp, llen + alen);
-			} else if (sfx) {
-			    add_match_str(NULL, NULL,
-					  map, ct + ol + alen, sfx);
-			    add_match_part(mp, l + aoff, wap, alen,
-					   l + loff, llen, op, ol, ol, sfx);
-			    add_match_sub(NULL, NULL, 0, wmp, ct);
-			} else {
-			    add_match_str(NULL, NULL,
-					  map, ct + ol + alen, sfx);
-			    if (both) {
-				add_match_sub(NULL, NULL, ol, op, ol);
-				ol = -1;
-			    } else
-				ct += ol;
-			    add_match_part(mp, l + aoff, wap, alen,
-					   l + loff, llen, wmp, ct, ol, sfx);
-			}
-		    }
-		    /* Now skip over the matched portion and the anchor. */
-		    llen += alen; alen += ict;
-		    if (sfx) {
-			l -= llen; w -= alen;
-		    } else {
-			l += llen; w += alen;
-		    }
-		    ll -= llen; il += llen;
-		    lw -= alen; iw += alen;
-		    bc += llen;
-
-		    if (!test)
-			while (bp &&
-			       bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
-			    bp->curpos = matchbufadded + bpc - bc + obc;
-			    bp = bp->next;
-			}
-		    ow = w;
-
-		    if (!llen && !alen) {
-			lm = mp;
-			if (he)
-			    mp = NULL;
-			else
-			    he = 1;
-		    } else {
-			lm = NULL; he = 0;
-		    }
-		    break;
-		} else if (ll >= mp->llen && lw >= mp->wlen) {
-		    /* Non-`*'-pattern. */
-		    char *tl, *tw;
-		    int tll, tlw, til, tiw;
-
-		    /* We do this only if the line- and word-substrings
-		     * are not equal. */
-		    if (!(mp->flags & (CMF_LEFT | CMF_RIGHT)) &&
-			mp->llen == mp->wlen &&
-			!(sfx ? strncmp(l - mp->llen, w - mp->wlen, mp->llen) :
-			  strncmp(l, w, mp->llen)))
-			continue;
-
-		    /* Using local variables to make the following
-		     * independent of whether we match a prefix or a
-		     * suffix. */
-		    if (sfx) {
-			tl = l - mp->llen; tw = w - mp->wlen;
-			til = ll - mp->llen; tiw = lw - mp->wlen;
-			tll = il + mp->llen; tlw = iw + mp->wlen;
-		    } else {
-			tl = l; tw = w;
-			til = il; tiw = iw;
-			tll = ll; tlw = lw;
-		    }
-		    if (mp->flags & CMF_LEFT) {
-			/* Try to match the left anchor, if any. */
-			if (til < mp->lalen || tiw < mp->lalen)
-			    continue;
-			else if (mp->left)
-			    t = pattern_match(mp->left, tl - mp->lalen,
-					      NULL, NULL) &&
-				pattern_match(mp->left, tw - mp->lalen,
-					      NULL, NULL);
-			else
-			    t = (!sfx && !il && !iw);
-		    }
-		    if (mp->flags & CMF_RIGHT) {
-			/* Try to match the right anchor, if any. */
-			if (tll < mp->llen + mp->ralen ||
-			    tlw < mp->wlen + mp->ralen)
-			    continue;
-			else if (mp->left)
-			    t = pattern_match(mp->right,
-					      tl + mp->llen - mp->ralen,
-					      NULL, NULL) &&
-				pattern_match(mp->right,
-					      tw + mp->wlen - mp->ralen,
-					      NULL, NULL);
-			else
-			    t = (sfx && !il && !iw);
-		    }
-		    /* Now try to match the line and word patterns. */
-		    if (!t ||
-			!pattern_match(mp->line, tl, NULL, ea) ||
-			!pattern_match(mp->word, tw, ea, NULL))
-			continue;
-
-		    /* Probably add the matched strings. */
-		    if (!test) {
-			if (sfx)
-			    add_match_str(NULL, NULL, w, ow - w, 0);
-			else
-			    add_match_str(NULL, NULL, ow, w - ow, 0);
-			add_match_str(mp, tl, tw, mp->wlen, 0);
-			if (sfx)
-			    add_match_sub(NULL, NULL, 0, w, ow - w);
-			else
-			    add_match_sub(NULL, NULL, 0, ow, w - ow);
-
-			add_match_sub(mp, tl, mp->llen, tw, mp->wlen);
-		    }
-		    if (sfx) {
-			l = tl;	w = tw;
-		    } else {
-			l += mp->llen; w += mp->wlen;
-		    }
-		    il += mp->llen; iw += mp->wlen;
-		    ll -= mp->llen; lw -= mp->wlen;
-		    bc += mp->llen;
-
-		    if (!test)
-			while (bp &&
-			       bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
-			    bp->curpos = matchbufadded + bpc - bc + obc;
-			    bp = bp->next;
-			}
-		    ow = w;
-		    lm = NULL;
-		    he = 0;
-		    break;
-		}
-	    }
-	}
-	if (mp)
-	    continue;
-
-	if (l[ind] == w[ind]) {
-	    /* No matcher could be used, but the strings have the same
-	     * character here, skip over it. */
-	    l += add; w += add;
-	    il++; iw++;
-	    ll--; lw--;
-	    bc++;
-	    if (!test)
-		while (bp && bc >= (useqbr ? bp->qpos : bp->pos)) {
-		    bp->curpos = matchbufadded + (sfx ? (ow - w) : (w - ow)) + obc;
-		    bp = bp->next;
-		}
-	    lm = NULL;
-	    he = 0;
-	} else {
-	    /* No matcher and different characters: l does not match w. */
-	    if (test)
-		return 0;
-
-	    abort_match();
-
-	    return -1;
-	}
-    }
-    /* If this is a recursive call, we just return if l matched w or not. */
-    if (test)
-	return (part || !ll);
-
-    /* In top-level calls, if ll is non-zero (unmatched portion in l),
-     * we have to free the collected clines. */
-    if (!part && ll) {
-	abort_match();
-
-	return -1;
-    }
-    if (rwlp)
-	*rwlp = iw - (sfx ? ow - w : w - ow);
-
-    /* If we matched a suffix, the anchors stored in the top-clines
-     * will be in the wrong clines: shifted by one. Adjust this. */
-    if (sfx && matchparts) {
-	Cline t, tn, s;
-
-	if (matchparts->prefix || matchparts->suffix) {
-	    t = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
-	    t->next = matchparts;
-	    if (matchparts->prefix)
-		t->prefix = (Cline) 1;
-	    else
-		t->suffix = (Cline) 1;
-	    matchparts = t;
-	}
-	for (t = matchparts; (tn = t->next); t = tn) {
-	    s = (tn->prefix ? tn->prefix : tn->suffix);
-	    if (t->suffix)
-		t->suffix = s;
-	    else
-		t->prefix = s;
-	}
-	t->prefix = t->suffix = NULL;
-    }
-    /* Finally, return the number of matched characters. */
-
-    *bpp = bp;
-    return (part ? il : iw);
-}
-
-/* Wrapper for match_str(), only for a certain length and only doing
- * the test. */
-
-/**/
-static int
-match_parts(char *l, char *w, int n, int part)
-{
-    char lsav = l[n], wsav = w[n];
-    int ret;
-
-    l[n] = w[n] = '\0';
-    ret = match_str(l, w, NULL, 0, NULL, 0, 1, part);
-    l[n] = lsav;
-    w[n] = wsav;
-
-    return ret;
-}
-
-/* Check if the word w is matched by the strings in pfx and sfx (the prefix
- * and the suffix from the line) or the pattern cp. In clp a cline list for
- * w is returned.
- * qu is non-zero if the words has to be quoted before processed any further.
- * bpl and bsl are used to report the positions where the brace-strings in
- * the prefix and the suffix have to be re-inserted if this match is inserted
- * in the line.
- * The return value is the string to use as a completion or NULL if the prefix
- * and the suffix don't match the word w. */
-
-/**/
-char *
-comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu,
-	   Brinfo *bpl, int bcp, Brinfo *bsl, int bcs, int *exact)
-{
-    char *r = NULL;
-
-    if (cp) {
-	/* We have a globcomplete-like pattern, just use that. */
-	int wl;
-
-	r = w;
-	if (!pattry(cp, r))
-	    return NULL;
-    
-	r = (qu ? quotename(r, NULL) : dupstring(r));
-	if (qu == 2 && r[0] == '\\' && r[1] == '~')
-	    chuck(r);
-	/* We still break it into parts here, trying to build a sensible
-	 * cline list for these matches, too. */
-	w = dupstring(w);
-	wl = strlen(w);
-	*clp = bld_parts(w, wl, wl, NULL);
-	*exact = 0;
-    } else {
-	Cline pli, plil;
-	int mpl, rpl, wl;
-
-	w = (qu ? quotename(w, NULL) : dupstring(w));
-	if (qu == 2 && w[0] == '\\' && w[1] == '~')
-	    chuck(w);
-
-	wl = strlen(w);
-
-	/* Always try to match the prefix. */
-
-	useqbr = qu;
-	if ((mpl = match_str(pfx, w, bpl, bcp, &rpl, 0, 0, 0)) < 0)
-	    return NULL;
-
-	if (sfx && *sfx) {
-	    int wpl = matchbufadded, msl, rsl;
-	    VARARR(char, wpfx, wpl);
-	    Cline mli, mlil;
-
-	    /* We also have a suffix to match, so first save the
-	     * contents of the global matching variables. */
-	    memcpy(wpfx, matchbuf, wpl);
-	    if (matchsubs) {
-		Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
-
-		tmp->prefix = matchsubs;
-		if (matchlastpart)
-		    matchlastpart->next = tmp;
-		else
-		    matchparts = tmp;
-	    }
-	    pli = matchparts;
-	    plil = matchlastpart;
-
-	    /* The try to match the suffix. */
-
-	    if ((msl = match_str(sfx, w + mpl, bsl, bcs, &rsl, 1, 0, 0)) < 0) {
-		free_cline(pli);
-
-		return NULL;
-	    }
-	    /* Matched, so add the string in the middle and the saved
-	     * string for the prefix, and build a combined cline list
-	     * for the prefix and the suffix. */
-	    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;
-	    }
-	    add_match_str(NULL, NULL, w + rpl, wl - rpl - rsl, 1);
-	    add_match_str(NULL, NULL, wpfx, wpl, 1);
-
-	    mli = bld_parts(w + rpl, wl - rpl - rsl,
-			    (mpl - rpl) + (msl - rsl), &mlil);
-	    mlil->flags |= CLF_MID;
-	    mlil->slen = msl - rsl;
-	    mlil->next = revert_cline(matchparts);
-
-	    if (plil)
-		plil->next = mli;
-	    else
-		pli = mli;
-	} else {
-	    /* Only a prefix, add the string and a part-cline for it. */
-	    add_match_str(NULL, NULL, w + rpl, wl - rpl, 0);
-
-	    add_match_part(NULL, NULL, NULL, 0, NULL, 0, w + rpl, wl - rpl,
-			   mpl - rpl, 0);
-	    pli = matchparts;
-	}
-	r = dupstring(matchbuf ? matchbuf : "");
-
-	*clp = pli;
-
-	/* Test if the string built is equal to the one from the line. */
-	if (sfx && *sfx) {
-	    int pl = strlen(pfx);
-
-	    *exact = (!strncmp(pfx, w, pl) && !strcmp(sfx, w + pl));
-	} else
-	    *exact = !strcmp(pfx, w);
-    }
-    if (!qu)
-	hasunqu = 1;
-
-    return r;
-}
-
-/* This builds all the possible line patterns for the pattern pat in the
- * buffer line. Initially line is the same as lp, but during recursive
- * calls lp is incremented for storing successive characters. Whenever
- * a full possible string is build, we test if this line matches the
- * string given by wlen and word. The in argument contains the characters
- * to use for the correspondence classes, it was filled by a call to 
- * pattern_match() in the calling function.
- * The return value is the length of the string matched in the word, it
- * is zero if we couldn't build a line that matches the word. */
-
-static int
-bld_line(Cpattern pat, char *line, char *lp,
-	 char *word, int wlen, unsigned char *in, int sfx)
-{
-    if (pat) {
-	/* Still working on the pattern. */
-
-	int i, l;
-	unsigned char c = 0;
-
-	/* Get the number of the character for a correspondence class
-	 * if it has a correxponding class. */
-	if (pat->equiv)
-	    if ((c = *in))
-		in++;
-
-	/* Walk through the table in the pattern and try the characters
-	 * that may appear in the current position. */
-	for (i = 0; i < 256; i++)
-	    if ((pat->equiv && c) ? (c == pat->tab[i]) : pat->tab[i]) {
-		*lp = i;
-		/* We stored the character, now call ourselves to build
-		 * the rest. */
-		if ((l = bld_line(pat->next, line, lp + 1, word, wlen,
-				  in, sfx)))
-		    return l;
-	    }
-    } else {
-	/* We reached the end, i.e. the line string is fully build, now
-	 * see if it matches the given word. */
-
-	Cmlist ms;
-	Cmatcher mp;
-	int l = lp - line, t, rl = 0, ind, add;
-	VARARR(unsigned char, ea, l + 1);
-
-	/* Quick test if the strings are exactly the same. */
-	if (l == wlen && !strncmp(line, word, l))
-	    return l;
-
-	if (sfx) {
-	    line = lp; word += wlen;
-	    ind = -1; add = -1;
-	} else {
-	    ind = 0; add = 1;
-	}
-	/* We loop through the whole line string built. */
-	while (l && wlen) {
-	    if (word[ind] == line[ind]) {
-		/* The same character in both strings, skip over. */
-		line += add; word += add;
-		l--; wlen--; rl++;
-	    } else {
-		t = 0;
-		for (ms = bmatchers; ms && !t; ms = ms->next) {
-		    mp = ms->matcher;
-		    if (!mp->flags && mp->wlen <= wlen && mp->llen <= l &&
-			pattern_match(mp->line, (sfx ? line - mp->llen : line),
-				      NULL, ea) &&
-			pattern_match(mp->word, (sfx ? word - mp->wlen : word),
-				      ea, NULL)) {
-			/* Both the line and the word pattern matched,
-			 * now skip over the matched portions. */
-			if (sfx) {
-			    line -= mp->llen; word -= mp->wlen;
-			} else {
-			    line += mp->llen; word += mp->wlen;
-			}
-			l -= mp->llen; wlen -= mp->wlen; rl += mp->wlen;
-			t = 1;
-		    }
-		}
-		if (!t)
-		    /* Didn't match, give up. */
-		    return 0;
-	    }
-	}
-	if (!l)
-	    /* Unmatched portion in the line built, return matched length. */
-	    return rl;
-    }
-    return 0;
-}
-
-/* This builds a string that may be put on the line that fully matches the
- * given strings. The return value is NULL if no such string could be built
- * or that string in local static memory, dup it. */
-
-static char *
-join_strs(int la, char *sa, int lb, char *sb)
-{
-    static char *rs = NULL;
-    static int rl = 0;
-
-    VARARR(unsigned char, ea, (la > lb ? la : lb) + 1);
-    Cmlist ms;
-    Cmatcher mp;
-    int t, bl, rr = rl;
-    char *rp = rs;
-
-    while (la && lb) {
-	if (*sa != *sb) {
-	    /* Different characters, try the matchers. */
-	    for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
-		mp = ms->matcher;
-		if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
-		    mp->wlen <= la && mp->wlen <= lb) {
-		    /* The pattern has no anchors and the word
-		     * pattern fits, try it. */
-		    if ((t = pattern_match(mp->word, sa, NULL, ea)) ||
-			pattern_match(mp->word, sb, NULL, ea)) {
-			/* It matched one of the strings, t says which one. */
-			VARARR(char, line, mp->llen + 1);
-			char **ap, **bp;
-			int *alp, *blp;
-
-			if (t) {
-			    ap = &sa; alp = &la;
-			    bp = &sb; blp = &lb;
-			} else {
-			    ap = &sb; alp = &lb;
-			    bp = &sa; blp = &la;
-			}
-			/* Now try to build a string that matches the other
-			 * string. */
-			if ((bl = bld_line(mp->line, line, line,
-					   *bp, *blp, ea, 0))) {
-			    /* Found one, put it into the return string. */
-			    line[mp->llen] = '\0';
-			    if (rr <= mp->llen) {
-				char *or = rs;
-
-				rs = realloc(rs, (rl += 20));
-				rr += 20;
-				rp += rs - or;
-			    }
-			    memcpy(rp, line, mp->llen);
-			    rp += mp->llen; rr -= mp->llen;
-			    *ap += mp->wlen; *alp -= mp->wlen;
-			    *bp += bl; *blp -= bl;
-			    t = 1;
-			} else
-			    t = 0;
-		    }
-		}
-	    }
-	    if (!t)
-		break;
-	} else {
-	    /* Same character, just take it. */
-	    if (rr <= 1) {
-		char *or = rs;
-
-		rs = realloc(rs, (rl += 20));
-		rr += 20;
-		rp += rs - or;
-	    }
-	    *rp++ = *sa; rr--;
-	    sa++; sb++;
-	    la--; lb--;
-	}
-    }
-    if (la || lb)
-	return NULL;
-
-    *rp = '\0';
-
-    return rs;
-}
-
-/* This compares the anchors stored in two top-level clines. */
-
-static int
-cmp_anchors(Cline o, Cline n, int join)
-{
-    int line = 0;
-    char *j;
-
-    /* First try the exact strings. */
-    if ((!(o->flags & CLF_LINE) && o->wlen == n->wlen &&
-	 (!o->word || !strncmp(o->word, n->word, o->wlen))) ||
-	(line = ((!o->line && !n->line && !o->wlen && !n->wlen) ||
-		 (o->llen == n->llen && o->line && n->line &&
-		  !strncmp(o->line, n->line, o->llen))))) {
-	if (line) {
-	    o->flags |= CLF_LINE;
-	    o->word = NULL;
-	    n->wlen = 0;
-	}
-	return 1;
-    }
-    /* Didn't work, try to build a string matching both anchors. */
-    if (join && !(o->flags & CLF_JOIN) && o->word && n->word &&
-	(j = join_strs(o->wlen, o->word, n->wlen, n->word))) {
-	o->flags |= CLF_JOIN;
-	o->wlen = strlen(j);
-	o->word = dupstring(j);
-
-	return 2;
-    }
-    return 0;
-}
-
-/* Below is the code to join two cline lists. This struct is used to walk
- * through a sub-list. */
-
-typedef struct cmdata *Cmdata;
-
-struct cmdata {
-    Cline cl, pcl;
-    char *str, *astr;
-    int len, alen, olen, line;
-};
-
-/* This is used to ensure that a cmdata struct contains usable data.
- * The return value is non-zero if we reached the end. */
-
-static int
-check_cmdata(Cmdata md, int sfx)
-{
-    /* We will use the str and len fields to contain the next sub-string
-     * in the list. If len is zero, we have to use the next cline. */
-    if (!md->len) {
-	/* If there is none, we reached the end. */
-	if (!md->cl)
-	    return 1;
-
-	/* Otherwise, get the string. Only the line-string or both.
-	 * We also have to adjust the pointer if this is for a suffix. */
-	if (md->cl->flags & CLF_LINE) {
-	    md->line = 1;
-	    md->len = md->cl->llen;
-	    md->str = md->cl->line;
-	} else {
-	    md->line = 0;
-	    md->len = md->olen = md->cl->wlen;
-	    if ((md->str = md->cl->word) && sfx)
-		md->str += md->len;
-	    md->alen = md->cl->llen;
-	    if ((md->astr = md->cl->line) && sfx)
-		md->astr += md->alen;
-	}
-	md->pcl = md->cl;
-	md->cl = md->cl->next;
-    }
-    return 0;
-}
-
-/* This puts the not-yet-matched portion back into the last cline and 
- * returns that. */
-
-static Cline
-undo_cmdata(Cmdata md, int sfx)
-{
-    Cline r = md->pcl;
-
-    if (md->line) {
-	r->word = NULL;
-	r->wlen = 0;
-	r->flags |= CLF_LINE;
-	r->llen = md->len;
-	r->line = md->str - (sfx ? md->len : 0);
-    } else if (md->len != md->olen) {
-	r->wlen = md->len;
-	r->word = md->str - (sfx ? md->len : 0);
-    }
-    return r;
-}
-
-/* This tries to build a string matching a sub-string in a sub-cline
- * that could not be matched otherwise. */
-
-static Cline
-join_sub(Cmdata md, char *str, int len, int *mlen, int sfx, int join)
-{
-    if (!check_cmdata(md, sfx)) {
-	char *ow = str, *nw = md->str;
-	int ol = len, nl = md->len;
-	Cmlist ms;
-	Cmatcher mp;
-	VARARR(unsigned char, ea, (ol > nl ? ol : nl) + 1);
-	int t;
-
-	if (sfx) {
-	    ow += ol; nw += nl;
-	}
-	for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
-	    mp = ms->matcher;
-	    /* We use only those patterns that match a non-empty
-	     * string in both the line and the word and that have
-	     * no anchors. */
-	    if (!mp->flags && mp->wlen > 0 && mp->llen > 0) {
-		/* We first test, if the old string matches already the
-		 * new one. */
-		if (mp->llen <= ol && mp->wlen <= nl &&
-		    pattern_match(mp->line, ow - (sfx ? mp->llen : 0),
-				  NULL, ea) &&
-		    pattern_match(mp->word, nw - (sfx ? mp->wlen : 0),
-				  ea, NULL)) {
-		    /* It did, update the contents of the cmdata struct
-		     * and return a cline for the matched part. */
-		    if (sfx)
-			md->str -= mp->wlen;
-		    else
-			md->str += mp->wlen;
-		    md->len -= mp->wlen;
-		    *mlen = mp->llen;
-
-		    return get_cline(NULL, 0, ow - (sfx ? mp->llen : 0),
-				     mp->llen, NULL, 0, 0);
-		}
-		/* Otherwise we will try to build a string that matches
-		 * both strings. But try the pattern only if the word-
-		 * pattern matches one of the strings. */
-		if (join && mp->wlen <= ol && mp->wlen <= nl &&
-		    ((t = pattern_match(mp->word, ow - (sfx ? mp->wlen : 0),
-				       NULL, ea)) ||
-		     pattern_match(mp->word, nw - (sfx ? mp->wlen : 0),
-				   NULL, ea))) {
-		    VARARR(char, line, mp->llen + 1);
-		    int bl;
-
-		    /* Then build all the possible lines and see
-		     * if one of them matches the other string. */
-		    if ((bl = bld_line(mp->line, line, line,
-				       (t ? nw : ow), (t ? nl : ol),
-				       ea, sfx))) {
-			/* Yep, one of the lines matched the other
-			 * string. */
-			line[mp->llen] = '\0';
-
-			if (t) {
-			    ol = mp->wlen; nl = bl;
-			} else {
-			    ol = bl; nl = mp->wlen;
-			}
-			if (sfx)
-			    md->str -= nl;
-			else
-			    md->str += nl;
-			md->len -= nl;
-			*mlen = ol;
-
-			return get_cline(NULL, 0, dupstring(line), mp->llen,
-					 NULL, 0, CLF_JOIN);
-		    }
-		}
-	    }
-	}
-    }
-    return NULL;
-}
-
-/* This is used to match a sub-string in a sub-cline. The length of the
- * matched portion is returned. This tests only for exact equality. */
-
-static int
-sub_match(Cmdata md, char *str, int len, int sfx)
-{
-    int ret = 0, l, ind, add;
-    char *p, *q;
-
-    if (sfx) {
-	str += len;
-	ind = -1; add = -1;
-    } else {
-	ind = 0; add = 1;
-    }
-    /* str and len describe the old string, in md we have the new one. */
-    while (len) {
-	if (check_cmdata(md, sfx))
-	    return ret;
-
-	for (l = 0, p = str, q = md->str;
-	     l < len && l < md->len && p[ind] == q[ind];
-	     l++, p += add, q += add);
-
-	if (l) {
-	    /* There was a common prefix, use it. */
-	    md->len -= l; len -= l;
-	    if (sfx) {
-		md->str -= l; str -= l;
-	    } else {
-		md->str += l; str += l;
-	    }
-	    ret += l;
-	} else if (md->line || md->len != md->olen || !md->astr)
-	    return ret;
-	else {
-	    /* We still have the line string to try. */
-	    md->line = 1;
-	    md->len = md->alen;
-	    md->str = md->astr;
-	}
-    }
-    return ret;
-}
-
-/* This is used to build a common prefix or suffix sub-list. If requested
- * it returns the unmatched cline lists in orest and nrest. */
-
-static void
-join_psfx(Cline ot, Cline nt, Cline *orest, Cline *nrest, int sfx)
-{
-    Cline p = NULL, o, n;
-    struct cmdata md, omd;
-    char **sstr = NULL;
-    int len, join = 0, line = 0, *slen = NULL;
-
-    if (sfx) {
-	o = ot->suffix; n = nt->suffix;
-    } else {
-	o = ot->prefix;	n = nt->prefix;
-    }
-    if (!o) {
-	if (orest)
-	    *orest = NULL;
-	if (nrest)
-	    *nrest = n;
-
-	return;
-    }
-    if (!n) {
-	if (sfx)
-	    ot->suffix = NULL;
-	else
-	    ot->prefix = NULL;
-
-	if (orest)
-	    *orest = o;
-	else
-	    free_cline(o);
-	if (nrest)
-	    *nrest = NULL;
-	return;
-    }
-    md.cl = n;
-    md.len = 0;
-
-    /* Walk through the old list. */
-    while (o) {
-	join = 0;
-	memcpy(&omd, &md, sizeof(struct cmdata));
-
-	/* We first get the length of the prefix equal in both strings. */
-	if (o->flags & CLF_LINE) {
-	    if ((len = sub_match(&md, o->line, o->llen, sfx)) != o->llen) {
-		join = 1; line = 1; slen = &(o->llen); sstr = &(o->line);
-	    }
-	} else if ((len = sub_match(&md, o->word, o->wlen, sfx)) != o->wlen) {
-	    if (o->line) {
-		memcpy(&md, &omd, sizeof(struct cmdata));
-		o->flags |= CLF_LINE | CLF_DIFF;
-
-		continue;
-	    }
-	    join = 1; line = 0; slen = &(o->wlen); sstr = &(o->word);
-	}
-	if (join) {
-	    /* There is a rest that is different in the two lists,
-	     * we try to build a new cline matching both strings. */
-	    Cline joinl;
-	    int jlen;
-
-	    if ((joinl = join_sub(&md, *sstr + len, *slen - len,
-				  &jlen, sfx, !(o->flags & CLF_JOIN)))) {
-		/* We have one, insert it into the list. */
-		joinl->flags |= CLF_DIFF;
-		if (len + jlen != *slen) {
-		    Cline rest;
-
-		    rest = get_cline(NULL, 0, *sstr + (sfx ? 0 : len + jlen),
-				     *slen - len - jlen, NULL, 0, 0);
-
-		    rest->next = o->next;
-		    joinl->next = rest;
-		} else
-		    joinl->next = o->next;
-
-		if (len) {
-		    if (sfx)
-			*sstr += *slen - len;
-		    *slen = len;
-		    o->next = joinl;
-		} else {
-		    o->next = NULL;
-		    free_cline(o);
-		    if (p)
-			p->next = joinl;
-		    else if (sfx)
-			ot->suffix = joinl;
-		    else
-			ot->prefix = joinl;
-		}
-		o = joinl;
-		join = 0;
-	    }
-	}
-	if (join) {
-	    /* We couldn't build a cline for a common string, so we
-	     * cut the list here. */
-	    if (len) {
-		Cline r;
-
-		if (orest) {
-		    if (line)
-			r = get_cline(o->line + len, *slen - len,
-				      NULL, 0, NULL, 0, o->flags);
-		    else
-			r = get_cline(NULL, 0, o->word + len, *slen - len,
-				      NULL, 0, o->flags);
-
-		    r->next = o->next;
-		    *orest = r;
-
-		    *slen = len;
-		    o->next = NULL;
-		} else {
-		    if (sfx)
-			*sstr += *slen - len;
-		    *slen = len;
-		    free_cline(o->next);
-		    o->next = NULL;
-		}
-	    } else {
-		if (p)
-		    p->next = NULL;
-		else if (sfx)
-		    ot->suffix = NULL;
-		else
-		    ot->prefix = NULL;
-
-		if (orest)
-		    *orest = o;
-		else
-		    free_cline(o);
-	    }
-	    if (!orest || !nrest)
-		ot->flags |= CLF_MISS;
-
-	    if (nrest)
-		*nrest = undo_cmdata(&md, sfx);
-
-	    return;
-	}
-	p = o;
-	o = o->next;
-    }
-    if (md.len || md.cl)
-	ot->flags |= CLF_MISS;
-    if (orest)
-	*orest = NULL;
-    if (nrest)
-	*nrest = undo_cmdata(&md, sfx);
-}
-
-/* This builds the common prefix and suffix for a mid-cline -- the one
- * describing the place where the prefix and the suffix meet. */
-
-static void
-join_mid(Cline o, Cline n)
-{
-    if (o->flags & CLF_JOIN) {
-	/* The JOIN flag is set in the old cline struct if it was
-	 * already joined with another one. In this case the suffix
-	 * field contains the suffix from previous calls. */
-	Cline nr;
-
-	join_psfx(o, n, NULL, &nr, 0);
-
-	n->suffix = revert_cline(nr);
-
-	join_psfx(o, n, NULL, NULL, 1);
-    } else {
-	/* This is the first time for both structs, so the prefix field
-	 * contains the whole sub-list. */
-	Cline or, nr;
-
-	o->flags |= CLF_JOIN;
-
-	/* We let us give both rests and use them as the suffixes. */
-	join_psfx(o, n, &or, &nr, 0);
-
-	if (or)
-	    or->llen = (o->slen > or->wlen ? or->wlen : o->slen);
-	o->suffix = revert_cline(or);
-	n->suffix = revert_cline(nr);
-
-	join_psfx(o, n, NULL, NULL, 1);
-    }
-    n->suffix = NULL;
-}
-
-/* This turns the sequence of anchor cline structs from b to e into a
- * prefix sequence, puts it before the prefix of e and then tries to
- * join that with the prefix of a.
- * This is needed if some matches had a anchor match spec and others
- * didn't. */
-
-static void
-sub_join(Cline a, Cline b, Cline e, int anew)
-{
-    if (!e->suffix && a->prefix) {
-	Cline op = e->prefix, n = NULL, *p = &n, t, ca;
-	int min = 0, max = 0;
-
-	for (; b != e; b = b->next) {
-	    if ((*p = t = b->prefix)) {
-		while (t->next)
-		    t = t->next;
-		p = &(t->next);
-	    }
-	    b->suffix = b->prefix = NULL;
-	    b->flags &= ~CLF_SUF;
-	    min += b->min;
-	    max += b->max;
-	    *p = b;
-	    p = &(b->next);
-	}
-	*p = e->prefix;
-	ca = a->prefix;
-
-	while (n != op) {
-	    e->prefix = cp_cline(n, 0);
-	    a->prefix = cp_cline(ca, 0);
-
-	    if (anew) {
-		join_psfx(e, a, NULL, NULL, 0);
-		if (e->prefix) {
-		    e->min += min;
-		    e->max += max;
-		    break;
-		}
-	    } else {
-		join_psfx(e, a, NULL, NULL, 0);
-		if (a->prefix) {
-		    a->min += min;
-		    a->max += max;
-		    break;
-		}
-	    }
-	    min -= n->min;
-	    max -= n->max;
-
-	    n = n->next;
-	}
-    }
-}
-
-/* This simplifies the cline list given as the first argument so that
- * it also matches the second list. */
-
-static Cline
-join_clines(Cline o, Cline n)
-{
-    cline_setlens(n, 1);
-
-    /* First time called, just return the new list. On further invocations
-     * we will get it as the first argument. */
-    if (!o)
-	return n;
-    else {
-	Cline oo = o, nn = n, po = NULL, pn = NULL;
-
-	/* Walk through the lists. */
-	while (o && n) {
-	    /* If one of them describes a new part and the other one does
-	     * not, synchronise them by searching an old part in the
-	     * other list. */
-	    if ((o->flags & CLF_NEW) && !(n->flags & CLF_NEW)) {
-		Cline t, tn;
-
-		for (t = o; (tn = t->next) && (tn->flags & CLF_NEW); t = tn);
-		if (tn && cmp_anchors(tn, n, 0)) {
-		    sub_join(n, o, tn, 1);
-
-		    if (po)
-			po->next = tn;
-		    else
-			oo = tn;
-		    t->next = NULL;
-		    free_cline(o);
-		    o = tn;
-		    o->flags |= CLF_MISS;
-		    continue;
-		}
-	    }
-	    if (!(o->flags & CLF_NEW) && (n->flags & CLF_NEW)) {
-		Cline t, tn;
-
-		for (t = n; (tn = t->next) && (tn->flags & CLF_NEW); t = tn);
-		if (tn && cmp_anchors(o, tn, 0)) {
-		    sub_join(o, n, tn, 0);
-
-		    n = tn;
-		    o->flags |= CLF_MISS;
-		    continue;
-		}
-	    }
-	    /* Almost the same as above, but for the case that they
-	     * describe different types of parts (prefix, suffix, or mid). */
-	    if ((o->flags & (CLF_SUF | CLF_MID)) !=
-		(n->flags & (CLF_SUF | CLF_MID))) {
-		Cline t, tn;
-
-		for (t = n;
-		     (tn = t->next) &&
-			 (tn->flags & (CLF_SUF | CLF_MID)) !=
-			 (o->flags  & (CLF_SUF | CLF_MID));
-		     t = tn);
-		if (tn && cmp_anchors(o, tn, 1)) {
-		    sub_join(o, n, tn, 0);
-
-		    n = tn;
-		    continue;
-		}
-		for (t = o;
-		     (tn = t->next) &&
-			 (tn->flags & (CLF_SUF | CLF_MID)) !=
-			 (n->flags  & (CLF_SUF | CLF_MID));
-		     t = tn);
-		if (tn && cmp_anchors(tn, n, 1)) {
-		    sub_join(n, o, tn, 1);
-		    if (po)
-			po->next = tn;
-		    else
-			oo = tn;
-		    t->next = NULL;
-		    free_cline(o);
-		    o = tn;
-		    continue;
-		}
-		if (o->flags & CLF_MID) {
-		    o->flags = (o->flags & ~CLF_MID) | (n->flags & CLF_SUF);
-		    if (n->flags & CLF_SUF) {
-			free_cline(o->prefix);
-			o->prefix = NULL;
-		    } else {
-			free_cline(o->suffix);
-			o->suffix = NULL;
-		    }
-		}
-		break;
-	    }
-	    /* Now see if they have matching anchors. If not, cut the list. */
-	    if (!(o->flags & CLF_MID) && !cmp_anchors(o, n, 1)) {
-		Cline t, tn;
-
-		for (t = n; (tn = t->next) && !cmp_anchors(o, tn, 1); t = tn);
-
-		if (tn) {
-		    sub_join(o, n, tn, 0);
-
-		    n = tn;
-		    o->flags |= CLF_MISS;
-		    continue;
-		} else {
-		    for (t = o; (tn = t->next) && !cmp_anchors(n, tn, 1);
-			 t = tn);
-
-		    if (tn) {
-			sub_join(n, o, tn, 1);
-
-			if (po)
-			    po->next = tn;
-			else
-			    oo = tn;
-			o = tn;
-			o->flags |= CLF_MISS;
-			continue;
-		    } else {
-			if (o->flags & CLF_SUF)
-			    break;
-
-			o->word = o->line = o->orig = NULL;
-			o->wlen = 0;
-			free_cline(o->next);
-			o->next = NULL;
-			o->flags |= CLF_MISS;
-		    }
-		}
-	    }
-	    /* Ok, they are equal, now copy the information about the
-             * original string if needed, calculate minimum and maximum
-	     * lengths, and join the sub-lists. */
-	    if (!o->orig && !o->olen) {
-		o->orig = n->orig;
-		o->olen = n->olen;
-	    }
-	    if (n->min < o->min)
-		o->min = n->min;
-	    if (n->max > o->max)
-		o->max = n->max;
-	    if (o->flags & CLF_MID)
-		join_mid(o, n);
-	    else
-		join_psfx(o, n, NULL, NULL, (o->flags & CLF_SUF));
-
-	    po = o;
-	    o = o->next;
-	    pn = n;
-	    n = n->next;
-	}
-	/* Free the rest of the old list. */
-	if (o) {
-	    if (po)
-		po->next = NULL;
-	    else
-		oo = NULL;
-
-	    free_cline(o);
-	}
-	free_cline(nn);
-
-	return oo;
-    }
-}
-
-/* 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_setunsetptr(0, 0, CP_EXACTSTR, 0);
-	    }
-	    ai->exactm = cm;
-	} else {
-	    ai->exact = 2;
-	    ai->exactm = NULL;
-	    if (incompfunc)
-		comp_setunsetptr(0, 0, 0, CP_EXACTSTR);
-	}
-    }
-    return cm;
-}
-
-/* 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);
-}
-
-/* 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);
-		}
-		if (dat->ylist) {
-		    endcmgroup(NULL);
-		    begcmgroup(NULL, gflags);
-		}
-		/* 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->ylist) {
-		if (dat->group) {
-		    endcmgroup(get_user_var(dat->ylist));
-		    begcmgroup(dat->group, gflags);
-		    if (dat->exp)
-			addexpl();
-		} else {
-		    if (dat->exp)
-			addexpl();
-		    endcmgroup(get_user_var(dat->ylist));
-		    begcmgroup("default", 0);
-		}
-	    } else 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);
-}
-
 /**/
 static int
 docompletion(char *s, int lst, int incmd)
 {
-    int ret = 0;
-
-    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;
-}
-
-/* This calls the given function for new style completion. */
-
-/**/
-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();
-	makecompparamsptr();
-	comp_setunsetptr(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;
+    struct compldat dat;
 
-	errflag = 0;
-	compmatcher++;
-    }
-    redup(osi, 0);
-    return 1;
-}
-
-/* This should probably be moved into tokenize(). */
+    dat.s = s;
+    dat.lst = lst;
+    dat.incmd = incmd;
 
-/**/
-static 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;
+    return runhookdef(COMPLETEHOOK, (void *) &dat);
 }
 
-/**/
-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;
-}
+/* Return the length of the common prefix of s and t. */
 
 /**/
 int
-set_comp_sep(void)
+pfxlen(char *s, char *t)
 {
-    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_setunsetptr(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;
-}
-
-/* Invalidate the completion list. */
+    int i = 0;
 
-/**/
-void
-invalidatelist(void)
-{
-    if (showinglist == -2)
-	listmatches();
-    if (validlist) {
-	freematches(lastmatches);
-	lastmatches = NULL;
-	hasoldlist = 0;
-    }
-    lastambig = menucmp = menuacc = validlist = showinglist = fromcomp = 0;
-    listdat.valid = 0;
-    if (listshown < 0)
-	listshown = 0;
-    minfo.cur = NULL;
-    minfo.asked = 0;
-    zsfree(minfo.prebr);
-    zsfree(minfo.postbr);
-    minfo.postbr = minfo.prebr = NULL;
-    compwidget = NULL;
+    while (*s && *s == *t)
+	s++, t++, i++;
+    return i;
 }
 
-/* Get the words from a variable or a compctl -k list. */
+/* Return the length of the common suffix of s and t. */
 
-/**/
-char **
-get_user_var(char *nam)
+#if 0
+static int
+sfxlen(char *s, char *t)
 {
-    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 (*s && *t) {
+	int i = 0;
+	char *s2 = s + strlen(s) - 1, *t2 = t + strlen(t) - 1;
 
-	if ((arr = getaparam(nam)) || (arr = gethparam(nam)))
-	    return (incompfunc ? arrdup(arr) : arr);
+	while (s2 >= s && t2 >= t && *s2 == *t2)
+	    s2--, t2--, i++;
 
-	if ((val = getsparam(nam))) {
-	    arr = (char **) zhalloc(2*sizeof(char *));
-	    arr[0] = (incompfunc ? dupstring(val) : val);
-	    arr[1] = NULL;
-	}
-	return arr;
-    }
+	return i;
+    } else
+	return 0;
 }
+#endif
 
 /* This is strcmp with ignoring backslashes. */
 
 /**/
-static int
+int
 strbpcmp(char **aa, char **bb)
 {
     char *a = *aa, *b = *bb;
@@ -5560,1966 +1738,123 @@ strbpcmp(char **aa, char **bb)
     return (int)(*a - *b);
 }
 
-/* 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 begins a new group of matches. */
-
-/**/
-static 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. */
-
-/**/
-static void
-endcmgroup(char **ylist)
-{
-    mgroup->ylist = ylist;
-}
-
-/* Add an explanation string to the current group, joining duplicates. */
-
-/**/
-static 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;
-}
-
-/* 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. */
-
-/**/
-static 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;
-}
-
-/* Return the real number of matches. */
-
-/**/
-zlong
-num_matches(int normal)
-{
-    int alt;
-
-    PERMALLOC {
-	alt = permmatches(0);
-    } LASTALLOC;
-
-    if (normal)
-	return (alt ? 0 : nmatches);
-    else
-	return (alt ? nmatches : 0);
-}
-
-/* Return the number of screen lines needed for the list. */
-
-/**/
-zlong
-list_lines(void)
-{
-    Cmgroup oam;
-
-    PERMALLOC {
-	permmatches(0);
-    } LASTALLOC;
-
-    oam = amatches;
-    amatches = pmatches;
-    listdat.valid = 0;
-    calclist();
-    listdat.valid = 0;
-    amatches = oam;
-
-    return listdat.nlines;
-}
-
-/**/
-void
-comp_list(char *v)
-{
-    zsfree(complist);
-    complist = ztrdup(v);
-
-    onlyexpl = (v && strstr(v, "expl"));
-}
-
-/**/
-
-/* 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;
-    }
-}
-
-/* Insert the given string into the command line.  If move is non-zero, *
- * the cursor position is changed and len is the length of the string   *
- * to insert (if it is -1, the length is calculated here).              *
- * The last argument says if we should quote the string.                */
-
-/**/
-static int
-inststrlen(char *str, int move, int len)
-{
-    if (!len || !str)
-	return 0;
-    if (len == -1)
-	len = strlen(str);
-    spaceinline(len);
-    strncpy((char *)(line + cs), str, len);
-    if (move)
-	cs += len;
-    return len;
-}
-
-/* This cuts the cline list before the stuff that isn't worth
- * inserting in the line. */
-
-static Cline
-cut_cline(Cline l)
-{
-    Cline q, p, e = NULL, maxp = NULL;
-    int sum = 0, max = 0, tmp, ls = 0;
-
-    /* If no match was added with matching, we don't really know
-     * which parts of the unambiguous string are worth keeping,
-     * so for now we keep everything (in the hope that this
-     * produces a string containing at least everything that was 
-     * originally on the line). */
-
-    if (!hasmatched) {
-	cline_setlens(l, 0);
-	return l;
-    }
-    e = l = cp_cline(l, 0);
-
-    /* First, search the last struct for which we have something on
-     * the line. Anything before that is kept. */
-
-    for (q = NULL, p = l; p; p = p->next) {
-	if (p->orig || p->olen || !(p->flags & CLF_NEW))
-	    e = p->next;
-	if (!p->suffix && (p->wlen || p->llen || p->prefix))
-	    q = p;
-    }
-    if (!e && q && !q->orig && !q->olen && (q->flags & CLF_MISS) &&
-	!(q->flags & CLF_MATCHED) && (q->word ? q->wlen : q->llen) < 3) {
-	q->word = q->line = NULL;
-	q->wlen = q->llen = 0;
-    }
-    /* Then keep all structs without missing characters. */
-
-    while (e && !(e->flags & CLF_MISS))
-	e = e->next;
-
-    if (e) {
-	/* Then we see if there is another struct with missing
-	 * characters. If not, we keep the whole list. */
-
-	for (p = e->next; p && !(p->flags & CLF_MISS); p = p->next);
-
-	if (p) {
-	    for (p = e; p; p = p->next) {
-		if (!(p->flags & CLF_MISS))
-		    sum += p->max;
-		else {
-		    tmp = cline_sublen(p);
-		    if (tmp > 2 && tmp > ((p->max + p->min) >> 1))
-			sum += tmp - (p->max - tmp);
-		    else if (tmp < p->min)
-			sum -= (((p->max + p->min) >> 1) - tmp) << (tmp < 2);
-		}
-		if (sum > max) {
-		    max = sum;
-		    maxp = p;
-		}
-	    }
-	    if (max)
-		e = maxp;
-	    else {
-		int len = 0;
-
-		cline_setlens(l, 0);
-		ls = 1;
-
-		for (p = e; p; p = p->next)
-		    len += p->max;
-
-		if (len > ((minmlen << 1) / 3))
-		    return l;
-	    }
-	    e->line = e->word = NULL;
-	    e->llen = e->wlen = e->olen = 0;
-	    e->next = NULL;
-	}
-    }
-    if (!ls)
-	cline_setlens(l, 0);
-
-    return l;
-}
-
-/* This builds the unambiguous string. If ins is non-zero, it is
- * immediatly inserted in the line. Otherwise csp is used to return
- * the relative cursor position in the string returned. */
-
-static char *
-cline_str(Cline l, int ins, int *csp)
-{
-    Cline s;
-    int ocs = cs, ncs, pcs, scs, pm, pmax, pmm, sm, smax, smm, d, dm, mid;
-    int i, j, li = 0, cbr;
-    Brinfo brp, brs;
-
-    l = cut_cline(l);
-
-    pmm = smm = dm = 0;
-    pm = pmax = sm = smax = d = mid = cbr = -1;
-
-    /* Get the information about the brace beginning and end we have
-     * to re-insert. */
-    if (ins) {
-	Brinfo bp;
-	int olen = we - wb;
-
-	if ((brp = brbeg)) {
-	    for (bp = brbeg; bp; bp = bp->next) {
-		bp->curpos = (hasunqu ? bp->pos : bp->qpos);
-		olen -= strlen(bp->str);
-	    }
-	}
-	if ((brs = lastbrend)) {
-	    for (bp = brend; bp; bp = bp->next)
-		olen -= strlen(bp->str);
-
-	    for (bp = brend; bp; bp = bp->next)
-		bp->curpos = olen - (hasunqu ? bp->pos : bp->qpos);
-	}
-	while (brp && !brp->curpos) {
-	    inststrlen(brp->str, 1, -1);
-	    brp = brp->next;
-	}
-	while (brs && !brs->curpos) {
-	    if (cbr < 0)
-		cbr = cs;
-	    inststrlen(brs->str, 1, -1);
-	    brs = brs->prev;
-	}
-    }
-    /* Walk through the top-level cline list. */
-    while (l) {
-	/* Insert the original string if no prefix. */
-	if (l->olen && !(l->flags & CLF_SUF) && !l->prefix) {
-	    pcs = cs + l->olen;
-	    inststrlen(l->orig, 1, l->olen);
-	} else {
-	    /* Otherwise insert the prefix. */
-	    for (s = l->prefix; s; s = s->next) {
-		pcs = cs + s->llen;
-		if (s->flags & CLF_LINE)
-		    inststrlen(s->line, 1, s->llen);
-		else
-		    inststrlen(s->word, 1, s->wlen);
-		scs = cs;
-
-		if ((s->flags & CLF_DIFF) && (!dm || (s->flags & CLF_MATCHED))) {
-		    d = cs; dm = s->flags & CLF_MATCHED;
-		}
-		li += s->llen;
-	    }
-	}
-	if (ins) {
-	    int ocs, bl;
-
-	    while (brp && li >= brp->curpos) {
-		ocs = cs;
-		bl = strlen(brp->str);
-		cs = pcs - (li - brp->curpos);
-		inststrlen(brp->str, 1, bl);
-		cs = ocs + bl;
-		pcs += bl;
-		scs += bl;
-		brp = brp->next;
-	    }
-	}
-	/* Remember the position if this is the first prefix with
-	 * missing characters. */
-	if ((l->flags & CLF_MISS) && !(l->flags & CLF_SUF) &&
-	    ((pmax < (l->min - l->max) && (!pmm || (l->flags & CLF_MATCHED))) ||
-	     ((l->flags & CLF_MATCHED) && !pmm))) {
-	    pm = cs; pmax = l->min - l->max; pmm = l->flags & CLF_MATCHED;
-	}
-	if (ins) {
-	    int ocs, bl;
-
-	    while (brs && li >= brs->curpos) {
-		ocs = cs;
-		bl = strlen(brs->str);
-		cs = scs - (li - brs->curpos);
-		if (cbr < 0)
-		    cbr = cs;
-		inststrlen(brs->str, 1, bl);
-		cs = ocs + bl;
-		pcs += bl;
-		brs = brs->prev;
-	    }
-	}
-	pcs = cs;
-	/* Insert the anchor. */
-	if (l->flags & CLF_LINE)
-	    inststrlen(l->line, 1, l->llen);
-	else
-	    inststrlen(l->word, 1, l->wlen);
-	scs = cs;
-	if (ins) {
-	    int ocs, bl;
-
-	    li += l->llen;
-
-	    while (brp && li >= brp->curpos) {
-		ocs = cs;
-		bl = strlen(brp->str);
-		cs = pcs + l->llen - (li - brp->curpos);
-		inststrlen(brp->str, 1, bl);
-		cs = ocs + bl;
-		pcs += bl;
-		scs += bl;
-		brp = brp->next;
-	    }
-	}
-	/* Remember the cursor position for suffixes and mids. */
-	if (l->flags & CLF_MISS) {
-	    if (l->flags & CLF_MID)
-		mid = cs;
-	    else if ((l->flags & CLF_SUF) && 
-		     ((smax < (l->min - l->max) &&
-		       (!smm || (l->flags & CLF_MATCHED))) ||
-		      ((l->flags & CLF_MATCHED) && !smm))) {
-		sm = cs; smax = l->min - l->max; smm = l->flags & CLF_MATCHED;
-	    }
-	}
-	if (ins) {
-	    int ocs, bl;
-
-	    while (brs && li >= brs->curpos) {
-		ocs = cs;
-		bl = strlen(brs->str);
-		cs = scs - (li - brs->curpos);
-		if (cbr < 0)
-		    cbr = cs;
-		inststrlen(brs->str, 1, bl);
-		cs = ocs + bl;
-		pcs += bl;
-		brs = brs->prev;
-	    }
-	}
-	/* And now insert the suffix or the original string. */
-	if (l->olen && (l->flags & CLF_SUF) && !l->suffix) {
-	    pcs = cs;
-	    inststrlen(l->orig, 1, l->olen);
-	    if (ins) {
-		int ocs, bl;
-
-		li += l->olen;
-
-		while (brp && li >= brp->curpos) {
-		    ocs = cs;
-		    bl = strlen(brp->str);
-		    cs = pcs + l->olen - (li - brp->curpos);
-		    inststrlen(brp->str, 1, bl);
-		    cs = ocs + bl;
-		    pcs += bl;
-		    brp = brp->next;
-		}
-		while (brs && li >= brs->curpos) {
-		    ocs = cs;
-		    bl = strlen(brs->str);
-		    cs = pcs + l->olen - (li - brs->curpos);
-		    if (cbr < 0)
-			cbr = cs;
-		    inststrlen(brs->str, 1, bl);
-		    cs = ocs + bl;
-		    pcs += bl;
-		    brs = brs->prev;
-		}
-	    }
-	} else {
-	    Cline js = NULL;
-
-	    for (j = -1, i = 0, s = l->suffix; s; s = s->next) {
-		if (j < 0 && (s->flags & CLF_DIFF))
-		    j = i, js = s;
-		pcs = cs;
-		if (s->flags & CLF_LINE) {
-		    inststrlen(s->line, 0, s->llen);
-		    i += s->llen; scs = cs + s->llen;
-		} else {
-		    inststrlen(s->word, 0, s->wlen);
-		    i += s->wlen; scs = cs + s->wlen;
-		}
-		if (ins) {
-		    int ocs, bl;
-
-		    li += s->llen;
-
-		    while (brp && li >= brp->curpos) {
-			ocs = cs;
-			bl = strlen(brp->str);
-			cs = pcs + (li - brp->curpos);
-			inststrlen(brp->str, 1, bl);
-			cs = ocs + bl;
-			pcs += bl;
-			scs += bl;
-			brp = brp->next;
-		    }
-		    while (brs && li >= brs->curpos) {
-			ocs = cs;
-			bl = strlen(brs->str);
-			cs = scs - (li - brs->curpos);
-			if (cbr < 0)
-			    cbr = cs;
-			inststrlen(brs->str, 1, bl);
-			cs = ocs + bl;
-			pcs += bl;
-			brs = brs->prev;
-		    }
-		}
-	    }
-	    cs += i;
-	    if (j >= 0 && (!dm || (js->flags & CLF_MATCHED))) {
-		d = cs - j; dm = js->flags & CLF_MATCHED;
-	    }
-	}
-	l = l->next;
-    }
-    if (ins) {
-	int ocs = cs;
-
-	for (; brp; brp = brp->next)
-	    inststrlen(brp->str, 1, -1);
-	for (; brs; brs = brs->prev) {
-	    if (cbr < 0)
-		cbr = cs;
-	    inststrlen(brs->str, 1, -1);
-	}
-	if (mid >= ocs)
-	    mid += cs - ocs;
-	if (pm >= ocs)
-	    pm += cs - ocs;
-	if (sm >= ocs)
-	    sm += cs - ocs;
-	if (d >= ocs)
-	    d += cs - ocs;
-    }
-    /* This calculates the new cursor position. If we had a mid cline
-     * with missing characters, we take this, otherwise if we have a
-     * prefix with missing characters, we take that, the same for a
-     * suffix, and finally a place where the matches differ. */
-    ncs = (cbr >= 0 ? cbr :
-	   (mid >= 0 ? mid :
-	    (pm >= 0 ? pm : (sm >= 0 ? sm : (d >= 0 ? d : cs)))));
-
-    if (!ins) {
-	/* We always inserted the string in the line. If that was not
-	 * requested, we copy it and remove from the line. */
-	char *r = zalloc((i = cs - ocs) + 1);
-
-	memcpy(r, (char *) (line + ocs), i);
-	r[i] = '\0';
-	cs = ocs;
-	foredel(i);
-
-	*csp = ncs - ocs;
-
-	return r;
-    }
-    lastend = cs;
-    cs = ncs;
-
-    return NULL;
-}
-
-/* This is a utility function using the function above to allow access
- * to the unambiguous string and cursor position via compstate. */
-
-/**/
-char *
-unambig_data(int *cp)
-{
-    static char *scache = NULL;
-    static int ccache;
-
-    if (mnum && ainfo) {
-	if (mnum != unambig_mnum) {
-	    zsfree(scache);
-	    scache = cline_str((ainfo->count ? ainfo->line : fainfo->line),
-			       0, &ccache);
-	}
-    } else if (mnum != unambig_mnum || !ainfo || !scache) {
-	zsfree(scache);
-	scache = ztrdup("");
-	ccache = 0;
-    }
-    unambig_mnum = mnum;
-    if (cp)
-	*cp = ccache + 1;
-
-    return scache;
-}
-
-/* Insert the given match. This returns the number of characters inserted.
- * scs is used to return the position where a automatically created suffix
- * has to be inserted. */
-
-/**/
-static int
-instmatch(Cmatch m, int *scs)
-{
-    int l, r = 0, ocs, a = cs, brb = 0, bradd, *brpos;
-    Brinfo bp;
-
-    zsfree(lastprebr);
-    zsfree(lastpostbr);
-    lastprebr = lastpostbr = NULL;
-
-    /* Ignored prefix. */
-    if (m->ipre) {
-	char *p = m->ipre + (menuacc ? m->qipl : 0);
-
-	inststrlen(p, 1, (l = strlen(p)));
-	r += l;
-    }
-    /* -P prefix. */
-    if (m->pre) {
-	inststrlen(m->pre, 1, (l = strlen(m->pre)));
-	r += l;
-    }
-    /* Path prefix. */
-    if (m->ppre) {
-	inststrlen(m->ppre, 1, (l = strlen(m->ppre)));
-	r += l;
-    }
-    /* The string itself. */
-    inststrlen(m->str, 1, (l = strlen(m->str)));
-    r += l;
-    ocs = cs;
-    /* Re-insert the brace beginnings, if any. */
-    if (brbeg) {
-	int pcs = cs;
-
-	l = 0;
-	for (bp = brbeg, brpos = m->brpl,
-		 bradd = (m->pre ? strlen(m->pre) : 0);
-	     bp; bp = bp->next, brpos++) {
-	    cs = a + *brpos + bradd;
-	    pcs = cs;
-	    l = strlen(bp->str);
-	    bradd += l;
-	    brpcs = cs;
-	    inststrlen(bp->str, 1, l);
-	    r += l;
-	    ocs += l;
-	}
-	lastprebr = (char *) zalloc(pcs - a + 1);
-	memcpy(lastprebr, (char *) line + a, pcs - a);
-	lastprebr[pcs - a] = '\0';
-	cs = ocs;
-    }
-    /* Path suffix. */
-    if (m->psuf) {
-	inststrlen(m->psuf, 1, (l = strlen(m->psuf)));
-	r += l;
-    }
-    /* Re-insert the brace end. */
-    if (brend) {
-	a = cs;
-	for (bp = brend, brpos = m->brsl, bradd = 0; bp; bp = bp->next, brpos++) {
-	    cs = a - *brpos;
-	    ocs = brscs = cs;
-	    l = strlen(bp->str);
-	    bradd += l;
-	    inststrlen(bp->str, 1, l);
-	    brb = cs;
-	    r += l;
-	}
-	cs = a + bradd;
-	if (scs)
-	    *scs = ocs;
-    } else {
-	brscs = -1;
-
-	if (scs)
-	    *scs = cs;
-    }
-    /* -S suffix */
-    if (m->suf) {
-	inststrlen(m->suf, 1, (l = strlen(m->suf)));
-	r += l;
-    }
-    /* ignored suffix */
-    if (m->isuf) {
-	inststrlen(m->isuf, 1, (l = strlen(m->isuf)));
-	r += l;
-    }
-    if (brend) {
-	lastpostbr = (char *) zalloc(cs - brb + 1);
-	memcpy(lastpostbr, (char *) line + brb, cs - brb);
-	lastpostbr[cs - brb] = '\0';
-    }
-    lastend = cs;
-    cs = ocs;
-
-    return r;
-}
-
-/* Check if the match has the given prefix/suffix before/after the
- * braces. */
-
-/**/
-int
-hasbrpsfx(Cmatch m, char *pre, char *suf)
-{
-    char *op = lastprebr, *os = lastpostbr;
-    VARARR(char, oline, ll);
-    int oll = ll, ocs = cs, ole = lastend, opcs = brpcs, oscs = brscs, ret;
-
-    memcpy(oline, line, ll);
-
-    lastprebr = lastpostbr = NULL;
-
-    instmatch(m, NULL);
-
-    cs = 0;
-    foredel(ll);
-    spaceinline(oll);
-    memcpy(line, oline, oll);
-    cs = ocs;
-    lastend = ole;
-    brpcs = opcs;
-    brscs = oscs;
-
-    ret = (((!pre && !lastprebr) ||
-	    (pre && lastprebr && !strcmp(pre, lastprebr))) &&
-	   ((!suf && !lastpostbr) ||
-	    (suf && lastpostbr && !strcmp(suf, lastpostbr))));
-
-    zsfree(lastprebr);
-    zsfree(lastpostbr);
-    lastprebr = op;
-    lastpostbr = os;
-
-    return ret;
-}
-
-/* Handle the case were we found more than one match. */
-
-/**/
-static int
-do_ambiguous(void)
-{
-    int ret = 0;
-
-    menucmp = menuacc = 0;
-
-    /* If we have to insert the first match, call do_single().  This is *
-     * how REC_EXACT takes effect.  We effectively turn the ambiguous   *
-     * completion into an unambiguous one.                              */
-    if (ainfo && ainfo->exact == 1 && useexact && !(fromcomp & FC_LINE)) {
-	minfo.cur = NULL;
-	do_single(ainfo->exactm);
-	invalidatelist();
-	return ret;
-    }
-    /* Setting lastambig here means that the completion is ambiguous and *
-     * AUTO_MENU might want to start a menu completion next time round,  *
-     * but this might be overridden below if we can complete an          *
-     * unambiguous prefix.                                               */
-    lastambig = 1;
-
-    if (usemenu || (haspattern && comppatinsert &&
-		    !strcmp(comppatinsert, "menu"))) {
-	/* We are in a position to start using menu completion due to one  *
-	 * of the menu completion options, or due to the menu-complete-    *
-	 * word command, or due to using GLOB_COMPLETE which does menu-    *
-	 * style completion regardless of the setting of the normal menu   *
-	 * completion options.                                             */
-	do_ambig_menu();
-    } else if (ainfo) {
-	int atend = (cs == we), la, eq, tcs;
-
-	minfo.cur = NULL;
-	minfo.asked = 0;
-
-	fixsuffix();
-
-	/* First remove the old string from the line. */
-	cs = wb;
-	foredel(we - wb);
-
-	/* Now get the unambiguous string and insert it into the line. */
-	cline_str(ainfo->line, 1, NULL);
-	if (eparq) {
-	    tcs = cs;
-	    cs = lastend;
-	    for (eq = eparq; eq; eq--)
-		inststrlen("\"", 0, 1);
-	    cs = tcs;
-	}
-	/* la is non-zero if listambiguous may be used. Copying and
-	 * comparing the line looks like BFI but it is the easiest
-	 * solution. Really. */
-	la = (ll != origll || strncmp(origline, (char *) line, ll));
-
-	/* If REC_EXACT and AUTO_MENU are set and what we inserted is an  *
-	 * exact match, we want menu completion the next time round       *
-	 * so we set fromcomp, to ensure that the word on the line is not *
-	 * taken as an exact match. Also we remember if we just moved the *
-	 * cursor into the word.                                          */
-	fromcomp = ((isset(AUTOMENU) ? FC_LINE : 0) |
-		    ((atend && cs != lastend) ? FC_INWORD : 0));
-
-	/* Probably move the cursor to the end. */
-	if (movetoend == 3)
-	    cs = lastend;
-
-	/* If the LIST_AMBIGUOUS option (meaning roughly `show a list only *
-	 * if the completion is completely ambiguous') is set, and some    *
-	 * prefix was inserted, return now, bypassing the list-displaying  *
-	 * code.  On the way, invalidate the list and note that we don't   *
-	 * want to enter an AUTO_MENU imediately.                          */
-	if (uselist == 3 && la) {
-	    int fc = fromcomp;
-
-	    invalidatelist();
-	    fromcomp = fc;
-	    lastambig = 0;
-	    clearlist = 1;
-	    return ret;
-	}
-    } else
-	return ret;
-
-    /* At this point, we might want a completion listing.  Show the listing *
-     * if it is needed.                                                     */
-    if (isset(LISTBEEP))
-	ret = 1;
-
-    if (uselist && (usemenu != 2 || (!listshown && !oldlist)) &&
-	((!showinglist && (!listshown || !oldlist)) ||
-	 (usemenu == 3 && !oldlist)) &&
-	(smatches >= 2 || (compforcelist && *compforcelist)))
-	showinglist = -2;
-
-    return ret;
-}
-
-/* This is a stat that ignores backslashes in the filename.  The `ls' *
- * parameter says if we have to do lstat() or stat().  I think this   *
- * should instead be done by use of a general function to expand a    *
- * filename (stripping backslashes), combined with the actual         *
- * (l)stat().                                                         */
-
-/**/
-int
-ztat(char *nam, struct stat *buf, int ls)
-{
-    char b[PATH_MAX], *p;
-
-    for (p = b; p < b + sizeof(b) - 1 && *nam; nam++)
-	if (*nam == '\\' && nam[1])
-	    *p++ = *++nam;
-	else
-	    *p++ = *nam;
-    *p = '\0';
-
-    return ls ? lstat(b, buf) : stat(b, buf);
-}
-
-/* Insert a single match in the command line. */
-
-/**/
-void
-do_single(Cmatch m)
-{
-    int l, sr = 0, scs;
-    int havesuff = 0;
-    int partest = (m->ripre || ((m->flags & CMF_ISPAR) && parpre));
-    char *str = m->str, *ppre = m->ppre, *psuf = m->psuf, *prpre = m->prpre;
-
-    if (!prpre) prpre = "";
-    if (!ppre) ppre = "";
-    if (!psuf) psuf = "";
-
-    fixsuffix();
-
-    if (!minfo.cur) {
-	/* We are currently not in a menu-completion, *
-	 * so set the position variables.             */
-	minfo.pos = wb;
-	minfo.we = (movetoend >= 2 || (movetoend == 1 && !menucmp));
-	minfo.end = we;
-    }
-    /* If we are already in a menu-completion or if we have done a *
-     * glob completion, we have to delete some of the stuff on the *
-     * command line.                                               */
-    if (minfo.cur)
-	l = minfo.len + minfo.insc;
-    else
-	l = we - wb;
-
-    minfo.insc = 0;
-    cs = minfo.pos;
-    foredel(l);
-
-    /* And then we insert the new string. */
-    minfo.len = instmatch(m, &scs);
-    minfo.end = cs;
-    cs = minfo.pos + minfo.len;
-
-    if (m->suf) {
-	havesuff = 1;
-	minfo.insc = ztrlen(m->suf);
-	minfo.len -= minfo.insc;
-	if (minfo.we) {
-	    minfo.end += minfo.insc;
-	    if (m->flags & CMF_REMOVE) {
-		makesuffixstr(m->remf, m->rems, minfo.insc);
-		if (minfo.insc == 1)
-		    suffixlen[STOUC(m->suf[0])] = 1;
-	    }
-	}
-    } else {
-	/* There is no user-specified suffix, *
-	 * so generate one automagically.     */
-	cs = scs;
-	if (partest && (m->flags & CMF_PARBR)) {
-	    int pq;
-
-	    /*{{*/
-	    /* Completing a parameter in braces.  Add a removable `}' suffix. */
-	    cs += eparq;
-	    for (pq = parq; pq; pq--)
-		inststrlen("\"", 1, 1);
-	    minfo.insc += parq;
-	    inststrlen("}", 1, 1);
-	    minfo.insc++;
-	    if (minfo.we)
-		minfo.end += minfo.insc;
-	    if (m->flags & CMF_PARNEST)
-		havesuff = 1;
-	}
-	if (((m->flags & CMF_FILE) || (partest && isset(AUTOPARAMSLASH))) &&
-	    cs > 0 && line[cs - 1] != '/') {
-	    /* If we have a filename or we completed a parameter name      *
-	     * and AUTO_PARAM_SLASH is set, lets see if it is a directory. *
-	     * If it is, we append a slash.                                */
-	    struct stat buf;
-	    char *p;
-	    int t = 0;
-
-	    if (m->ipre && m->ipre[0] == '~' && !m->ipre[1])
-		t = 1;
-	    else {
-		/* Build the path name. */
-		if (partest && !*psuf && !(m->flags & CMF_PARNEST)) {
-		    int ne = noerrs;
-
-		    p = (char *) zhalloc(strlen((m->flags & CMF_ISPAR) ?
-						parpre : m->ripre) +
-					 strlen(str) + 2);
-		    sprintf(p, "%s%s%c",
-			    ((m->flags & CMF_ISPAR) ? parpre : m->ripre), str,
-			    ((m->flags & CMF_PARBR) ? Outbrace : '\0'));
-		    noerrs = 1;
-		    parsestr(p);
-		    singsub(&p);
-		    errflag = 0;
-		    noerrs = ne;
-		} else {
-		    p = (char *) zhalloc(strlen(prpre) + strlen(str) +
-				 strlen(psuf) + 3);
-		    sprintf(p, "%s%s%s", ((prpre && *prpre) ?
-					  prpre : "./"), str, psuf);
-		}
-		/* And do the stat. */
-		t = (!(sr = ztat(p, &buf, 0)) && S_ISDIR(buf.st_mode));
-	    }
-	    if (t) {
-		/* It is a directory, so add the slash. */
-		havesuff = 1;
-		inststrlen("/", 1, 1);
-		minfo.insc++;
-		if (minfo.we)
-		    minfo.end++;
-		if (!menucmp || minfo.we) {
-		    if (m->remf || m->rems)
-			makesuffixstr(m->remf, m->rems, 1);
-		    else if (isset(AUTOREMOVESLASH)) {
-			makesuffix(1);
-			suffixlen['/'] = 1;
-		    }
-		}
-	    }
-	}
-	if (!minfo.insc)
-	    cs = minfo.pos + minfo.len - m->qisl;
-    }
-    /* If completing in a brace expansion... */
-    if (brbeg) {
-	if (havesuff) {
-	    /*{{*/
-	    /* If a suffix was added, and is removable, let *
-	     * `,' and `}' remove it.                       */
-	    if (isset(AUTOPARAMKEYS))
-		suffixlen[','] = suffixlen['}'] = suffixlen[256];
-	} else if (!menucmp) {
-	    /*{{*/
-	    /* Otherwise, add a `,' suffix, and let `}' remove it. */
-	    cs = scs;
-	    havesuff = 1;
-	    inststrlen(",", 1, 1);
-	    minfo.insc++;
-	    makesuffix(1);
-	    if ((!menucmp || minfo.we) && isset(AUTOPARAMKEYS))
-		suffixlen[','] = suffixlen['}'] = 1;
-	}
-    } else if (!havesuff && (!(m->flags & CMF_FILE) || !sr)) {
-	/* If we didn't add a suffix, add a space, unless we are *
-	 * doing menu completion or we are completing files and  *
-	 * the string doesn't name an existing file.             */
-	if (m->autoq && (!m->isuf || m->isuf[0] != m->autoq)) {
-	    inststrlen(&(m->autoq), 1, 1);
-	    minfo.insc++;
-	}
-	if (!menucmp && (usemenu != 3 || insspace)) {
-	    inststrlen(" ", 1, 1);
-	    minfo.insc++;
-	    if (minfo.we)
-		makesuffix(1);
-	}
-    }
-    if (minfo.we && partest && isset(AUTOPARAMKEYS))
-	makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), minfo.insc - parq);
-
-    if ((menucmp && !minfo.we) || !movetoend) {
-	cs = minfo.end;
-	if (cs + m->qisl == lastend)
-	    cs += minfo.insc;
-    }
-    {
-	Cmatch *om = minfo.cur;
-	struct chdata dat;
-
-	dat.matches = amatches;
-	dat.num = nmatches;
-	dat.cur = m;
-
-	if (menucmp)
-	    minfo.cur = &m;
-	runhookdef(INSERTMATCHHOOK, (void *) &dat);
-	minfo.cur = om;
-    }
-}
-
-/* This maps the value in v into the range [0,m-1], decrementing v
- * if it is non-negative and making negative values count backwards. */
-
-static int
-comp_mod(int v, int m)
-{
-    if (v >= 0)
-	v--;
-    if (v >= 0)
-	return v % m;
-    else {
-	while (v < 0)
-	    v += m;
-	return v;
-    }
-}
-
-/* This handles the beginning of menu-completion. */
-
-/**/
-static void
-do_ambig_menu(void)
-{
-    Cmatch *mc;
-
-    if (usemenu != 3) {
-	menucmp = 1;
-	menuacc = 0;
-	minfo.cur = NULL;
-    } else {
-	if (oldlist) {
-	    if (oldins && minfo.cur)
-		acceptlast();
-	} else
-	    minfo.cur = NULL;
-    }
-    if (insgroup) {
-	insgnum = comp_mod(insgnum, lastpermgnum);
-	for (minfo.group = amatches;
-	     minfo.group && (minfo.group)->num != insgnum + 1;
-	     minfo.group = (minfo.group)->next);
-	if (!minfo.group || !(minfo.group)->mcount) {
-	    minfo.cur = NULL;
-	    minfo.asked = 0;
-	    return;
-	}
-	insmnum = comp_mod(insmnum, (minfo.group)->mcount);
-    } else {
-	insmnum = comp_mod(insmnum, lastpermmnum);
-	for (minfo.group = amatches;
-	     minfo.group && (minfo.group)->mcount <= insmnum;
-	     minfo.group = (minfo.group)->next)
-	    insmnum -= (minfo.group)->mcount;
-	if (!minfo.group) {
-	    minfo.cur = NULL;
-	    minfo.asked = 0;
-	    return;
-	}
-    }
-    mc = (minfo.group)->matches + insmnum;
-    do_single(*mc);
-    minfo.cur = mc;
-}
-
-/* Return the length of the common prefix of s and t. */
-
-/**/
-int
-pfxlen(char *s, char *t)
-{
-    int i = 0;
-
-    while (*s && *s == *t)
-	s++, t++, i++;
-    return i;
-}
-
-/* Return the length of the common suffix of s and t. */
-
-#if 0
-static int
-sfxlen(char *s, char *t)
-{
-    if (*s && *t) {
-	int i = 0;
-	char *s2 = s + strlen(s) - 1, *t2 = t + strlen(t) - 1;
-
-	while (s2 >= s && t2 >= t && *s2 == *t2)
-	    s2--, t2--, i++;
-
-	return i;
-    } else
-	return 0;
-}
-#endif
-
-/* This is used to print the explanation string. *
- * It returns the number of lines printed.       */
+/* This is used to print expansions. */
 
 /**/
 int
-printfmt(char *fmt, int n, int dopr, int doesc)
-{
-    char *p = fmt, nc[DIGBUFSIZE];
-    int l = 0, cc = 0, b = 0, s = 0, u = 0, m;
-
-    for (; *p; p++) {
-	/* Handle the `%' stuff (%% == %, %n == <number of matches>). */
-	if (doesc && *p == '%') {
-	    if (*++p) {
-		m = 0;
-		switch (*p) {
-		case '%':
-		    if (dopr)
-			putc('%', shout);
-		    cc++;
-		    break;
-		case 'n':
-		    sprintf(nc, "%d", n);
-		    if (dopr)
-			fprintf(shout, nc);
-		    cc += strlen(nc);
-		    break;
-		case 'B':
-		    b = 1;
-		    if (dopr)
-			tcout(TCBOLDFACEBEG);
-		    break;
-		case 'b':
-		    b = 0; m = 1;
-		    if (dopr)
-			tcout(TCALLATTRSOFF);
-		    break;
-		case 'S':
-		    s = 1;
-		    if (dopr)
-			tcout(TCSTANDOUTBEG);
-		    break;
-		case 's':
-		    s = 0; m = 1;
-		    if (dopr)
-			tcout(TCSTANDOUTEND);
-		    break;
-		case 'U':
-		    u = 1;
-		    if (dopr)
-			tcout(TCUNDERLINEBEG);
-		    break;
-		case 'u':
-		    u = 0; m = 1;
-		    if (dopr)
-			tcout(TCUNDERLINEEND);
-		    break;
-		case '{':
-		    for (p++; *p && (*p != '%' || p[1] != '}'); p++, cc++)
-			if (dopr)
-			    putc(*p, shout);
-		    if (*p)
-			p++;
-		    else
-			p--;
-		    break;
-		}
-		if (dopr && m) {
-		    if (b)
-			tcout(TCBOLDFACEBEG);
-		    if (s)
-			tcout(TCSTANDOUTBEG);
-		    if (u)
-			tcout(TCUNDERLINEBEG);
-		}
-	    } else
-		break;
-	} else {
-	    cc++;
-	    if (*p == '\n') {
-		if (dopr) {
-		    if (tccan(TCCLEAREOL))
-			tcout(TCCLEAREOL);
-		    else {
-			int s = columns - 1 - (cc % columns);
-
-			while (s-- > 0)
-			    putc(' ', shout);
-		    }
-		}
-		l += 1 + (cc / columns);
-		cc = 0;
-	    }
-	    if (dopr)
-		putc(*p, shout);
-	}
-    }
-    if (dopr) {
-	if (tccan(TCCLEAREOL))
-	    tcout(TCCLEAREOL);
-	else {
-	    int s = columns - 1 - (cc % columns);
-
-	    while (s-- > 0)
-		putc(' ', shout);
-	}
-    }
-    return l + (cc / columns);
-}
-
-/* This skips over matches that are not to be listed. */
-
-/**/
-Cmatch *
-skipnolist(Cmatch *p)
-{
-    while (*p && (((*p)->flags & (CMF_NOLIST | CMF_HIDE)) ||
-		  ((*p)->disp && ((*p)->flags & (CMF_DISPLINE | CMF_HIDE)))))
-	p++;
-
-    return p;
-}
-
-/* List the matches.  Note that the list entries are metafied. */
-
-/**/
-void
-listmatches(void)
-{
-    struct chdata dat;
-
-#ifdef DEBUG
-    /* Sanity check */
-    if (!validlist) {
-	showmsg("BUG: listmatches called with bogus list");
-	return;
-    }
-#endif
-
-    dat.matches = amatches;
-    dat.num = nmatches;
-    dat.cur = NULL;
-    runhookdef(LISTMATCHESHOOK, (void *) &dat);
-}
-
-/**/
-void
-calclist(void)
+listlist(LinkList l)
 {
-    Cmgroup g;
-    Cmatch *p, m;
-    Cexpl *e;
-    int hidden = 0, nlist = 0, nlines = 0, add = 2 + isset(LISTTYPES);
-    int max = 0, i;
-    VARARR(int, mlens, nmatches + 1);
-
-    if (listdat.valid && onlyexpl == listdat.onlyexpl &&
-	menuacc == listdat.menuacc &&
-	lines == listdat.lines && columns == listdat.columns)
-	return;
-
-    for (g = amatches; g; g = g->next) {
-	char **pp = g->ylist;
-	int nl = 0, l, glong = 1, gshort = columns, ndisp = 0, totl = 0;
-
-	if (!onlyexpl && pp) {
-	    /* We have an ylist, lets see, if it contains newlines. */
-	    hidden = 1;
-	    while (!nl && *pp)
-		nl = !!strchr(*pp++, '\n');
-
-	    pp = g->ylist;
-	    if (nl || !pp[1]) {
-		/* Yup, there are newlines, count lines. */
-		char *nlptr, *sptr;
-
-		g->flags |= CGF_LINES;
-		hidden = 1;
-		while ((sptr = *pp)) {
-		    while (sptr && *sptr) {
-			nlines += (nlptr = strchr(sptr, '\n'))
-			    ? 1 + (nlptr-sptr)/columns
-			    : strlen(sptr)/columns;
-			sptr = nlptr ? nlptr+1 : NULL;
-		    }
-		    nlines++;
-		    pp++;
-		}
-		nlines--;
-	    } else {
-		while (*pp) {
-		    l = strlen(*pp);
-		    ndisp++;
-		    if (l > glong)
-			glong = l;
-		    if (l < gshort)
-			gshort = l;
-		    totl += l;
-		    nlist++;
-		    pp++;
-		}
-	    }
-	} else if (!onlyexpl) {
-	    for (p = g->matches; (m = *p); p++) {
-		if (menuacc && !hasbrpsfx(m, minfo.prebr, minfo.postbr)) {
-		    m->flags |= CMF_HIDE;
-		    continue;
-		}
-		m->flags &= ~CMF_HIDE;
+    int num = countlinknodes(l);
+    VARARR(char *, data, (num + 1));
+    LinkNode node;
+    char **p;
+    VARARR(int, lens, num);
+    VARARR(int, widths, columns);
+    int longest = 0, shortest = columns, totl = 0;
+    int len, ncols, nlines, tolast, col, i, max, pack = 0, *lenp;
+
+    for (node = firstnode(l), p = data; node; incnode(node), p++)
+	*p = (char *) getdata(node);
+    *p = NULL;
 
-		if (m->disp) {
-		    if (m->flags & CMF_DISPLINE) {
-			nlines += 1 + printfmt(m->disp, 0, 0, 0);
-			g->flags |= CGF_HASDL;
-		    } else {
-			l = niceztrlen(m->disp);
-			ndisp++;
-			if (l > glong)
-			    glong = l;
-			if (l < gshort)
-			    gshort = l;
-			totl += l;
-			mlens[m->gnum] = l;
+    qsort((void *) data, num, sizeof(char *),
+	  (int (*) _((const void *, const void *))) strbpcmp);
+
+    for (p = data, lenp = lens; *p; p++, lenp++) {
+	len = *lenp = niceztrlen(*p) + 2;
+	if (len > longest)
+	    longest = len;
+	if (len < shortest)
+	    shortest = len;
+	totl += len;
+    }
+    if ((ncols = ((columns + 2) / longest))) {
+	int tlines, tline, tcols, maxlen, nth, width;
+
+	nlines = (num + ncols - 1) / ncols;
+
+	if (isset(LISTPACKED)) {
+	    if (isset(LISTROWSFIRST)) {
+		int count, tcol, first, maxlines = 0, llines;
+
+		for (tcols = columns / shortest; tcols > ncols;
+		     tcols--) {
+		    for (nth = first = maxlen = width = maxlines =
+			     llines = tcol = 0,
+			     count = num;
+			 count > 0; count--) {
+			if (!(nth % tcols))
+			    llines++;
+			if (lens[nth] > maxlen)
+			    maxlen = lens[nth];
+			nth += tcols;
+			tlines++;
+			if (nth >= num) {
+			    if ((width += maxlen) >= columns)
+				break;
+			    widths[tcol++] = maxlen;
+			    maxlen = 0;
+			    nth = ++first;
+			    if (llines > maxlines)
+				maxlines = llines;
+			    llines = 0;
+			}
 		    }
-		    nlist++;
-		} else if (!(m->flags & CMF_NOLIST)) {
-		    l = niceztrlen(m->str);
-		    ndisp++;
-		    if (l > glong)
-			glong = l;
-		    if (l < gshort)
-			gshort = l;
-		    totl += l;
-		    mlens[m->gnum] = l;
-		    nlist++;
-		} else
-		    hidden = 1;
-	    }
-	}
-	if ((e = g->expls)) {
-	    while (*e) {
-		if ((*e)->count)
-		    nlines += 1 + printfmt((*e)->str, (*e)->count, 0, 1);
-		e++;
-	    }
-	}
-	g->totl = totl + (ndisp * add);
-	g->dcount = ndisp;
-	g->width = glong + add;
-	g->shortest = gshort + add;
-	if ((g->cols = columns / g->width) > g->dcount)
-	    g->cols = g->dcount;
-	if (g->cols) {
-	    i = g->cols * g->width - add;
-	    if (i > max)
-		max = i;
-	}
-    }
-    if (!onlyexpl) {
-	for (g = amatches; g; g = g->next) {
-	    char **pp;
-	    int glines = 0;
-
-	    zfree(g->widths, 0);
-	    g->widths = NULL;
-
-	    if ((pp = g->ylist)) {
-		if (!(g->flags & CGF_LINES)) {
-		    if (g->cols) {
-			glines += (arrlen(pp) + g->cols - 1) / g->cols;
-			if (g->cols > 1)
-			    g->width += (max - (g->width * g->cols - add)) / g->cols;
-		    } else {
-			g->cols = 1;
-			g->width = 1;
-			
-			while (*pp)
-			    glines += 1 + (strlen(*pp++) / columns);
+		    if (nth < num) {
+			widths[tcol++] = maxlen;
+			width += maxlen;
 		    }
+		    if (!count && width < columns)
+			break;
 		}
+		if (tcols > ncols)
+		    tlines = maxlines;
 	    } else {
-		if (g->cols) {
-		    glines += (g->dcount + g->cols - 1) / g->cols;
-		    if (g->cols > 1)
-			g->width += (max - (g->width * g->cols - add)) / g->cols;
-		} else if (!(g->flags & CGF_LINES)) {
-		    g->cols = 1;
-		    g->width = 0;
-		    
-		    for (p = g->matches; (m = *p); p++)
-			if (!(m->flags & CMF_HIDE)) {
-			    if (m->disp) {
-				if (!(m->flags & CMF_DISPLINE))
-				    glines += 1 + (mlens[m->gnum] / columns);
-			    } else if (!(m->flags & CMF_NOLIST))
-				glines += 1 + ((1 + mlens[m->gnum]) / columns);
-			}
-		}
-	    }
-	    g->lins = glines;
-	    nlines += glines;
-	}
-    }
-    if (!onlyexpl && isset(LISTPACKED)) {
-	char **pp;
-	int *ws, tlines, tline, tcols, maxlen, nth, width;
-
-	for (g = amatches; g; g = g->next) {
-	    ws = g->widths = (int *) zalloc(columns * sizeof(int));
-	    memset(ws, 0, columns * sizeof(int));
-	    tlines = g->lins;
-	    tcols = g->cols;
-	    width = 0;
-
-	    if ((pp = g->ylist)) {
-		if (!(g->flags & CGF_LINES)) {
-		    int yl = arrlen(pp), i;
-		    VARARR(int, ylens, yl);
-
-		    for (i = 0; *pp; i++, pp++)
-			ylens[i] = strlen(*pp) + add;
-
-		    if (isset(LISTROWSFIRST)) {
-			int count, tcol, first, maxlines = 0, llines;
-
-			for (tcols = columns / g->shortest; tcols > g->cols;
-			     tcols--) {
-			    for (nth = first = maxlen = width = maxlines =
-				     llines = tcol = 0,
-				     count = g->dcount;
-				 count > 0; count--) {
-				if (ylens[nth] > maxlen)
-				    maxlen = ylens[nth];
-				nth += tcols;
-				tlines++;
-				if (nth >= g->dcount) {
-				    if ((width += maxlen) >= columns)
-					break;
-				    ws[tcol++] = maxlen;
-				    maxlen = 0;
-				    nth = ++first;
-				    if (llines > maxlines)
-					maxlines = llines;
-				    llines = 0;
-				}
-			    }
-			    if (nth < yl) {
-				ws[tcol++] = maxlen;
-				width += maxlen;
-			    }
-			    if (!count && width < columns)
-				break;
-			}
-			if (tcols > g->cols)
-			    tlines = maxlines;
-		    } else {
-			for (tlines = ((g->totl + columns) / columns);
-			     tlines < g->lins; tlines++) {
-			    for (pp = g->ylist, nth = tline = width =
-				     maxlen = tcols = 0;
-				 *pp; nth++, pp++) {
-				if (ylens[nth] > maxlen)
-				    maxlen = ylens[nth];
-				if (++tline == tlines) {
-				    if ((width += maxlen) >= columns)
-					break;
-				    ws[tcols++] = maxlen;
-				    maxlen = tline = 0;
-				}
-			    }
-			    if (tline) {
-				ws[tcols++] = maxlen;
-				width += maxlen;
-			    }
-			    if (nth == yl && width < columns)
+		for (tlines = ((totl + columns) / columns);
+		     tlines < nlines; tlines++) {
+		    for (p = data, nth = tline = width =
+			     maxlen = tcols = 0;
+			 *p; nth++, p++) {
+			if (lens[nth] > maxlen)
+			    maxlen = lens[nth];
+			if (++tline == tlines) {
+			    if ((width += maxlen) >= columns)
 				break;
+			    widths[tcols++] = maxlen;
+			    maxlen = tline = 0;
 			}
 		    }
-		}
-	    } else if (g->width) {
-		if (isset(LISTROWSFIRST)) {
-		    int addlen, count, tcol, maxlines = 0, llines, i;
-		    Cmatch *first;
-
-		    for (tcols = columns / g->shortest; tcols > g->cols;
-			 tcols--) {
-			p = first = skipnolist(g->matches);
-			for (maxlen = width = maxlines = llines = tcol = 0,
-				 count = g->dcount;
-			     count > 0; count--) {
-			    m = *p;
-			    addlen = mlens[m->gnum] + add;
-			    if (addlen > maxlen)
-				maxlen = addlen;
-			    for (i = tcols; i && *p; i--)
-				p = skipnolist(p + 1);
-
-			    llines++;
-			    if (!*p) {
-				if (llines > maxlines)
-				    maxlines = llines;
-				llines = 0;
-
-				if ((width += maxlen) >= columns)
-				    break;
-				ws[tcol++] = maxlen;
-				maxlen = 0;
-
-				p = first = skipnolist(first + 1);
-			    }
-			}
-			if (tlines) {
-			    ws[tcol++] = maxlen;
-			    width += maxlen;
-			}
-			if (!count && width < columns)
-			    break;
-		    }
-		    if (tcols > g->cols)
-			tlines = maxlines;
-		} else {
-		    int addlen;
-
-		    for (tlines = ((g->totl + columns) / columns);
-			 tlines < g->lins; tlines++) {
-			for (p = g->matches, nth = tline = width =
-				 maxlen = tcols = 0;
-			     (m = *p); p++, nth++) {
-			    if (!(m->flags &
-				  (m->disp ? (CMF_DISPLINE | CMF_HIDE) :
-				   (CMF_NOLIST | CMF_HIDE)))) {
-				addlen = mlens[m->gnum] + add;
-				if (addlen > maxlen)
-				    maxlen = addlen;
-				if (++tline == tlines) {
-				    if ((width += maxlen) >= columns)
-					break;
-				    ws[tcols++] = maxlen;
-				    maxlen = tline = 0;
-				}
-			    }
-			}
-			if (tline) {
-			    ws[tcols++] = maxlen;
-			    width += maxlen;
-			}
-			if (nth == g->dcount && width < columns)
-			    break;
+		    if (tline) {
+			widths[tcols++] = maxlen;
+			width += maxlen;
 		    }
+		    if (nth == num && width < columns)
+			break;
 		}
 	    }
-	    if (tlines == g->lins) {
-		zfree(ws, columns * sizeof(int));
-		g->widths = NULL;
-	    } else {
-		nlines += tlines - g->lins;
-		g->lins = tlines;
-		g->cols = tcols;
-		g->totl = width;
-		width -= add;
-		if (width > max)
-		    max = width;
+	    if ((pack = (tlines < nlines))) {
+		nlines = tlines;
+		ncols = tcols;
 	    }
 	}
-	for (g = amatches; g; g = g->next) {
-	    if (g->widths) {
-		int *p, a = (max - g->totl + add) / g->cols;
-
-		for (i = g->cols, p = g->widths; i; i--, p++)
-		    *p += a;
-	    } else if (g->width && g->cols > 1)
-		g->width += (max - (g->width * g->cols - add)) / g->cols;
-	}
+    } else {
+	nlines = 0;
+	for (p = data; *p; p++)
+	    nlines += 1 + (strlen(*p) / columns);
     }
-    listdat.valid = 1;
-    listdat.hidden = hidden;
-    listdat.nlist = nlist;
-    listdat.nlines = nlines;
-    listdat.menuacc = menuacc;
-    listdat.onlyexpl = onlyexpl;
-    listdat.columns = columns;
-    listdat.lines = lines;
-}
-
-/**/
-int asklist(void)
-{
     /* Set the cursor below the prompt. */
     trashzle();
-    showinglist = listshown = 0;
 
-    clearflag = (isset(USEZLE) && !termflags &&
-		 complastprompt && *complastprompt);
+    tolast = ((zmult == 1) == !!isset(ALWAYSLASTPROMPT));
+    clearflag = (isset(USEZLE) && !termflags && tolast);
 
-    /* Maybe we have to ask if the user wants to see the list. */
-    if ((!minfo.cur || !minfo.asked) &&
-	((complistmax && listdat.nlist > complistmax) ||
-	 (!complistmax && listdat.nlines >= lines))) {
+    max = getiparam("LISTMAX");
+    if ((max && num > max) || (!max && nlines > lines)) {
 	int qup;
+
 	zsetterm();
 	qup = printfmt("zsh: do you wish to see all %n possibilities? ",
-		       listdat.nlist, 1, 1);
+		       num, 1, 1);
 	fflush(shout);
 	if (getzlequery() != 'y') {
 	    if (clearflag) {
@@ -7530,8 +1865,6 @@ int asklist(void)
 		tcmultout(TCUP, TCMULTUP, nlnct);
 	    } else
 		putc('\n', shout);
-	    if (minfo.cur)
-		minfo.asked = 2;
 	    return 1;
 	}
 	if (clearflag) {
@@ -7542,304 +1875,60 @@ int asklist(void)
 	} else
 	    putc('\n', shout);
 	settyinfo(&shttyinfo);
-	if (minfo.cur)
-	    minfo.asked = 1;
     }
-    return 0;
-}
-
-/**/
-int
-printlist(int over, CLPrintFunc printm)
-{
-    Cmgroup g;
-    Cmatch *p, m;
-    Cexpl *e;
-    int pnl = 0, cl = (over ? listdat.nlines : -1);
-    int mc = 0, ml = 0, printed = 0;
-
-    if (cl < 2) {
-	cl = -1;
-	if (tccan(TCCLEAREOD))
-	    tcout(TCCLEAREOD);
-    }
-    g = amatches;
-    while (g) {
-	char **pp = g->ylist;
-
-	if ((e = g->expls)) {
-	    int l;
-
-	    while (*e) {
-		if ((*e)->count) {
-		    if (pnl) {
+    if (ncols) {
+	if (isset(LISTROWSFIRST)) {
+	    for (col = 1, p = data, lenp = lens; *p;
+		 p++, lenp++, col++) {
+		nicezputs(*p, shout);
+		if (col == ncols) {
+		    col = 0;
+		    if (p[1])
 			putc('\n', shout);
-			pnl = 0;
-			ml++;
-			if (cl >= 0 && --cl <= 1) {
-			    cl = -1;
-			    if (tccan(TCCLEAREOD))
-				tcout(TCCLEAREOD);
-			}
-		    }
-		    l = printfmt((*e)->str, (*e)->count, 1, 1);
-		    ml += l;
-		    if (cl >= 0 && (cl -= l) <= 1) {
-			cl = -1;
-			if (tccan(TCCLEAREOD))
-			    tcout(TCCLEAREOD);
-		    }
-		    pnl = 1;
-		}
-		e++;
-	    }
-	}
-	if (!listdat.onlyexpl && pp && *pp) {
-	    if (pnl) {
-		putc('\n', shout);
-		pnl = 0;
-		ml++;
-		if (cl >= 0 && --cl <= 1) {
-		    cl = -1;
-		    if (tccan(TCCLEAREOD))
-			tcout(TCCLEAREOD);
-		}
-	    }
-	    if (g->flags & CGF_LINES) {
-		while (*pp) {
-		    zputs(*pp, shout);
-		    if (*++pp)
-			putc('\n', shout);
-		}
-	    } else {
-		int n = g->lcount, nl, nc, i, a;
-		char **pq;
-
-		nl = nc = g->lins;
-
-		while (n && nl--) {
-		    i = g->cols;
-		    mc = 0;
-		    pq = pp;
-		    while (n && i--) {
-			if (pq - g->ylist >= g->lcount)
-			    break;
-			zputs(*pq, shout);
-			if (i) {
-			    a = (g->widths ? g->widths[mc] : g->width) -
-				strlen(*pq);
-			    while (a--)
-				putc(' ', shout);
-			}
-			pq += (isset(LISTROWSFIRST) ? 1 : nc);
-			mc++;
-			n--;
-		    }
-		    if (n) {
-			putc('\n', shout);
-			ml++;
-			if (cl >= 0 && --cl <= 1) {
-			    cl = -1;
-			    if (tccan(TCCLEAREOD))
-				tcout(TCCLEAREOD);
-			}
-		    }
-		    pp += (isset(LISTROWSFIRST) ? g->cols : 1);
-		}
-	    }
-	} else if (!listdat.onlyexpl && g->lcount) {
-	    int n = g->dcount, nl, nc, i, j, wid;
-	    Cmatch *q;
-
-	    nl = nc = g->lins;
-
-	    if (g->flags & CGF_HASDL) {
-		for (p = g->matches; (m = *p); p++)
-		    if (m->disp && (m->flags & CMF_DISPLINE)) {
-			if (pnl) {
-			    putc('\n', shout);
-			    pnl = 0;
-			    ml++;
-			    if (cl >= 0 && --cl <= 1) {
-				cl = -1;
-				if (tccan(TCCLEAREOD))
-				    tcout(TCCLEAREOD);
-			    }
-			}
-			printed++;
-			printm(g, p, 0, ml, 1, 0, NULL, NULL);
-			pnl = 1;
-		    }
-	    }
-	    if (n && pnl) {
-		putc('\n', shout);
-		pnl = 0;
-		ml++;
-		if (cl >= 0 && --cl <= 1) {
-		    cl = -1;
-		    if (tccan(TCCLEAREOD))
-			tcout(TCCLEAREOD);
+		} else {
+		    if ((i = (pack ? widths[col - 1] : longest) - *lenp + 2) > 0)
+			while (i--)
+			    putc(' ', shout);
 		}
 	    }
-	    for (p = skipnolist(g->matches); n && nl--;) {
-		i = g->cols;
-		mc = 0;
-		q = p;
-		while (n && i--) {
-		    wid = (g->widths ? g->widths[mc] : g->width);
-		    if (!(m = *q)) {
-			printm(g, NULL, mc, ml, (!i), wid, NULL, NULL);
+	} else {
+	    char **f;
+	    int *fl, line;
+
+	    for (f = data, fl = lens, line = 0; line < nlines;
+		 f++, fl++, line++) {
+		for (col = 1, p = f, lenp = fl; *p; col++) {
+		    nicezputs(*p, shout);
+		    if (col == ncols)
 			break;
-		    }
-		    if (!m->disp && (m->flags & CMF_FILE)) {
-			struct stat buf;
-			char *pb;
-
-			pb = (char *) zhalloc((m->prpre ? strlen(m->prpre) : 0) +
-					     3 + strlen(m->str));
-			sprintf(pb, "%s%s", (m->prpre ? m->prpre : "./"),
-				m->str);
-
-			if (ztat(pb, &buf, 1))
-			    printm(g, q, mc, ml, (!i), wid, NULL, NULL);
-			else
-			    printm(g, q, mc, ml, (!i), wid, pb, &buf);
-		    } else
-			printm(g, q, mc, ml, (!i), wid, NULL, NULL);
-
-		    printed++;
-
-		    if (--n)
-			for (j = (isset(LISTROWSFIRST) ? 1 : nc); j && *q; j--)
-			    q = skipnolist(q + 1);
-		    mc++;
+		    if ((i = (pack ? widths[col - 1] : longest) - *lenp + 2) > 0)
+			while (i--)
+			    putc(' ', shout);
+		    for (i = nlines; i && *p; i--, p++, lenp++);
 		}
-		while (i-- > 0)
-		    printm(g, NULL, mc++, ml, (!i),
-			   (g->widths ? g->widths[mc] : g->width), NULL, NULL);
-
-		if (n) {
+		if (line + 1 < nlines)
 		    putc('\n', shout);
-		    ml++;
-		    if (cl >= 0 && --cl <= 1) {
-			cl = -1;
-			if (tccan(TCCLEAREOD))
-			    tcout(TCCLEAREOD);
-		    }
-		    if (nl)
-			for (j = (isset(LISTROWSFIRST) ? g->cols : 1); j && *p; j--)
-			    p = skipnolist(p + 1);
-		}
 	    }
 	}
-	if (g->lcount)
-	    pnl = 1;
-	g = g->next;
+    } else {
+	for (p = data; *p; p++) {
+	    nicezputs(*p, shout);
+	    putc('\n', shout);
+	}
     }
     if (clearflag) {
-	/* Move the cursor up to the prompt, if always_last_prompt *
-	 * is set and all that...                                  */
-	if ((ml = listdat.nlines + nlnct - 1) < lines) {
-	    tcmultout(TCUP, TCMULTUP, ml);
+	if ((nlines += nlnct - 1) < lines) {
+	    tcmultout(TCUP, TCMULTUP, nlines);
 	    showinglist = -1;
 	} else
 	    clearflag = 0, putc('\n', shout);
     } else
 	putc('\n', shout);
-    listshown = (clearflag ? 1 : -1);
-
-    return printed;
-}
-
-static void
-iprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
-	char *path, struct stat *buf)
-{
-    Cmatch m;
-    int len = 0;
-
-    if (!mp)
-	return;
-
-    m = *mp;
-    if (m->disp) {
-	if (m->flags & CMF_DISPLINE) {
-	    printfmt(m->disp, 0, 1, 0);
-	    return;
-	}
-	nicezputs(m->disp, shout);
-	len = niceztrlen(m->disp);
-    } else {
-	nicezputs(m->str, shout);
-	len = niceztrlen(m->str);
-
-	if (isset(LISTTYPES)) {
-	    if (buf)
-		putc(file_type(buf->st_mode), shout);
-	    len++;
-	}
-    }
-    if (!lastc) {
-	len = width - len;
-
-	while (len-- > 0)
-	    putc(' ', shout);
-    }
-}
-
-/**/
-int
-ilistmatches(Hookdef dummy, Chdata dat)
-{
-    calclist();
-
-    if (!listdat.nlines) {
-	showinglist = listshown = 0;
-	return 1;
-    }
-    if (asklist())
-	return 0;
-
-    printlist(0, iprintm);
-
-    return 0;
-}
-
-/* This is used to print expansions. */
-
-/**/
-int
-listlist(LinkList l)
-{
-    struct cmgroup dg;
-    int vl = validlist, sm = smatches, nm = nmatches;
-    char *oclp = complastprompt;
-    Cmgroup am = amatches;
-    struct cldata odat;
-
-    memcpy(&odat, &listdat, sizeof(struct cldata));
 
     if (listshown)
 	showagain = 1;
 
-    complastprompt = ((zmult == 1) == !!isset(ALWAYSLASTPROMPT) ? "yes" : NULL);
-    smatches = 1;
-    validlist = 1;
-    memset(&dg, 0, sizeof(struct cmgroup));
-    dg.ylist = (char **) makearray(l, 0, 1, &(dg.lcount), NULL, NULL);
-    nmatches = dg.lcount;
-    amatches = &dg;
-    listdat.valid = 0;
-    ilistmatches(NULL, NULL);
-    amatches = am;
-
-    validlist = vl;
-    smatches = sm;
-    nmatches = nm;
-    complastprompt = oclp;
-    memcpy(&listdat, &odat, sizeof(struct cldata));
-
-    return !dg.lcount;
+    return !num;
 }
 
 /* Expand the history references. */