about summary refs log tree commit diff
path: root/Src/Zle
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Zle')
-rw-r--r--Src/Zle/comp.h202
-rw-r--r--Src/Zle/compctl.c3252
-rw-r--r--Src/Zle/compctl.h160
-rw-r--r--Src/Zle/compctl.mdd9
-rw-r--r--Src/Zle/complete.c1332
-rw-r--r--Src/Zle/complete.mdd11
-rw-r--r--Src/Zle/complist.c2
-rw-r--r--Src/Zle/complist.mdd2
-rw-r--r--Src/Zle/computil.mdd2
-rw-r--r--Src/Zle/zle.h17
-rw-r--r--Src/Zle/zle.mdd70
-rw-r--r--Src/Zle/zle_keymap.c3
-rw-r--r--Src/Zle/zle_main.c135
-rw-r--r--Src/Zle/zle_thingy.c8
-rw-r--r--Src/Zle/zle_tricky.c2355
-rw-r--r--Src/Zle/zleparameter.c257
-rw-r--r--Src/Zle/zleparameter.mdd5
17 files changed, 4324 insertions, 3498 deletions
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 8b31d97fa..68575cff4 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -1,5 +1,5 @@
 /*
- * comp.h - header file for completion
+ * complete.h - header file for completion
  *
  * This file is part of zsh, the Z shell.
  *
@@ -27,143 +27,10 @@
  *
  */
 
-#undef compctlread
-
-typedef struct compctlp  *Compctlp;
-typedef struct compctl   *Compctl;
-typedef struct compcond  *Compcond;
-typedef struct patcomp   *Patcomp;
 typedef struct cmatcher  *Cmatcher;
 typedef struct cmlist    *Cmlist;
 typedef struct cpattern  *Cpattern;
 typedef struct menuinfo  *Menuinfo;
-
-/* node for compctl hash table (compctltab) */
-
-struct compctlp {
-    HashNode next;		/* next in hash chain               */
-    char *nam;			/* command name                     */
-    int flags;			/* CURRENTLY UNUSED                 */
-    Compctl cc;			/* pointer to the compctl desc.     */
-};
-
-/* for the list of pattern compctls */
-
-struct patcomp {
-    Patcomp next;
-    char *pat;
-    Compctl cc;
-};
-
-/* compctl -x condition */
-
-struct compcond {
-    Compcond and, or;		/* the next or'ed/and'ed conditions    */
-    int type;			/* the type (CCT_*)                    */
-    int n;			/* the array length                    */
-    union {			/* these structs hold the data used to */
-	struct {		/* test this condition                 */
-	    int *a, *b;		/* CCT_POS, CCT_NUMWORDS               */
-	}
-	r;
-	struct {		/* CCT_CURSTR, CCT_CURPAT,... */
-	    int *p;
-	    char **s;
-	}
-	s;
-	struct {		/* CCT_RANGESTR,... */
-	    char **a, **b;
-	}
-	l;
-    }
-    u;
-};
-
-#define CCT_UNUSED     0
-#define CCT_POS        1
-#define CCT_CURSTR     2
-#define CCT_CURPAT     3
-#define CCT_WORDSTR    4
-#define CCT_WORDPAT    5
-#define CCT_CURSUF     6
-#define CCT_CURPRE     7
-#define CCT_CURSUB     8
-#define CCT_CURSUBC    9
-#define CCT_NUMWORDS  10
-#define CCT_RANGESTR  11
-#define CCT_RANGEPAT  12
-#define CCT_QUOTE     13
-
-/* Contains the real description for compctls */
-
-struct compctl {
-    int refc;			/* reference count                         */
-    Compctl next;		/* next compctl for -x                     */
-    unsigned long mask, mask2;	/* masks of things to complete (CC_*)      */
-    char *keyvar;		/* for -k (variable)                       */
-    char *glob;			/* for -g (globbing)                       */
-    char *str;			/* for -s (expansion)                      */
-    char *func;			/* for -K (function)                       */
-    char *widget;		/* for -i (function)                       */
-    char *explain;		/* for -X (explanation)                    */
-    char *ylist;		/* for -y (user-defined desc. for listing) */
-    char *prefix, *suffix;	/* for -P and -S (prefix, suffix)          */
-    char *subcmd;		/* for -l (command name to use)            */
-    char *substr;		/* for -1 (command name to use)            */
-    char *withd;		/* for -w (with directory                  */
-    char *hpat;			/* for -H (history pattern)                */
-    int hnum;			/* for -H (number of events to search)     */
-    char *gname;		/* for -J and -V (group name)              */
-    Compctl ext;		/* for -x (first of the compctls after -x) */
-    Compcond cond;		/* for -x (condition for this compctl)     */
-    Compctl xor;		/* for + (next of the xor'ed compctls)     */
-    Cmatcher matcher;		/* matcher control (-M) */
-    char *mstr;			/* matcher string */
-};
-
-/* objects to complete (mask) */
-#define CC_FILES	(1<<0)
-#define CC_COMMPATH	(1<<1)
-#define CC_REMOVE	(1<<2)
-#define CC_OPTIONS	(1<<3)
-#define CC_VARS		(1<<4)
-#define CC_BINDINGS	(1<<5)
-#define CC_ARRAYS	(1<<6)
-#define CC_INTVARS	(1<<7)
-#define CC_SHFUNCS	(1<<8)
-#define CC_PARAMS	(1<<9)
-#define CC_ENVVARS	(1<<10)
-#define CC_JOBS		(1<<11)
-#define CC_RUNNING	(1<<12)
-#define CC_STOPPED	(1<<13)
-#define CC_BUILTINS	(1<<14)
-#define CC_ALREG	(1<<15)
-#define CC_ALGLOB	(1<<16)
-#define CC_USERS	(1<<17)
-#define CC_DISCMDS	(1<<18)
-#define CC_EXCMDS	(1<<19)
-#define CC_SCALARS	(1<<20)
-#define CC_READONLYS	(1<<21)
-#define CC_SPECIALS	(1<<22)
-#define CC_DELETE	(1<<23)
-#define CC_NAMED	(1<<24)
-#define CC_QUOTEFLAG	(1<<25)
-#define CC_EXTCMDS	(1<<26)
-#define CC_RESWDS	(1<<27)
-#define CC_DIRS		(1<<28)
-
-#define CC_EXPANDEXPL	(1<<30)
-#define CC_RESERVED	(1<<31)
-
-/* objects to complete (mask2) */
-#define CC_NOSORT	(1<<0)
-#define CC_XORCONT	(1<<1)
-#define CC_CCCONT	(1<<2)
-#define CC_PATCONT	(1<<3)
-#define CC_DEFCONT	(1<<4)
-#define CC_UNIQCON      (1<<5)
-#define CC_UNIQALL      (1<<6)
-
 typedef struct cexpl *Cexpl;
 typedef struct cmgroup *Cmgroup;
 typedef struct cmatch *Cmatch;
@@ -191,7 +58,6 @@ struct cmgroup {
     int ecount;			/* number of explanation string */
     Cexpl *expls;		/* explanation strings */
     int ccount;			/* number of compctls used */
-    Compctl *ccs;		/* the compctls used */
     LinkList lexpls;		/* list of explanation string while building */
     LinkList lmatches;		/* list of matches */
     LinkList lfmatches;		/* list of matches without fignore */
@@ -272,22 +138,68 @@ struct cmatcher {
     int ralen;			/* length of right anchor */
 };
 
-
 #define CMF_LINE  1
 #define CMF_LEFT  2
 #define CMF_RIGHT 4
 
-
 struct cpattern {
     Cpattern next;		/* next sub-pattern */
     unsigned char tab[256];	/* table of matched characters */
     int equiv;			/* if this is a {...} class */
 };
 
-/* Flags for makecomplist*(). Things not to do. */
+/* This is a special return value for parse_cmatcher(), *
+ * signalling an error. */
+
+#define pcm_err ((Cmatcher) 1)
+
+/* Information about what to put on the line as the unambiguous string.
+ * The code always keeps lists of these structs up to date while
+ * matches are added (in the aminfo structs below).
+ * The lists have two levels: in the first one we have one struct per
+ * word-part, where parts are separated by the anchors of `*' patterns.
+ * These structs have pointers (in the prefix and suffix fields) to
+ * lists of cline structs describing the strings before or after the
+ * the anchor. */
+
+typedef struct cline *Cline;
+typedef struct clsub Clsub;
+
+struct cline {
+    Cline next;
+    int flags;
+    char *line;
+    int llen;
+    char *word;
+    int wlen;
+    char *orig;
+    int olen;
+    int slen;
+    Cline prefix, suffix;
+    int min, max;
+};
 
-#define CFN_FIRST   1
-#define CFN_DEFAULT 2
+#define CLF_MISS      1
+#define CLF_DIFF      2
+#define CLF_SUF       4
+#define CLF_MID       8
+#define CLF_NEW      16
+#define CLF_LINE     32
+#define CLF_JOIN     64
+#define CLF_MATCHED 128
+
+/* Information for ambiguous completions. One for fignore ignored and   *
+ * one for normal completion. */
+
+typedef struct aminfo *Aminfo;
+
+struct aminfo {
+    Cmatch firstm;		/* the first match                        */
+    int exact;			/* if there was an exact match            */
+    Cmatch exactm;		/* the exact match (if any)               */
+    int count;			/* number of matches                      */
+    Cline line;			/* unambiguous line string                */
+};
 
 /* Information about menucompletion stuff. */
 
@@ -463,3 +375,13 @@ struct chdata {
 
 #define CP_KEYPARAMS   27
 #define CP_ALLKEYS     ((unsigned int) 0x7ffffff)
+
+/* 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)
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index d09fff259..d00e317c2 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -31,8 +31,24 @@
 #include "compctl.pro"
 #define GLOBAL_PROTOTYPES
 #include "zle_tricky.pro"
+#include "complete.pro"
 #undef GLOBAL_PROTOTYPES
 
+/* Default completion infos */
+ 
+/**/
+struct compctl cc_compos, cc_default, cc_first, cc_dummy;
+ 
+/* Hash table for completion info for commands */
+ 
+/**/
+HashTable compctltab;
+
+/* List of pattern compctls */
+
+/**/
+Patcomp patcomps;
+
 #define COMP_LIST	(1<<0)	/* -L */
 #define COMP_COMMAND	(1<<1)	/* -C */
 #define COMP_DEFAULT	(1<<2)	/* -D */
@@ -48,105 +64,219 @@ static int cclist;
 /* Mask for determining what to print */
 static unsigned long showmask = 0;
 
-/* This is a special return value for parse_cmatcher(), *
- * signalling an error. */
+/**/
+static void
+createcompctltable(void)
+{
+    compctltab = newhashtable(23, "compctltab", NULL);
+
+    compctltab->hash        = hasher;
+    compctltab->emptytable  = emptyhashtable;
+    compctltab->filltable   = NULL;
+    compctltab->cmpnodes    = strcmp;
+    compctltab->addnode     = addhashnode;
+    compctltab->getnode     = gethashnode2;
+    compctltab->getnode2    = gethashnode2;
+    compctltab->removenode  = removehashnode;
+    compctltab->disablenode = NULL;
+    compctltab->enablenode  = NULL;
+    compctltab->freenode    = freecompctlp;
+    compctltab->printnode   = printcompctlp;
+
+    patcomps = NULL;
+}
 
-#define pcm_err ((Cmatcher) 1)
+/**/
+static void
+freecompctlp(HashNode hn)
+{
+    Compctlp ccp = (Compctlp) hn;
 
-/* Copy a list of completion matchers. */
+    zsfree(ccp->nam);
+    freecompctl(ccp->cc);
+    zfree(ccp, sizeof(struct compctlp));
+}
 
-static Cmlist
-cpcmlist(Cmlist l)
+/**/
+void
+freecompctl(Compctl cc)
 {
-    Cmlist r = NULL, *p = &r, n;
+    if (cc == &cc_default ||
+ 	cc == &cc_first ||
+	cc == &cc_compos ||
+	--cc->refc > 0)
+	return;
 
-    while (l) {
-	*p = n = (Cmlist) zalloc(sizeof(struct cmlist));
-	n->next = NULL;
-	n->matcher = cpcmatcher(l->matcher);
-	n->str = ztrdup(l->str);
+    zsfree(cc->keyvar);
+    zsfree(cc->glob);
+    zsfree(cc->str);
+    zsfree(cc->func);
+    zsfree(cc->explain);
+    zsfree(cc->ylist);
+    zsfree(cc->prefix);
+    zsfree(cc->suffix);
+    zsfree(cc->hpat);
+    zsfree(cc->gname);
+    zsfree(cc->subcmd);
+    zsfree(cc->substr);
+    if (cc->cond)
+	freecompcond(cc->cond);
+    if (cc->ext) {
+	Compctl n, m;
 
-	p = &(n->next);
-	l = l->next;
+	n = cc->ext;
+	do {
+	    m = (Compctl) (n->next);
+	    freecompctl(n);
+	    n = m;
+	}
+	while (n);
     }
-    return r;
+    if (cc->xor && cc->xor != &cc_default)
+	freecompctl(cc->xor);
+    if (cc->matcher)
+	freecmatcher(cc->matcher);
+    zsfree(cc->mstr);
+    zfree(cc, sizeof(struct compctl));
 }
 
-/* Copy a completion matcher list. */
-
 /**/
-static Cmatcher
-cpcmatcher(Cmatcher m)
+void
+freecompcond(void *a)
 {
-    Cmatcher r = NULL, *p = &r, n;
-
-    while (m) {
-	*p = n = (Cmatcher) zalloc(sizeof(struct cmatcher));
-
-	n->refc = 1;
-	n->next = NULL;
-	n->flags = m->flags;
-	n->line = cpcpattern(m->line);
-	n->llen = m->llen;
-	n->word = cpcpattern(m->word);
-	n->wlen = m->wlen;
-	n->left = cpcpattern(m->left);
-	n->lalen = m->lalen;
-	n->right = cpcpattern(m->right);
-	n->ralen = m->ralen;
-
-	p = &(n->next);
-	m = m->next;
-    }
-    return r;
+    Compcond cc = (Compcond) a;
+    Compcond and, or, c;
+    int n;
+
+    for (c = cc; c; c = or) {
+	or = c->or;
+	for (; c; c = and) {
+	    and = c->and;
+	    if (c->type == CCT_POS ||
+		c->type == CCT_NUMWORDS) {
+		free(c->u.r.a);
+		free(c->u.r.b);
+	    } else if (c->type == CCT_CURSUF ||
+		       c->type == CCT_CURPRE) {
+		for (n = 0; n < c->n; n++)
+		    if (c->u.s.s[n])
+			zsfree(c->u.s.s[n]);
+		free(c->u.s.s);
+	    } else if (c->type == CCT_RANGESTR ||
+		       c->type == CCT_RANGEPAT) {
+		for (n = 0; n < c->n; n++)
+		    if (c->u.l.a[n])
+			zsfree(c->u.l.a[n]);
+		free(c->u.l.a);
+		for (n = 0; n < c->n; n++)
+		    if (c->u.l.b[n])
+			zsfree(c->u.l.b[n]);
+		free(c->u.l.b);
+	    } else {
+		for (n = 0; n < c->n; n++)
+		    if (c->u.s.s[n])
+			zsfree(c->u.s.s[n]);
+		free(c->u.s.p);
+		free(c->u.s.s);
+	    }
+	    zfree(c, sizeof(struct compcond));
+	}
+    }
 }
 
-/* Copy a completion matcher pattern. */
-
 /**/
-static Cpattern
-cpcpattern(Cpattern o)
+int
+compctlread(char *name, char **args, char *ops, char *reply)
 {
-    Cpattern r = NULL, *p = &r, n;
+    char *buf, *bptr;
 
-    while (o) {
-	*p = n = (Cpattern) zalloc(sizeof(struct cpattern));
+    /* only allowed to be called for completion */
+    if (!incompctlfunc) {
+	zwarnnam(name, "option valid only in functions called for completion",
+		NULL, 0);
+	return 1;
+    }
 
-	n->next = NULL;
-	memcpy(n->tab, o->tab, 256);
-	n->equiv = o->equiv;
+    if (ops['l']) {
+	/* -ln gives the index of the word the cursor is currently on, which is
+	available in cs (but remember that Zsh counts from one, not zero!) */
+	if (ops['n']) {
+	    char nbuf[14];
+
+	    if (ops['e'] || ops['E'])
+		printf("%d\n", cs + 1);
+	    if (!ops['e']) {
+		sprintf(nbuf, "%d", cs + 1);
+		setsparam(reply, ztrdup(nbuf));
+	    }
+	    return 0;
+	}
+	/* without -n, the current line is assigned to the given parameter as a
+	scalar */
+	if (ops['e'] || ops['E']) {
+	    zputs((char *) line, stdout);
+	    putchar('\n');
+	}
+	if (!ops['e'])
+	    setsparam(reply, ztrdup((char *) line));
+    } else {
+	int i;
 
-	p = &(n->next);
-	o = o->next;
-    }
-    return r;
-}
+	/* -cn gives the current cursor position within the current word, which
+	is available in clwpos (but remember that Zsh counts from one, not
+	zero!) */
+	if (ops['n']) {
+	    char nbuf[14];
+
+	    if (ops['e'] || ops['E'])
+		printf("%d\n", clwpos + 1);
+	    if (!ops['e']) {
+		sprintf(nbuf, "%d", clwpos + 1);
+		setsparam(reply, ztrdup(nbuf));
+	    }
+	    return 0;
+	}
+	/* without -n, the words of the current line are assigned to the given
+	parameters separately */
+	if (ops['A'] && !ops['e']) {
+	    /* the -A option means that one array is specified, instead of
+	    many parameters */
+	    char **p, **b = (char **)zcalloc((clwnum + 1) * sizeof(char *));
 
-/* Set the global match specs. */
+	    for (i = 0, p = b; i < clwnum; p++, i++)
+		*p = ztrdup(clwords[i]);
 
-/**/
-static int
-set_gmatcher(char *name, char **argv)
-{
-    Cmlist l = NULL, *q = &l, n;
-    Cmatcher m;
+	    setaparam(reply, b);
+	    return 0;
+	}
+	if (ops['e'] || ops['E']) {
+	    for (i = 0; i < clwnum; i++) {
+		zputs(clwords[i], stdout);
+		putchar('\n');
+	    }
 
-    while (*argv) {
-	if ((m = parse_cmatcher(name, *argv)) == pcm_err)
-	    return 1;
-	*q = n = (Cmlist) zhalloc(sizeof(struct cmlist));
-	n->next = NULL;
-	n->matcher = m;
-	n->str = *argv++;
+	    if (ops['e'])
+		return 0;
+	}
 
-	q = &(n->next);
-    }
-    freecmlist(cmatcher);
-    PERMALLOC {
-	cmatcher = cpcmlist(l);
-    } LASTALLOC;
+	for (i = 0; i < clwnum && *args; reply = *args++, i++)
+	    setsparam(reply, ztrdup(clwords[i]));
+
+	if (i < clwnum) {
+	    int j, len;
 
-    return 1;
+	    for (j = i, len = 0; j < clwnum; len += strlen(clwords[j++]));
+	    bptr = buf = zalloc(len + j - i);
+	    while (i < clwnum) {
+		strucpy(&bptr, clwords[i++]);
+		*bptr++ = ' ';
+	    }
+	    bptr[-1] = '\0';
+	} else
+	    buf = ztrdup("");
+	setsparam(reply, buf);
+    }
+    return 0;
 }
 
 /* Try to get the global matcher from the given compctl. */
@@ -190,208 +320,6 @@ print_gmatcher(int ac)
     }
 }
 
-/* Parse a string for matcher control, containing multiple matchers. */
-
-/**/
-static Cmatcher
-parse_cmatcher(char *name, char *s)
-{
-    Cmatcher ret = NULL, r = NULL, n;
-    Cpattern line, word, left, right;
-    int fl, ll, wl, lal, ral, err;
-
-    if (!*s)
-	return NULL;
-
-    while (*s) {
-	while (*s && inblank(*s)) s++;
-
-	if (!*s) break;
-
-	switch (*s) {
-	case 'l': fl = CMF_LEFT; break;
-	case 'r': fl = CMF_RIGHT; break;
-	case 'm': fl = 0; break;
-	case 'L': fl = CMF_LEFT | CMF_LINE; break;
-	case 'R': fl = CMF_RIGHT | CMF_LINE; break;
-	case 'M': fl = CMF_LINE; break;
-	default:
-	    zwarnnam(name, "unknown match specification character `%c'", NULL, *s);
-	    return pcm_err;
-	}
-	if (s[1] != ':') {
-	    zwarnnam(name, "missing `:'", NULL, 0);
-	    return pcm_err;
-	}
-	s += 2;
-	if (!*s) {
-	    zwarnnam(name, "missing patterns", NULL, 0);
-	    return pcm_err;
-	}
-	if (fl & CMF_LEFT) {
-	    left = parse_pattern(name, &s, &lal, '|', &err);
-	    if (err)
-		return pcm_err;
-	    if (!*s || !*++s) {
-		zwarnnam(name, "missing line pattern", NULL, 0);
-		return pcm_err;
-	    }
-	} else
-	    left = NULL;
-
-	line = parse_pattern(name, &s, &ll, ((fl & CMF_RIGHT) ? '|' : '='),
-			     &err);
-	if (err)
-	    return pcm_err;
-	if ((fl & CMF_RIGHT) && (!*s || !*++s)) {
-	    zwarnnam(name, "missing right anchor", NULL, 0);
-	} else if (!(fl & CMF_RIGHT)) {
-	    if (!*s) {
-		zwarnnam(name, "missing word pattern", NULL, 0);
-		return pcm_err;
-	    }
-	    s++;
-	}
-	if (fl & CMF_RIGHT) {
-	    right = parse_pattern(name, &s, &ral, '=', &err);
-	    if (err)
-		return pcm_err;
-	    if (!*s) {
-		zwarnnam(name, "missing word pattern", NULL, 0);
-		return pcm_err;
-	    }
-	    s++;
-	} else
-	    right = NULL;
-
-	if (*s == '*') {
-	    if (!(fl & (CMF_LEFT | CMF_RIGHT))) {
-		zwarnnam(name, "need anchor for `*'", NULL, 0);
-		return pcm_err;
-	    }
-	    word = NULL;
-	    wl = -1;
-	    s++;
-	} else {
-	    word = parse_pattern(name, &s, &wl, 0, &err);
-
-	    if (!word && !line) {
-		zwarnnam(name, "need non-empty word or line pattern", NULL, 0);
-		return pcm_err;
-	    }
-	}
-	if (err)
-	    return pcm_err;
-
-	n = (Cmatcher) zcalloc(sizeof(*ret));
-	n->next = NULL;
-	n->flags = fl;
-	n->line = line;
-	n->llen = ll;
-	n->word = word;
-	n->wlen = wl;
-	n->left = left;
-	n->lalen = lal;
-	n->right = right;
-	n->ralen = ral;
-
-	if (ret)
-	    r->next = n;
-	else
-	    ret = n;
-
-	r = n;
-    }
-    return ret;
-}
-
-/* Parse a pattern for matcher control. */
-
-/**/
-static Cpattern
-parse_pattern(char *name, char **sp, int *lp, char e, int *err)
-{
-    Cpattern ret = NULL, r = NULL, n;
-    unsigned char *s = (unsigned char *) *sp;
-    int l = 0;
-
-    *err = 0;
-
-    while (*s && (e ? (*s != e) : !inblank(*s))) {
-	n = (Cpattern) hcalloc(sizeof(*n));
-	n->next = NULL;
-	n->equiv = 0;
-
-	if (*s == '[') {
-	    s = parse_class(n, s + 1, ']');
-	    if (!*s) {
-		*err = 1;
-		zwarnnam(name, "unterminated character class", NULL, 0);
-		return NULL;
-	    }
-	} else if (*s == '{') {
-	    n->equiv = 1;
-	    s = parse_class(n, s + 1, '}');
-	    if (!*s) {
-		*err = 1;
-		zwarnnam(name, "unterminated character class", NULL, 0);
-		return NULL;
-	    }
-	} else if (*s == '?') {
-	    memset(n->tab, 1, 256);
-	} else if (*s == '*' || *s == '(' || *s == ')' || *s == '=') {
-	    *err = 1;
-	    zwarnnam(name, "invalid pattern character `%c'", NULL, *s);
-	    return NULL;
-	} else {
-	    if (*s == '\\' && s[1])
-		s++;
-
-	    memset(n->tab, 0, 256);
-	    n->tab[*s] = 1;
-	}
-	if (ret)
-	    r->next = n;
-	else
-	    ret = n;
-
-	r = n;
-
-	l++;
-	s++;
-    }
-    *sp = (char *) s;
-    *lp = l;
-    return ret;
-}
-
-/* Parse a character class for matcher control. */
-
-/**/
-static unsigned char *
-parse_class(Cpattern p, unsigned char *s, unsigned char e)
-{
-    int n = 0, i = 1, j, eq = (e == '}'), k = 1;
-
-    if (!eq && (*s == '!' || *s == '^') && s[1] != e) { n = 1; s++; }
-
-    memset(p->tab, n, 256);
-
-    n = !n;
-    while (*s && (k || *s != e)) {
-	if (s[1] == '-' && s[2] != e) {
-	    /* a run of characters */
-	    for (j = (int) *s; j <= (int) s[2]; j++)
-		p->tab[j] = (eq ? i++ : n);
-
-	    s += 3;
-	} else
-	    p->tab[*s++] = (eq ? i++ : n);
-	k = 0;
-    }
-    return s;
-}
-
 /* Parse the basic flags for `compctl' */
 
 /**/
@@ -599,19 +527,6 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
 		    *argv = "" - 1;
 		}
 		break;
-	    case 'i':
-		if ((*argv)[1]) {
-		    cct.widget = (*argv) + 1;
-		    *argv = "" - 1;
-		} else if (!argv[1]) {
-		    zwarnnam(name, "function name expected after -%c", NULL,
-			    **argv);
-		    return 1;
-		} else {
-		    cct.widget = *++argv;
-		    *argv = "" - 1;
-		}
-		break;
 	    case 'Y':
 		cct.mask |= CC_EXPANDEXPL;
 		goto expl;
@@ -1260,7 +1175,6 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
     zsfree(cc->glob);
     zsfree(cc->str);
     zsfree(cc->func);
-    zsfree(cc->widget);
     zsfree(cc->explain);
     zsfree(cc->ylist);
     zsfree(cc->prefix);
@@ -1282,7 +1196,6 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
     cc->glob = ztrdup(cct->glob);
     cc->str = ztrdup(cct->str);
     cc->func = ztrdup(cct->func);
-    cc->widget = ztrdup(cct->widget);
     cc->explain = ztrdup(cct->explain);
     cc->ylist = ztrdup(cct->ylist);
     cc->prefix = ztrdup(cct->prefix);
@@ -1494,7 +1407,6 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
 	printif(cc->gname, 'J');
     printif(cc->keyvar, 'k');
     printif(cc->func, 'K');
-    printif(cc->widget, 'i');
     printif(cc->explain, (cc->mask & CC_EXPANDEXPL) ? 'Y' : 'X');
     printif(cc->ylist, 'y');
     printif(cc->prefix, 'P');
@@ -1716,955 +1628,2094 @@ bin_compctl(char *name, char **argv, char *ops, int func)
     return ret;
 }
 
-/**/
-static int
-bin_compgen(char *name, char **argv, char *ops, int func)
-{
-    Compctl cc;
-    int ret = 0;
-
-    if (incompfunc != 1) {
-	zerrnam(name, "can only be called from completion function", NULL, 0);
-	return 1;
-    }
-    cc = (Compctl) zcalloc(sizeof(*cc));
-    cclist = 0;
-    showmask = 0;
+/* Flags for makecomplist*(). Things not to do. */
 
-    if (get_compctl(name, &argv, cc, 1, 0, 1))
-	ret = 1;
-    else if (*argv) {
-	zerrnam(name, "command names illegal", NULL, 0);
-	ret = 1;
-    } else
-	ret = makecomplistcallptr(cc);
+#define CFN_FIRST   1
+#define CFN_DEFAULT 2
 
-    freecompctl(cc);
-    return ret;
-}
-
-/**/
 static int
-bin_compadd(char *name, char **argv, char *ops, int func)
+bin_compcall(char *name, char **argv, char *ops, int func)
 {
-    struct cadata dat;
-    char *p, **sp, *e, *m = NULL;
-    int dm;
-    Cmatcher match = NULL;
-
     if (incompfunc != 1) {
 	zerrnam(name, "can only be called from completion function", NULL, 0);
 	return 1;
     }
-    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.match = NULL;
-    dat.flags = 0;
-    dat.aflags = CAF_MATCH;
-
-    for (; *argv && **argv ==  '-'; argv++) {
-	if (!(*argv)[1]) {
-	    argv++;
-	    break;
-	}
-	for (p = *argv + 1; *p; p++) {
-	    sp = NULL;
-	    e = NULL;
-	    dm = 0;
-	    switch (*p) {
-	    case 'q':
-		dat.flags |= CMF_REMOVE;
-		break;
-	    case 'Q':
-		dat.aflags |= CAF_QUOTE;
-		break;
-	    case 'f':
-		dat.flags |= CMF_FILE;
-		break;
-	    case 'e':
-		dat.flags |= CMF_ISPAR;
-		break;
-	    case 'F':
-		sp = &(dat.ign);
-		e = "string expected after -%c";
-		break;
-	    case 'n':
-		dat.flags |= CMF_NOLIST;
-		break;
-	    case 'U':
-		dat.aflags &= ~CAF_MATCH;
-		break;
-	    case 'P':
-		sp = &(dat.pre);
-		e = "string expected after -%c";
-		break;
-	    case 'S':
-		sp = &(dat.suf);
-		e = "string expected after -%c";
-		break;
-	    case 'J':
-		sp = &(dat.group);
-		e = "group name expected after -%c";
-		break;
-	    case 'V':
-		if (!dat.group)
-		    dat.aflags |= CAF_NOSORT;
-		sp = &(dat.group);
-		e = "group name expected after -%c";
-		break;
-	    case '1':
-		if (!(dat.aflags & CAF_UNIQCON))
-		    dat.aflags |= CAF_UNIQALL;
-		break;
-	    case '2':
-		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";
-		break;
-	    case 'I':
-		sp = &(dat.isuf);
-		e = "string expected after -%c";
-		break;
-	    case 'p':
-		sp = &(dat.ppre);
-		e = "string expected after -%c";
-		break;
-	    case 's':
-		sp = &(dat.psuf);
-		e = "string expected after -%c";
-		break;
-	    case 'W':
-		sp = &(dat.prpre);
-		e = "string expected after -%c";
-		break;
-	    case 'a':
-		dat.aflags |= CAF_ALT;
-		break;
-	    case 'M':
-		sp = &m;
-		e = "matching specification expected after -%c";
-		dm = 1;
-		break;
-	    case 'X':
-		sp = &(dat.exp);
-		e = "string expected after -%c";
-		break;
-	    case 'r':
-		dat.flags |= CMF_REMOVE;
-		sp = &(dat.rems);
-		e = "string expected after -%c";
-		break;
-	    case 'R':
-		dat.flags |= CMF_REMOVE;
-		sp = &(dat.remf);
-		e = "function name expected after -%c";
-		break;
-	    case 'A':
-		sp = &(dat.apar);
-		e = "parameter name expected after -%c";
-		break;
-	    case 'O':
-		sp = &(dat.opar);
-		e = "parameter name expected after -%c";
-		break;
-	    case 'D':
-		sp = &(dat.dpar);
-		e = "parameter name expected after -%c";
-		break;
-	    case 'd':
-		sp = &(dat.disp);
-		e = "parameter name expected after -%c";
-		break;
-	    case 'l':
-		dat.flags |= CMF_DISPLINE;
-		break;
-	    case '-':
-		argv++;
-		goto ca_args;
-	    default:
-		zerrnam(name, "bad option: -%c", NULL, *p);
-		return 1;
-	    }
-	    if (sp) {
-		if (p[1]) {
-		    if (!*sp)
-			*sp = p + 1;
-		    p = "" - 1;
-		} else if (argv[1]) {
-		    argv++;
-		    if (!*sp)
-			*sp = *argv;
-		    p = "" - 1;
-		} else {
-		    zerrnam(name, e, NULL, *p);
-		    return 1;
-		}
-		if (dm && (match = parse_cmatcher(name, m)) == pcm_err) {
-		    match = NULL;
-		    return 1;
-		}
-	    }
-	}
-    }
- ca_args:
-    if (!*argv)
-	return 1;
-
-    dat.match = match = cpcmatcher(match);
-    dm = addmatchesptr(&dat, argv);
-    freecmatcher(match);
-
-    return dm;
+    return makecomplistctl((ops['T'] ? 0 : CFN_FIRST) |
+			   (ops['D'] ? 0 : CFN_DEFAULT));
 }
 
-#define CVT_RANGENUM 0
-#define CVT_RANGEPAT 1
-#define CVT_PRENUM   2
-#define CVT_PREPAT   3
-#define CVT_SUFNUM   4
-#define CVT_SUFPAT   5
-
-/**/
-void
-ignore_prefix(int l)
-{
-    if (l) {
-	char *tmp, sav;
-	int pl = strlen(compprefix);
+/*
+ * Functions to generate matches.
+ */
 
-	if (l > pl)
-	    l = pl;
+/* A pointer to the compctl we are using. */
 
-	sav = compprefix[l];
+static Compctl curcc;
 
-	compprefix[l] = '\0';
-	tmp = tricat(compiprefix, compprefix, "");
-	zsfree(compiprefix);
-	compiprefix = tmp;
-	compprefix[l] = sav;
-	tmp = ztrdup(compprefix + l);
-	zsfree(compprefix);
-	compprefix = tmp;
-    }
-}
+/* A list of all compctls we have already used. */
 
-/**/
-void
-ignore_suffix(int l)
-{
-    if (l) {
-	char *tmp, sav;
-	int sl = strlen(compsuffix);
-
-	if ((l = sl - l) < 0)
-	    l = 0;
-
-	tmp = tricat(compsuffix + l, compisuffix, "");
-	zsfree(compisuffix);
-	compisuffix = tmp;
-	sav = compsuffix[l];
-	compsuffix[l] = '\0';
-	tmp = ztrdup(compsuffix);
-	compsuffix[l] = sav;
-	zsfree(compsuffix);
-	compsuffix = tmp;
-    }
-}
+static LinkList ccused, lastccused;
 
-/**/
-void
-restrict_range(int b, int e)
-{
-    int wl = arrlen(compwords) - 1;
+/* A stack of currently used compctls. */
 
-    if (wl && b >= 0 && e >= 0 && (b > 0 || e < wl)) {
-	int i;
-	char **p, **q, **pp;
+static LinkList ccstack;
 
-	if (e > wl)
-	    e = wl;
+/* The beginning and end of a word range to be used by -l. */
 
-	i = e - b + 1;
-	p = (char **) zcalloc((i + 1) * sizeof(char *));
+static int brange, erange;
 
-	for (q = p, pp = compwords + b; i; i--, q++, pp++)
-	    *q = ztrdup(*pp);
-	freearray(compwords);
-	compwords = p;
-	compcurrent -= b;
-    }
-}
+/* This is used to detect when and what to continue. */
 
-static int
-do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
-{
-    switch (test) {
-    case CVT_RANGENUM:
-	{
-	    int l = arrlen(compwords);
+static unsigned long ccont;
 
-	    if (na < 0)
-		na += l;
-	    else
-		na--;
-	    if (nb < 0)
-		nb += l;
-	    else
-		nb--;
+/* Two patterns used when doing glob-completion.  The first one is built *
+ * from the whole word we are completing and the second one from that    *
+ * part of the word that was identified as a possible filename.          */
 
-	    if (compcurrent - 1 < na || compcurrent - 1 > nb)
-		return 0;
-	    if (mod)
-		restrict_range(na, nb);
-	    return 1;
-	}
-    case CVT_RANGEPAT:
-	{
-	    char **p;
-	    int i, l = arrlen(compwords), t = 0, b = 0, e = l - 1;
-	    Patprog pp;
+static Patprog patcomp, filecomp;
 
-	    i = compcurrent - 1;
-	    if (i < 0 || i >= l)
-		return 0;
+/* We store the following prefixes/suffixes:                               *
+ * lpre/lsuf -- what's on the line                                         *
+ * rpre/rsuf -- same as lpre/lsuf, but expanded                            *
+ * ppre/psuf   -- the path prefix/suffix                                   *
+ * lppre/lpsuf -- the path prefix/suffix, unexpanded                       *
+ * fpre/fsuf   -- prefix/suffix of the pathname component the cursor is in *
+ * prpre       -- ppre in expanded form usable for opendir                 *
+ * qipre, qisuf-- ingnored quoted string                                   *
+ *                                                                         *
+ * The integer variables hold the lengths of lpre, lsuf, rpre, rsuf,       *
+ * fpre, fsuf, lppre, and lpsuf.  noreal is non-zero if we have rpre/rsuf. */
 
-	    singsub(&sa);
-	    pp = patcompile(sa, PAT_STATIC, NULL);
+static char *lpre, *lsuf;
+static char *rpre, *rsuf;
+static char *ppre, *psuf, *lppre, *lpsuf, *prpre;
+static char *fpre, *fsuf;
+static char *qfpre, *qfsuf, *qrpre, *qrsuf, *qlpre, *qlsuf;
+static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl;
+static int noreal;
 
-	    for (i--, p = compwords + i; i >= 0; p--, i--) {
-		if (pattry(pp, *p)) {
-		    b = i + 1;
-		    t = 1;
-		    break;
-		}
-	    }
-	    if (t && sb) {
-		int tt = 0;
+/* This is either zero or equal to the special character the word we are *
+ * trying to complete starts with (e.g. Tilde or Equals).                */
 
-		singsub(&sb);
-		pp = patcompile(sb, PAT_STATIC, NULL);
+static char ic;
 
-		for (i++, p = compwords + i; i < l; p++, i++) {
-		    if (pattry(pp, *p)) {
-			e = i - 1;
-			tt = 1;
-			break;
-		    }
-		}
-		if (tt && i < compcurrent)
-		    t = 0;
-	    }
-	    if (e < b)
-		t = 0;
-	    if (t && mod)
-		restrict_range(b, e);
-	    return t;
-	}
-    case CVT_PRENUM:
-    case CVT_SUFNUM:
-	if (!na)
-	    return 1;
-	if (na > 0 &&
-	    strlen(test == CVT_PRENUM ? compprefix : compsuffix) >= na) {
-	    if (mod) {
-		if (test == CVT_PRENUM)
-		    ignore_prefix(na);
-		else
-		    ignore_suffix(na);
-		return 1;
-	    }
-	    return 0;
-	}
-    case CVT_PREPAT:
-    case CVT_SUFPAT:
-	{
-	    Patprog pp;
+/* This variable says what we are currently adding to the list of matches. */
 
-	    if (!na)
-		return 0;
+static int addwhat;
 
-	    if (!(pp = patcompile(sa, PAT_STATIC, 0)))
-		return 0;
+/* Convenience macro for calling bslashquote() (formerly quotename()). *
+ * This uses the instring variable above.                              */
 
-	    if (test == CVT_PREPAT) {
-		int l, add;
-		char *p, sav;
+#define quotename(s, e) bslashquote(s, e, instring)
 
-		if (!(l = strlen(compprefix)))
-		    return 0;
-		if (na < 0) {
-		    p = compprefix + l;
-		    na = -na;
-		    add = -1;
-		} else {
-		    p = compprefix + 1;
-		    add = 1;
-		}
-		for (; l; l--, p += add) {
-		    sav = *p;
-		    *p = '\0';
-		    test = pattry(pp, compprefix);
-		    *p = sav;
-		    if (test && !--na)
-			break;
-		}
-		if (!l)
-		    return 0;
-		if (mod)
-		    ignore_prefix(p - compprefix);
-	    } else {
-		int l, ol, add;
-		char *p;
+/* Hook functions */
 
-		if (!(ol = l = strlen(compsuffix)))
-		    return 0;
-		if (na < 0) {
-		    p = compsuffix;
-		    na = -na;
-		    add = 1;
-		} else {
-		    p = compsuffix + l - 1;
-		    add = -1;
-		}
-		for (; l; l--, p += add)
-		    if (pattry(pp, p) && !--na)
-			break;
+static int
+ccmakehookfn(Hookdef dummy, struct ccmakedat *dat)
+{
+    makecomplistglobal(dat->str, dat->incmd, dat->lst, 0);
 
-		if (!l)
-		    return 0;
-		if (mod)
-		    ignore_suffix(ol - (p - compsuffix));
-	    }
-	    return 1;
-	}
-    }
     return 0;
 }
 
-/**/
 static int
-bin_compset(char *name, char **argv, char *ops, int func)
+ccbeforehookfn(Hookdef dummy, void *zdup)
 {
-    int test = 0, na = 0, nb = 0;
-    char *sa = NULL, *sb = NULL;
+    ccused = newlinklist();
+    ccstack = newlinklist();
 
-    if (incompfunc != 1) {
-	zerrnam(name, "can only be called from completion function", NULL, 0);
-	return 1;
-    }
-    if (argv[0][0] != '-') {
-	zerrnam(name, "missing option", NULL, 0);
-	return 1;
-    }
-    switch (argv[0][1]) {
-    case 'n': test = CVT_RANGENUM; break;
-    case 'N': test = CVT_RANGEPAT; break;
-    case 'p': test = CVT_PRENUM; break;
-    case 'P': test = CVT_PREPAT; break;
-    case 's': test = CVT_SUFNUM; break;
-    case 'S': test = CVT_SUFPAT; break;
-    case 'q': return set_comp_sepptr();
-    default:
-	zerrnam(name, "bad option -%c", NULL, argv[0][1]);
-	return 1;
-    }
-    if (argv[0][2]) {
-	sa = argv[0] + 2;
-	sb = argv[1];
-	na = 2;
-    } else {
-	if (!(sa = argv[1])) {
-	    zerrnam(name, "missing string for option -%c", NULL, argv[0][1]);
-	    return 1;
-	}
-	sb = argv[2];
-	na = 3;
-    }
-    if (((test == CVT_PRENUM || test == CVT_SUFNUM) ? !!sb :
-	 (sb && argv[na]))) {
-	zerrnam(name, "too many arguments", NULL, 0);
-	return 1;
-    }
-    switch (test) {
-    case CVT_RANGENUM:
-	na = atoi(sa);
-	nb = (sb ? atoi(sb) : -1);
-	break;
-    case CVT_RANGEPAT:
-	tokenize(sa);
-	sa = rembslash(sa);
-	remnulargs(sa);
-	if (sb) {
-	    tokenize(sb);
-	    sb = rembslash(sb);
-	    remnulargs(sb);
-	}
-	break;
-    case CVT_PRENUM:
-    case CVT_SUFNUM:
-	na = atoi(sa);
-	break;
-    case CVT_PREPAT:
-    case CVT_SUFPAT:
-	if (sb) {
-	    na = atoi(sa);
-	    sa = sb;
-	} else
-	    na = -1;
-	tokenize(sa);
-	sa = rembslash(sa);
-	remnulargs(sa);
-	break;
-    }
-    return !do_comp_vars(test, na, sa, nb, sb, 1);
+    return 0;
 }
 
-/**/
 static int
-bin_compcall(char *name, char **argv, char *ops, int func)
+ccafterhookfn(Hookdef dummy, void *zdup)
 {
-    if (incompfunc != 1) {
-	zerrnam(name, "can only be called from completion function", NULL, 0);
-	return 1;
-    }
-    return makecomplistctlptr((ops['T'] ? 0 : CFN_FIRST) |
-			      (ops['D'] ? 0 : CFN_DEFAULT));
-}
+    LinkNode n;
 
-/* Definitions for the special parameters. Note that these have to match the
- * order of the CP_* bits in comp.h */
+    if (zdup) {
+	if (lastccused)
+	    freelinklist(lastccused, (FreeFunc) freecompctl);
 
-#define VAL(X) ((void *) (&(X)))
-struct compparam {
-    char *name;
-    int type;
-    void *var, *set, *get;
-};
-
-static struct compparam comprparams[] = {
-    { "words", PM_ARRAY, VAL(compwords), NULL, NULL },
-    { "CURRENT", PM_INTEGER, VAL(compcurrent), NULL, NULL },
-    { "PREFIX", PM_SCALAR, VAL(compprefix), NULL, NULL },
-    { "SUFFIX", PM_SCALAR, VAL(compsuffix), NULL, NULL },
-    { "IPREFIX", PM_SCALAR, VAL(compiprefix), NULL, NULL },
-    { "ISUFFIX", PM_SCALAR, VAL(compisuffix), NULL, NULL },
-    { "QIPREFIX", PM_SCALAR | PM_READONLY, VAL(compqiprefix), NULL, NULL },
-    { "QISUFFIX", PM_SCALAR | PM_READONLY, VAL(compqisuffix), NULL, NULL },
-    { NULL, 0, NULL, NULL, NULL }
-};
+	PERMALLOC {
+	    lastccused = newlinklist();
+	    for (n = firstnode(ccused); n; incnode(n))
+		addlinknode(lastccused, getdata(n));
+	} LASTALLOC;
+    } else
+	for (n = firstnode(ccused); n; incnode(n))
+	    if (((Compctl) getdata(n)) != &cc_dummy)
+		freecompctl((Compctl) getdata(n));
 
-static struct compparam compkparams[] = {
-    { "nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_nmatches) },
-    { "matcher", PM_INTEGER, VAL(compmatcher), NULL, NULL },
-    { "matcher_string", PM_SCALAR, VAL(compmatcherstr), NULL, NULL },
-    { "total_matchers", PM_INTEGER, VAL(compmatchertot), NULL, NULL },
-    { "context", PM_SCALAR, VAL(compcontext), NULL, NULL },
-    { "parameter", PM_SCALAR, VAL(compparameter), NULL, NULL },
-    { "redirect", PM_SCALAR, VAL(compredirect), NULL, NULL },
-    { "quote", PM_SCALAR | PM_READONLY, VAL(compquote), NULL, NULL },
-    { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL, NULL },
-    { "restore", PM_SCALAR, VAL(comprestore), NULL, NULL },
-    { "list", PM_SCALAR, NULL, VAL(set_complist), VAL(get_complist) },
-    { "force_list", PM_SCALAR, VAL(compforcelist), NULL, NULL },
-    { "insert", PM_SCALAR, VAL(compinsert), NULL, NULL },
-    { "exact", PM_SCALAR, VAL(compexact), NULL, NULL },
-    { "exact_string", PM_SCALAR, VAL(compexactstr), NULL, NULL },
-    { "pattern_match", PM_SCALAR, VAL(comppatmatch), NULL, NULL },
-    { "pattern_insert", PM_SCALAR, VAL(comppatinsert), NULL, NULL },
-    { "unambiguous", PM_SCALAR | PM_READONLY, NULL, NULL, VAL(get_unambig) },
-    { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL, NULL,
-      VAL(get_unambig_curs) },
-    { "list_max", PM_INTEGER, VAL(complistmax), NULL, NULL },
-    { "last_prompt", PM_SCALAR, VAL(complastprompt), NULL, NULL },
-    { "to_end", PM_SCALAR, VAL(comptoend), NULL, NULL },
-    { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL },
-    { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL },
-    { "vared", PM_SCALAR, VAL(compvared), NULL, NULL },
-    { "alternate_nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_anmatches) },
-    { "list_lines", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_listlines) },
-    { NULL, 0, NULL, NULL, NULL }
-};
+    return 0;
+}
 
-#define COMPSTATENAME "compstate"
+/* This adds a match to the list of matches.  The string to add is given   *
+ * in s, the type of match is given in the global variable addwhat and     *
+ * the parameter t (if not NULL) is a pointer to a hash node node which    *
+ * may be used to give other information to this function.                 *
+ *                                                                         *
+ * addwhat contains either one of the special values (negative, see below) *
+ * or the inclusive OR of some of the CC_* flags used for compctls.        */
 
+/**/
 static void
-addcompparams(struct compparam *cp, Param *pp)
+addmatch(char *s, char *t)
 {
-    for (; cp->name; cp++, pp++) {
-	Param pm = createparam(cp->name,
-			       cp->type |PM_SPECIAL|PM_REMOVABLE|PM_LOCAL);
-	if (!pm)
-	    pm = (Param) paramtab->getnode(paramtab, cp->name);
-	DPUTS(!pm, "param not set in addcompparams");
-
-	*pp = pm;
-	pm->level = locallevel + 1;
-	if ((pm->u.data = cp->var)) {
-	    switch(PM_TYPE(cp->type)) {
-	    case PM_SCALAR:
-		pm->sets.cfn = strvarsetfn;
-		pm->gets.cfn = strvargetfn;
-		break;
-	    case PM_INTEGER:
-		pm->sets.ifn = intvarsetfn;
-		pm->gets.ifn = intvargetfn;
-		pm->ct = 10;
-		break;
-	    case PM_ARRAY:
-		pm->sets.afn = arrvarsetfn;
-		pm->gets.afn = arrvargetfn;
-		break;
-	    }
+    int isfile = 0, isalt = 0, isexact;
+    char *ms = NULL, *tt;
+    HashNode hn;
+    Param pm;
+    Cline lc = NULL;
+    Brinfo bp, bpl = brbeg, bsl = brend, bpt, bst;
+
+    for (bp = brbeg; bp; bp = bp->next)
+	bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos);
+    for (bp = brend; bp; bp = bp->next)
+	bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos);
+
+    /*
+     * addwhat: -5 is for files,
+     *          -6 is for glob expansions,
+     *          -8 is for executable files (e.g. command paths),
+     *          -9 is for parameters
+     *          -7 is for command names (from cmdnamtab)
+     *          -4 is for a cdable parameter
+     *          -3 is for executable command names.
+     *          -2 is for anything unquoted
+     *          -1 is for other file specifications
+     *          (things with `~' or `=' at the beginning, ...).
+     */
+
+    /* Just to make the code cleaner */
+    hn = (HashNode) t;
+    pm = (Param) t;
+
+    if (addwhat == -1 || addwhat == -5 || addwhat == -6 ||
+	addwhat == CC_FILES || addwhat == -7 || addwhat == -8) {
+	int ppl = (ppre ? strlen(ppre) : 0), psl = (psuf ? strlen(psuf) : 0);
+
+	while (bpl && bpl->curpos < ppl)
+	    bpl = bpl->next;
+	while (bsl && bsl->curpos < psl)
+	    bsl = bsl->next;
+
+	if ((addwhat == CC_FILES ||
+	     addwhat == -5) && !*psuf) {
+	    /* If this is a filename, do the fignore check. */
+	    char **pt = fignore;
+	    int filell, sl = strlen(s);
+
+	    for (isalt = 0; !isalt && *pt; pt++)
+		if ((filell = strlen(*pt)) < sl &&
+		    !strcmp(*pt, s + sl - filell))
+		    isalt = 1;
+	}
+	ms = ((addwhat == CC_FILES || addwhat == -6 ||
+	       addwhat == -5 || addwhat == -8) ? 
+	      comp_match(qfpre, qfsuf, s, filecomp, &lc, (ppre && *ppre ? 1 : 2),
+			 &bpl, ppl ,&bsl, psl, &isexact) :
+	      comp_match(fpre, fsuf, s, filecomp, &lc, 0,
+			 &bpl, ppl, &bsl, psl, &isexact));
+	if (!ms)
+	    return;
+
+	if (addwhat == -7 && !findcmd(s, 0))
+	    return;
+	isfile = CMF_FILE;
+    } else if (addwhat == CC_QUOTEFLAG || addwhat == -2  ||
+	      (addwhat == -3 && !(hn->flags & DISABLED)) ||
+	      (addwhat == -4 && (PM_TYPE(pm->flags) == PM_SCALAR) &&
+	       !pm->level && (tt = pm->gets.cfn(pm)) && *tt == '/')    ||
+	      (addwhat == -9 && !(hn->flags & PM_UNSET) && !pm->level) ||
+	      (addwhat > 0 &&
+	       ((!(hn->flags & PM_UNSET) &&
+		 (((addwhat & CC_ARRAYS)    &&  (hn->flags & PM_ARRAY))    ||
+		  ((addwhat & CC_INTVARS)   &&  (hn->flags & PM_INTEGER))  ||
+		  ((addwhat & CC_ENVVARS)   &&  (hn->flags & PM_EXPORTED)) ||
+		  ((addwhat & CC_SCALARS)   &&  (hn->flags & PM_SCALAR))   ||
+		  ((addwhat & CC_READONLYS) &&  (hn->flags & PM_READONLY)) ||
+		  ((addwhat & CC_SPECIALS)  &&  (hn->flags & PM_SPECIAL))  ||
+		  ((addwhat & CC_PARAMS)    && !(hn->flags & PM_EXPORTED))) &&
+		 !pm->level) ||
+		((( addwhat & CC_SHFUNCS)				  ||
+		  ( addwhat & CC_BUILTINS)				  ||
+		  ( addwhat & CC_EXTCMDS)				  ||
+		  ( addwhat & CC_RESWDS)				  ||
+		  ((addwhat & CC_ALREG)   && !(hn->flags & ALIAS_GLOBAL)) ||
+		  ((addwhat & CC_ALGLOB)  &&  (hn->flags & ALIAS_GLOBAL))) &&
+		 (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) ||
+		  ((addwhat & CC_EXCMDS)  && !(hn->flags & DISABLED)))) ||
+		((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) {
+	char *p1, *s1, *p2, *s2;
+
+	if (addwhat == CC_QUOTEFLAG) {
+	    p1 = qrpre; s1 = qrsuf;
+	    p2 = rpre;  s2 = rsuf;
 	} else {
-	    pm->sets.cfn = (void (*) _((Param, char *))) cp->set;
-	    pm->gets.cfn = (char *(*) _((Param))) cp->get;
+	    p1 = qlpre; s1 = qlsuf;
+	    p2 = lpre;  s2 = lsuf;
+	}
+	bpt = bpl;
+	bst = bsl;
+
+	if (!(ms = comp_match(p1, s1, s, patcomp, &lc,
+			      (addwhat == CC_QUOTEFLAG),
+			      &bpl, strlen(p1), &bsl, strlen(s1),
+			      &isexact))) {
+	    bpl = bpt;
+	    bsl = bst;
+	    if (!(ms = comp_match(p2, s2, s, NULL, &lc,
+				  (addwhat == CC_QUOTEFLAG),
+				  &bpl, strlen(p2), &bsl, strlen(s2),
+				  &isexact)))
+		return;
 	}
-	pm->unsetfn = compunsetfn;
     }
+    if (!ms)
+	return;
+    add_match_data(isalt, ms, lc, ipre, ripre, isuf, 
+		   (incompfunc ? dupstring(curcc->prefix) : curcc->prefix),
+		   prpre, 
+		   (isfile ? lppre : NULL), NULL,
+		   (isfile ? lpsuf : NULL), NULL,
+		   (incompfunc ? dupstring(curcc->suffix) : curcc->suffix),
+		   (mflags | isfile), isexact);
 }
 
 /**/
-void
-makecompparams(void)
+static void
+maketildelist(void)
 {
-    Param cpm;
-    HashTable tht;
-
-    addcompparams(comprparams, comprpms);
-
-    if (!(cpm = createparam(COMPSTATENAME,
-			    PM_SPECIAL|PM_REMOVABLE|PM_LOCAL|PM_HASHED)))
-	cpm = (Param) paramtab->getnode(paramtab, COMPSTATENAME);
-    DPUTS(!cpm, "param not set in makecompparams");
-
-    comprpms[CPN_COMPSTATE] = cpm;
-    tht = paramtab;
-    cpm->level = locallevel;
-    cpm->gets.hfn = get_compstate;
-    cpm->sets.hfn = set_compstate;
-    cpm->unsetfn = compunsetfn;
-    cpm->u.hash = paramtab = newparamtable(31, COMPSTATENAME);
-    addcompparams(compkparams, compkpms);
-    paramtab = tht;
+    /* add all the usernames to the named directory table */
+    nameddirtab->filltable(nameddirtab);
+
+    scanhashtable(nameddirtab, 0, (addwhat==-1) ? 0 : ND_USERNAME, 0,
+		  addhnmatch, 0);
 }
 
+/* This does the check for compctl -x `n' and `N' patterns. */
+
 /**/
-static HashTable
-get_compstate(Param pm)
+int
+getcpat(char *str, int cpatindex, char *cpat, int class)
 {
-    return pm->u.hash;
+    char *s, *t, *p;
+    int d = 0;
+
+    if (!str || !*str)
+	return -1;
+
+    cpat = rembslash(cpat);
+
+    if (!cpatindex)
+	cpatindex++, d = 0;
+    else if ((d = (cpatindex < 0)))
+	cpatindex = -cpatindex;
+
+    for (s = d ? str + strlen(str) - 1 : str;
+	 d ? (s >= str) : *s;
+	 d ? s-- : s++) {
+	for (t = s, p = cpat; *t && *p; p++) {
+	    if (class) {
+		if (*p == *s && !--cpatindex)
+		    return (int)(s - str + 1);
+	    } else if (*t++ != *p)
+		break;
+	}
+	if (!class && !*p && !--cpatindex)
+	    return t - str;
+    }
+    return -1;
 }
 
+/* Dump a hash table (without sorting).  For each element the addmatch  *
+ * function is called and at the beginning the addwhat variable is set. *
+ * This could be done using scanhashtable(), but this is easy and much  *
+ * more efficient.                                                      */
+
 /**/
 static void
-set_compstate(Param pm, HashTable ht)
+dumphashtable(HashTable ht, int what)
 {
-    struct compparam *cp;
-    Param *pp;
     HashNode hn;
     int i;
-    struct value v;
-    char *str;
+
+    addwhat = what;
 
     for (i = 0; i < ht->hsize; i++)
 	for (hn = ht->nodes[i]; hn; hn = hn->next)
-	    for (cp = compkparams,
-		 pp = compkpms; cp->name; cp++, pp++)
-		if (!strcmp(hn->nam, cp->name)) {
-		    v.isarr = v.inv = v.a = 0;
-		    v.b = -1;
-		    v.arr = NULL;
-		    v.pm = (Param) hn;
-		    if (cp->type == PM_INTEGER)
-			*((zlong *) cp->var) = getintvalue(&v);
-		    else if ((str = getstrvalue(&v))) {
-			zsfree(*((char **) cp->var));
-			*((char **) cp->var) = ztrdup(str);
-		    }
-		    (*pp)->flags &= ~PM_UNSET;
-
-		    break;
-		}
-    deleteparamtable(ht);
+	    addmatch(hn->nam, (char *) hn);
 }
 
-/**/
-static zlong
-get_nmatches(Param pm)
-{
-    return num_matchesptr(1);
-}
+/* ScanFunc used by maketildelist() et al. */
 
 /**/
-static zlong
-get_anmatches(Param pm)
+static void
+addhnmatch(HashNode hn, int flags)
 {
-    return num_matchesptr(0);
+    addmatch(hn->nam, NULL);
 }
 
+/* Perform expansion on the given string and return the result. *
+ * During this errors are not reported.                         */
+
 /**/
-static zlong
-get_listlines(Param pm)
+static char *
+getreal(char *str)
 {
-    return list_linesptr();
+    LinkList l = newlinklist();
+    int ne = noerrs;
+
+    noerrs = 1;
+    addlinknode(l, dupstring(str));
+    prefork(l, 0);
+    noerrs = ne;
+    if (!errflag && nonempty(l) &&
+	((char *) peekfirst(l)) && ((char *) peekfirst(l))[0])
+	return dupstring(peekfirst(l));
+    errflag = 0;
+
+    return dupstring(str);
 }
 
+/* This reads a directory and adds the files to the list of  *
+ * matches.  The parameters say which files should be added. */
+
 /**/
 static void
-set_complist(Param pm, char *v)
+gen_matches_files(int dirs, int execs, int all)
 {
-    comp_listptr(v);
+    DIR *d;
+    struct stat buf;
+    char *n, p[PATH_MAX], *q = NULL, *e;
+    LinkList l = NULL;
+    int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat;
+
+    opts[NULLGLOB] = 1;
+
+    if (*psuf) {
+	/* If there is a path suffix, check if it doesn't have a `*' or *
+	 * `)' at the end (this is used to determine if we should use   *
+	 * globbing).                                                   */
+	q = psuf + strlen(psuf) - 1;
+	ns = !(*q == Star || *q == Outpar);
+	l = newlinklist();
+	/* And generate only directory names. */
+	dirs = 1;
+	all = execs = 0;
+    }
+    /* Open directory. */
+    if ((d = opendir((prpre && *prpre) ? prpre : "."))) {
+	/* If we search only special files, prepare a path buffer for stat. */
+	if (!all && prpre) {
+	    strcpy(p, prpre);
+	    q = p + strlen(prpre);
+	}
+	/* Fine, now read the directory. */
+	while ((n = zreaddir(d, 1)) && !errflag) {
+	    /* Ignore files beginning with `.' unless the thing we found on *
+	     * the command line also starts with a dot or GLOBDOTS is set.  */
+	    if (*n != '.' || *fpre == '.' || isset(GLOBDOTS)) {
+		addwhat = execs ? -8 : -5;
+		if (filecomp)
+		    /* If we have a pattern for the filename check, use it. */
+		    test = pattry(filecomp, n);
+		else {
+		    /* Otherwise use the prefix and suffix strings directly. */
+		    e = n + strlen(n) - fsl;
+		    if ((test = !strncmp(n, fpre, fpl)))
+			test = !strcmp(e, fsuf);
+		    if (!test && mstack) {
+			test = 1;
+			addwhat = CC_FILES;
+		    }
+		}
+		/* Filename didn't match? */
+		if (!test)
+		    continue;
+		if (!all) {
+		    /* We still have to check the file type, so prepare *
+		     * the path buffer by appending the filename.       */
+		    strcpy(q, n);
+		    /* And do the stat. */
+		    if (stat(p, &buf) < 0)
+			continue;
+		}
+		if (all ||
+		    (dirs && S_ISDIR(buf.st_mode)) ||
+		    (execs && S_ISREG(buf.st_mode) && (buf.st_mode&S_IXUGO))) {
+		    /* If we want all files or the file has the right type... */
+		    if (*psuf) {
+			/* We have to test for a path suffix. */
+			int o = strlen(p), tt;
+
+			/* Append it to the path buffer. */
+			strcpy(p + o, psuf);
+
+			/* Do we have to use globbing? */
+			if (ispattern ||
+			    (ns && comppatmatch && *comppatmatch)) {
+			    /* Yes, so append a `*' if needed. */
+			    if (ns && comppatmatch && *comppatmatch == '*') {
+				int tl = strlen(p);
+
+				p[tl] = Star;
+				p[tl + 1] = '\0';
+			    }
+			    /* Do the globbing... */
+			    remnulargs(p);
+			    addlinknode(l, p);
+			    globlist(l);
+			    /* And see if that produced a filename. */
+			    tt = nonempty(l);
+			    while (ugetnode(l));
+			} else
+			    /* Otherwise just check, if we have access *
+			     * to the file.                            */
+			    tt = !access(p, F_OK);
+
+			p[o] = '\0';
+			if (tt)
+			    /* Ok, we can add the filename to the *
+			     * list of matches.                   */
+			    addmatch(dupstring(n), NULL);
+		    } else
+			/* We want all files, so just add the name *
+			 * to the matches.                         */
+			addmatch(dupstring(n), NULL);
+		}
+	    }
+	}
+	closedir(d);
+    }
+    opts[NULLGLOB] = ng;
+    addwhat = aw;
 }
 
-/**/
-static char *
-get_complist(Param pm)
-{
-    return complist;
-}
+/* This returns the node with the given data. */
+/* ...should probably be moved to linklist.c. */
 
-/**/
-static char *
-get_unambig(Param pm)
+static LinkNode
+findnode(LinkList list, void *dat)
 {
-    return unambig_dataptr(NULL);
+    LinkNode tmp = list->first;
+
+    while (tmp && tmp->dat != dat) tmp = tmp->next;
+
+    return tmp;
 }
 
-/**/
-static zlong
-get_unambig_curs(Param pm)
-{
-    int c;
+/* A simple counter to avoid endless recursion between old and new style *
+ * completion. */
 
-    unambig_dataptr(&c);
+static int cdepth = 0;
 
-    return c;
-}
+#define MAX_CDEPTH 16
 
 /**/
-static void
-compunsetfn(Param pm, int exp)
+static int
+makecomplistctl(int flags)
 {
-    if (exp) {
-	if (PM_TYPE(pm->flags) == PM_SCALAR) {
-	    zsfree(*((char **) pm->u.data));
-	    *((char **) pm->u.data) = ztrdup("");
-	} else if (PM_TYPE(pm->flags) == PM_ARRAY) {
-	    freearray(*((char ***) pm->u.data));
-	    *((char ***) pm->u.data) = zcalloc(sizeof(char *));
-	}
-	pm->flags |= PM_UNSET;
-    }
+    int ret;
+
+    if (cdepth == MAX_CDEPTH)
+	return 0;
+
+    cdepth++;
+    SWITCHHEAPS(compheap) {
+	HEAPALLOC {
+	    int ooffs = offs, lip, lp;
+	    char *str = comp_str(&lip, &lp, 0), *t;
+	    char *os = cmdstr, **ow = clwords, **p, **q, qc;
+	    int on = clwnum, op = clwpos, ois =  instring, oib = inbackt;
+	    char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, oaq = autoq;
+
+	    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 : "");
+	    isuf = dupstring(compisuffix);
+	    ctokenize(isuf);
+	    remnulargs(isuf);
+	    clwnum = arrlen(compwords);
+	    clwpos = compcurrent - 1;
+	    cmdstr = ztrdup(compwords[0]);
+	    clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
+	    for (p = compwords, q = clwords; *p; p++, q++) {
+		t = dupstring(*p);
+		tokenize(t);
+		remnulargs(t);
+		*q = ztrdup(t);
+	    }
+	    *q = NULL;
+	    offs = lip + lp;
+	    incompfunc = 2;
+	    ret = makecomplistglobal(str, !clwpos, COMP_COMPLETE, flags);
+	    incompfunc = 1;
+	    isuf = oisuf;
+	    zsfree(qipre);
+	    zsfree(qisuf);
+	    qipre = oqp;
+	    qisuf = oqs;
+	    instring = ois;
+	    inbackt = oib;
+	    autoq = oaq;
+	    offs = ooffs;
+	    zsfree(cmdstr);
+	    freearray(clwords);
+	    cmdstr = os;
+	    clwords = ow;
+	    clwnum = on;
+	    clwpos = op;
+	} LASTALLOC;
+    } SWITCHBACKHEAPS;
+    cdepth--;
+
+    return ret;
 }
 
+/* This function gets the compctls for the given command line and *
+ * adds all completions for them. */
+
 /**/
-void
-comp_setunset(int rset, int runset, int kset, int kunset)
+static int
+makecomplistglobal(char *os, int incmd, int lst, int flags)
 {
-    Param *p;
+    Compctl cc = NULL;
+    char *s;
 
-    if (comprpms && (rset >= 0 || runset >= 0)) {
-	for (p = comprpms; rset || runset; rset >>= 1, runset >>= 1, p++) {
-	    if (rset & 1)
-		(*p)->flags &= ~PM_UNSET;
-	    if (runset & 1)
-		(*p)->flags |= PM_UNSET;
+    ccont = CC_CCCONT;
+    cc_dummy.suffix = NULL;
+
+    if (linwhat == IN_ENV) {
+        /* Default completion for parameter values. */
+	if (!(flags & CFN_DEFAULT)) {
+	    cc = &cc_default;
+	    keypm = NULL;
+	}
+    } else if (linwhat == IN_MATH) {
+	if (!(flags & CFN_DEFAULT)) {
+	    if (insubscr >= 2) {
+		/* Inside subscript of assoc array, complete keys. */
+		cc_dummy.mask = 0;
+		cc_dummy.suffix = (insubscr == 2 ? "]" : "");
+	    } else {
+		/* Other math environment, complete paramete names. */
+		keypm = NULL;
+		cc_dummy.mask = CC_PARAMS;
+	    }
+	    cc = &cc_dummy;
+	    cc_dummy.refc = 10000;
+	}
+    } else if (linwhat == IN_COND) {
+	/* We try to be clever here: in conditions we complete option   *
+	 * names after a `-o', file names after `-nt', `-ot', and `-ef' *
+	 * and file names and parameter names elsewhere.                */
+	if (!(flags & CFN_DEFAULT)) {
+	    s = clwpos ? clwords[clwpos - 1] : "";
+	    cc_dummy.mask = !strcmp("-o", s) ? CC_OPTIONS :
+		((*s == '-' && s[1] && !s[2]) ||
+		 !strcmp("-nt", s) ||
+		 !strcmp("-ot", s) ||
+		 !strcmp("-ef", s)) ? CC_FILES :
+		(CC_FILES | CC_PARAMS);
+	    cc = &cc_dummy;
+	    cc_dummy.refc = 10000;
+	    keypm = NULL;
 	}
+    } else if (linredir) {
+	if (!(flags & CFN_DEFAULT)) {
+	    /* In redirections use default completion. */
+	    cc = &cc_default;
+	    keypm = NULL;
+	}
+    } else {
+	/* Otherwise get the matches for the command. */
+	keypm = NULL;
+	return makecomplistcmd(os, incmd, flags);
     }
-    if (comprpms && (kset >= 0 || kunset >= 0)) {
-	for (p = compkpms; kset || kunset; kset >>= 1, kunset >>= 1, p++) {
-	    if (kset & 1)
-		(*p)->flags &= ~PM_UNSET;
-	    if (kunset & 1)
-		(*p)->flags |= PM_UNSET;
+    if (cc) {
+	/* First, use the -T compctl. */
+	if (!(flags & CFN_FIRST)) {
+	    makecomplistcc(&cc_first, os, incmd);
+
+	    if (!(ccont & CC_CCCONT))
+		return 0;
 	}
+	makecomplistcc(cc, os, incmd);
+	return 1;
     }
+    return 0;
 }
 
+/* This produces the matches for a command. */
+
 /**/
 static int
-comp_wrapper(List list, FuncWrap w, char *name)
+makecomplistcmd(char *os, int incmd, int flags)
 {
-    if (incompfunc != 1)
-	return 1;
-    else {
-	char *orest, *opre, *osuf, *oipre, *oisuf, **owords;
-	char *oqipre, *oqisuf, *oq, *oqi;
-	zlong ocur;
-	unsigned int runset = 0, kunset = 0, m, sm;
-	Param *pp;
-
-	m = CP_WORDS | CP_CURRENT | CP_PREFIX | CP_SUFFIX | 
-	    CP_IPREFIX | CP_ISUFFIX | CP_QIPREFIX | CP_QISUFFIX;
-	for (pp = comprpms, sm = 1; m; pp++, m >>= 1, sm <<= 1) {
-	    if ((m & 1) && ((*pp)->flags & PM_UNSET))
-		runset |= sm;
-	}
-	if (compkpms[CPN_RESTORE]->flags & PM_UNSET)
-	    kunset = CP_RESTORE;
-	orest = comprestore;
-	comprestore = ztrdup("auto");
-	ocur = compcurrent;
-	opre = dupstring(compprefix);
-	osuf = dupstring(compsuffix);
-	oipre = dupstring(compiprefix);
-	oisuf = dupstring(compisuffix);
-	oqipre = dupstring(compqiprefix);
-	oqisuf = dupstring(compqisuffix);
-	oq = dupstring(compquote);
-	oqi = dupstring(compquoting);
-
-	HEAPALLOC {
-	    owords = arrdup(compwords);
-	} LASTALLOC;
+    Compctl cc;
+    Compctlp ccp;
+    char *s;
+    int ret = 0;
 
-	runshfunc(list, w, name);
-
-	if (comprestore && !strcmp(comprestore, "auto")) {
-	    compcurrent = ocur;
-	    zsfree(compprefix);
-	    compprefix = ztrdup(opre);
-	    zsfree(compsuffix);
-	    compsuffix = ztrdup(osuf);
-	    zsfree(compiprefix);
-	    compiprefix = ztrdup(oipre);
-	    zsfree(compisuffix);
-	    compisuffix = ztrdup(oisuf);
-	    zsfree(compqiprefix);
-	    compqiprefix = ztrdup(oqipre);
-	    zsfree(compqisuffix);
-	    compqisuffix = ztrdup(oqisuf);
-	    zsfree(compquote);
-	    compquote = ztrdup(oq);
-	    zsfree(compquoting);
-	    compquoting = ztrdup(oqi);
-	    freearray(compwords);
-	    PERMALLOC {
-		compwords = arrdup(owords);
-	    } LASTALLOC;
-	    comp_setunset(CP_COMPSTATE |
-			  (~runset & (CP_WORDS | CP_CURRENT | CP_PREFIX |
-				     CP_SUFFIX | CP_IPREFIX | CP_ISUFFIX |
-				     CP_QIPREFIX | CP_QISUFFIX)),
-			  (runset & CP_ALLREALS),
-			  (~kunset & CP_RESTORE), (kunset & CP_ALLKEYS));
-	} else
-	    comp_setunset(CP_COMPSTATE, 0, (~kunset & CP_RESTORE),
-			  (kunset & CP_RESTORE));
-	zsfree(comprestore);
-	comprestore = orest;
+    /* First, use the -T compctl. */
+    if (!(flags & CFN_FIRST)) {
+	makecomplistcc(&cc_first, os, incmd);
 
-	return 0;
+	if (!(ccont & CC_CCCONT))
+	    return 0;
     }
+    /* Then search the pattern compctls, with the command name and the *
+     * full pathname of the command. */
+    if (cmdstr) {
+	ret |= makecomplistpc(os, incmd);
+	if (!(ccont & CC_CCCONT))
+	    return ret;
+    }
+    /* If the command string starts with `=', try the path name of the *
+     * command. */
+    if (cmdstr && cmdstr[0] == Equals) {
+	char *c = findcmd(cmdstr + 1, 1);
+
+	if (c) {
+	    zsfree(cmdstr);
+	    cmdstr = ztrdup(c);
+	}
+    }
+
+    /* Find the compctl for this command, trying the full name and then *
+     * the trailing pathname component. If that doesn't yield anything, *
+     * use default completion. */
+    if (incmd)
+	cc = &cc_compos;
+    else if (!(cmdstr &&
+	  (((ccp = (Compctlp) compctltab->getnode(compctltab, cmdstr)) &&
+	    (cc = ccp->cc)) ||
+	   ((s = dupstring(cmdstr)) && remlpaths(&s) &&
+	    (ccp = (Compctlp) compctltab->getnode(compctltab, s)) &&
+	    (cc = ccp->cc))))) {
+	if (flags & CFN_DEFAULT)
+	    return ret;
+	cc = &cc_default;
+    } else
+	ret|= 1;
+    makecomplistcc(cc, os, incmd);
+    return ret;
 }
 
+/* This add the matches for the pattern compctls. */
+
 /**/
 static int
-comp_check(void)
+makecomplistpc(char *os, int incmd)
 {
-    if (incompfunc != 1) {
-	zerr("condition can only be used in completion function", NULL, 0);
-	return 0;
+    Patcomp pc;
+    Patprog pat;
+    char *s = findcmd(cmdstr, 1);
+    int ret = 0;
+
+    for (pc = patcomps; pc; pc = pc->next) {
+	if ((pat = patcompile(pc->pat, PAT_STATIC, NULL)) &&
+	    (pattry(pat, cmdstr) ||
+	     (s && pattry(pat, s)))) {
+	    makecomplistcc(pc->cc, os, incmd);
+	    ret |= 2;
+	    if (!(ccont & CC_CCCONT))
+		return ret;
+	}
     }
-    return 1;
+    return ret;
 }
 
+/* This produces the matches for one compctl. */
+
 /**/
-static int
-cond_psfix(char **a, int id)
+static void
+makecomplistcc(Compctl cc, char *s, int incmd)
 {
-    if (comp_check()) {
-	if (a[1])
-	    return do_comp_vars(id, cond_val(a, 0), cond_str(a, 1, 1),
-				0, NULL, 0);
-	else
-	    return do_comp_vars(id, -1, cond_str(a, 0, 1), 0, NULL, 0);
-    }
-    return 0;
+    cc->refc++;
+    addlinknode(ccused, cc);
+
+    ccont = 0;
+
+    makecomplistor(cc, s, incmd, 0, 0);
 }
 
+/* This adds the completions for one run of [x]or'ed completions. */
+
 /**/
-static int
-cond_range(char **a, int id)
+static void
+makecomplistor(Compctl cc, char *s, int incmd, int compadd, int sub)
 {
-    return do_comp_vars(CVT_RANGEPAT, 0, cond_str(a, 0, 1), 0,
-			(id ? cond_str(a, 1, 1) : NULL), 0);
+    int mn, ct, um = usemenu;
+
+    /* Loop over xors. */
+    do {
+	mn = mnum;
+
+	/* Loop over ors. */
+	do {
+	    /* Reset the range information if we are not in a sub-list. */
+	    if (!sub) {
+		brange = 0;
+		erange = clwnum - 1;
+	    }
+	    usemenu = 0;
+	    makecomplistlist(cc, s, incmd, compadd);
+	    um |= usemenu;
+
+	    ct = cc->mask2 & CC_XORCONT;
+
+	    cc = cc->xor;
+	} while (cc && ct);
+
+	/* Stop if we got some matches. */
+	if (mn != mnum)
+	    break;
+	if (cc) {
+	    ccont &= ~(CC_DEFCONT | CC_PATCONT);
+	    if (!sub)
+		ccont &= ~CC_CCCONT;
+	}
+    } while (cc);
+
+    usemenu = um;
 }
 
+/* This dispatches for simple and extended completion. */
+
 /**/
 static void
-cmsetfn(Param pm, char **v)
+makecomplistlist(Compctl cc, char *s, int incmd, int compadd)
 {
-    set_gmatcher(pm->nam, v);
+    int oloffs = offs, owe = we, owb = wb, ocs = cs;
+
+    if (cc->ext)
+	/* Handle extended completion. */
+	makecomplistext(cc, s, incmd);
+    else
+	/* Only normal flags. */
+	makecomplistflags(cc, s, incmd, compadd);
+
+    /* Reset some information variables for the next try. */
+    errflag = 0;
+    offs = oloffs;
+    wb = owb;
+    we = owe;
+    cs = ocs;
 }
 
+/* This add matches for extended completion patterns */
+
 /**/
-static char **
-cmgetfn(Param pm)
+static void
+makecomplistext(Compctl occ, char *os, int incmd)
 {
-    int num;
-    Cmlist p;
-    char **ret, **q;
-
-    for (num = 0, p = cmatcher; p; p = p->next, num++);
-
-    ret = (char **) zhalloc((num + 1) * sizeof(char *));
+    Compctl compc;
+    Compcond or, cc;
+    Patprog pprog;
+    int compadd, m = 0, d = 0, t, tt, i, j, a, b, ins;
+    char *sc = NULL, *s, *ss;
+
+    ins = (instring ? instring : (inbackt ? 3 : 0));
+
+    /* This loops over the patterns separated by `-'s. */
+    for (compc = occ->ext; compc; compc = compc->next) {
+	compadd = t = brange = 0;
+	erange = clwnum - 1;
+	/* This loops over OR'ed patterns. */
+	for (cc = compc->cond; cc && !t; cc = or) {
+	    or = cc->or;
+	    /* This loops over AND'ed patterns. */
+	    for (t = 1; cc && t; cc = cc->and) {
+		/* And this loops over [...] pairs. */
+		for (t = i = 0; i < cc->n && !t; i++) {
+		    s = NULL;
+		    brange = 0;
+		    erange = clwnum - 1;
+		    switch (cc->type) {
+		    case CCT_QUOTE:
+			t = ((cc->u.s.s[i][0] == 's' && ins == 1) ||
+			     (cc->u.s.s[i][0] == 'd' && ins == 2) ||
+			     (cc->u.s.s[i][0] == 'b' && ins == 3));
+			break;
+		    case CCT_POS:
+			tt = clwpos;
+			goto cct_num;
+		    case CCT_NUMWORDS:
+			tt = clwnum;
+		    cct_num:
+			if ((a = cc->u.r.a[i]) < 0)
+			    a += clwnum;
+			if ((b = cc->u.r.b[i]) < 0)
+			    b += clwnum;
+			if (cc->type == CCT_POS)
+			    brange = a, erange = b;
+			t = (tt >= a && tt <= b);
+			break;
+		    case CCT_CURSUF:
+		    case CCT_CURPRE:
+			s = ztrdup(clwpos < clwnum ? os : "");
+			untokenize(s);
+			if (isset(COMPLETEINWORD)) s[offs] = '\0';
+			sc = rembslash(cc->u.s.s[i]);
+			a = strlen(sc);
+			if (!strncmp(s, sc, a)) {
+			    compadd = (cc->type == CCT_CURSUF ? a : 0);
+			    t = 1;
+			}
+			break;
+		    case CCT_CURSUB:
+		    case CCT_CURSUBC:
+			if (clwpos < 0 || clwpos >= clwnum)
+			    t = 0;
+			else {
+			    s = ztrdup(os);
+			    untokenize(s);
+			    if (isset(COMPLETEINWORD)) s[offs] = '\0';
+			    a = getcpat(s,
+					cc->u.s.p[i],
+					cc->u.s.s[i],
+					cc->type == CCT_CURSUBC);
+			    if (a != -1)
+				compadd = a, t = 1;
+			}
+			break;
+			
+		    case CCT_CURPAT:
+		    case CCT_CURSTR:
+			tt = clwpos;
+			goto cct_str;
+		    case CCT_WORDPAT:
+		    case CCT_WORDSTR:
+			tt = 0;
+		    cct_str:
+			if ((a = tt + cc->u.s.p[i]) < 0)
+			    a += clwnum;
+			s = ztrdup((a < 0 || a >= clwnum) ? "" :
+				   clwords[a]);
+			untokenize(s);
+			
+			if (cc->type == CCT_CURPAT ||
+			    cc->type == CCT_WORDPAT) {
+			    tokenize(ss = dupstring(cc->u.s.s[i]));
+			    t = ((pprog = patcompile(ss, PAT_STATIC, NULL)) &&
+				 pattry(pprog, s));
+			} else
+			    t = (!strcmp(s, rembslash(cc->u.s.s[i])));
+			break;
+		    case CCT_RANGESTR:
+		    case CCT_RANGEPAT:
+			if (cc->type == CCT_RANGEPAT)
+			    tokenize(sc = dupstring(cc->u.l.a[i]));
+			for (j = clwpos - 1; j > 0; j--) {
+			    untokenize(s = ztrdup(clwords[j]));
+			    if (cc->type == CCT_RANGESTR)
+				sc = rembslash(cc->u.l.a[i]);
+			    if (cc->type == CCT_RANGESTR ?
+				!strncmp(s, sc, strlen(sc)) :
+				((pprog = patcompile(sc, PAT_STATIC, 0)) &&
+				 pattry(pprog, s))) {
+				zsfree(s);
+				brange = j + 1;
+				t = 1;
+				break;
+			    }
+			    zsfree(s);
+			}
+			if (t && cc->u.l.b[i]) {
+			    if (cc->type == CCT_RANGEPAT)
+				tokenize(sc = dupstring(cc->u.l.b[i]));
+			    for (j++; j < clwnum; j++) {
+				untokenize(s = ztrdup(clwords[j]));
+				if (cc->type == CCT_RANGESTR)
+				    sc = rembslash(cc->u.l.b[i]);
+				if (cc->type == CCT_RANGESTR ?
+				    !strncmp(s, sc, strlen(sc)) :
+				    ((pprog = patcompile(sc, PAT_STATIC, 0)) &&
+				     pattry(pprog, s))) {
+				    zsfree(s);
+				    erange = j - 1;
+				    t = clwpos <= erange;
+				    break;
+				}
+				zsfree(s);
+			    }
+			}
+			s = NULL;
+		    }
+		    zsfree(s);
+		}
+	    }
+	}
+	if (t) {
+	    /* The patterns matched, use the flags. */
+	    m = 1;
+	    ccont &= ~(CC_PATCONT | CC_DEFCONT);
+	    makecomplistor(compc, os, incmd, compadd, 1);
+	    if (!d && (ccont & CC_DEFCONT)) {
+		d = 1;
+		compadd = 0;
+		brange = 0;
+		erange = clwnum - 1;
+		makecomplistflags(occ, os, incmd, 0);
+	    }
+	    if (!(ccont & CC_PATCONT))
+		break;
+	}
+    }
+    /* If no pattern matched, use the standard flags. */
+    if (!m) {
+	compadd = 0;
+	brange = 0;
+	erange = clwnum - 1;
+	makecomplistflags(occ, os, incmd, 0);
+    }
+}
 
-    for (q = ret, p = cmatcher; p; p = p->next, q++)
-	*q = dupstring(p->str);
-    *q = NULL;
+/**/
+static int
+sep_comp_string(char *ss, char *s, int noffs)
+{
+    LinkList foo = newlinklist();
+    LinkNode n;
+    int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs;
+    int sl = strlen(ss), tl, got = 0, i = 0, cur = -1, oll = ll;
+    int ois = instring, oib = inbackt;
+    char *tmp, *p, *ns, *ol = (char *) line, sav, oaq = autoq, *qp, *qs;
+
+    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 = sl + 3 + strlen(s));
+    strcpy(tmp, ss);
+    tmp[sl] = ' ';
+    memcpy(tmp + sl + 1, s, noffs);
+    tmp[(scs = cs = sl + 1 + noffs)] = 'x';
+    strcpy(tmp + sl + 2 + noffs, s + noffs);
+    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 - sl - 1)];
+    s[i] = '\0';
+    qp = tricat(qipre, s, "");
+    s[i] = sav;
+    if (swe < swb)
+	swe = swb;
+    swe -= sl + 1;
+    sl = strlen(s);
+    if (swe > sl)
+	swe = sl, ns[swe - swb + 1] = '\0';
+    qs = tricat(s + swe, qisuf, "");
+    sl = strlen(ns);
+    if (soffs > sl)
+	soffs = sl;
+
+    {
+	char **ow = clwords, *os = cmdstr, *oqp = qipre, *oqs = qisuf;
+	int olws = clwsize, olwn = clwnum, olwp = clwpos;
+	int obr = brange, oer = erange, oof = offs;
+	unsigned long occ = ccont;
+
+	clwsize = clwnum = countlinknodes(foo);
+	clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
+	for (n = firstnode(foo), i = 0; n; incnode(n), i++) {
+	    p = clwords[i] = (char *) getdata(n);
+	    untokenize(p);
+	}
+	clwords[i] = NULL;
+	clwpos = cur;
+	cmdstr = ztrdup(clwords[0]);
+	brange = 0;
+	erange = clwnum - 1;
+	qipre = qp;
+	qisuf = qs;
+	offs = soffs;
+	ccont = CC_CCCONT;
+	makecomplistcmd(ns, !clwpos, CFN_FIRST);
+	ccont = occ;
+	offs = oof;
+	zsfree(cmdstr);
+	cmdstr = os;
+	freearray(clwords);
+	clwords = ow;
+	clwsize = olws;
+	clwnum = olwn;
+	clwpos = olwp;
+	brange = obr;
+	erange = oer;
+	zsfree(qipre);
+	qipre = oqp;
+	zsfree(qisuf);
+	qisuf = oqs;
+    }
+    autoq = oaq;
+    instring = ois;
+    inbackt = oib;
 
-    return ret;
+    return 0;
 }
 
+/* This adds the completions for the flags in the given compctl. */
+
 /**/
 static void
-cmunsetfn(Param pm, int exp)
+makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 {
-    char *dummy[1];
+    int t, sf1, sf2, ooffs, um = usemenu, delit, oaw, gflags;
+    int mn = mnum, ohp = haspattern;
+    char *p, *sd = NULL, *tt, *s1, *s2, *os =  dupstring(s);
+    struct cmlist ms;
+
+    ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT));
+
+    if (incompfunc != 1 && findnode(ccstack, cc))
+	return;
+
+    MUSTUSEHEAP("complistflags");
+
+    addlinknode(ccstack, cc);
+
+    if (incompfunc != 1 && allccs) {
+	if (findnode(allccs, cc)) {
+	    uremnode(ccstack, firstnode(ccstack));
+	    return;
+	}
+	addlinknode(allccs, cc);
+    }
+    /* Go to the end of the word if complete_in_word is not set. */
+    if (unset(COMPLETEINWORD) && cs != we)
+	cs = we, offs = strlen(s);
+
+    s = dupstring(s);
+    delit = ispattern = 0;
+    usemenu = um;
+    patcomp = filecomp = NULL;
+    rpre = rsuf = lpre = lsuf = ppre = psuf = lppre = lpsuf =
+	fpre = fsuf = ipre = ripre = prpre = 
+	qfpre = qfsuf = qrpre = qrsuf = qlpre = qlsuf = NULL;
+
+    curcc = cc;
+
+    mflags = 0;
+    gflags = (((cc->mask2 & CC_NOSORT ) ? CGF_NOSORT  : 0) |
+	      ((cc->mask2 & CC_UNIQALL) ? CGF_UNIQALL : 0) |
+	      ((cc->mask2 & CC_UNIQCON) ? CGF_UNIQCON : 0));
+    if (cc->gname) {
+	endcmgroup(NULL);
+	begcmgroup(cc->gname, gflags);
+    }
+    if (cc->ylist) {
+	endcmgroup(NULL);
+	begcmgroup(NULL, gflags);
+    }
+    if (cc->mask & CC_REMOVE)
+	mflags |= CMF_REMOVE;
+    if (cc->explain) {
+	curexpl = (Cexpl) zhalloc(sizeof(struct cexpl));
+	curexpl->count = curexpl->fcount = 0;
+    } else
+	curexpl = NULL;
+    /* compadd is the number of characters we have to ignore at the  *
+     * beginning of the word.                                        */
+    if (compadd) {
+	ipre = dupstring(s);
+	ipre[compadd] = '\0';
+	untokenize(ipre);
+	wb += compadd;
+	s += compadd;
+	if ((offs -= compadd) < 0) {
+	    /* It's bigger than our word prefix, so we can't help here... */
+	    uremnode(ccstack, firstnode(ccstack));
+	    return;
+	}
+    } else
+	ipre = NULL;
+
+    if (cc->matcher) {
+	ms.next = mstack;
+	ms.matcher = cc->matcher;
+	mstack = &ms;
+
+	if (!mnum)
+	    add_bmatchers(cc->matcher);
+
+	addlinknode(matchers, cc->matcher);
+	cc->matcher->refc++;
+    }
+    if (mnum && (mstack || bmatchers))
+	update_bmatchers();
+
+    /* Insert the prefix (compctl -P), if any. */
+    if (cc->prefix) {
+	int pl = 0;
+
+	if (*s) {
+	    char *dp = rembslash(cc->prefix);
+	    /* First find out how much of the prefix is already on the line. */
+	    sd = dupstring(s);
+	    untokenize(sd);
+	    pl = pfxlen(dp, sd);
+	    s += pl;
+	    sd += pl;
+	    offs -= pl;
+	}
+    }
+    /* Does this compctl have a suffix (compctl -S)? */
+    if (cc->suffix) {
+	char *sdup = rembslash(cc->suffix);
+	int sl = strlen(sdup), suffixll;
+
+	/* Ignore trailing spaces. */
+	for (p = sdup + sl - 1; p >= sdup && *p == ' '; p--, sl--);
+	p[1] = '\0';
+
+	if (!sd) {
+	    sd = dupstring(s);
+	    untokenize(sd);
+	}
+	/* If the suffix is already there, ignore it (and don't add *
+	 * it again).                                               */
+	if (*sd && (suffixll = strlen(sd)) >= sl &&
+	    offs <= suffixll - sl && !strcmp(sdup, sd + suffixll - sl))
+	    s[suffixll - sl] = '\0';
+    }
+    /* Do we have one of the special characters `~' and `=' at the beginning? */
+    if (incompfunc || ((ic = *s) != Tilde && ic != Equals))
+	ic = 0;
+
+    /* Check if we have to complete a parameter name... */
+    if (!incompfunc && (p = check_param(s, 1, 0))) {
+	s = p;
+	/* And now make sure that we complete parameter names. */
+	cc = &cc_dummy;
+	cc_dummy.refc = 10000;
+	cc_dummy.mask = CC_PARAMS | CC_ENVVARS;
+    }
+    ooffs = offs;
+    /* If we have to ignore the word, do that. */
+    if (cc->mask & CC_DELETE) {
+	delit = 1;
+	*s = '\0';
+	offs = 0;
+	if (isset(AUTOMENU))
+	    usemenu = 1;
+    }
+
+    /* Compute line prefix/suffix. */
+    lpl = offs;
+    lpre = zhalloc(lpl + 1);
+    memcpy(lpre, s, lpl);
+    lpre[lpl] = '\0';
+    qlpre = quotename(lpre, NULL);
+    lsuf = dupstring(s + offs);
+    lsl = strlen(lsuf);
+    qlsuf = quotename(lsuf, NULL);
+
+    /* First check for ~.../... */
+    if (ic == Tilde) {
+	for (p = lpre + lpl; p > lpre; p--)
+	    if (*p == '/')
+		break;
+
+	if (*p == '/')
+	    ic = 0;
+    }
+    /* Compute real prefix/suffix. */
+
+    noreal = !delit;
+    for (p = lpre; *p && *p != String && *p != Tick; p++);
+    tt = ic && !ispar ? lpre + 1 : lpre;
+    rpre = (*p || *lpre == Tilde || *lpre == Equals) ?
+	(noreal = 0, getreal(tt)) :
+	dupstring(tt);
+    qrpre = quotename(rpre, NULL);
+
+    for (p = lsuf; *p && *p != String && *p != Tick; p++);
+    rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf);
+    qrsuf = quotename(rsuf, NULL);
+
+    /* Check if word is a pattern. */
+
+    for (s1 = NULL, sf1 = 0, p = rpre + (rpl = strlen(rpre)) - 1;
+	 p >= rpre && (ispattern != 3 || !sf1);
+	 p--)
+	if (itok(*p) && (p > rpre || (*p != Equals && *p != Tilde)))
+	    ispattern |= sf1 ? 1 : 2;
+	else if (*p == '/') {
+	    sf1++;
+	    if (!s1)
+		s1 = p;
+	}
+    rsl = strlen(rsuf);
+    for (s2 = NULL, sf2 = t = 0, p = rsuf; *p && (!t || !sf2); p++)
+	if (itok(*p))
+	    t |= sf2 ? 4 : 2;
+	else if (*p == '/') {
+	    sf2++;
+	    if (!s2)
+		s2 = p;
+	}
+    ispattern = ispattern | t;
+
+    /* But if we were asked not to do glob completion, we never treat the *
+     * thing as a pattern.                                                */
+    if (!comppatmatch || !*comppatmatch)
+	ispattern = 0;
+
+    if (ispattern) {
+	/* The word should be treated as a pattern, so compute the matcher. */
+	p = (char *) zhalloc(rpl + rsl + 2);
+	strcpy(p, rpre);
+	if (rpl && p[rpl - 1] != Star &&
+	    (!comppatmatch || *comppatmatch == '*')) {
+	    p[rpl] = Star;
+	    strcpy(p + rpl + 1, rsuf);
+	} else
+	    strcpy(p + rpl, rsuf);
+	patcomp = patcompile(p, 0, NULL);
+	haspattern = 1;
+    }
+    if (!patcomp) {
+	untokenize(rpre);
+	untokenize(rsuf);
+
+	rpl = strlen(rpre);
+	rsl = strlen(rsuf);
+    }
+    untokenize(lpre);
+    untokenize(lsuf);
+
+    if (!(cc->mask & CC_DELETE))
+	hasmatched = 1;
+
+    /* Handle completion of files specially (of course). */
+
+    if ((cc->mask & (CC_FILES | CC_DIRS | CC_COMMPATH)) || cc->glob) {
+	/* s1 and s2 point to the last/first slash in the prefix/suffix. */
+	if (!s1)
+	    s1 = rpre;
+	if (!s2)
+	    s2 = rsuf + rsl;
+
+	/* Compute the path prefix/suffix. */
+	if (*s1 != '/')
+	    ppre = "";
+	else
+	    ppre = dupstrpfx(rpre, s1 - rpre + 1);
+	psuf = dupstring(s2);
+
+	if (cs != wb) {
+	    char save = line[cs];
+
+	    line[cs] = 0;
+	    lppre = dupstring((char *) line + wb +
+			      (qipre && *qipre ?
+			       (strlen(qipre) -
+				(*qipre == '\'' || *qipre == '\"')) : 0));
+	    line[cs] = save;
+	    if (brbeg) {
+		Brinfo bp;
+
+		for (bp = brbeg; bp; bp = bp->next)
+		    strcpy(lppre + bp->qpos,
+			   lppre + bp->qpos + strlen(bp->str));
+	    }
+	    if ((p = strrchr(lppre, '/'))) {
+		p[1] = '\0';
+		lppl = strlen(lppre);
+	    } else if (!sf1) {
+		lppre = NULL;
+		lppl = 0;
+	    } else {
+		lppre = ppre;
+		lppl = strlen(lppre);
+	    }
+	} else {
+	    lppre = NULL;
+	    lppl = 0;
+	}
+	if (cs != we) {
+	    int end = we;
+	    char save = line[end];
 
-    dummy[0] = NULL;
-    set_gmatcher(pm->nam, dummy);
+	    if (qisuf && *qisuf) {
+		int ql = strlen(qisuf);
+
+		end -= ql - (qisuf[ql-1] == '\'' || qisuf[ql-1] == '"');
+	    }
+	    line[end] = 0;
+	    lpsuf = dupstring((char *) (line + cs));
+	    line[end] = save;
+	    if (brend) {
+		Brinfo bp;
+		char *p;
+		int bl;
+
+		for (bp = brend; bp; bp = bp->next) {
+		    p = lpsuf + (we - cs) - bp->qpos - (bl = strlen(bp->str));
+		    strcpy(p, p + bl);
+		}
+	    }
+	    if (!(lpsuf = strchr(lpsuf, '/')) && sf2)
+		lpsuf = psuf;
+	    lpsl = (lpsuf ? strlen(lpsuf) : 0);
+	} else {
+	    lpsuf = NULL;
+	    lpsl = 0;
+	}
+
+	/* And get the file prefix. */
+	fpre = dupstring(((s1 == s || s1 == rpre || ic) &&
+			  (*s != '/' || cs == wb)) ? s1 : s1 + 1);
+	qfpre = quotename(fpre, NULL);
+	/* And the suffix. */
+	fsuf = dupstrpfx(rsuf, s2 - rsuf);
+	qfsuf = quotename(fsuf, NULL);
+
+	if (comppatmatch && *comppatmatch && (ispattern & 2)) {
+	    int t2;
+
+	    /* We have to use globbing, so compute the pattern from *
+	     * the file prefix and suffix with a `*' between them.  */
+	    p = (char *) zhalloc((t2 = strlen(fpre)) + strlen(fsuf) + 2);
+	    strcpy(p, fpre);
+	    if ((!t2 || p[t2 - 1] != Star) && *fsuf != Star &&
+		(!comppatmatch || *comppatmatch == '*'))
+		p[t2++] = Star;
+	    strcpy(p + t2, fsuf);
+	    filecomp = patcompile(p, 0, NULL);
+	}
+	if (!filecomp) {
+	    untokenize(fpre);
+	    untokenize(fsuf);
+
+	    fpl = strlen(fpre);
+	    fsl = strlen(fsuf);
+	}
+	addwhat = -1;
+
+	/* Completion after `~', maketildelist adds the usernames *
+	 * and named directories.                                 */
+	if (ic == Tilde) {
+	    char *oi = ipre;
+
+	    ipre = (ipre ? dyncat("~", ipre) : "~");
+	    maketildelist();
+	    ipre = oi;
+	} else if (ic == Equals) {
+	    /* Completion after `=', get the command names from *
+	     * the cmdnamtab and aliases from aliastab.         */
+	    char *oi = ipre;
+
+	    ipre = (ipre ? dyncat("=", ipre) : "=");
+	    if (isset(HASHLISTALL))
+		cmdnamtab->filltable(cmdnamtab);
+	    dumphashtable(cmdnamtab, -7);
+	    dumphashtable(aliastab, -2);
+	    ipre = oi;
+	} else {
+	    /* Normal file completion... */
+	    if (ispattern & 1) {
+		/* But with pattern matching. */
+		LinkList l = newlinklist();
+		LinkNode n;
+		int ng = opts[NULLGLOB];
+
+		opts[NULLGLOB] = 1;
+
+		addwhat = 0;
+		p = (char *) zhalloc(lpl + lsl + 3);
+		strcpy(p, lpre);
+		if (*lsuf != '*' && *lpre && lpre[lpl - 1] != '*')
+		    strcat(p, "*");
+		strcat(p, lsuf);
+		if (*lsuf && lsuf[lsl - 1] != '*' && lsuf[lsl - 1] != ')')
+		    strcat(p, "*");
+
+		/* Do the globbing. */
+		tokenize(p);
+		remnulargs(p);
+		addlinknode(l, p);
+		globlist(l);
+
+		if (nonempty(l)) {
+		    /* And add the resulting words. */
+		    mflags |= CMF_FILE;
+		    for (n = firstnode(l); n; incnode(n))
+			addmatch(getdata(n), NULL);
+		    mflags &= !CMF_FILE;
+		}
+		opts[NULLGLOB] = ng;
+	    } else {
+		char **dirs = 0, *ta[2];
+
+		/* No pattern matching. */
+		addwhat = CC_FILES;
+
+		if (cc->withd) {
+		    char **pp, **npp, *tp;
+		    int tl = strlen(ppre) + 2, pl;
+
+		    if ((pp = get_user_var(cc->withd))) {
+			dirs = npp =
+			    (char**) zhalloc(sizeof(char *)*(arrlen(pp)+1));
+			while (*pp) {
+			    pl = strlen(*pp);
+			    tp = (char *) zhalloc(strlen(*pp) + tl);
+			    strcpy(tp, *pp);
+			    tp[pl] = '/';
+			    strcpy(tp + pl + 1, ppre);
+			    *npp++ = tp;
+			    pp++;
+			}
+			*npp = '\0';
+		    }
+		}
+		if (!dirs) {
+		    dirs = ta;
+		    if (cc->withd) {
+			char *tp;
+			int pl = strlen(cc->withd);
+
+			ta[0] = tp = (char *) zhalloc(strlen(ppre) + pl + 2);
+			strcpy(tp, cc->withd);
+			tp[pl] = '/';
+			strcpy(tp + pl + 1, ppre);
+		    } else
+			ta[0] = ppre;
+		    ta[1] = NULL;
+		}
+		while (*dirs) {
+		    prpre = *dirs;
+
+		    if (sf2)
+			/* We are in the path, so add only directories. */
+			gen_matches_files(1, 0, 0);
+		    else {
+			if (cc->mask & CC_FILES)
+			    /* Add all files. */
+			    gen_matches_files(0, 0, 1);
+			else if (cc->mask & CC_COMMPATH) {
+			    /* Completion of command paths. */
+			    if (sf1 || cc->withd)
+				/* There is a path prefix, so add *
+				 * directories and executables.   */
+				gen_matches_files(1, 1, 0);
+			    else {
+				/* No path prefix, so add the things *
+				 * reachable via the PATH variable.  */
+				char **pc = path, *pp = prpre;
+
+				for (; *pc; pc++)
+				    if (!**pc || (pc[0][0] == '.' && !pc[0][1]))
+					break;
+				if (*pc) {
+				    prpre = "./";
+				    gen_matches_files(1, 1, 0);
+				    prpre = pp;
+				}
+			    }
+			} else if (cc->mask & CC_DIRS)
+			    gen_matches_files(1, 0, 0);
+			/* The compctl has a glob pattern (compctl -g). */
+			if (cc->glob) {
+			    int ns, pl = strlen(prpre), o, paalloc;
+			    char *g = dupstring(cc->glob), *pa;
+			    char *p2, *p3;
+			    int ne = noerrs, md = opts[MARKDIRS];
+
+			    /* These are used in the globbing code to make *
+			     * things a bit faster.                        */
+			    if (ispattern || mstack)
+				glob_pre = glob_suf = NULL;
+			    else {
+				glob_pre = fpre;
+				glob_suf = fsuf;
+			    }
+			    noerrs = 1;
+			    addwhat = -6;
+			    o = strlen(prpre);
+			    pa = (char *)zalloc(paalloc = o + PATH_MAX);
+			    strcpy(pa, prpre);
+			    opts[MARKDIRS] = 0;
+
+			    /* The compctl -g string may contain more than *
+			     * one pattern, so we need a loop.             */
+			    while (*g) {
+				LinkList l = newlinklist();
+				int ng;
+
+				/* Find the blank terminating the pattern. */
+				while (*g && inblank(*g))
+				    g++;
+				/* Oops, we already reached the end of the
+				   string. */
+				if (!*g)
+				    break;
+				for (p = g + 1; *p && !inblank(*p); p++)
+				    if (*p == '\\' && p[1])
+					p++;
+				/* Get the pattern string. */
+				tokenize(g = dupstrpfx(g, p - g));
+				if (*g == '=')
+				    *g = Equals;
+				if (*g == '~')
+				    *g = Tilde;
+				remnulargs(g);
+				if ((*g == Equals || *g == Tilde) && !cc->withd) {
+				/* The pattern has a `~' or `=' at the  *
+				 * beginning, so we expand this and use *
+				 * the result.                          */
+				    filesub(&g, 0);
+				    addlinknode(l, dupstring(g));
+				} else if (*g == '/' && !cc->withd)
+				/* The pattern is a full path (starting *
+				 * with '/'), so add it unchanged.      */
+				    addlinknode(l, dupstring(g));
+				else {
+				/* It's a simple pattern, so append it to *
+				 * the path we have on the command line.  */
+				    int minlen = o + strlen(g);
+				    if (minlen >= paalloc)
+					pa = (char *)
+					    zrealloc(pa, paalloc = minlen+1);
+				    strcpy(pa + o, g);
+				    addlinknode(l, dupstring(pa));
+				}
+				/* Do the globbing. */
+				ng = opts[NULLGLOB];
+				opts[NULLGLOB] = 1;
+				globlist(l);
+				opts[NULLGLOB] = ng;
+				/* Get the results. */
+				if (nonempty(l) && peekfirst(l)) {
+				    for (p2 = (char *)peekfirst(l); *p2; p2++)
+					if (itok(*p2))
+					    break;
+				    if (!*p2) {
+					if ((*g == Equals || *g == Tilde ||
+					     *g == '/') || cc->withd) {
+					    /* IF the pattern started with `~',  *
+					     * `=', or `/', add the result only, *
+					     * if it really matches what we have *
+					     * on the line.                      *
+					     * Do this if an initial directory   *
+					     * was specified, too.               */
+					    while ((p2 = (char *)ugetnode(l)))
+						if (strpfx(prpre, p2))
+						    addmatch(p2 + pl, NULL);
+					} else {
+					    /* Otherwise ignore the path we *
+					     * prepended to the pattern.    */
+					    while ((p2 = p3 =
+						    (char *)ugetnode(l))) {
+						for (ns = sf1; *p3 && ns; p3++)
+						    if (*p3 == '/')
+							ns--;
+
+						addmatch(p3, NULL);
+					    }
+					}
+				    }
+				}
+				pa[o] = '\0';
+				g = p;
+			    }
+			    glob_pre = glob_suf = NULL;
+			    noerrs = ne;
+			    opts[MARKDIRS] = md;
+
+			    zfree(pa, paalloc);
+			}
+		    }
+		    dirs++;
+		}
+		prpre = NULL;
+	    }
+	}
+	lppre = lpsuf = NULL;
+	lppl = lpsl = 0;
+    }
+    if (ic) {
+	/* Now change the `~' and `=' tokens to the real characters so *
+	 * that things starting with these characters will be added.   */
+	rpre = dyncat((ic == Tilde) ? "~" : "=", rpre);
+	rpl++;
+	qrpre = dyncat((ic == Tilde) ? "~" : "=", qrpre);
+    }
+    if (!ic && (cc->mask & CC_COMMPATH) && !*ppre && !*psuf) {
+	/* If we have to complete commands, add alias names, *
+	 * shell functions and builtins too.                 */
+	dumphashtable(aliastab, -3);
+	dumphashtable(reswdtab, -3);
+	dumphashtable(shfunctab, -3);
+	dumphashtable(builtintab, -3);
+	if (isset(HASHLISTALL))
+	    cmdnamtab->filltable(cmdnamtab);
+	dumphashtable(cmdnamtab, -3);
+	/* And parameter names if autocd and cdablevars are set. */
+	if (isset(AUTOCD) && isset(CDABLEVARS))
+	    dumphashtable(paramtab, -4);
+    }
+    oaw = addwhat = (cc->mask & CC_QUOTEFLAG) ? -2 : CC_QUOTEFLAG;
+
+    if (cc->mask & CC_NAMED)
+	/* Add named directories. */
+	dumphashtable(nameddirtab, addwhat);
+    if (cc->mask & CC_OPTIONS)
+	/* Add option names. */
+	dumphashtable(optiontab, addwhat);
+    if (cc->mask & CC_VARS) {
+	/* And parameter names. */
+	dumphashtable(paramtab, -9);
+	addwhat = oaw;
+    }
+    if (cc->mask & CC_BINDINGS) {
+	/* And zle function names... */
+	dumphashtable(thingytab, CC_BINDINGS);
+	addwhat = oaw;
+    }
+    if (cc->keyvar) {
+	/* This adds things given to the compctl -k flag *
+	 * (from a parameter or a list of words).        */
+	char **usr = get_user_var(cc->keyvar);
+
+	if (usr)
+	    while (*usr)
+		addmatch(*usr++, NULL);
+    }
+    if (cc->mask & CC_USERS) {
+	/* Add user names. */
+	maketildelist();
+	addwhat = oaw;
+    }
+    if (cc->func) {
+	/* This handles the compctl -K flag. */
+	List list;
+	char **r;
+	int lv = lastval;
+	    
+	/* Get the function. */
+	if ((list = getshfunc(cc->func)) != &dummy_list) {
+	    /* We have it, so build a argument list. */
+	    LinkList args = newlinklist();
+	    int osc = sfcontext;
+		
+	    addlinknode(args, cc->func);
+		
+	    if (delit) {
+		p = dupstrpfx(os, ooffs);
+		untokenize(p);
+		addlinknode(args, p);
+		p = dupstring(os + ooffs);
+		untokenize(p);
+		addlinknode(args, p);
+	    } else {
+		addlinknode(args, lpre);
+		addlinknode(args, lsuf);
+	    }
+		
+	    /* This flag allows us to use read -l and -c. */
+	    if (incompfunc != 1)
+		incompctlfunc = 1;
+	    sfcontext = SFC_COMPLETE;
+	    /* Call the function. */
+	    doshfunc(cc->func, list, args, 0, 1);
+	    sfcontext = osc;
+	    incompctlfunc = 0;
+	    /* And get the result from the reply parameter. */
+	    if ((r = get_user_var("reply")))
+		while (*r)
+		    addmatch(*r++, NULL);
+	}
+	lastval = lv;
+    }
+    if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) {
+	/* Get job names. */
+	int i;
+	char *j;
+
+	for (i = 0; i < MAXJOB; i++)
+	    if ((jobtab[i].stat & STAT_INUSE) &&
+		jobtab[i].procs && jobtab[i].procs->text) {
+		int stopped = jobtab[i].stat & STAT_STOPPED;
+
+		j = dupstring(jobtab[i].procs->text);
+		if ((cc->mask & CC_JOBS) ||
+		    (stopped && (cc->mask & CC_STOPPED)) ||
+		    (!stopped && (cc->mask & CC_RUNNING)))
+		    addmatch(j, NULL);
+	    }
+    }
+    if (cc->str) {
+	/* Get the stuff from a compctl -s. */
+	LinkList foo = newlinklist();
+	LinkNode n;
+	int first = 1, ng = opts[NULLGLOB], oowe = we, oowb = wb;
+	char *tmpbuf;
+
+	opts[NULLGLOB] = 1;
+
+	/* Put the string in the lexer buffer and call the lexer to *
+	 * get the words we have to expand.                        */
+	zleparse = 1;
+	lexsave();
+	tmpbuf = (char *)zhalloc(strlen(cc->str) + 5);
+	sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */
+	inpush(tmpbuf, 0, NULL);
+	strinbeg(0);
+	noaliases = 1;
+	do {
+	    ctxtlex();
+	    if (tok == ENDINPUT || tok == LEXERR)
+		break;
+	    if (!first && tokstr && *tokstr)
+		addlinknode(foo, ztrdup(tokstr));
+	    first = 0;
+	} while (tok != ENDINPUT && tok != LEXERR);
+	noaliases = 0;
+	strinend();
+	inpop();
+	errflag = zleparse = 0;
+	lexrestore();
+	/* Fine, now do full expansion. */
+	prefork(foo, 0);
+	if (!errflag) {
+	    globlist(foo);
+	    if (!errflag)
+		/* And add the resulting words as matches. */
+		for (n = firstnode(foo); n; incnode(n))
+		    addmatch((char *)n->dat, NULL);
+	}
+	opts[NULLGLOB] = ng;
+	we = oowe;
+	wb = oowb;
+    }
+    if (cc->hpat) {
+	/* We have a pattern to take things from the history. */
+	Patprog pprogc = NULL;
+	char *e, *h, hpatsav;
+	int i = addhistnum(curhist,-1,HIST_FOREIGN), n = cc->hnum;
+	Histent he = quietgethistent(i, GETHIST_UPWARD);
+
+	/* Parse the pattern, if it isn't the null string. */
+	if (*(cc->hpat)) {
+	    char *thpat = dupstring(cc->hpat);
+
+	    tokenize(thpat);
+	    pprogc = patcompile(thpat, 0, NULL);
+	}
+	/* n holds the number of history line we have to search. */
+	if (!n)
+	    n = -1;
+
+	/* Now search the history. */
+	while (n-- && he) {
+	    int iwords;
+	    for (iwords = he->nwords - 1; iwords >= 0; iwords--) {
+		h = he->text + he->words[iwords*2];
+		e = he->text + he->words[iwords*2+1];
+		hpatsav = *e;
+		*e = '\0';
+		/* We now have a word from the history, ignore it *
+		 * if it begins with a quote or `$'.              */
+		if (*h != '\'' && *h != '"' && *h != '`' && *h != '$' &&
+		    (!pprogc || pattry(pprogc, h)))
+		    /* Otherwise add it if it was matched. */
+		    addmatch(dupstring(h), NULL);
+		if (hpatsav)
+		    *e = hpatsav;
+	    }
+	    he = up_histent(he);
+	}
+    }
+    if ((t = cc->mask & (CC_ARRAYS | CC_INTVARS | CC_ENVVARS | CC_SCALARS |
+			 CC_READONLYS | CC_SPECIALS | CC_PARAMS)))
+	/* Add various flavours of parameters. */
+	dumphashtable(paramtab, t);
+    if ((t = cc->mask & CC_SHFUNCS))
+	/* Add shell functions. */
+	dumphashtable(shfunctab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+    if ((t = cc->mask & CC_BUILTINS))
+	/* Add builtins. */
+	dumphashtable(builtintab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+    if ((t = cc->mask & CC_EXTCMDS)) {
+	/* Add external commands */
+	if (isset(HASHLISTALL))
+	    cmdnamtab->filltable(cmdnamtab);
+	dumphashtable(cmdnamtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+    }
+    if ((t = cc->mask & CC_RESWDS))
+	/* Add reserved words */
+	dumphashtable(reswdtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+    if ((t = cc->mask & (CC_ALREG | CC_ALGLOB)))
+	/* Add the two types of aliases. */
+	dumphashtable(aliastab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+    if (keypm && cc == &cc_dummy) {
+	/* Add the keys of the parameter in keypm. */
+	scanhashtable(keypm->gets.hfn(keypm), 0, 0, PM_UNSET, addhnmatch, 0);
+	keypm = NULL;
+	cc_dummy.suffix = NULL;
+    }
+    if (!errflag && cc->ylist) {
+	/* generate the user-defined display list: if anything fails, *
+	 * we silently allow the normal completion list to be used.   */
+	char **yaptr = NULL, *uv = NULL;
+	List list;
+
+	if (cc->ylist[0] == '$' || cc->ylist[0] == '(') {
+	    /* from variable */
+	    uv = cc->ylist + (cc->ylist[0] == '$');
+	} else if ((list = getshfunc(cc->ylist)) != &dummy_list) {
+	    /* from function:  pass completions as arg list */
+	    LinkList args = newlinklist();
+	    LinkNode ln;
+	    Cmatch m;
+	    int osc = sfcontext;
+
+	    addlinknode(args, cc->ylist);
+	    for (ln = firstnode(matches); ln; ln = nextnode(ln)) {
+		m = (Cmatch) getdata(ln);
+		if (m->ppre) {
+		    char *p = (char *) zhalloc(strlen(m->ppre) + strlen(m->str) +
+					      strlen(m->psuf) + 1);
+
+		    sprintf(p, "%s%s%s", m->ppre, m->str, m->psuf);
+		    addlinknode(args, dupstring(p));
+		} else
+		    addlinknode(args, dupstring(m->str));
+	    }
+
+	    /* No harm in allowing read -l and -c here, too */
+	    if (incompfunc != 1)
+		incompctlfunc = 1;
+	    sfcontext = SFC_COMPLETE;
+	    doshfunc(cc->ylist, list, args, 0, 1);
+	    sfcontext = osc;
+	    incompctlfunc = 0;
+	    uv = "reply";
+	}
+	if (uv)
+	    yaptr = get_user_var(uv);
+	if ((tt = cc->explain)) {
+	    tt = dupstring(tt);
+	    if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
+		singsub(&tt);
+		untokenize(tt);
+	    }
+	    curexpl->str = tt;
+	    if (cc->gname) {
+		endcmgroup(yaptr);
+		begcmgroup(cc->gname, gflags);
+		addexpl();
+	    } else {
+		addexpl();
+		endcmgroup(yaptr);
+		begcmgroup("default", 0);
+	    }
+	}
+    } else if ((tt = cc->explain)) {
+	tt = dupstring(tt);
+	if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
+	    singsub(&tt);
+	    untokenize(tt);
+	}
+	curexpl->str = tt;
+	addexpl();
+    }
+    if (cc->subcmd) {
+	/* Handle -l sub-completion. */
+	char **ow = clwords, *os = cmdstr, *ops = NULL;
+	int oldn = clwnum, oldp = clwpos, br;
+	unsigned long occ = ccont;
+	
+	ccont = CC_CCCONT;
+	
+	/* So we restrict the words-array. */
+	if (brange >= clwnum)
+	    brange = clwnum - 1;
+	if (brange < 1)
+	    brange = 1;
+	if (erange >= clwnum)
+	    erange = clwnum - 1;
+	if (erange < 1)
+	    erange = 1;
+	clwnum = erange - brange + 1;
+	clwpos = clwpos - brange;
+	br = brange;
+
+	if (cc->subcmd[0]) {
+	    /* And probably put the command name given to the flag *
+	     * in the array.                                       */
+	    clwpos++;
+	    clwnum++;
+	    incmd = 0;
+	    ops = clwords[br - 1];
+	    clwords[br - 1] = ztrdup(cc->subcmd);
+	    cmdstr = ztrdup(cc->subcmd);
+	    clwords += br - 1;
+	} else {
+	    cmdstr = ztrdup(clwords[br]);
+	    incmd = !clwpos;
+	    clwords += br;
+	}
+	/* Produce the matches. */
+	makecomplistcmd(s, incmd, CFN_FIRST);
+
+	/* And restore the things we changed. */
+	clwords = ow;
+	zsfree(cmdstr);
+	cmdstr = os;
+	clwnum = oldn;
+	clwpos = oldp;
+	if (ops) {
+	    zsfree(clwords[br - 1]);
+	    clwords[br - 1] = ops;
+	}
+	ccont = occ;
+    }
+    if (cc->substr)
+	sep_comp_string(cc->substr, s, offs);
+    uremnode(ccstack, firstnode(ccstack));
+    if (cc->matcher)
+	mstack = mstack->next;
+
+    if (mn == mnum)
+	haspattern = ohp;
 }
 
+
 static struct builtin bintab[] = {
     BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL),
-    BUILTIN("compgen", 0, bin_compgen, 1, -1, 0, NULL, NULL),
-    BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL),
-    BUILTIN("compset", 0, bin_compset, 1, 3, 0, NULL, NULL),
     BUILTIN("compcall", 0, bin_compcall, 0, 0, 0, "TD", NULL),
 };
 
-static struct conddef cotab[] = {
-    CONDDEF("prefix", 0, cond_psfix, 1, 2, CVT_PREPAT),
-    CONDDEF("suffix", 0, cond_psfix, 1, 2, CVT_SUFPAT),
-    CONDDEF("between", 0, cond_range, 2, 2, 1),
-    CONDDEF("after", 0, cond_range, 1, 1, 0),
-};
-
-static struct funcwrap wrapper[] = {
-    WRAPDEF(comp_wrapper),
-};
-
-static struct paramdef patab[] = {
-    PARAMDEF("compmatchers", PM_ARRAY|PM_SPECIAL, NULL, cmsetfn, cmgetfn, cmunsetfn)
-};
-
 /**/
 int
 setup_compctl(Module m)
 {
-    compctltab->printnode = printcompctlp;
-    makecompparamsptr = makecompparams;
-    comp_setunsetptr = comp_setunset;
+    compctlreadptr = compctlread;
+    createcompctltable();
+    cc_compos.mask = CC_COMMPATH;
+    cc_compos.mask2 = 0;
+    cc_default.refc = 10000;
+    cc_default.mask = CC_FILES;
+    cc_default.mask2 = 0;
+    cc_first.refc = 10000;
+    cc_first.mask = 0;
+    cc_first.mask2 = CC_CCCONT;
+
+    lastccused = NULL;
+
     return 0;
 }
 
@@ -2672,12 +3723,10 @@ setup_compctl(Module m)
 int
 boot_compctl(Module m)
 {
-    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)) |
-	 !addwrapper(m, wrapper)))
-	return 1;
-    return 0;
+    addhookfunc("compctl_make", (Hookfn) ccmakehookfn);
+    addhookfunc("compctl_before", (Hookfn) ccbeforehookfn);
+    addhookfunc("compctl_after", (Hookfn) ccafterhookfn);
+    return (addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) != 1);
 }
 
 #ifdef MODULE
@@ -2686,10 +3735,10 @@ boot_compctl(Module m)
 int
 cleanup_compctl(Module m)
 {
+    deletehookfunc("compctl_make", (Hookfn) ccmakehookfn);
+    deletehookfunc("compctl_before", (Hookfn) ccbeforehookfn);
+    deletehookfunc("compctl_after", (Hookfn) ccafterhookfn);
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
-    deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab));
-    deletewrapper(m, wrapper);
     return 0;
 }
 
@@ -2697,9 +3746,12 @@ cleanup_compctl(Module m)
 int
 finish_compctl(Module m)
 {
-    compctltab->printnode = NULL;
-    makecompparamsptr = NULL;
-    comp_setunsetptr = NULL;
+    deletehashtable(compctltab);
+
+    if (lastccused)
+	freelinklist(lastccused, (FreeFunc) freecompctl);
+
+    compctlreadptr = fallback_compctlread;
     return 0;
 }
 
diff --git a/Src/Zle/compctl.h b/Src/Zle/compctl.h
new file mode 100644
index 000000000..9a8ba5692
--- /dev/null
+++ b/Src/Zle/compctl.h
@@ -0,0 +1,160 @@
+/*
+ * comp.h - header file for completion
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#undef compctlread
+
+typedef struct compctlp  *Compctlp;
+typedef struct compctl   *Compctl;
+typedef struct compcond  *Compcond;
+typedef struct patcomp   *Patcomp;
+
+/* node for compctl hash table (compctltab) */
+
+struct compctlp {
+    HashNode next;		/* next in hash chain               */
+    char *nam;			/* command name                     */
+    int flags;			/* CURRENTLY UNUSED                 */
+    Compctl cc;			/* pointer to the compctl desc.     */
+};
+
+/* for the list of pattern compctls */
+
+struct patcomp {
+    Patcomp next;
+    char *pat;
+    Compctl cc;
+};
+
+/* compctl -x condition */
+
+struct compcond {
+    Compcond and, or;		/* the next or'ed/and'ed conditions    */
+    int type;			/* the type (CCT_*)                    */
+    int n;			/* the array length                    */
+    union {			/* these structs hold the data used to */
+	struct {		/* test this condition                 */
+	    int *a, *b;		/* CCT_POS, CCT_NUMWORDS               */
+	}
+	r;
+	struct {		/* CCT_CURSTR, CCT_CURPAT,... */
+	    int *p;
+	    char **s;
+	}
+	s;
+	struct {		/* CCT_RANGESTR,... */
+	    char **a, **b;
+	}
+	l;
+    }
+    u;
+};
+
+#define CCT_UNUSED     0
+#define CCT_POS        1
+#define CCT_CURSTR     2
+#define CCT_CURPAT     3
+#define CCT_WORDSTR    4
+#define CCT_WORDPAT    5
+#define CCT_CURSUF     6
+#define CCT_CURPRE     7
+#define CCT_CURSUB     8
+#define CCT_CURSUBC    9
+#define CCT_NUMWORDS  10
+#define CCT_RANGESTR  11
+#define CCT_RANGEPAT  12
+#define CCT_QUOTE     13
+
+/* Contains the real description for compctls */
+
+struct compctl {
+    int refc;			/* reference count                         */
+    Compctl next;		/* next compctl for -x                     */
+    unsigned long mask, mask2;	/* masks of things to complete (CC_*)      */
+    char *keyvar;		/* for -k (variable)                       */
+    char *glob;			/* for -g (globbing)                       */
+    char *str;			/* for -s (expansion)                      */
+    char *func;			/* for -K (function)                       */
+    char *explain;		/* for -X (explanation)                    */
+    char *ylist;		/* for -y (user-defined desc. for listing) */
+    char *prefix, *suffix;	/* for -P and -S (prefix, suffix)          */
+    char *subcmd;		/* for -l (command name to use)            */
+    char *substr;		/* for -1 (command name to use)            */
+    char *withd;		/* for -w (with directory                  */
+    char *hpat;			/* for -H (history pattern)                */
+    int hnum;			/* for -H (number of events to search)     */
+    char *gname;		/* for -J and -V (group name)              */
+    Compctl ext;		/* for -x (first of the compctls after -x) */
+    Compcond cond;		/* for -x (condition for this compctl)     */
+    Compctl xor;		/* for + (next of the xor'ed compctls)     */
+    Cmatcher matcher;		/* matcher control (-M) */
+    char *mstr;			/* matcher string */
+};
+
+/* objects to complete (mask) */
+#define CC_FILES	(1<<0)
+#define CC_COMMPATH	(1<<1)
+#define CC_REMOVE	(1<<2)
+#define CC_OPTIONS	(1<<3)
+#define CC_VARS		(1<<4)
+#define CC_BINDINGS	(1<<5)
+#define CC_ARRAYS	(1<<6)
+#define CC_INTVARS	(1<<7)
+#define CC_SHFUNCS	(1<<8)
+#define CC_PARAMS	(1<<9)
+#define CC_ENVVARS	(1<<10)
+#define CC_JOBS		(1<<11)
+#define CC_RUNNING	(1<<12)
+#define CC_STOPPED	(1<<13)
+#define CC_BUILTINS	(1<<14)
+#define CC_ALREG	(1<<15)
+#define CC_ALGLOB	(1<<16)
+#define CC_USERS	(1<<17)
+#define CC_DISCMDS	(1<<18)
+#define CC_EXCMDS	(1<<19)
+#define CC_SCALARS	(1<<20)
+#define CC_READONLYS	(1<<21)
+#define CC_SPECIALS	(1<<22)
+#define CC_DELETE	(1<<23)
+#define CC_NAMED	(1<<24)
+#define CC_QUOTEFLAG	(1<<25)
+#define CC_EXTCMDS	(1<<26)
+#define CC_RESWDS	(1<<27)
+#define CC_DIRS		(1<<28)
+
+#define CC_EXPANDEXPL	(1<<30)
+#define CC_RESERVED	(1<<31)
+
+/* objects to complete (mask2) */
+#define CC_NOSORT	(1<<0)
+#define CC_XORCONT	(1<<1)
+#define CC_CCCONT	(1<<2)
+#define CC_PATCONT	(1<<3)
+#define CC_DEFCONT	(1<<4)
+#define CC_UNIQCON      (1<<5)
+#define CC_UNIQALL      (1<<6)
diff --git a/Src/Zle/compctl.mdd b/Src/Zle/compctl.mdd
index c52285c72..1bebb9338 100644
--- a/Src/Zle/compctl.mdd
+++ b/Src/Zle/compctl.mdd
@@ -1,9 +1,6 @@
-moddeps="comp1"
+moddeps="complete"
 
-autobins="compctl compgen compadd compset"
-
-autoprefixconds="prefix suffix between after"
-
-autoparams="compmatchers"
+autobins="compctl"
 
+headers="compctl.h"
 objects="compctl.o"
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
new file mode 100644
index 000000000..283b8de62
--- /dev/null
+++ b/Src/Zle/complete.c
@@ -0,0 +1,1332 @@
+/*
+ * complete.c - the complete module
+ *
+ * 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"
+#include "complete.pro"
+#define GLOBAL_PROTOTYPES
+#include "zle_tricky.pro"
+#undef GLOBAL_PROTOTYPES
+
+/**/
+void
+freecmlist(Cmlist l)
+{
+    Cmlist n;
+
+    while (l) {
+	n = l->next;
+	freecmatcher(l->matcher);
+	zsfree(l->str);
+
+	zfree(l, sizeof(struct cmlist));
+
+	l = n;
+    }
+}
+
+/**/
+void
+freecmatcher(Cmatcher m)
+{
+    Cmatcher n;
+
+    if (!m || --(m->refc))
+	return;
+
+    while (m) {
+	n = m->next;
+	freecpattern(m->line);
+	freecpattern(m->word);
+	freecpattern(m->left);
+	freecpattern(m->right);
+
+	zfree(m, sizeof(struct cmatcher));
+
+	m = n;
+    }
+}
+
+/**/
+void
+freecpattern(Cpattern p)
+{
+    Cpattern n;
+
+    while (p) {
+	n = p->next;
+	zfree(p, sizeof(struct cpattern));
+
+	p = n;
+    }
+}
+
+/* Copy a list of completion matchers. */
+
+static Cmlist
+cpcmlist(Cmlist l)
+{
+    Cmlist r = NULL, *p = &r, n;
+
+    while (l) {
+	*p = n = (Cmlist) zalloc(sizeof(struct cmlist));
+	n->next = NULL;
+	n->matcher = cpcmatcher(l->matcher);
+	n->str = ztrdup(l->str);
+
+	p = &(n->next);
+	l = l->next;
+    }
+    return r;
+}
+
+/* Copy a completion matcher list. */
+
+/**/
+Cmatcher
+cpcmatcher(Cmatcher m)
+{
+    Cmatcher r = NULL, *p = &r, n;
+
+    while (m) {
+	*p = n = (Cmatcher) zalloc(sizeof(struct cmatcher));
+
+	n->refc = 1;
+	n->next = NULL;
+	n->flags = m->flags;
+	n->line = cpcpattern(m->line);
+	n->llen = m->llen;
+	n->word = cpcpattern(m->word);
+	n->wlen = m->wlen;
+	n->left = cpcpattern(m->left);
+	n->lalen = m->lalen;
+	n->right = cpcpattern(m->right);
+	n->ralen = m->ralen;
+
+	p = &(n->next);
+	m = m->next;
+    }
+    return r;
+}
+
+/* Copy a completion matcher pattern. */
+
+/**/
+static Cpattern
+cpcpattern(Cpattern o)
+{
+    Cpattern r = NULL, *p = &r, n;
+
+    while (o) {
+	*p = n = (Cpattern) zalloc(sizeof(struct cpattern));
+
+	n->next = NULL;
+	memcpy(n->tab, o->tab, 256);
+	n->equiv = o->equiv;
+
+	p = &(n->next);
+	o = o->next;
+    }
+    return r;
+}
+
+/* Set the global match specs. */
+
+/**/
+int
+set_gmatcher(char *name, char **argv)
+{
+    Cmlist l = NULL, *q = &l, n;
+    Cmatcher m;
+
+    while (*argv) {
+	if ((m = parse_cmatcher(name, *argv)) == pcm_err)
+	    return 1;
+	*q = n = (Cmlist) zhalloc(sizeof(struct cmlist));
+	n->next = NULL;
+	n->matcher = m;
+	n->str = *argv++;
+
+	q = &(n->next);
+    }
+    freecmlist(cmatcher);
+    PERMALLOC {
+	cmatcher = cpcmlist(l);
+    } LASTALLOC;
+
+    return 1;
+}
+
+/* Parse a string for matcher control, containing multiple matchers. */
+
+/**/
+Cmatcher
+parse_cmatcher(char *name, char *s)
+{
+    Cmatcher ret = NULL, r = NULL, n;
+    Cpattern line, word, left, right;
+    int fl, ll, wl, lal, ral, err;
+
+    if (!*s)
+	return NULL;
+
+    while (*s) {
+	while (*s && inblank(*s)) s++;
+
+	if (!*s) break;
+
+	switch (*s) {
+	case 'l': fl = CMF_LEFT; break;
+	case 'r': fl = CMF_RIGHT; break;
+	case 'm': fl = 0; break;
+	case 'L': fl = CMF_LEFT | CMF_LINE; break;
+	case 'R': fl = CMF_RIGHT | CMF_LINE; break;
+	case 'M': fl = CMF_LINE; break;
+	default:
+	    zwarnnam(name, "unknown match specification character `%c'", NULL, *s);
+	    return pcm_err;
+	}
+	if (s[1] != ':') {
+	    zwarnnam(name, "missing `:'", NULL, 0);
+	    return pcm_err;
+	}
+	s += 2;
+	if (!*s) {
+	    zwarnnam(name, "missing patterns", NULL, 0);
+	    return pcm_err;
+	}
+	if (fl & CMF_LEFT) {
+	    left = parse_pattern(name, &s, &lal, '|', &err);
+	    if (err)
+		return pcm_err;
+	    if (!*s || !*++s) {
+		zwarnnam(name, "missing line pattern", NULL, 0);
+		return pcm_err;
+	    }
+	} else
+	    left = NULL;
+
+	line = parse_pattern(name, &s, &ll, ((fl & CMF_RIGHT) ? '|' : '='),
+			     &err);
+	if (err)
+	    return pcm_err;
+	if ((fl & CMF_RIGHT) && (!*s || !*++s)) {
+	    zwarnnam(name, "missing right anchor", NULL, 0);
+	} else if (!(fl & CMF_RIGHT)) {
+	    if (!*s) {
+		zwarnnam(name, "missing word pattern", NULL, 0);
+		return pcm_err;
+	    }
+	    s++;
+	}
+	if (fl & CMF_RIGHT) {
+	    right = parse_pattern(name, &s, &ral, '=', &err);
+	    if (err)
+		return pcm_err;
+	    if (!*s) {
+		zwarnnam(name, "missing word pattern", NULL, 0);
+		return pcm_err;
+	    }
+	    s++;
+	} else
+	    right = NULL;
+
+	if (*s == '*') {
+	    if (!(fl & (CMF_LEFT | CMF_RIGHT))) {
+		zwarnnam(name, "need anchor for `*'", NULL, 0);
+		return pcm_err;
+	    }
+	    word = NULL;
+	    wl = -1;
+	    s++;
+	} else {
+	    word = parse_pattern(name, &s, &wl, 0, &err);
+
+	    if (!word && !line) {
+		zwarnnam(name, "need non-empty word or line pattern", NULL, 0);
+		return pcm_err;
+	    }
+	}
+	if (err)
+	    return pcm_err;
+
+	n = (Cmatcher) zcalloc(sizeof(*ret));
+	n->next = NULL;
+	n->flags = fl;
+	n->line = line;
+	n->llen = ll;
+	n->word = word;
+	n->wlen = wl;
+	n->left = left;
+	n->lalen = lal;
+	n->right = right;
+	n->ralen = ral;
+
+	if (ret)
+	    r->next = n;
+	else
+	    ret = n;
+
+	r = n;
+    }
+    return ret;
+}
+
+/* Parse a pattern for matcher control. */
+
+/**/
+static Cpattern
+parse_pattern(char *name, char **sp, int *lp, char e, int *err)
+{
+    Cpattern ret = NULL, r = NULL, n;
+    unsigned char *s = (unsigned char *) *sp;
+    int l = 0;
+
+    *err = 0;
+
+    while (*s && (e ? (*s != e) : !inblank(*s))) {
+	n = (Cpattern) hcalloc(sizeof(*n));
+	n->next = NULL;
+	n->equiv = 0;
+
+	if (*s == '[') {
+	    s = parse_class(n, s + 1, ']');
+	    if (!*s) {
+		*err = 1;
+		zwarnnam(name, "unterminated character class", NULL, 0);
+		return NULL;
+	    }
+	} else if (*s == '{') {
+	    n->equiv = 1;
+	    s = parse_class(n, s + 1, '}');
+	    if (!*s) {
+		*err = 1;
+		zwarnnam(name, "unterminated character class", NULL, 0);
+		return NULL;
+	    }
+	} else if (*s == '?') {
+	    memset(n->tab, 1, 256);
+	} else if (*s == '*' || *s == '(' || *s == ')' || *s == '=') {
+	    *err = 1;
+	    zwarnnam(name, "invalid pattern character `%c'", NULL, *s);
+	    return NULL;
+	} else {
+	    if (*s == '\\' && s[1])
+		s++;
+
+	    memset(n->tab, 0, 256);
+	    n->tab[*s] = 1;
+	}
+	if (ret)
+	    r->next = n;
+	else
+	    ret = n;
+
+	r = n;
+
+	l++;
+	s++;
+    }
+    *sp = (char *) s;
+    *lp = l;
+    return ret;
+}
+
+/* Parse a character class for matcher control. */
+
+/**/
+static unsigned char *
+parse_class(Cpattern p, unsigned char *s, unsigned char e)
+{
+    int n = 0, i = 1, j, eq = (e == '}'), k = 1;
+
+    if (!eq && (*s == '!' || *s == '^') && s[1] != e) { n = 1; s++; }
+
+    memset(p->tab, n, 256);
+
+    n = !n;
+    while (*s && (k || *s != e)) {
+	if (s[1] == '-' && s[2] != e) {
+	    /* a run of characters */
+	    for (j = (int) *s; j <= (int) s[2]; j++)
+		p->tab[j] = (eq ? i++ : n);
+
+	    s += 3;
+	} else
+	    p->tab[*s++] = (eq ? i++ : n);
+	k = 0;
+    }
+    return s;
+}
+
+/**/
+static int
+bin_compadd(char *name, char **argv, char *ops, int func)
+{
+    struct cadata dat;
+    char *p, **sp, *e, *m = NULL;
+    int dm;
+    Cmatcher match = NULL;
+
+    if (incompfunc != 1) {
+	zerrnam(name, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
+    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.match = NULL;
+    dat.flags = 0;
+    dat.aflags = CAF_MATCH;
+
+    for (; *argv && **argv ==  '-'; argv++) {
+	if (!(*argv)[1]) {
+	    argv++;
+	    break;
+	}
+	for (p = *argv + 1; *p; p++) {
+	    sp = NULL;
+	    e = NULL;
+	    dm = 0;
+	    switch (*p) {
+	    case 'q':
+		dat.flags |= CMF_REMOVE;
+		break;
+	    case 'Q':
+		dat.aflags |= CAF_QUOTE;
+		break;
+	    case 'f':
+		dat.flags |= CMF_FILE;
+		break;
+	    case 'e':
+		dat.flags |= CMF_ISPAR;
+		break;
+	    case 'F':
+		sp = &(dat.ign);
+		e = "string expected after -%c";
+		break;
+	    case 'n':
+		dat.flags |= CMF_NOLIST;
+		break;
+	    case 'U':
+		dat.aflags &= ~CAF_MATCH;
+		break;
+	    case 'P':
+		sp = &(dat.pre);
+		e = "string expected after -%c";
+		break;
+	    case 'S':
+		sp = &(dat.suf);
+		e = "string expected after -%c";
+		break;
+	    case 'J':
+		sp = &(dat.group);
+		e = "group name expected after -%c";
+		break;
+	    case 'V':
+		if (!dat.group)
+		    dat.aflags |= CAF_NOSORT;
+		sp = &(dat.group);
+		e = "group name expected after -%c";
+		break;
+	    case '1':
+		if (!(dat.aflags & CAF_UNIQCON))
+		    dat.aflags |= CAF_UNIQALL;
+		break;
+	    case '2':
+		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";
+		break;
+	    case 'I':
+		sp = &(dat.isuf);
+		e = "string expected after -%c";
+		break;
+	    case 'p':
+		sp = &(dat.ppre);
+		e = "string expected after -%c";
+		break;
+	    case 's':
+		sp = &(dat.psuf);
+		e = "string expected after -%c";
+		break;
+	    case 'W':
+		sp = &(dat.prpre);
+		e = "string expected after -%c";
+		break;
+	    case 'a':
+		dat.aflags |= CAF_ALT;
+		break;
+	    case 'M':
+		sp = &m;
+		e = "matching specification expected after -%c";
+		dm = 1;
+		break;
+	    case 'X':
+		sp = &(dat.exp);
+		e = "string expected after -%c";
+		break;
+	    case 'r':
+		dat.flags |= CMF_REMOVE;
+		sp = &(dat.rems);
+		e = "string expected after -%c";
+		break;
+	    case 'R':
+		dat.flags |= CMF_REMOVE;
+		sp = &(dat.remf);
+		e = "function name expected after -%c";
+		break;
+	    case 'A':
+		sp = &(dat.apar);
+		e = "parameter name expected after -%c";
+		break;
+	    case 'O':
+		sp = &(dat.opar);
+		e = "parameter name expected after -%c";
+		break;
+	    case 'D':
+		sp = &(dat.dpar);
+		e = "parameter name expected after -%c";
+		break;
+	    case 'd':
+		sp = &(dat.disp);
+		e = "parameter name expected after -%c";
+		break;
+	    case 'l':
+		dat.flags |= CMF_DISPLINE;
+		break;
+	    case '-':
+		argv++;
+		goto ca_args;
+	    default:
+		zerrnam(name, "bad option: -%c", NULL, *p);
+		return 1;
+	    }
+	    if (sp) {
+		if (p[1]) {
+		    if (!*sp)
+			*sp = p + 1;
+		    p = "" - 1;
+		} else if (argv[1]) {
+		    argv++;
+		    if (!*sp)
+			*sp = *argv;
+		    p = "" - 1;
+		} else {
+		    zerrnam(name, e, NULL, *p);
+		    return 1;
+		}
+		if (dm && (match = parse_cmatcher(name, m)) == pcm_err) {
+		    match = NULL;
+		    return 1;
+		}
+	    }
+	}
+    }
+ ca_args:
+    if (!*argv)
+	return 1;
+
+    dat.match = match = cpcmatcher(match);
+    dm = addmatches(&dat, argv);
+    freecmatcher(match);
+
+    return dm;
+}
+
+#define CVT_RANGENUM 0
+#define CVT_RANGEPAT 1
+#define CVT_PRENUM   2
+#define CVT_PREPAT   3
+#define CVT_SUFNUM   4
+#define CVT_SUFPAT   5
+
+/**/
+void
+ignore_prefix(int l)
+{
+    if (l) {
+	char *tmp, sav;
+	int pl = strlen(compprefix);
+
+	if (l > pl)
+	    l = pl;
+
+	sav = compprefix[l];
+
+	compprefix[l] = '\0';
+	tmp = tricat(compiprefix, compprefix, "");
+	zsfree(compiprefix);
+	compiprefix = tmp;
+	compprefix[l] = sav;
+	tmp = ztrdup(compprefix + l);
+	zsfree(compprefix);
+	compprefix = tmp;
+    }
+}
+
+/**/
+void
+ignore_suffix(int l)
+{
+    if (l) {
+	char *tmp, sav;
+	int sl = strlen(compsuffix);
+
+	if ((l = sl - l) < 0)
+	    l = 0;
+
+	tmp = tricat(compsuffix + l, compisuffix, "");
+	zsfree(compisuffix);
+	compisuffix = tmp;
+	sav = compsuffix[l];
+	compsuffix[l] = '\0';
+	tmp = ztrdup(compsuffix);
+	compsuffix[l] = sav;
+	zsfree(compsuffix);
+	compsuffix = tmp;
+    }
+}
+
+/**/
+void
+restrict_range(int b, int e)
+{
+    int wl = arrlen(compwords) - 1;
+
+    if (wl && b >= 0 && e >= 0 && (b > 0 || e < wl)) {
+	int i;
+	char **p, **q, **pp;
+
+	if (e > wl)
+	    e = wl;
+
+	i = e - b + 1;
+	p = (char **) zcalloc((i + 1) * sizeof(char *));
+
+	for (q = p, pp = compwords + b; i; i--, q++, pp++)
+	    *q = ztrdup(*pp);
+	freearray(compwords);
+	compwords = p;
+	compcurrent -= b;
+    }
+}
+
+static int
+do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
+{
+    switch (test) {
+    case CVT_RANGENUM:
+	{
+	    int l = arrlen(compwords);
+
+	    if (na < 0)
+		na += l;
+	    else
+		na--;
+	    if (nb < 0)
+		nb += l;
+	    else
+		nb--;
+
+	    if (compcurrent - 1 < na || compcurrent - 1 > nb)
+		return 0;
+	    if (mod)
+		restrict_range(na, nb);
+	    return 1;
+	}
+    case CVT_RANGEPAT:
+	{
+	    char **p;
+	    int i, l = arrlen(compwords), t = 0, b = 0, e = l - 1;
+	    Patprog pp;
+
+	    i = compcurrent - 1;
+	    if (i < 0 || i >= l)
+		return 0;
+
+	    singsub(&sa);
+	    pp = patcompile(sa, PAT_STATIC, NULL);
+
+	    for (i--, p = compwords + i; i >= 0; p--, i--) {
+		if (pattry(pp, *p)) {
+		    b = i + 1;
+		    t = 1;
+		    break;
+		}
+	    }
+	    if (t && sb) {
+		int tt = 0;
+
+		singsub(&sb);
+		pp = patcompile(sb, PAT_STATIC, NULL);
+
+		for (i++, p = compwords + i; i < l; p++, i++) {
+		    if (pattry(pp, *p)) {
+			e = i - 1;
+			tt = 1;
+			break;
+		    }
+		}
+		if (tt && i < compcurrent)
+		    t = 0;
+	    }
+	    if (e < b)
+		t = 0;
+	    if (t && mod)
+		restrict_range(b, e);
+	    return t;
+	}
+    case CVT_PRENUM:
+    case CVT_SUFNUM:
+	if (!na)
+	    return 1;
+	if (na > 0 &&
+	    strlen(test == CVT_PRENUM ? compprefix : compsuffix) >= na) {
+	    if (mod) {
+		if (test == CVT_PRENUM)
+		    ignore_prefix(na);
+		else
+		    ignore_suffix(na);
+		return 1;
+	    }
+	    return 0;
+	}
+    case CVT_PREPAT:
+    case CVT_SUFPAT:
+	{
+	    Patprog pp;
+
+	    if (!na)
+		return 0;
+
+	    if (!(pp = patcompile(sa, PAT_STATIC, 0)))
+		return 0;
+
+	    if (test == CVT_PREPAT) {
+		int l, add;
+		char *p, sav;
+
+		if (!(l = strlen(compprefix)))
+		    return 0;
+		if (na < 0) {
+		    p = compprefix + l;
+		    na = -na;
+		    add = -1;
+		} else {
+		    p = compprefix + 1;
+		    add = 1;
+		}
+		for (; l; l--, p += add) {
+		    sav = *p;
+		    *p = '\0';
+		    test = pattry(pp, compprefix);
+		    *p = sav;
+		    if (test && !--na)
+			break;
+		}
+		if (!l)
+		    return 0;
+		if (mod)
+		    ignore_prefix(p - compprefix);
+	    } else {
+		int l, ol, add;
+		char *p;
+
+		if (!(ol = l = strlen(compsuffix)))
+		    return 0;
+		if (na < 0) {
+		    p = compsuffix;
+		    na = -na;
+		    add = 1;
+		} else {
+		    p = compsuffix + l - 1;
+		    add = -1;
+		}
+		for (; l; l--, p += add)
+		    if (pattry(pp, p) && !--na)
+			break;
+
+		if (!l)
+		    return 0;
+		if (mod)
+		    ignore_suffix(ol - (p - compsuffix));
+	    }
+	    return 1;
+	}
+    }
+    return 0;
+}
+
+/**/
+static int
+bin_compset(char *name, char **argv, char *ops, int func)
+{
+    int test = 0, na = 0, nb = 0;
+    char *sa = NULL, *sb = NULL;
+
+    if (incompfunc != 1) {
+	zerrnam(name, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
+    if (argv[0][0] != '-') {
+	zerrnam(name, "missing option", NULL, 0);
+	return 1;
+    }
+    switch (argv[0][1]) {
+    case 'n': test = CVT_RANGENUM; break;
+    case 'N': test = CVT_RANGEPAT; break;
+    case 'p': test = CVT_PRENUM; break;
+    case 'P': test = CVT_PREPAT; break;
+    case 's': test = CVT_SUFNUM; break;
+    case 'S': test = CVT_SUFPAT; break;
+    case 'q': return set_comp_sep();
+    default:
+	zerrnam(name, "bad option -%c", NULL, argv[0][1]);
+	return 1;
+    }
+    if (argv[0][2]) {
+	sa = argv[0] + 2;
+	sb = argv[1];
+	na = 2;
+    } else {
+	if (!(sa = argv[1])) {
+	    zerrnam(name, "missing string for option -%c", NULL, argv[0][1]);
+	    return 1;
+	}
+	sb = argv[2];
+	na = 3;
+    }
+    if (((test == CVT_PRENUM || test == CVT_SUFNUM) ? !!sb :
+	 (sb && argv[na]))) {
+	zerrnam(name, "too many arguments", NULL, 0);
+	return 1;
+    }
+    switch (test) {
+    case CVT_RANGENUM:
+	na = atoi(sa);
+	nb = (sb ? atoi(sb) : -1);
+	break;
+    case CVT_RANGEPAT:
+	tokenize(sa);
+	sa = rembslash(sa);
+	remnulargs(sa);
+	if (sb) {
+	    tokenize(sb);
+	    sb = rembslash(sb);
+	    remnulargs(sb);
+	}
+	break;
+    case CVT_PRENUM:
+    case CVT_SUFNUM:
+	na = atoi(sa);
+	break;
+    case CVT_PREPAT:
+    case CVT_SUFPAT:
+	if (sb) {
+	    na = atoi(sa);
+	    sa = sb;
+	} else
+	    na = -1;
+	tokenize(sa);
+	sa = rembslash(sa);
+	remnulargs(sa);
+	break;
+    }
+    return !do_comp_vars(test, na, sa, nb, sb, 1);
+}
+
+/* Definitions for the special parameters. Note that these have to match the
+ * order of the CP_* bits in comp.h */
+
+#define VAL(X) ((void *) (&(X)))
+struct compparam {
+    char *name;
+    int type;
+    void *var, *set, *get;
+};
+
+static struct compparam comprparams[] = {
+    { "words", PM_ARRAY, VAL(compwords), NULL, NULL },
+    { "CURRENT", PM_INTEGER, VAL(compcurrent), NULL, NULL },
+    { "PREFIX", PM_SCALAR, VAL(compprefix), NULL, NULL },
+    { "SUFFIX", PM_SCALAR, VAL(compsuffix), NULL, NULL },
+    { "IPREFIX", PM_SCALAR, VAL(compiprefix), NULL, NULL },
+    { "ISUFFIX", PM_SCALAR, VAL(compisuffix), NULL, NULL },
+    { "QIPREFIX", PM_SCALAR | PM_READONLY, VAL(compqiprefix), NULL, NULL },
+    { "QISUFFIX", PM_SCALAR | PM_READONLY, VAL(compqisuffix), NULL, NULL },
+    { NULL, 0, NULL, NULL, NULL }
+};
+
+static struct compparam compkparams[] = {
+    { "nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_nmatches) },
+    { "matcher", PM_INTEGER, VAL(compmatcher), NULL, NULL },
+    { "matcher_string", PM_SCALAR, VAL(compmatcherstr), NULL, NULL },
+    { "total_matchers", PM_INTEGER, VAL(compmatchertot), NULL, NULL },
+    { "context", PM_SCALAR, VAL(compcontext), NULL, NULL },
+    { "parameter", PM_SCALAR, VAL(compparameter), NULL, NULL },
+    { "redirect", PM_SCALAR, VAL(compredirect), NULL, NULL },
+    { "quote", PM_SCALAR | PM_READONLY, VAL(compquote), NULL, NULL },
+    { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL, NULL },
+    { "restore", PM_SCALAR, VAL(comprestore), NULL, NULL },
+    { "list", PM_SCALAR, NULL, VAL(set_complist), VAL(get_complist) },
+    { "force_list", PM_SCALAR, VAL(compforcelist), NULL, NULL },
+    { "insert", PM_SCALAR, VAL(compinsert), NULL, NULL },
+    { "exact", PM_SCALAR, VAL(compexact), NULL, NULL },
+    { "exact_string", PM_SCALAR, VAL(compexactstr), NULL, NULL },
+    { "pattern_match", PM_SCALAR, VAL(comppatmatch), NULL, NULL },
+    { "pattern_insert", PM_SCALAR, VAL(comppatinsert), NULL, NULL },
+    { "unambiguous", PM_SCALAR | PM_READONLY, NULL, NULL, VAL(get_unambig) },
+    { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL, NULL,
+      VAL(get_unambig_curs) },
+    { "list_max", PM_INTEGER, VAL(complistmax), NULL, NULL },
+    { "last_prompt", PM_SCALAR, VAL(complastprompt), NULL, NULL },
+    { "to_end", PM_SCALAR, VAL(comptoend), NULL, NULL },
+    { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL },
+    { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL },
+    { "vared", PM_SCALAR, VAL(compvared), NULL, NULL },
+    { "alternate_nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_anmatches) },
+    { "list_lines", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_listlines) },
+    { NULL, 0, NULL, NULL, NULL }
+};
+
+#define COMPSTATENAME "compstate"
+
+static void
+addcompparams(struct compparam *cp, Param *pp)
+{
+    for (; cp->name; cp++, pp++) {
+	Param pm = createparam(cp->name,
+			       cp->type |PM_SPECIAL|PM_REMOVABLE|PM_LOCAL);
+	if (!pm)
+	    pm = (Param) paramtab->getnode(paramtab, cp->name);
+	DPUTS(!pm, "param not set in addcompparams");
+
+	*pp = pm;
+	pm->level = locallevel + 1;
+	if ((pm->u.data = cp->var)) {
+	    switch(PM_TYPE(cp->type)) {
+	    case PM_SCALAR:
+		pm->sets.cfn = strvarsetfn;
+		pm->gets.cfn = strvargetfn;
+		break;
+	    case PM_INTEGER:
+		pm->sets.ifn = intvarsetfn;
+		pm->gets.ifn = intvargetfn;
+		pm->ct = 10;
+		break;
+	    case PM_ARRAY:
+		pm->sets.afn = arrvarsetfn;
+		pm->gets.afn = arrvargetfn;
+		break;
+	    }
+	} else {
+	    pm->sets.cfn = (void (*) _((Param, char *))) cp->set;
+	    pm->gets.cfn = (char *(*) _((Param))) cp->get;
+	}
+	pm->unsetfn = compunsetfn;
+    }
+}
+
+/**/
+void
+makecompparams(void)
+{
+    Param cpm;
+    HashTable tht;
+
+    addcompparams(comprparams, comprpms);
+
+    if (!(cpm = createparam(COMPSTATENAME,
+			    PM_SPECIAL|PM_REMOVABLE|PM_LOCAL|PM_HASHED)))
+	cpm = (Param) paramtab->getnode(paramtab, COMPSTATENAME);
+    DPUTS(!cpm, "param not set in makecompparams");
+
+    comprpms[CPN_COMPSTATE] = cpm;
+    tht = paramtab;
+    cpm->level = locallevel;
+    cpm->gets.hfn = get_compstate;
+    cpm->sets.hfn = set_compstate;
+    cpm->unsetfn = compunsetfn;
+    cpm->u.hash = paramtab = newparamtable(31, COMPSTATENAME);
+    addcompparams(compkparams, compkpms);
+    paramtab = tht;
+}
+
+/**/
+static HashTable
+get_compstate(Param pm)
+{
+    return pm->u.hash;
+}
+
+/**/
+static void
+set_compstate(Param pm, HashTable ht)
+{
+    struct compparam *cp;
+    Param *pp;
+    HashNode hn;
+    int i;
+    struct value v;
+    char *str;
+
+    for (i = 0; i < ht->hsize; i++)
+	for (hn = ht->nodes[i]; hn; hn = hn->next)
+	    for (cp = compkparams,
+		 pp = compkpms; cp->name; cp++, pp++)
+		if (!strcmp(hn->nam, cp->name)) {
+		    v.isarr = v.inv = v.a = 0;
+		    v.b = -1;
+		    v.arr = NULL;
+		    v.pm = (Param) hn;
+		    if (cp->type == PM_INTEGER)
+			*((zlong *) cp->var) = getintvalue(&v);
+		    else if ((str = getstrvalue(&v))) {
+			zsfree(*((char **) cp->var));
+			*((char **) cp->var) = ztrdup(str);
+		    }
+		    (*pp)->flags &= ~PM_UNSET;
+
+		    break;
+		}
+    deleteparamtable(ht);
+}
+
+/**/
+static zlong
+get_nmatches(Param pm)
+{
+    return num_matches(1);
+}
+
+/**/
+static zlong
+get_anmatches(Param pm)
+{
+    return num_matches(0);
+}
+
+/**/
+static zlong
+get_listlines(Param pm)
+{
+    return list_lines();
+}
+
+/**/
+static void
+set_complist(Param pm, char *v)
+{
+    comp_list(v);
+}
+
+/**/
+static char *
+get_complist(Param pm)
+{
+    return complist;
+}
+
+/**/
+static char *
+get_unambig(Param pm)
+{
+    return unambig_data(NULL);
+}
+
+/**/
+static zlong
+get_unambig_curs(Param pm)
+{
+    int c;
+
+    unambig_data(&c);
+
+    return c;
+}
+
+/**/
+static void
+compunsetfn(Param pm, int exp)
+{
+    if (exp) {
+	if (PM_TYPE(pm->flags) == PM_SCALAR) {
+	    zsfree(*((char **) pm->u.data));
+	    *((char **) pm->u.data) = ztrdup("");
+	} else if (PM_TYPE(pm->flags) == PM_ARRAY) {
+	    freearray(*((char ***) pm->u.data));
+	    *((char ***) pm->u.data) = zcalloc(sizeof(char *));
+	}
+	pm->flags |= PM_UNSET;
+    }
+}
+
+/**/
+void
+comp_setunset(int rset, int runset, int kset, int kunset)
+{
+    Param *p;
+
+    if (comprpms && (rset >= 0 || runset >= 0)) {
+	for (p = comprpms; rset || runset; rset >>= 1, runset >>= 1, p++) {
+	    if (rset & 1)
+		(*p)->flags &= ~PM_UNSET;
+	    if (runset & 1)
+		(*p)->flags |= PM_UNSET;
+	}
+    }
+    if (comprpms && (kset >= 0 || kunset >= 0)) {
+	for (p = compkpms; kset || kunset; kset >>= 1, kunset >>= 1, p++) {
+	    if (kset & 1)
+		(*p)->flags &= ~PM_UNSET;
+	    if (kunset & 1)
+		(*p)->flags |= PM_UNSET;
+	}
+    }
+}
+
+/**/
+static int
+comp_wrapper(List list, FuncWrap w, char *name)
+{
+    if (incompfunc != 1)
+	return 1;
+    else {
+	char *orest, *opre, *osuf, *oipre, *oisuf, **owords;
+	char *oqipre, *oqisuf, *oq, *oqi;
+	zlong ocur;
+	unsigned int runset = 0, kunset = 0, m, sm;
+	Param *pp;
+
+	m = CP_WORDS | CP_CURRENT | CP_PREFIX | CP_SUFFIX | 
+	    CP_IPREFIX | CP_ISUFFIX | CP_QIPREFIX | CP_QISUFFIX;
+	for (pp = comprpms, sm = 1; m; pp++, m >>= 1, sm <<= 1) {
+	    if ((m & 1) && ((*pp)->flags & PM_UNSET))
+		runset |= sm;
+	}
+	if (compkpms[CPN_RESTORE]->flags & PM_UNSET)
+	    kunset = CP_RESTORE;
+	orest = comprestore;
+	comprestore = ztrdup("auto");
+	ocur = compcurrent;
+	opre = dupstring(compprefix);
+	osuf = dupstring(compsuffix);
+	oipre = dupstring(compiprefix);
+	oisuf = dupstring(compisuffix);
+	oqipre = dupstring(compqiprefix);
+	oqisuf = dupstring(compqisuffix);
+	oq = dupstring(compquote);
+	oqi = dupstring(compquoting);
+
+	HEAPALLOC {
+	    owords = arrdup(compwords);
+	} LASTALLOC;
+
+	runshfunc(list, w, name);
+
+	if (comprestore && !strcmp(comprestore, "auto")) {
+	    compcurrent = ocur;
+	    zsfree(compprefix);
+	    compprefix = ztrdup(opre);
+	    zsfree(compsuffix);
+	    compsuffix = ztrdup(osuf);
+	    zsfree(compiprefix);
+	    compiprefix = ztrdup(oipre);
+	    zsfree(compisuffix);
+	    compisuffix = ztrdup(oisuf);
+	    zsfree(compqiprefix);
+	    compqiprefix = ztrdup(oqipre);
+	    zsfree(compqisuffix);
+	    compqisuffix = ztrdup(oqisuf);
+	    zsfree(compquote);
+	    compquote = ztrdup(oq);
+	    zsfree(compquoting);
+	    compquoting = ztrdup(oqi);
+	    freearray(compwords);
+	    PERMALLOC {
+		compwords = arrdup(owords);
+	    } LASTALLOC;
+	    comp_setunset(CP_COMPSTATE |
+			  (~runset & (CP_WORDS | CP_CURRENT | CP_PREFIX |
+				     CP_SUFFIX | CP_IPREFIX | CP_ISUFFIX |
+				     CP_QIPREFIX | CP_QISUFFIX)),
+			  (runset & CP_ALLREALS),
+			  (~kunset & CP_RESTORE), (kunset & CP_ALLKEYS));
+	} else
+	    comp_setunset(CP_COMPSTATE, 0, (~kunset & CP_RESTORE),
+			  (kunset & CP_RESTORE));
+	zsfree(comprestore);
+	comprestore = orest;
+
+	return 0;
+    }
+}
+
+/**/
+static int
+comp_check(void)
+{
+    if (incompfunc != 1) {
+	zerr("condition can only be used in completion function", NULL, 0);
+	return 0;
+    }
+    return 1;
+}
+
+/**/
+static int
+cond_psfix(char **a, int id)
+{
+    if (comp_check()) {
+	if (a[1])
+	    return do_comp_vars(id, cond_val(a, 0), cond_str(a, 1, 1),
+				0, NULL, 0);
+	else
+	    return do_comp_vars(id, -1, cond_str(a, 0, 1), 0, NULL, 0);
+    }
+    return 0;
+}
+
+/**/
+static int
+cond_range(char **a, int id)
+{
+    return do_comp_vars(CVT_RANGEPAT, 0, cond_str(a, 0, 1), 0,
+			(id ? cond_str(a, 1, 1) : NULL), 0);
+}
+
+/**/
+static void
+cmsetfn(Param pm, char **v)
+{
+    set_gmatcher(pm->nam, v);
+}
+
+/**/
+static char **
+cmgetfn(Param pm)
+{
+    int num;
+    Cmlist p;
+    char **ret, **q;
+
+    for (num = 0, p = cmatcher; p; p = p->next, num++);
+
+    ret = (char **) zhalloc((num + 1) * sizeof(char *));
+
+    for (q = ret, p = cmatcher; p; p = p->next, q++)
+	*q = dupstring(p->str);
+    *q = NULL;
+
+    return ret;
+}
+
+/**/
+static void
+cmunsetfn(Param pm, int exp)
+{
+    char *dummy[1];
+
+    dummy[0] = NULL;
+    set_gmatcher(pm->nam, dummy);
+}
+
+static struct builtin bintab[] = {
+    BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL),
+    BUILTIN("compset", 0, bin_compset, 1, 3, 0, NULL, NULL),
+};
+
+static struct conddef cotab[] = {
+    CONDDEF("prefix", 0, cond_psfix, 1, 2, CVT_PREPAT),
+    CONDDEF("suffix", 0, cond_psfix, 1, 2, CVT_SUFPAT),
+    CONDDEF("between", 0, cond_range, 2, 2, 1),
+    CONDDEF("after", 0, cond_range, 1, 1, 0),
+};
+
+static struct funcwrap wrapper[] = {
+    WRAPDEF(comp_wrapper),
+};
+
+static struct paramdef patab[] = {
+    PARAMDEF("compmatchers", PM_ARRAY|PM_SPECIAL, NULL, cmsetfn, cmgetfn, cmunsetfn)
+};
+
+/**/
+int
+setup_complete(Module m)
+{
+    makecompparamsptr = makecompparams;
+    comp_setunsetptr = comp_setunset;
+
+    return 0;
+}
+
+/**/
+int
+boot_complete(Module m)
+{
+    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)) |
+	  !addwrapper(m, wrapper)))
+	return 1;
+    return 0;
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_complete(Module m)
+{
+    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
+    deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab));
+    deletewrapper(m, wrapper);
+    return 0;
+}
+
+/**/
+int
+finish_complete(Module m)
+{
+    makecompparamsptr = NULL;
+    comp_setunsetptr = NULL;
+
+    return 0;
+}
+
+#endif
diff --git a/Src/Zle/complete.mdd b/Src/Zle/complete.mdd
new file mode 100644
index 000000000..628058e2a
--- /dev/null
+++ b/Src/Zle/complete.mdd
@@ -0,0 +1,11 @@
+hasexport=1
+
+moddeps="zle"
+
+autobins="compgen compadd compset"
+
+autoprefixconds="prefix suffix between after"
+
+autoparams="compmatchers"
+
+objects="complete.o"
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 2ffde6b48..dc1037122 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -415,7 +415,7 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
 	    }
 	    putc(file_type(buf->st_mode), shout);
 	    len++;
-	}
+        }
 	if ((len = width - len - 2) > 0) {
 	    if (m->gnum != mselect) {
 		zcoff();
diff --git a/Src/Zle/complist.mdd b/Src/Zle/complist.mdd
index 8ea60b0a8..16cd5f1af 100644
--- a/Src/Zle/complist.mdd
+++ b/Src/Zle/complist.mdd
@@ -1,3 +1,3 @@
-moddeps="comp1 zle"
+moddeps="complete"
 
 objects="complist.o"
diff --git a/Src/Zle/computil.mdd b/Src/Zle/computil.mdd
index e4193610c..56f633414 100644
--- a/Src/Zle/computil.mdd
+++ b/Src/Zle/computil.mdd
@@ -1,4 +1,4 @@
-moddeps="compctl zle"
+moddeps="complete"
 
 objects="computil.o"
 
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index ff515e7c4..470aa890f 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -146,6 +146,17 @@ typedef struct cutbuffer *Cutbuffer;
 
 /* Convenience macros for the hooks */
 
-#define LISTMATCHESHOOK (zlehooks + 0)
-#define INSERTMATCHHOOK (zlehooks + 1)
-#define MENUSTARTHOOK   (zlehooks + 2)
+#define LISTMATCHESHOOK   (zlehooks + 0)
+#define INSERTMATCHHOOK   (zlehooks + 1)
+#define MENUSTARTHOOK     (zlehooks + 2)
+#define COMPCTLMAKEHOOK   (zlehooks + 3)
+#define COMPCTLBEFOREHOOK (zlehooks + 4)
+#define COMPCTLAFTERHOOK  (zlehooks + 5)
+
+/* compctl hook data structs */
+
+struct ccmakedat {
+    char *str;
+    int incmd;
+    int lst;
+};
diff --git a/Src/Zle/zle.mdd b/Src/Zle/zle.mdd
index fec2e213b..95702e948 100644
--- a/Src/Zle/zle.mdd
+++ b/Src/Zle/zle.mdd
@@ -1,5 +1,75 @@
 hasexport=1
 
+autobins="bindkey vared zle"
+
+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"
+
+:<<\Make
+zle_things.h: thingies.list zle_things.sed
+	( \
+	    echo '/** zle_things.h                              **/'; \
+	    echo '/** indices of and pointers to known thingies **/'; \
+	    echo; \
+	    echo 'enum {'; \
+	    sed -n -f $(sdir)/zle_things.sed < thingies.list; \
+	    echo '    ZLE_BUILTIN_THINGY_COUNT'; \
+	    echo '};'; \
+	) > $@
+
+zle_widget.h: widgets.list zle_widget.sed
+	( \
+	    echo '/** zle_widget.h                                **/'; \
+	    echo '/** indices of and pointers to internal widgets **/'; \
+	    echo; \
+	    echo 'enum {'; \
+	    sed -n -f $(sdir)/zle_widget.sed < widgets.list; \
+	    echo '    ZLE_BUILTIN_WIDGET_COUNT'; \
+	    echo '};'; \
+	) > $@
+
+thingies.list: iwidgets.list
+	( \
+	    echo '/** thingies.list                            **/'; \
+	    echo '/** thingy structures for the known thingies **/'; \
+	    echo; \
+	    echo '/* format: T("name", TH_FLAGS, w_widget, t_nextthingy) */'; \
+	    echo; \
+	    sed -e 's/#.*//; /^$$/d; s/" *,.*/"/' \
+		-e 's/^"/T("/; s/$$/, 0,/; h' \
+		-e 's/-//g; s/^.*"\(.*\)".*/w_\1, t_D\1)/' \
+		-e 'H; g; s/\n/ /' \
+		< $(sdir)/iwidgets.list; \
+	    sed -e 's/#.*//; /^$$/d; s/" *,.*/"/' \
+		-e 's/^"/T("./; s/$$/, TH_IMMORTAL,/; h' \
+		-e 's/-//g; s/^.*"\.\(.*\)".*/w_\1, t_\1)/' \
+		-e 'H; g; s/\n/ /' \
+		< $(sdir)/iwidgets.list; \
+	) > $@
+
+widgets.list: iwidgets.list
+	( \
+	    echo '/** widgets.list                               **/'; \
+	    echo '/** widget structures for the internal widgets **/'; \
+	    echo; \
+	    echo '/* format: W(ZLE_FLAGS, t_firstname, functionname) */'; \
+	    echo; \
+	    sed -e 's/#.*//; /^$$/d; s/-//g' \
+		-e 's/^"\(.*\)" *, *\([^ ]*\) *, *\(.*\)/W(\3, t_\1, \2)/' \
+		< $(sdir)/iwidgets.list; \
+	) > $@
+
+zle_bindings.o zle_bindings..o: zle_widget.h widgets.list thingies.list
+
+clean-here: clean.zle
+clean.zle:
+	rm -f zle_things.h zle_widget.h widgets.list thingies.list
+Make
+hasexport=1
+
 moddeps="comp1"
 
 autobins="bindkey vared zle"
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 28bc96b64..37c32d581 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -104,7 +104,8 @@ char *curkeymapname;
 
 /* the hash table of keymap names */
 
-static HashTable keymapnamtab;
+/**/
+HashTable keymapnamtab;
 
 /* key sequence reading data */
 
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index d7ffbe259..56ecd02ba 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -30,6 +30,70 @@
 #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). */
+
+/**/
+int incompctlfunc;
+
+/* != 0 if we are in a new style completion function */
+
+/**/
+int incompfunc;
+
+/* 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;
+
 /* != 0 if we're done editing */
 
 /**/
@@ -961,6 +1025,9 @@ 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),
 };
 
 /**/
@@ -974,17 +1041,6 @@ setup_zle(Module m)
     spaceinlineptr = spaceinline;
     zlereadptr = zleread;
 
-    addmatchesptr = addmatches;
-    comp_strptr = comp_str;
-    getcpatptr = getcpat;
-    makecomplistcallptr = makecomplistcall;
-    makecomplistctlptr = makecomplistctl;
-    num_matchesptr = num_matches;
-    list_linesptr = list_lines;
-    comp_listptr = comp_list;
-    unambig_dataptr = unambig_data;
-    set_comp_sepptr = set_comp_sep;
-
     getkeyptr = getkey;
 
     /* initialise the thingies */
@@ -1001,6 +1057,23 @@ setup_zle(Module m)
 
     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;
+
+    clwords = (char **) zcalloc((clwsize = 16) * sizeof(char *));
+
+    makecompparamsptr = NULL;
+    comp_setunsetptr = NULL;
+
     return 0;
 }
 
@@ -1057,19 +1130,37 @@ finish_zle(Module m)
     spaceinlineptr = noop_function_int;
     zlereadptr = fallback_zleread;
 
-    addmatchesptr = NULL;
-    comp_strptr = NULL;
-    getcpatptr = NULL;
-    makecomplistcallptr = NULL;
-    makecomplistctlptr = NULL;
-    num_matchesptr = NULL;
-    list_linesptr = NULL;
-    comp_listptr = NULL;
-    unambig_dataptr = NULL;
-    set_comp_sepptr = NULL;
-
     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_thingy.c b/Src/Zle/zle_thingy.c
index 4f1bb03f2..72a0f120d 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -551,13 +551,13 @@ bin_zle_complete(char *name, char **args, char *ops, char func)
     Widget w, cw;
 
 #ifdef DYNAMIC
-    if (!require_module(name, "compctl", 0, 0)) {
-	zerrnam(name, "can't load compctl module", NULL, 0);
+    if (!require_module(name, "complete", 0, 0)) {
+	zerrnam(name, "can't load complete module", NULL, 0);
 	return 1;
     }
 #else
-    if (!makecompparamsptr) {
-	zerrnam(name, "compctl module not available", NULL, 0);
+    if (!module_linked("complete")) {
+	zerrnam(name, "complete module not available", NULL, 0);
 	return 1;
     }
 #endif
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 5c21f946b..5803ac392 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -42,24 +42,6 @@
  * file only are not translated: they remain indexes into the metafied  *
  * line.                                                                */
 
-#ifdef HAVE_NIS_PLUS
-# include <rpcsvc/nis.h>
-#else
-# ifdef HAVE_NIS
-#  include	<rpc/types.h>
-#  include	<rpc/rpc.h>
-#  include	<rpcsvc/ypclnt.h>
-#  include	<rpcsvc/yp_prot.h>
-
-/* This is used when getting usernames from the NIS. */
-typedef struct {
-    int len;
-    char *s;
-}
-dopestring;
-# endif
-#endif
-
 
 #define inststr(X) inststrlen((X),1,-1)
 
@@ -68,14 +50,31 @@ dopestring;
 static char *origline;
 static int origcs, origll;
 
+/* Words on the command line, for use in completion */
+ 
+/**/
+int clwsize, clwnum, clwpos;
+/**/
+char **clwords;
+
 /* wb and we hold the beginning/end position of the word we are completing. */
 
-static int wb, we;
+/**/
+int wb, we;
 
 /* offs is the cursor position within the tokenized *
  * current word after removing nulargs.             */
 
-static int offs;
+/**/
+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                           */
+
+/**/
+char *ipre, *ripre, *isuf, *qipre, *qisuf, autoq;
 
 /* the last completion widget called */
 
@@ -86,7 +85,8 @@ static Widget lastcompwidget;
  * 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. */
 
-static int usemenu, useglob, useexact, useline, uselist;
+/**/
+int usemenu, useglob, useexact, useline, uselist;
 
 /* Non-zero if we should keep an old list. */
 
@@ -131,7 +131,8 @@ static int hasunqu, useqbr, brpcs, brscs;
 /* The list of matches.  fmatches contains the matches we first ignore *
  * because of fignore.                                                 */
 
-static LinkList matches, fmatches;
+/**/
+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
@@ -178,45 +179,13 @@ struct cldata listdat;
 
 /* This flag is non-zero if we are completing a pattern (with globcomplete) */
 
-static int ispattern, haspattern;
+/**/
+int ispattern, haspattern;
 
 /* Non-zero if at least one match was added without -U. */
 
-static int hasmatched;
-
-/* Two patterns used when doing glob-completion.  The first one is built *
- * from the whole word we are completing and the second one from that    *
- * part of the word that was identified as a possible filename.          */
-
-static Patprog patcomp, filecomp;
-
-/* We store the following prefixes/suffixes:                               *
- * lpre/lsuf -- what's on the line                                         *
- * rpre/rsuf -- same as lpre/lsuf, but expanded                            *
- *                                                                         *
- * ... and if we are completing files, too:                                *
- * ppre/psuf   -- the path prefix/suffix                                   *
- * lppre/lpsuf -- the path prefix/suffix, unexpanded                       *
- * fpre/fsuf   -- prefix/suffix of the pathname component the cursor is in *
- * prpre       -- ppre in expanded form usable for opendir                 *
- * ipre,ripre  -- the ignored prefix (quoted and unquoted)                 *
- * isuf        -- the ignored suffix                                       *
- * qipre, qisuf-- ingnored quoted string                                   *
- * autoq       -- quotes to automatically insert                           *
- *                                                                         *
- * The integer variables hold the lengths of lpre, lsuf, rpre, rsuf,       *
- * fpre, fsuf, lppre, and lpsuf.  noreal is non-zero if we have rpre/rsuf. */
-
-static char *lpre, *lsuf;
-static char *rpre, *rsuf;
-static char *ppre, *psuf, *lppre, *lpsuf, *prpre;
-static char *fpre, *fsuf;
-static char *ipre, *ripre;
-static char *isuf;
-static char *qfpre, *qfsuf, *qrpre, *qrsuf, *qlpre, *qlsuf;
-static char *qipre, *qisuf, autoq;
-static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl;
-static int noreal;
+/**/
+int hasmatched;
 
 /* A parameter expansion prefix (like ${). */
 
@@ -226,15 +195,6 @@ static char *parpre;
 
 static int parflags;
 
-/* This is either zero or equal to the special character the word we are *
- * trying to complete starts with (e.g. Tilde or Equals).                */
-
-static char ic;
-
-/* This variable says what we are currently adding to the list of matches. */
-
-static int addwhat;
-
 /* This holds the word we are completing in quoted from. */
 
 static char *qword;
@@ -245,7 +205,8 @@ static Cmgroup mgroup;
 
 /* Match counters: all matches, normal matches (not alternate set). */
 
-static int mnum;
+/**/
+int mnum;
 
 /* The match counter when unambig_data() was called. */
 
@@ -253,7 +214,8 @@ static int unambig_mnum;
 
 /* Match flags for all matches in this group. */
 
-static int mflags;
+/**/
+int mflags;
 
 /* Length of longest/shortest match. */
 
@@ -263,87 +225,30 @@ static int maxmlen, minmlen;
  * a pointer to the current cexpl structure. */
 
 static LinkList expls;
-static Cexpl expl;
-
-/* A pointer to the compctl we are using. */
-
-static Compctl curcc;
-
-/* A list of all compctls we have already used. */
-
-static LinkList ccused;
 
-/* A list of all compctls used so far. */
-
-static LinkList allccs;
-
-/* A stack of currently used compctls. */
-
-static LinkList ccstack;
+/**/
+Cexpl curexpl;
 
 /* A stack of completion matchers to be used. */
 
-static Cmlist mstack;
+/**/
+Cmlist mstack;
 
 /* The completion matchers used when building new stuff for the line. */
 
-static Cmlist bmatchers;
+/**/
+Cmlist bmatchers;
 
 /* A list with references to all matchers we used. */
 
-static LinkList matchers;
-
-/* Information about what to put on the line as the unambiguous string.
- * The code always keeps lists of these structs up to date while
- * matches are added (in the aminfo structs below).
- * The lists have two levels: in the first one we have one struct per
- * word-part, where parts are separated by the anchors of `*' patterns.
- * These structs have pointers (in the prefix and suffix fields) to
- * lists of cline structs describing the strings before or after the
- * the anchor. */
-
-typedef struct cline *Cline;
-typedef struct clsub Clsub;
-
-struct cline {
-    Cline next;
-    int flags;
-    char *line;
-    int llen;
-    char *word;
-    int wlen;
-    char *orig;
-    int olen;
-    int slen;
-    Cline prefix, suffix;
-    int min, max;
-};
-
-#define CLF_MISS      1
-#define CLF_DIFF      2
-#define CLF_SUF       4
-#define CLF_MID       8
-#define CLF_NEW      16
-#define CLF_LINE     32
-#define CLF_JOIN     64
-#define CLF_MATCHED 128
+/**/
+LinkList matchers;
 
 /* A heap of free Cline structures. */
 
 static Cline freecl;
 
-/* Information for ambiguous completions. One for fignore ignored and   *
- * one for normal completion. */
-
-typedef struct aminfo *Aminfo;
-
-struct aminfo {
-    Cmatch firstm;		/* the first match                        */
-    int exact;			/* if there was an exact match            */
-    Cmatch exactm;		/* the exact match (if any)               */
-    int count;			/* number of matches                      */
-    Cline line;			/* unambiguous line string                */
-};
+/* Ambiguous information. */
 
 static Aminfo ainfo, fainfo;
 
@@ -354,10 +259,19 @@ static char *compfunc = NULL;
 
 /* The memory heap to use for new style completion generation. */
 
-static Heap compheap;
+/**/
+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)
@@ -370,14 +284,6 @@ usetab(void)
     return 1;
 }
 
-enum { COMP_COMPLETE,
-       COMP_LIST_COMPLETE,
-       COMP_SPELL,
-       COMP_EXPAND,
-       COMP_EXPAND_COMPLETE,
-       COMP_LIST_EXPAND };
-#define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND)
-
 /* Non-zero if the last completion done was ambiguous (used to find   *
  * out if AUTOMENU should start).  More precisely, it's nonzero after *
  * successfully doing any completion, unless the completion was       *
@@ -648,7 +554,8 @@ acceptandmenucomplete(char **args)
 /* These are flags saying if we are completing in the command *
  * position, in a redirection, or in a parameter expansion.   */
 
-static int lincmd, linredir, ispar, parq, eparq, linwhat, linarr;
+/**/
+int lincmd, linredir, ispar, parq, eparq, linwhat, linarr;
 
 /* The string for the redirection operator. */
 
@@ -657,7 +564,8 @@ static char *rdstr;
 /* This holds the name of the current command (used to find the right *
  * compctl).                                                          */
 
-static char *cmdstr;
+/**/
+char *cmdstr;
 
 /* This hold the name of the variable we are working on. */
 
@@ -665,11 +573,13 @@ static char *varname;
 
 /* != 0 if we are in a subscript */
 
-static int insubscr;
+/**/
+int insubscr;
 
 /* Parameter pointer for completing keys of an assoc array. */
 
-static Param keypm;
+/**/
+Param keypm;
 
 /* 1 if we are completing in a quoted string (or inside `...`) */
 
@@ -681,6 +591,25 @@ 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.                                */
 
@@ -761,7 +690,8 @@ cmphaswilds(char *str)
 
 /* Check if we have to complete a parameter name. */
 
-static char *
+/**/
+char *
 check_param(char *s, int set, int test)
 {
     char *p;
@@ -1245,7 +1175,7 @@ addx(char **ptmp)
 /* Like dupstring, but add an extra space at the end of the string. */
 
 /**/
-static char *
+char *
 dupstrspace(const char *str)
 {
     int len = strlen((char *)str);
@@ -1338,7 +1268,7 @@ dupbrinfo(Brinfo p, Brinfo *last)
 static char *
 get_comp_string(void)
 {
-    int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins, ia, parct;
+    int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins, ia, parct, varq = 0;
     char *s = NULL, *linptr, *tmp, *p, *tt = NULL;
 
     freebrinfo(brbeg);
@@ -1417,9 +1347,15 @@ get_comp_string(void)
 		for (j = 0, p = tokstr; *p; p++)
 		    if (*p == Snull || *p == Dnull)
 			j++;
-		if (j & 1)
-		    tok = STRING;
-	    }
+		if (j & 1) {
+		    if (lincmd && strchr(tokstr, '=')) {
+			varq = 1;
+			tok = ENVSTRING;
+		    } else
+			tok = STRING;
+		}
+	    } else if (tok == ENVSTRING)
+		varq = 0;
 	    if (tok == ENVARRAY) {
 		linarr = 1;
 		zsfree(varname);
@@ -1556,6 +1492,10 @@ get_comp_string(void)
 	} else if (t0 == ENVSTRING) {
 	    char sav;
 	    /* The cursor was inside a parameter assignment. */
+
+	    if (varq)
+		tt = clwords[clwpos];
+
 	    for (s = tt; iident(*s); s++);
 	    sav = *s;
 	    *s = '\0';
@@ -2964,7 +2904,8 @@ match_parts(char *l, char *w, int n, int part)
  * 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. */
 
-static char *
+/**/
+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)
 {
@@ -3911,7 +3852,8 @@ join_clines(Cline o, Cline n)
 
 /* This adds all the data we have for a match. */
 
-static Cmatch
+/**/
+Cmatch
 add_match_data(int alt, char *str, Cline line,
 	       char *ipre, char *ripre, char *isuf,
 	       char *pre, char *prpre,
@@ -4181,11 +4123,11 @@ add_match_data(int alt, char *str, Cline line,
     newmatches = 1;
 
     /* One more match for this explanation. */
-    if (expl) {
+    if (curexpl) {
 	if (alt)
-	    expl->fcount++;
+	    curexpl->fcount++;
 	else
-	    expl->count++;
+	    curexpl->count++;
     }
     if (!ai->firstm)
 	ai->firstm = cm;
@@ -4315,11 +4257,11 @@ addmatches(Cadata dat, char **argv)
 		dparl = newlinklist();
 	    }
 	    if (dat->exp) {
-		expl = (Cexpl) zhalloc(sizeof(struct cexpl));
-		expl->count = expl->fcount = 0;
-		expl->str = dupstring(dat->exp);
+		curexpl = (Cexpl) zhalloc(sizeof(struct cexpl));
+		curexpl->count = curexpl->fcount = 0;
+		curexpl->str = dupstring(dat->exp);
 	    } else
-		expl = NULL;
+		curexpl = NULL;
 
 	    /* Store the matcher in our stack of matchers. */
 	    if (dat->match) {
@@ -4613,431 +4555,6 @@ addmatches(Cadata dat, char **argv)
     return (mnum == nm);
 }
 
-/* This adds a match to the list of matches.  The string to add is given   *
- * in s, the type of match is given in the global variable addwhat and     *
- * the parameter t (if not NULL) is a pointer to a hash node node which    *
- * may be used to give other information to this function.                 *
- *                                                                         *
- * addwhat contains either one of the special values (negative, see below) *
- * or the inclusive OR of some of the CC_* flags used for compctls.        */
-
-/**/
-static void
-addmatch(char *s, char *t)
-{
-    int isfile = 0, isalt = 0, isexact;
-    char *ms = NULL, *tt;
-    HashNode hn;
-    Param pm;
-    Cline lc = NULL;
-    Brinfo bp, bpl = brbeg, bsl = brend, bpt, bst;
-
-    for (bp = brbeg; bp; bp = bp->next)
-	bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos);
-    for (bp = brend; bp; bp = bp->next)
-	bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos);
-
-    /*
-     * addwhat: -5 is for files,
-     *          -6 is for glob expansions,
-     *          -8 is for executable files (e.g. command paths),
-     *          -9 is for parameters
-     *          -7 is for command names (from cmdnamtab)
-     *          -4 is for a cdable parameter
-     *          -3 is for executable command names.
-     *          -2 is for anything unquoted
-     *          -1 is for other file specifications
-     *          (things with `~' or `=' at the beginning, ...).
-     */
-
-    /* Just to make the code cleaner */
-    hn = (HashNode) t;
-    pm = (Param) t;
-
-    if (addwhat == -1 || addwhat == -5 || addwhat == -6 ||
-	addwhat == CC_FILES || addwhat == -7 || addwhat == -8) {
-	int ppl = (ppre ? strlen(ppre) : 0), psl = (psuf ? strlen(psuf) : 0);
-
-	while (bpl && bpl->curpos < ppl)
-	    bpl = bpl->next;
-	while (bsl && bsl->curpos < psl)
-	    bsl = bsl->next;
-
-	if ((addwhat == CC_FILES ||
-	     addwhat == -5) && !*psuf) {
-	    /* If this is a filename, do the fignore check. */
-	    char **pt = fignore;
-	    int filell, sl = strlen(s);
-
-	    for (isalt = 0; !isalt && *pt; pt++)
-		if ((filell = strlen(*pt)) < sl &&
-		    !strcmp(*pt, s + sl - filell))
-		    isalt = 1;
-	}
-	ms = ((addwhat == CC_FILES || addwhat == -6 ||
-	       addwhat == -5 || addwhat == -8) ? 
-	      comp_match(qfpre, qfsuf, s, filecomp, &lc, (ppre && *ppre ? 1 : 2),
-			 &bpl, ppl ,&bsl, psl, &isexact) :
-	      comp_match(fpre, fsuf, s, filecomp, &lc, 0,
-			 &bpl, ppl, &bsl, psl, &isexact));
-	if (!ms)
-	    return;
-
-	if (addwhat == -7 && !findcmd(s, 0))
-	    return;
-	isfile = CMF_FILE;
-    } else if (addwhat == CC_QUOTEFLAG || addwhat == -2  ||
-	      (addwhat == -3 && !(hn->flags & DISABLED)) ||
-	      (addwhat == -4 && (PM_TYPE(pm->flags) == PM_SCALAR) &&
-	       !pm->level && (tt = pm->gets.cfn(pm)) && *tt == '/')    ||
-	      (addwhat == -9 && !(hn->flags & PM_UNSET) && !pm->level) ||
-	      (addwhat > 0 &&
-	       ((!(hn->flags & PM_UNSET) &&
-		 (((addwhat & CC_ARRAYS)    &&  (hn->flags & PM_ARRAY))    ||
-		  ((addwhat & CC_INTVARS)   &&  (hn->flags & PM_INTEGER))  ||
-		  ((addwhat & CC_ENVVARS)   &&  (hn->flags & PM_EXPORTED)) ||
-		  ((addwhat & CC_SCALARS)   &&  (hn->flags & PM_SCALAR))   ||
-		  ((addwhat & CC_READONLYS) &&  (hn->flags & PM_READONLY)) ||
-		  ((addwhat & CC_SPECIALS)  &&  (hn->flags & PM_SPECIAL))  ||
-		  ((addwhat & CC_PARAMS)    && !(hn->flags & PM_EXPORTED))) &&
-		 !pm->level) ||
-		((( addwhat & CC_SHFUNCS)				  ||
-		  ( addwhat & CC_BUILTINS)				  ||
-		  ( addwhat & CC_EXTCMDS)				  ||
-		  ( addwhat & CC_RESWDS)				  ||
-		  ((addwhat & CC_ALREG)   && !(hn->flags & ALIAS_GLOBAL)) ||
-		  ((addwhat & CC_ALGLOB)  &&  (hn->flags & ALIAS_GLOBAL))) &&
-		 (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) ||
-		  ((addwhat & CC_EXCMDS)  && !(hn->flags & DISABLED)))) ||
-		((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) {
-	char *p1, *s1, *p2, *s2;
-
-	if (addwhat == CC_QUOTEFLAG) {
-	    p1 = qrpre; s1 = qrsuf;
-	    p2 = rpre;  s2 = rsuf;
-	} else {
-	    p1 = qlpre; s1 = qlsuf;
-	    p2 = lpre;  s2 = lsuf;
-	}
-	bpt = bpl;
-	bst = bsl;
-
-	if (!(ms = comp_match(p1, s1, s, patcomp, &lc,
-			      (addwhat == CC_QUOTEFLAG),
-			      &bpl, strlen(p1), &bsl, strlen(s1),
-			      &isexact))) {
-	    bpl = bpt;
-	    bsl = bst;
-	    if (!(ms = comp_match(p2, s2, s, NULL, &lc,
-				  (addwhat == CC_QUOTEFLAG),
-				  &bpl, strlen(p2), &bsl, strlen(s2),
-				  &isexact)))
-		return;
-	}
-    }
-    if (!ms)
-	return;
-    add_match_data(isalt, ms, lc, ipre, ripre, isuf, 
-		   (incompfunc ? dupstring(curcc->prefix) : curcc->prefix),
-		   prpre, 
-		   (isfile ? lppre : NULL), NULL,
-		   (isfile ? lpsuf : NULL), NULL,
-		   (incompfunc ? dupstring(curcc->suffix) : curcc->suffix),
-		   (mflags | isfile), isexact);
-}
-
-#ifdef HAVE_NIS_PLUS
-static int
-match_username(nis_name table, nis_object *object, void *userdata)
-{
-    if (errflag)
-	return 1;
-    else {
-	static char buf[40];
-	register entry_col *ec =
-	    object->zo_data.objdata_u.en_data.en_cols.en_cols_val;
-	register int l = minimum(ec->ec_value.ec_value_len, 39);
-
-	memcpy(buf, ec->ec_value.ec_value_val, l);
-	buf[l] = '\0';
-
-	addmatch(dupstring(buf), NULL);
-    }
-    return 0;
-}
-#else
-# ifdef HAVE_NIS
-static int
-match_username(int status, char *key, int keylen, char *val, int vallen, dopestring *data)
-{
-    if (errflag || status != YP_TRUE)
-	return 1;
-
-    if (vallen > keylen && val[keylen] == ':') {
-	val[keylen] = '\0';
-	addmatch(dupstring(val), NULL);
-    }
-    return 0;
-}
-# endif /* HAVE_NIS */
-#endif  /* HAVE_NIS_PLUS */
-
-/**/
-static void
-maketildelist(void)
-{
-#if defined(HAVE_NIS) || defined(HAVE_NIS_PLUS)
-    FILE *pwf;
-    char buf[BUFSIZ], *p;
-    int skipping;
-
-# ifndef HAVE_NIS_PLUS
-    char domain[YPMAXDOMAIN];
-    struct ypall_callback cb;
-    dopestring data;
-
-    data.s = fpre;
-    data.len = fpl;
-    /* Get potential matches from NIS and cull those without local accounts */
-    if (getdomainname(domain, YPMAXDOMAIN) == 0) {
-	cb.foreach = (int (*)()) match_username;
-	cb.data = (char *)&data;
-	yp_all(domain, PASSWD_MAP, &cb);
-/*	for (n = firstnode(matches); n; incnode(n))
-	    if (getpwnam(getdata(n)) == NULL)
-		uremnode(matches, n);*/
-    }
-# else  /* HAVE_NIS_PLUS */
-       /* Maybe we should turn this string into a #define'd constant...? */
-
-    nis_list("passwd.org_dir", EXPAND_NAME|ALL_RESULTS|FOLLOW_LINKS|FOLLOW_PATH,
-	     match_username, 0);
-# endif
-    /* Don't forget the non-NIS matches from the flat passwd file */
-    if ((pwf = fopen(PASSWD_FILE, "r")) != NULL) {
-	skipping = 0;
-	while (fgets(buf, BUFSIZ, pwf) != NULL) {
-	    if (strchr(buf, '\n') != NULL) {
-		if (!skipping) {
-		    if ((p = strchr(buf, ':')) != NULL) {
-			*p = '\0';
-			addmatch(dupstring(buf), NULL);
-		    }
-		} else
-		    skipping = 0;
-	    } else
-		skipping = 1;
-	}
-	fclose(pwf);
-    }
-#else  /* no NIS or NIS_PLUS */
-    /* add all the usernames to the named directory table */
-    nameddirtab->filltable(nameddirtab);
-#endif
-
-    scanhashtable(nameddirtab, 0, (addwhat==-1) ? 0 : ND_USERNAME, 0,
-	    addhnmatch, 0);
-}
-
-/* This does the check for compctl -x `n' and `N' patterns. */
-
-/**/
-int
-getcpat(char *str, int cpatindex, char *cpat, int class)
-{
-    char *s, *t, *p;
-    int d = 0;
-
-    if (!str || !*str)
-	return -1;
-
-    cpat = rembslash(cpat);
-
-    if (!cpatindex)
-	cpatindex++, d = 0;
-    else if ((d = (cpatindex < 0)))
-	cpatindex = -cpatindex;
-
-    for (s = d ? str + strlen(str) - 1 : str;
-	 d ? (s >= str) : *s;
-	 d ? s-- : s++) {
-	for (t = s, p = cpat; *t && *p; p++) {
-	    if (class) {
-		if (*p == *s && !--cpatindex)
-		    return (int)(s - str + 1);
-	    } else if (*t++ != *p)
-		break;
-	}
-	if (!class && !*p && !--cpatindex)
-	    return t - str;
-    }
-    return -1;
-}
-
-/* Dump a hash table (without sorting).  For each element the addmatch  *
- * function is called and at the beginning the addwhat variable is set. *
- * This could be done using scanhashtable(), but this is easy and much  *
- * more efficient.                                                      */
-
-/**/
-static void
-dumphashtable(HashTable ht, int what)
-{
-    HashNode hn;
-    int i;
-
-    addwhat = what;
-
-    for (i = 0; i < ht->hsize; i++)
-	for (hn = ht->nodes[i]; hn; hn = hn->next)
-	    addmatch(hn->nam, (char *) hn);
-}
-
-/* ScanFunc used by maketildelist() et al. */
-
-/**/
-static void
-addhnmatch(HashNode hn, int flags)
-{
-    addmatch(hn->nam, NULL);
-}
-
-/* Perform expansion on the given string and return the result. *
- * During this errors are not reported.                         */
-
-/**/
-static char *
-getreal(char *str)
-{
-    LinkList l = newlinklist();
-    int ne = noerrs;
-
-    noerrs = 1;
-    addlinknode(l, dupstring(str));
-    prefork(l, 0);
-    noerrs = ne;
-    if (!errflag && nonempty(l) &&
-	((char *) peekfirst(l)) && ((char *) peekfirst(l))[0])
-	return dupstring(peekfirst(l));
-    errflag = 0;
-
-    return dupstring(str);
-}
-
-/* This reads a directory and adds the files to the list of  *
- * matches.  The parameters say which files should be added. */
-
-/**/
-static void
-gen_matches_files(int dirs, int execs, int all)
-{
-    DIR *d;
-    struct stat buf;
-    char *n, p[PATH_MAX], *q = NULL, *e;
-    LinkList l = NULL;
-    int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat;
-
-    opts[NULLGLOB] = 1;
-
-    if (*psuf) {
-	/* If there is a path suffix, check if it doesn't have a `*' or *
-	 * `)' at the end (this is used to determine if we should use   *
-	 * globbing).                                                   */
-	q = psuf + strlen(psuf) - 1;
-	ns = !(*q == Star || *q == Outpar);
-	l = newlinklist();
-	/* And generate only directory names. */
-	dirs = 1;
-	all = execs = 0;
-    }
-    /* Open directory. */
-    if ((d = opendir((prpre && *prpre) ? prpre : "."))) {
-	/* If we search only special files, prepare a path buffer for stat. */
-	if (!all && prpre) {
-	    strcpy(p, prpre);
-	    q = p + strlen(prpre);
-	}
-	/* Fine, now read the directory. */
-	while ((n = zreaddir(d, 1)) && !errflag) {
-	    /* Ignore files beginning with `.' unless the thing we found on *
-	     * the command line also starts with a dot or GLOBDOTS is set.  */
-	    if (*n != '.' || *fpre == '.' || isset(GLOBDOTS)) {
-		addwhat = execs ? -8 : -5;
-		if (filecomp)
-		    /* If we have a pattern for the filename check, use it. */
-		    test = pattry(filecomp, n);
-		else {
-		    /* Otherwise use the prefix and suffix strings directly. */
-		    e = n + strlen(n) - fsl;
-		    if ((test = !strncmp(n, fpre, fpl)))
-			test = !strcmp(e, fsuf);
-		    if (!test && mstack) {
-			test = 1;
-			addwhat = CC_FILES;
-		    }
-		}
-		/* Filename didn't match? */
-		if (!test)
-		    continue;
-		if (!all) {
-		    /* We still have to check the file type, so prepare *
-		     * the path buffer by appending the filename.       */
-		    strcpy(q, n);
-		    /* And do the stat. */
-		    if (stat(p, &buf) < 0)
-			continue;
-		}
-		if (all ||
-		    (dirs && S_ISDIR(buf.st_mode)) ||
-		    (execs && S_ISREG(buf.st_mode) && (buf.st_mode&S_IXUGO))) {
-		    /* If we want all files or the file has the right type... */
-		    if (*psuf) {
-			/* We have to test for a path suffix. */
-			int o = strlen(p), tt;
-
-			/* Append it to the path buffer. */
-			strcpy(p + o, psuf);
-
-			/* Do we have to use globbing? */
-			if (ispattern ||
-			    (ns && comppatmatch && *comppatmatch)) {
-			    /* Yes, so append a `*' if needed. */
-			    if (ns && comppatmatch && *comppatmatch == '*') {
-				int tl = strlen(p);
-
-				p[tl] = Star;
-				p[tl + 1] = '\0';
-			    }
-			    /* Do the globbing... */
-			    remnulargs(p);
-			    addlinknode(l, p);
-			    globlist(l);
-			    /* And see if that produced a filename. */
-			    tt = nonempty(l);
-			    while (ugetnode(l));
-			} else
-			    /* Otherwise just check, if we have access *
-			     * to the file.                            */
-			    tt = !access(p, F_OK);
-
-			p[o] = '\0';
-			if (tt)
-			    /* Ok, we can add the filename to the *
-			     * list of matches.                   */
-			    addmatch(dupstring(n), NULL);
-		    } else
-			/* We want all files, so just add the name *
-			 * to the matches.                         */
-			addmatch(dupstring(n), NULL);
-		}
-	    }
-	}
-	closedir(d);
-    }
-    opts[NULLGLOB] = ng;
-    addwhat = aw;
-}
-
 /**/
 static int
 docompletion(char *s, int lst, int incmd)
@@ -5180,44 +4697,6 @@ docompletion(char *s, int lst, int incmd)
 	    useline != 2 && (!oldlist || !listshown)) {
 	    onlyexpl = 1;
 	    showinglist = -2;
-#if 0
-	    Cmgroup g = amatches;
-	    Cexpl *e;
-	    int up = 0, tr = 1, nn = 0;
-
-	    if (!nmatches)
-		ret = 1;
-
-	    while (g) {
-		if ((e = g->expls))
-		    while (*e) {
-			if ((*e)->count) {
-			    if (tr) {
-				trashzle();
-				tr = 0;
-			    }
-			    if (nn) {
-				up++;
-				putc('\n', shout);
-			    }
-			    up += printfmt((*e)->str, (*e)->count, 1, 1);
-			    nn = 1;
-			}
-			e++;
-		    }
-		g = g->next;
-	    }
-	    if (!tr) {
-		clearflag = (isset(USEZLE) && !termflags &&
-			      complastprompt && *complastprompt);
-
-		if (clearflag && up + nlnct < lines)
-		    tcmultout(TCUP, TCMULTUP, up + nlnct);
-		else
-		    putc('\n', shout);
-		fflush(shout);
-	    }
-#endif
 	}
       compend:
 	for (n = firstnode(matchers); n; incnode(n))
@@ -5530,14 +5009,6 @@ callcompfunc(char *s, char *fn)
     lastval = lv;
 }
 
-/* The beginning and end of a word range to be used by -l. */
-
-static int brange, erange;
-
-/* This is used to detect when and what to continue. */
-
-static unsigned long ccont;
-
 /* 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 *
@@ -5620,26 +5091,24 @@ makecomplist(char *s, int incmd, int lst)
 	begcmgroup("default", 0);
 	menucmp = menuacc = newmatches = onlyexpl = 0;
 
-	ccused = newlinklist();
-	ccstack = newlinklist();
+	runhookdef(COMPCTLBEFOREHOOK, NULL);
 
 	s = dupstring(os);
 	if (compfunc)
 	    callcompfunc(s, compfunc);
-	else
-	    makecomplistglobal(s, incmd, lst, 0);
+	else {
+	    struct ccmakedat dat;
 
+	    dat.str = s;
+	    dat.incmd = incmd;
+	    dat.lst = lst;
+	    runhookdef(COMPCTLMAKEHOOK, (void *) &dat);
+	}
 	endcmgroup(NULL);
 
-	if (amatches && !oldlist)
-	    amatches->ccs = (Compctl *) makearray(ccused, 0, 0,
-						  &(amatches->ccount), NULL, NULL);
-	else {
-	    LinkNode n;
+	runhookdef(COMPCTLAFTERHOOK,
+		   (void *) ((amatches && !oldlist) ? 1L : 0L));
 
-	    for (n = firstnode(ccused); n; incnode(n))
-		freecompctl((Compctl) getdata(n));
-	}
 	if (oldlist) {
 	    nmatches = onm;
 	    validlist = 1;
@@ -5754,25 +5223,17 @@ set_comp_sep(void)
 {
     int lip, lp;
     char *s = comp_str(&lip, &lp, 0);
-
-    if (compisuffix)
-	s = dyncat(s, compisuffix);
-    untokenize(s);
-
-    return sep_comp_string("", s, lip + lp, 0);
-}
-
-/**/
-static int
-sep_comp_string(char *ss, char *s, int noffs, int rec)
-{
     LinkList foo = newlinklist();
     LinkNode n;
     int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs;
-    int sl = strlen(ss), tl, got = 0, i = 0, cur = -1, oll = ll;
-    int ois = instring, oib = inbackt;
+    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;
 
@@ -5782,14 +5243,12 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
     addedx = 1;
     noerrs = 1;
     lexsave();
-    tmp = (char *) zhalloc(tl = sl + 3 + strlen(s));
-    strcpy(tmp, ss);
-    tmp[sl] = ' ';
-    memcpy(tmp + sl + 1, s, noffs);
-    tmp[(scs = cs = sl + 1 + noffs)] = 'x';
-    strcpy(tmp + sl + 2 + noffs, s + noffs);
-    if (incompfunc)
-	tmp = rembslash(tmp);
+    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;
@@ -5882,59 +5341,22 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
 	    chuck(p--);
 	}
     }
-    sav = s[(i = swb - sl - 1)];
+    sav = s[(i = swb - 1)];
     s[i] = '\0';
-    qp = tricat(qipre, (incompfunc ? rembslash(s) : s), "");
+    qp = tricat(qipre, rembslash(s), "");
     s[i] = sav;
     if (swe < swb)
 	swe = swb;
-    swe -= sl + 1;
+    swe--;
     sl = strlen(s);
     if (swe > sl)
 	swe = sl, ns[swe - swb + 1] = '\0';
-    qs = tricat((incompfunc ? rembslash(s + swe) : s + swe), qisuf, "");
+    qs = tricat(rembslash(s + swe), qisuf, "");
     sl = strlen(ns);
     if (soffs > sl)
 	soffs = sl;
 
-    if (rec) {
-	char **ow = clwords, *os = cmdstr, *oqp = qipre, *oqs = qisuf;
-	int olws = clwsize, olwn = clwnum, olwp = clwpos;
-	int obr = brange, oer = erange, oof = offs;
-	unsigned long occ = ccont;
-
-	clwsize = clwnum = countlinknodes(foo);
-	clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
-	for (n = firstnode(foo), i = 0; n; incnode(n), i++) {
-	    p = clwords[i] = (char *) getdata(n);
-	    untokenize(p);
-	}
-	clwords[i] = NULL;
-	clwpos = cur;
-	cmdstr = ztrdup(clwords[0]);
-	brange = 0;
-	erange = clwnum - 1;
-	qipre = qp;
-	qisuf = qs;
-	offs = soffs;
-	ccont = CC_CCCONT;
-	makecomplistcmd(ns, !clwpos, CFN_FIRST);
-	ccont = occ;
-	offs = oof;
-	zsfree(cmdstr);
-	cmdstr = os;
-	freearray(clwords);
-	clwords = ow;
-	clwsize = olws;
-	clwnum = olwn;
-	clwpos = olwp;
-	brange = obr;
-	erange = oer;
-	zsfree(qipre);
-	qipre = oqp;
-	zsfree(qisuf);
-	qisuf = oqs;
-    } else {
+    {
 	int set = CP_QUOTE | CP_QUOTING, unset = 0;
 
 	zsfree(compquote);
@@ -6005,1492 +5427,6 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
     return 0;
 }
 
-/**/
-int
-makecomplistcall(Compctl cc)
-{
-    int nm = mnum;
-
-    SWITCHHEAPS(compheap) {
-	HEAPALLOC {
-	    int ooffs = offs, lip, lp, ois = instring, oib = inbackt;
-	    char *str = comp_str(&lip, &lp, 0), qc;
-	    char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, oaq = autoq;
-
-	    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';
-	    }
-	    isuf = dupstring(compisuffix);
-	    ctokenize(isuf);
-	    remnulargs(isuf);
-	    qipre = ztrdup(compqiprefix ? compqiprefix : "");
-	    qisuf = ztrdup(compqisuffix ? compqisuffix : "");
-	    offs = lip + lp;
-	    cc->refc++;
-	    ccont = 0;
-	    if (!cc->ylist && !cc->gname) {
-		endcmgroup(NULL);
-		begcmgroup("default", 0);
-	    }
-	    makecomplistor(cc, str, lincmd, lip, 0);
-	    offs = ooffs;
-	    isuf = oisuf;
-	    zsfree(qipre);
-	    zsfree(qisuf);
-	    qipre = oqp;
-	    qisuf = oqs;
-	    instring = ois;
-	    inbackt = oib;
-	    autoq = oaq;
-	} LASTALLOC;
-    } SWITCHBACKHEAPS;
-
-    return (mnum == nm);
-}
-
-/* A simple counter to avoid endless recursion between old and new style *
- * completion. */
-
-static int cdepth = 0;
-
-#define MAX_CDEPTH 16
-
-/**/
-int
-makecomplistctl(int flags)
-{
-    int ret;
-
-    if (cdepth == MAX_CDEPTH)
-	return 0;
-
-    cdepth++;
-    SWITCHHEAPS(compheap) {
-	HEAPALLOC {
-	    int ooffs = offs, lip, lp;
-	    char *str = comp_str(&lip, &lp, 0), *t;
-	    char *os = cmdstr, **ow = clwords, **p, **q, qc;
-	    int on = clwnum, op = clwpos, ois =  instring, oib = inbackt;
-	    char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, oaq = autoq;
-
-	    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 : "");
-	    isuf = dupstring(compisuffix);
-	    ctokenize(isuf);
-	    remnulargs(isuf);
-	    clwnum = arrlen(compwords);
-	    clwpos = compcurrent - 1;
-	    cmdstr = ztrdup(compwords[0]);
-	    clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
-	    for (p = compwords, q = clwords; *p; p++, q++) {
-		t = dupstring(*p);
-		tokenize(t);
-		remnulargs(t);
-		*q = ztrdup(t);
-	    }
-	    *q = NULL;
-	    offs = lip + lp;
-	    incompfunc = 2;
-	    ret = makecomplistglobal(str, !clwpos, COMP_COMPLETE, flags);
-	    incompfunc = 1;
-	    isuf = oisuf;
-	    zsfree(qipre);
-	    zsfree(qisuf);
-	    qipre = oqp;
-	    qisuf = oqs;
-	    instring = ois;
-	    inbackt = oib;
-	    autoq = oaq;
-	    offs = ooffs;
-	    zsfree(cmdstr);
-	    freearray(clwords);
-	    cmdstr = os;
-	    clwords = ow;
-	    clwnum = on;
-	    clwpos = op;
-	} LASTALLOC;
-    } SWITCHBACKHEAPS;
-    cdepth--;
-
-    return ret;
-}
-
-/* This function gets the compctls for the given command line and *
- * adds all completions for them. */
-
-/**/
-static int
-makecomplistglobal(char *os, int incmd, int lst, int flags)
-{
-    Compctl cc = NULL;
-    char *s;
-
-    ccont = CC_CCCONT;
-    cc_dummy.suffix = NULL;
-
-    if (linwhat == IN_ENV) {
-        /* Default completion for parameter values. */
-	if (!(flags & CFN_DEFAULT)) {
-	    cc = &cc_default;
-	    keypm = NULL;
-	}
-    } else if (linwhat == IN_MATH) {
-	if (!(flags & CFN_DEFAULT)) {
-	    if (insubscr >= 2) {
-		/* Inside subscript of assoc array, complete keys. */
-		cc_dummy.mask = 0;
-		cc_dummy.suffix = (insubscr == 2 ? "]" : "");
-	    } else {
-		/* Other math environment, complete paramete names. */
-		keypm = NULL;
-		cc_dummy.mask = CC_PARAMS;
-	    }
-	    cc = &cc_dummy;
-	    cc_dummy.refc = 10000;
-	}
-    } else if (linwhat == IN_COND) {
-	/* We try to be clever here: in conditions we complete option   *
-	 * names after a `-o', file names after `-nt', `-ot', and `-ef' *
-	 * and file names and parameter names elsewhere.                */
-	if (!(flags & CFN_DEFAULT)) {
-	    s = clwpos ? clwords[clwpos - 1] : "";
-	    cc_dummy.mask = !strcmp("-o", s) ? CC_OPTIONS :
-		((*s == '-' && s[1] && !s[2]) ||
-		 !strcmp("-nt", s) ||
-		 !strcmp("-ot", s) ||
-		 !strcmp("-ef", s)) ? CC_FILES :
-		(CC_FILES | CC_PARAMS);
-	    cc = &cc_dummy;
-	    cc_dummy.refc = 10000;
-	    keypm = NULL;
-	}
-    } else if (linredir) {
-	if (!(flags & CFN_DEFAULT)) {
-	    /* In redirections use default completion. */
-	    cc = &cc_default;
-	    keypm = NULL;
-	}
-    } else {
-	/* Otherwise get the matches for the command. */
-	keypm = NULL;
-	return makecomplistcmd(os, incmd, flags);
-    }
-    if (cc) {
-	/* First, use the -T compctl. */
-	if (!(flags & CFN_FIRST)) {
-	    makecomplistcc(&cc_first, os, incmd);
-
-	    if (!(ccont & CC_CCCONT))
-		return 0;
-	}
-	makecomplistcc(cc, os, incmd);
-	return 1;
-    }
-    return 0;
-}
-
-/* This produces the matches for a command. */
-
-/**/
-static int
-makecomplistcmd(char *os, int incmd, int flags)
-{
-    Compctl cc;
-    Compctlp ccp;
-    char *s;
-    int ret = 0;
-
-    /* First, use the -T compctl. */
-    if (!(flags & CFN_FIRST)) {
-	makecomplistcc(&cc_first, os, incmd);
-
-	if (!(ccont & CC_CCCONT))
-	    return 0;
-    }
-    /* Then search the pattern compctls, with the command name and the *
-     * full pathname of the command. */
-    if (cmdstr) {
-	ret |= makecomplistpc(os, incmd);
-	if (!(ccont & CC_CCCONT))
-	    return ret;
-    }
-    /* If the command string starts with `=', try the path name of the *
-     * command. */
-    if (cmdstr && cmdstr[0] == Equals) {
-	char *c = findcmd(cmdstr + 1, 1);
-
-	if (c) {
-	    zsfree(cmdstr);
-	    cmdstr = ztrdup(c);
-	}
-    }
-
-    /* Find the compctl for this command, trying the full name and then *
-     * the trailing pathname component. If that doesn't yield anything, *
-     * use default completion. */
-    if (incmd)
-	cc = &cc_compos;
-    else if (!(cmdstr &&
-	  (((ccp = (Compctlp) compctltab->getnode(compctltab, cmdstr)) &&
-	    (cc = ccp->cc)) ||
-	   ((s = dupstring(cmdstr)) && remlpaths(&s) &&
-	    (ccp = (Compctlp) compctltab->getnode(compctltab, s)) &&
-	    (cc = ccp->cc))))) {
-	if (flags & CFN_DEFAULT)
-	    return ret;
-	cc = &cc_default;
-    } else
-	ret|= 1;
-    makecomplistcc(cc, os, incmd);
-    return ret;
-}
-
-/* This add the matches for the pattern compctls. */
-
-/**/
-static int
-makecomplistpc(char *os, int incmd)
-{
-    Patcomp pc;
-    Patprog pat;
-    char *s = findcmd(cmdstr, 1);
-    int ret = 0;
-
-    for (pc = patcomps; pc; pc = pc->next) {
-	if ((pat = patcompile(pc->pat, PAT_STATIC, NULL)) &&
-	    (pattry(pat, cmdstr) ||
-	     (s && pattry(pat, s)))) {
-	    makecomplistcc(pc->cc, os, incmd);
-	    ret |= 2;
-	    if (!(ccont & CC_CCCONT))
-		return ret;
-	}
-    }
-    return ret;
-}
-
-/* This produces the matches for one compctl. */
-
-/**/
-static void
-makecomplistcc(Compctl cc, char *s, int incmd)
-{
-    cc->refc++;
-    addlinknode(ccused, cc);
-
-    ccont = 0;
-
-    makecomplistor(cc, s, incmd, 0, 0);
-}
-
-/* This adds the completions for one run of [x]or'ed completions. */
-
-/**/
-static void
-makecomplistor(Compctl cc, char *s, int incmd, int compadd, int sub)
-{
-    int mn, ct, um = usemenu;
-
-    /* Loop over xors. */
-    do {
-	mn = mnum;
-
-	/* Loop over ors. */
-	do {
-	    /* Reset the range information if we are not in a sub-list. */
-	    if (!sub) {
-		brange = 0;
-		erange = clwnum - 1;
-	    }
-	    usemenu = 0;
-	    makecomplistlist(cc, s, incmd, compadd);
-	    um |= usemenu;
-
-	    ct = cc->mask2 & CC_XORCONT;
-
-	    cc = cc->xor;
-	} while (cc && ct);
-
-	/* Stop if we got some matches. */
-	if (mn != mnum)
-	    break;
-	if (cc) {
-	    ccont &= ~(CC_DEFCONT | CC_PATCONT);
-	    if (!sub)
-		ccont &= ~CC_CCCONT;
-	}
-    } while (cc);
-
-    usemenu = um;
-}
-
-/* This dispatches for simple and extended completion. */
-
-/**/
-static void
-makecomplistlist(Compctl cc, char *s, int incmd, int compadd)
-{
-    int oloffs = offs, owe = we, owb = wb, ocs = cs;
-
-    if (cc->ext)
-	/* Handle extended completion. */
-	makecomplistext(cc, s, incmd);
-    else
-	/* Only normal flags. */
-	makecomplistflags(cc, s, incmd, compadd);
-
-    /* Reset some information variables for the next try. */
-    errflag = 0;
-    offs = oloffs;
-    wb = owb;
-    we = owe;
-    cs = ocs;
-}
-
-/* This add matches for extended completion patterns */
-
-/**/
-static void
-makecomplistext(Compctl occ, char *os, int incmd)
-{
-    Compctl compc;
-    Compcond or, cc;
-    Patprog pprog;
-    int compadd, m = 0, d = 0, t, tt, i, j, a, b, ins;
-    char *sc = NULL, *s, *ss;
-
-    ins = (instring ? instring : (inbackt ? 3 : 0));
-
-    /* This loops over the patterns separated by `-'s. */
-    for (compc = occ->ext; compc; compc = compc->next) {
-	compadd = t = brange = 0;
-	erange = clwnum - 1;
-	/* This loops over OR'ed patterns. */
-	for (cc = compc->cond; cc && !t; cc = or) {
-	    or = cc->or;
-	    /* This loops over AND'ed patterns. */
-	    for (t = 1; cc && t; cc = cc->and) {
-		/* And this loops over [...] pairs. */
-		for (t = i = 0; i < cc->n && !t; i++) {
-		    s = NULL;
-		    brange = 0;
-		    erange = clwnum - 1;
-		    switch (cc->type) {
-		    case CCT_QUOTE:
-			t = ((cc->u.s.s[i][0] == 's' && ins == 1) ||
-			     (cc->u.s.s[i][0] == 'd' && ins == 2) ||
-			     (cc->u.s.s[i][0] == 'b' && ins == 3));
-			break;
-		    case CCT_POS:
-			tt = clwpos;
-			goto cct_num;
-		    case CCT_NUMWORDS:
-			tt = clwnum;
-		    cct_num:
-			if ((a = cc->u.r.a[i]) < 0)
-			    a += clwnum;
-			if ((b = cc->u.r.b[i]) < 0)
-			    b += clwnum;
-			if (cc->type == CCT_POS)
-			    brange = a, erange = b;
-			t = (tt >= a && tt <= b);
-			break;
-		    case CCT_CURSUF:
-		    case CCT_CURPRE:
-			s = ztrdup(clwpos < clwnum ? os : "");
-			untokenize(s);
-			if (isset(COMPLETEINWORD)) s[offs] = '\0';
-			sc = rembslash(cc->u.s.s[i]);
-			a = strlen(sc);
-			if (!strncmp(s, sc, a)) {
-			    compadd = (cc->type == CCT_CURSUF ? a : 0);
-			    t = 1;
-			}
-			break;
-		    case CCT_CURSUB:
-		    case CCT_CURSUBC:
-			if (clwpos < 0 || clwpos >= clwnum)
-			    t = 0;
-			else {
-			    s = ztrdup(os);
-			    untokenize(s);
-			    if (isset(COMPLETEINWORD)) s[offs] = '\0';
-			    a = getcpat(s,
-					cc->u.s.p[i],
-					cc->u.s.s[i],
-					cc->type == CCT_CURSUBC);
-			    if (a != -1)
-				compadd = a, t = 1;
-			}
-			break;
-			
-		    case CCT_CURPAT:
-		    case CCT_CURSTR:
-			tt = clwpos;
-			goto cct_str;
-		    case CCT_WORDPAT:
-		    case CCT_WORDSTR:
-			tt = 0;
-		    cct_str:
-			if ((a = tt + cc->u.s.p[i]) < 0)
-			    a += clwnum;
-			s = ztrdup((a < 0 || a >= clwnum) ? "" :
-				   clwords[a]);
-			untokenize(s);
-			
-			if (cc->type == CCT_CURPAT ||
-			    cc->type == CCT_WORDPAT) {
-			    tokenize(ss = dupstring(cc->u.s.s[i]));
-			    t = ((pprog = patcompile(ss, PAT_STATIC, NULL)) &&
-				 pattry(pprog, s));
-			} else
-			    t = (!strcmp(s, rembslash(cc->u.s.s[i])));
-			break;
-		    case CCT_RANGESTR:
-		    case CCT_RANGEPAT:
-			if (cc->type == CCT_RANGEPAT)
-			    tokenize(sc = dupstring(cc->u.l.a[i]));
-			for (j = clwpos - 1; j > 0; j--) {
-			    untokenize(s = ztrdup(clwords[j]));
-			    if (cc->type == CCT_RANGESTR)
-				sc = rembslash(cc->u.l.a[i]);
-			    if (cc->type == CCT_RANGESTR ?
-				!strncmp(s, sc, strlen(sc)) :
-				((pprog = patcompile(sc, PAT_STATIC, 0)) &&
-				 pattry(pprog, s))) {
-				zsfree(s);
-				brange = j + 1;
-				t = 1;
-				break;
-			    }
-			    zsfree(s);
-			}
-			if (t && cc->u.l.b[i]) {
-			    if (cc->type == CCT_RANGEPAT)
-				tokenize(sc = dupstring(cc->u.l.b[i]));
-			    for (j++; j < clwnum; j++) {
-				untokenize(s = ztrdup(clwords[j]));
-				if (cc->type == CCT_RANGESTR)
-				    sc = rembslash(cc->u.l.b[i]);
-				if (cc->type == CCT_RANGESTR ?
-				    !strncmp(s, sc, strlen(sc)) :
-				    ((pprog = patcompile(sc, PAT_STATIC, 0)) &&
-				     pattry(pprog, s))) {
-				    zsfree(s);
-				    erange = j - 1;
-				    t = clwpos <= erange;
-				    break;
-				}
-				zsfree(s);
-			    }
-			}
-			s = NULL;
-		    }
-		    zsfree(s);
-		}
-	    }
-	}
-	if (t) {
-	    /* The patterns matched, use the flags. */
-	    m = 1;
-	    ccont &= ~(CC_PATCONT | CC_DEFCONT);
-	    makecomplistor(compc, os, incmd, compadd, 1);
-	    if (!d && (ccont & CC_DEFCONT)) {
-		d = 1;
-		compadd = 0;
-		brange = 0;
-		erange = clwnum - 1;
-		makecomplistflags(occ, os, incmd, 0);
-	    }
-	    if (!(ccont & CC_PATCONT))
-		break;
-	}
-    }
-    /* If no pattern matched, use the standard flags. */
-    if (!m) {
-	compadd = 0;
-	brange = 0;
-	erange = clwnum - 1;
-	makecomplistflags(occ, os, incmd, 0);
-    }
-}
-
-/* This returns the node with the given data. */
-/* ...should probably be moved to linklist.c. */
-
-static LinkNode
-findnode(LinkList list, void *dat)
-{
-    LinkNode tmp = list->first;
-
-    while (tmp && tmp->dat != dat) tmp = tmp->next;
-
-    return tmp;
-}
-
-/* This adds the completions for the flags in the given compctl. */
-
-/**/
-static void
-makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
-{
-    int t, sf1, sf2, ooffs, um = usemenu, delit, oaw, gflags;
-    int mn = mnum, ohp = haspattern;
-    char *p, *sd = NULL, *tt, *s1, *s2, *os =  dupstring(s);
-    struct cmlist ms;
-
-    ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT));
-
-    if (incompfunc != 1 && findnode(ccstack, cc))
-	return;
-
-    MUSTUSEHEAP("complistflags");
-
-    addlinknode(ccstack, cc);
-
-    if (incompfunc != 1 && allccs) {
-	if (findnode(allccs, cc)) {
-	    uremnode(ccstack, firstnode(ccstack));
-	    return;
-	}
-	addlinknode(allccs, cc);
-    }
-    /* Go to the end of the word if complete_in_word is not set. */
-    if (unset(COMPLETEINWORD) && cs != we)
-	cs = we, offs = strlen(s);
-
-    s = dupstring(s);
-    delit = ispattern = 0;
-    usemenu = um;
-    patcomp = filecomp = NULL;
-    rpre = rsuf = lpre = lsuf = ppre = psuf = lppre = lpsuf =
-	fpre = fsuf = ipre = ripre = prpre = 
-	qfpre = qfsuf = qrpre = qrsuf = qlpre = qlsuf = NULL;
-
-    curcc = cc;
-
-    mflags = 0;
-    gflags = (((cc->mask2 & CC_NOSORT ) ? CGF_NOSORT  : 0) |
-	      ((cc->mask2 & CC_UNIQALL) ? CGF_UNIQALL : 0) |
-	      ((cc->mask2 & CC_UNIQCON) ? CGF_UNIQCON : 0));
-    if (cc->gname) {
-	endcmgroup(NULL);
-	begcmgroup(cc->gname, gflags);
-    }
-    if (cc->ylist) {
-	endcmgroup(NULL);
-	begcmgroup(NULL, gflags);
-    }
-    if (cc->mask & CC_REMOVE)
-	mflags |= CMF_REMOVE;
-    if (cc->explain) {
-	expl = (Cexpl) zhalloc(sizeof(struct cexpl));
-	expl->count = expl->fcount = 0;
-    } else
-	expl = NULL;
-    /* compadd is the number of characters we have to ignore at the  *
-     * beginning of the word.                                        */
-    if (compadd) {
-	ipre = dupstring(s);
-	ipre[compadd] = '\0';
-	untokenize(ipre);
-	wb += compadd;
-	s += compadd;
-	if ((offs -= compadd) < 0) {
-	    /* It's bigger than our word prefix, so we can't help here... */
-	    uremnode(ccstack, firstnode(ccstack));
-	    return;
-	}
-    } else
-	ipre = NULL;
-
-    if (cc->matcher) {
-	ms.next = mstack;
-	ms.matcher = cc->matcher;
-	mstack = &ms;
-
-	if (!mnum)
-	    add_bmatchers(cc->matcher);
-
-	addlinknode(matchers, cc->matcher);
-	cc->matcher->refc++;
-    }
-    if (mnum && (mstack || bmatchers))
-	update_bmatchers();
-
-    /* Insert the prefix (compctl -P), if any. */
-    if (cc->prefix) {
-	int pl = 0;
-
-	if (*s) {
-	    char *dp = rembslash(cc->prefix);
-	    /* First find out how much of the prefix is already on the line. */
-	    sd = dupstring(s);
-	    untokenize(sd);
-	    pl = pfxlen(dp, sd);
-	    s += pl;
-	    sd += pl;
-	    offs -= pl;
-	}
-    }
-    /* Does this compctl have a suffix (compctl -S)? */
-    if (cc->suffix) {
-	char *sdup = rembslash(cc->suffix);
-	int sl = strlen(sdup), suffixll;
-
-	/* Ignore trailing spaces. */
-	for (p = sdup + sl - 1; p >= sdup && *p == ' '; p--, sl--);
-	p[1] = '\0';
-
-	if (!sd) {
-	    sd = dupstring(s);
-	    untokenize(sd);
-	}
-	/* If the suffix is already there, ignore it (and don't add *
-	 * it again).                                               */
-	if (*sd && (suffixll = strlen(sd)) >= sl &&
-	    offs <= suffixll - sl && !strcmp(sdup, sd + suffixll - sl))
-	    s[suffixll - sl] = '\0';
-    }
-    /* Do we have one of the special characters `~' and `=' at the beginning? */
-    if (incompfunc || ((ic = *s) != Tilde && ic != Equals))
-	ic = 0;
-
-    /* Check if we have to complete a parameter name... */
-    if (!incompfunc && (p = check_param(s, 1, 0))) {
-	s = p;
-	/* And now make sure that we complete parameter names. */
-	cc = &cc_dummy;
-	cc_dummy.refc = 10000;
-	cc_dummy.mask = CC_PARAMS | CC_ENVVARS;
-    }
-    ooffs = offs;
-    /* If we have to ignore the word, do that. */
-    if (cc->mask & CC_DELETE) {
-	delit = 1;
-	*s = '\0';
-	offs = 0;
-	if (isset(AUTOMENU))
-	    usemenu = 1;
-    }
-
-    /* Compute line prefix/suffix. */
-    lpl = offs;
-    lpre = zhalloc(lpl + 1);
-    memcpy(lpre, s, lpl);
-    lpre[lpl] = '\0';
-    qlpre = quotename(lpre, NULL);
-    lsuf = dupstring(s + offs);
-    lsl = strlen(lsuf);
-    qlsuf = quotename(lsuf, NULL);
-
-    /* First check for ~.../... */
-    if (ic == Tilde) {
-	for (p = lpre + lpl; p > lpre; p--)
-	    if (*p == '/')
-		break;
-
-	if (*p == '/')
-	    ic = 0;
-    }
-    /* Compute real prefix/suffix. */
-
-    noreal = !delit;
-    for (p = lpre; *p && *p != String && *p != Tick; p++);
-    tt = ic && !ispar ? lpre + 1 : lpre;
-    rpre = (*p || *lpre == Tilde || *lpre == Equals) ?
-	(noreal = 0, getreal(tt)) :
-	dupstring(tt);
-    qrpre = quotename(rpre, NULL);
-
-    for (p = lsuf; *p && *p != String && *p != Tick; p++);
-    rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf);
-    qrsuf = quotename(rsuf, NULL);
-
-    /* Check if word is a pattern. */
-
-    for (s1 = NULL, sf1 = 0, p = rpre + (rpl = strlen(rpre)) - 1;
-	 p >= rpre && (ispattern != 3 || !sf1);
-	 p--)
-	if (itok(*p) && (p > rpre || (*p != Equals && *p != Tilde)))
-	    ispattern |= sf1 ? 1 : 2;
-	else if (*p == '/') {
-	    sf1++;
-	    if (!s1)
-		s1 = p;
-	}
-    rsl = strlen(rsuf);
-    for (s2 = NULL, sf2 = t = 0, p = rsuf; *p && (!t || !sf2); p++)
-	if (itok(*p))
-	    t |= sf2 ? 4 : 2;
-	else if (*p == '/') {
-	    sf2++;
-	    if (!s2)
-		s2 = p;
-	}
-    ispattern = ispattern | t;
-
-    /* But if we were asked not to do glob completion, we never treat the *
-     * thing as a pattern.                                                */
-    if (!comppatmatch || !*comppatmatch)
-	ispattern = 0;
-
-    if (ispattern) {
-	/* The word should be treated as a pattern, so compute the matcher. */
-	p = (char *) zhalloc(rpl + rsl + 2);
-	strcpy(p, rpre);
-	if (rpl && p[rpl - 1] != Star &&
-	    (!comppatmatch || *comppatmatch == '*')) {
-	    p[rpl] = Star;
-	    strcpy(p + rpl + 1, rsuf);
-	} else
-	    strcpy(p + rpl, rsuf);
-	patcomp = patcompile(p, 0, NULL);
-	haspattern = 1;
-    }
-    if (!patcomp) {
-	untokenize(rpre);
-	untokenize(rsuf);
-
-	rpl = strlen(rpre);
-	rsl = strlen(rsuf);
-    }
-    untokenize(lpre);
-    untokenize(lsuf);
-
-    if (!(cc->mask & CC_DELETE))
-	hasmatched = 1;
-
-    /* Handle completion of files specially (of course). */
-
-    if ((cc->mask & (CC_FILES | CC_DIRS | CC_COMMPATH)) || cc->glob) {
-	/* s1 and s2 point to the last/first slash in the prefix/suffix. */
-	if (!s1)
-	    s1 = rpre;
-	if (!s2)
-	    s2 = rsuf + rsl;
-
-	/* Compute the path prefix/suffix. */
-	if (*s1 != '/')
-	    ppre = "";
-	else
-	    ppre = dupstrpfx(rpre, s1 - rpre + 1);
-	psuf = dupstring(s2);
-
-	if (cs != wb) {
-	    char save = line[cs];
-
-	    line[cs] = 0;
-	    lppre = dupstring((char *) line + wb +
-			      (qipre && *qipre ?
-			       (strlen(qipre) -
-				(*qipre == '\'' || *qipre == '\"')) : 0));
-	    line[cs] = save;
-	    if (brbeg) {
-		Brinfo bp;
-
-		for (bp = brbeg; bp; bp = bp->next)
-		    strcpy(lppre + bp->qpos,
-			   lppre + bp->qpos + strlen(bp->str));
-	    }
-	    if ((p = strrchr(lppre, '/'))) {
-		p[1] = '\0';
-		lppl = strlen(lppre);
-	    } else if (!sf1) {
-		lppre = NULL;
-		lppl = 0;
-	    } else {
-		lppre = ppre;
-		lppl = strlen(lppre);
-	    }
-	} else {
-	    lppre = NULL;
-	    lppl = 0;
-	}
-	if (cs != we) {
-	    int end = we;
-	    char save = line[end];
-
-	    if (qisuf && *qisuf) {
-		int ql = strlen(qisuf);
-
-		end -= ql - (qisuf[ql-1] == '\'' || qisuf[ql-1] == '"');
-	    }
-	    line[end] = 0;
-	    lpsuf = dupstring((char *) (line + cs));
-	    line[end] = save;
-	    if (brend) {
-		Brinfo bp;
-		char *p;
-		int bl;
-
-		for (bp = brend; bp; bp = bp->next) {
-		    p = lpsuf + (we - cs) - bp->qpos - (bl = strlen(bp->str));
-		    strcpy(p, p + bl);
-		}
-	    }
-	    if (!(lpsuf = strchr(lpsuf, '/')) && sf2)
-		lpsuf = psuf;
-	    lpsl = (lpsuf ? strlen(lpsuf) : 0);
-	} else {
-	    lpsuf = NULL;
-	    lpsl = 0;
-	}
-
-	/* And get the file prefix. */
-	fpre = dupstring(((s1 == s || s1 == rpre || ic) &&
-			  (*s != '/' || cs == wb)) ? s1 : s1 + 1);
-	qfpre = quotename(fpre, NULL);
-	/* And the suffix. */
-	fsuf = dupstrpfx(rsuf, s2 - rsuf);
-	qfsuf = quotename(fsuf, NULL);
-
-	if (comppatmatch && *comppatmatch && (ispattern & 2)) {
-	    int t2;
-
-	    /* We have to use globbing, so compute the pattern from *
-	     * the file prefix and suffix with a `*' between them.  */
-	    p = (char *) zhalloc((t2 = strlen(fpre)) + strlen(fsuf) + 2);
-	    strcpy(p, fpre);
-	    if ((!t2 || p[t2 - 1] != Star) && *fsuf != Star &&
-		(!comppatmatch || *comppatmatch == '*'))
-		p[t2++] = Star;
-	    strcpy(p + t2, fsuf);
-	    filecomp = patcompile(p, 0, NULL);
-	}
-	if (!filecomp) {
-	    untokenize(fpre);
-	    untokenize(fsuf);
-
-	    fpl = strlen(fpre);
-	    fsl = strlen(fsuf);
-	}
-	addwhat = -1;
-
-	/* Completion after `~', maketildelist adds the usernames *
-	 * and named directories.                                 */
-	if (ic == Tilde) {
-	    char *oi = ipre;
-
-	    ipre = (ipre ? dyncat("~", ipre) : "~");
-	    maketildelist();
-	    ipre = oi;
-	} else if (ic == Equals) {
-	    /* Completion after `=', get the command names from *
-	     * the cmdnamtab and aliases from aliastab.         */
-	    char *oi = ipre;
-
-	    ipre = (ipre ? dyncat("=", ipre) : "=");
-	    if (isset(HASHLISTALL))
-		cmdnamtab->filltable(cmdnamtab);
-	    dumphashtable(cmdnamtab, -7);
-	    dumphashtable(aliastab, -2);
-	    ipre = oi;
-	} else {
-	    /* Normal file completion... */
-	    if (ispattern & 1) {
-		/* But with pattern matching. */
-		LinkList l = newlinklist();
-		LinkNode n;
-		int ng = opts[NULLGLOB];
-
-		opts[NULLGLOB] = 1;
-
-		addwhat = 0;
-		p = (char *) zhalloc(lpl + lsl + 3);
-		strcpy(p, lpre);
-		if (*lsuf != '*' && *lpre && lpre[lpl - 1] != '*')
-		    strcat(p, "*");
-		strcat(p, lsuf);
-		if (*lsuf && lsuf[lsl - 1] != '*' && lsuf[lsl - 1] != ')')
-		    strcat(p, "*");
-
-		/* Do the globbing. */
-		tokenize(p);
-		remnulargs(p);
-		addlinknode(l, p);
-		globlist(l);
-
-		if (nonempty(l)) {
-		    /* And add the resulting words. */
-		    mflags |= CMF_FILE;
-		    for (n = firstnode(l); n; incnode(n))
-			addmatch(getdata(n), NULL);
-		    mflags &= !CMF_FILE;
-		}
-		opts[NULLGLOB] = ng;
-	    } else {
-		char **dirs = 0, *ta[2];
-
-		/* No pattern matching. */
-		addwhat = CC_FILES;
-
-		if (cc->withd) {
-		    char **pp, **npp, *tp;
-		    int tl = strlen(ppre) + 2, pl;
-
-		    if ((pp = get_user_var(cc->withd))) {
-			dirs = npp =
-			    (char**) zhalloc(sizeof(char *)*(arrlen(pp)+1));
-			while (*pp) {
-			    pl = strlen(*pp);
-			    tp = (char *) zhalloc(strlen(*pp) + tl);
-			    strcpy(tp, *pp);
-			    tp[pl] = '/';
-			    strcpy(tp + pl + 1, ppre);
-			    *npp++ = tp;
-			    pp++;
-			}
-			*npp = '\0';
-		    }
-		}
-		if (!dirs) {
-		    dirs = ta;
-		    if (cc->withd) {
-			char *tp;
-			int pl = strlen(cc->withd);
-
-			ta[0] = tp = (char *) zhalloc(strlen(ppre) + pl + 2);
-			strcpy(tp, cc->withd);
-			tp[pl] = '/';
-			strcpy(tp + pl + 1, ppre);
-		    } else
-			ta[0] = ppre;
-		    ta[1] = NULL;
-		}
-		while (*dirs) {
-		    prpre = *dirs;
-
-		    if (sf2)
-			/* We are in the path, so add only directories. */
-			gen_matches_files(1, 0, 0);
-		    else {
-			if (cc->mask & CC_FILES)
-			    /* Add all files. */
-			    gen_matches_files(0, 0, 1);
-			else if (cc->mask & CC_COMMPATH) {
-			    /* Completion of command paths. */
-			    if (sf1 || cc->withd)
-				/* There is a path prefix, so add *
-				 * directories and executables.   */
-				gen_matches_files(1, 1, 0);
-			    else {
-				/* No path prefix, so add the things *
-				 * reachable via the PATH variable.  */
-				char **pc = path, *pp = prpre;
-
-				for (; *pc; pc++)
-				    if (!**pc || (pc[0][0] == '.' && !pc[0][1]))
-					break;
-				if (*pc) {
-				    prpre = "./";
-				    gen_matches_files(1, 1, 0);
-				    prpre = pp;
-				}
-			    }
-			} else if (cc->mask & CC_DIRS)
-			    gen_matches_files(1, 0, 0);
-			/* The compctl has a glob pattern (compctl -g). */
-			if (cc->glob) {
-			    int ns, pl = strlen(prpre), o, paalloc;
-			    char *g = dupstring(cc->glob), *pa;
-			    char *p2, *p3;
-			    int ne = noerrs, md = opts[MARKDIRS];
-
-			    /* These are used in the globbing code to make *
-			     * things a bit faster.                        */
-			    if (ispattern || mstack)
-				glob_pre = glob_suf = NULL;
-			    else {
-				glob_pre = fpre;
-				glob_suf = fsuf;
-			    }
-			    noerrs = 1;
-			    addwhat = -6;
-			    o = strlen(prpre);
-			    pa = (char *)zalloc(paalloc = o + PATH_MAX);
-			    strcpy(pa, prpre);
-			    opts[MARKDIRS] = 0;
-
-			    /* The compctl -g string may contain more than *
-			     * one pattern, so we need a loop.             */
-			    while (*g) {
-				LinkList l = newlinklist();
-				int ng;
-
-				/* Find the blank terminating the pattern. */
-				while (*g && inblank(*g))
-				    g++;
-				/* Oops, we already reached the end of the
-				   string. */
-				if (!*g)
-				    break;
-				for (p = g + 1; *p && !inblank(*p); p++)
-				    if (*p == '\\' && p[1])
-					p++;
-				/* Get the pattern string. */
-				tokenize(g = dupstrpfx(g, p - g));
-				if (*g == '=')
-				    *g = Equals;
-				if (*g == '~')
-				    *g = Tilde;
-				remnulargs(g);
-				if ((*g == Equals || *g == Tilde) && !cc->withd) {
-				/* The pattern has a `~' or `=' at the  *
-				 * beginning, so we expand this and use *
-				 * the result.                          */
-				    filesub(&g, 0);
-				    addlinknode(l, dupstring(g));
-				} else if (*g == '/' && !cc->withd)
-				/* The pattern is a full path (starting *
-				 * with '/'), so add it unchanged.      */
-				    addlinknode(l, dupstring(g));
-				else {
-				/* It's a simple pattern, so append it to *
-				 * the path we have on the command line.  */
-				    int minlen = o + strlen(g);
-				    if (minlen >= paalloc)
-					pa = (char *)
-					    zrealloc(pa, paalloc = minlen+1);
-				    strcpy(pa + o, g);
-				    addlinknode(l, dupstring(pa));
-				}
-				/* Do the globbing. */
-				ng = opts[NULLGLOB];
-				opts[NULLGLOB] = 1;
-				globlist(l);
-				opts[NULLGLOB] = ng;
-				/* Get the results. */
-				if (nonempty(l) && peekfirst(l)) {
-				    for (p2 = (char *)peekfirst(l); *p2; p2++)
-					if (itok(*p2))
-					    break;
-				    if (!*p2) {
-					if ((*g == Equals || *g == Tilde ||
-					     *g == '/') || cc->withd) {
-					    /* IF the pattern started with `~',  *
-					     * `=', or `/', add the result only, *
-					     * if it really matches what we have *
-					     * on the line.                      *
-					     * Do this if an initial directory   *
-					     * was specified, too.               */
-					    while ((p2 = (char *)ugetnode(l)))
-						if (strpfx(prpre, p2))
-						    addmatch(p2 + pl, NULL);
-					} else {
-					    /* Otherwise ignore the path we *
-					     * prepended to the pattern.    */
-					    while ((p2 = p3 =
-						    (char *)ugetnode(l))) {
-						for (ns = sf1; *p3 && ns; p3++)
-						    if (*p3 == '/')
-							ns--;
-
-						addmatch(p3, NULL);
-					    }
-					}
-				    }
-				}
-				pa[o] = '\0';
-				g = p;
-			    }
-			    glob_pre = glob_suf = NULL;
-			    noerrs = ne;
-			    opts[MARKDIRS] = md;
-
-			    zfree(pa, paalloc);
-			}
-		    }
-		    dirs++;
-		}
-		prpre = NULL;
-	    }
-	}
-	lppre = lpsuf = NULL;
-	lppl = lpsl = 0;
-    }
-    if (ic) {
-	/* Now change the `~' and `=' tokens to the real characters so *
-	 * that things starting with these characters will be added.   */
-	rpre = dyncat((ic == Tilde) ? "~" : "=", rpre);
-	rpl++;
-	qrpre = dyncat((ic == Tilde) ? "~" : "=", qrpre);
-    }
-    if (!ic && (cc->mask & CC_COMMPATH) && !*ppre && !*psuf) {
-	/* If we have to complete commands, add alias names, *
-	 * shell functions and builtins too.                 */
-	dumphashtable(aliastab, -3);
-	dumphashtable(reswdtab, -3);
-	dumphashtable(shfunctab, -3);
-	dumphashtable(builtintab, -3);
-	if (isset(HASHLISTALL))
-	    cmdnamtab->filltable(cmdnamtab);
-	dumphashtable(cmdnamtab, -3);
-	/* And parameter names if autocd and cdablevars are set. */
-	if (isset(AUTOCD) && isset(CDABLEVARS))
-	    dumphashtable(paramtab, -4);
-    }
-    oaw = addwhat = (cc->mask & CC_QUOTEFLAG) ? -2 : CC_QUOTEFLAG;
-
-    if (cc->mask & CC_NAMED)
-	/* Add named directories. */
-	dumphashtable(nameddirtab, addwhat);
-    if (cc->mask & CC_OPTIONS)
-	/* Add option names. */
-	dumphashtable(optiontab, addwhat);
-    if (cc->mask & CC_VARS) {
-	/* And parameter names. */
-	dumphashtable(paramtab, -9);
-	addwhat = oaw;
-    }
-    if (cc->mask & CC_BINDINGS) {
-	/* And zle function names... */
-	dumphashtable(thingytab, CC_BINDINGS);
-	addwhat = oaw;
-    }
-    if (cc->keyvar) {
-	/* This adds things given to the compctl -k flag *
-	 * (from a parameter or a list of words).        */
-	char **usr = get_user_var(cc->keyvar);
-
-	if (usr)
-	    while (*usr)
-		addmatch(*usr++, NULL);
-    }
-    if (cc->mask & CC_USERS) {
-	/* Add user names. */
-	maketildelist();
-	addwhat = oaw;
-    }
-    if (cc->widget) {
-	char **ocfa = cfargs;
-	int ocfr = cfret;
-
-	cfargs = zlenoargs;
-	callcompfunc(os, cc->widget);
-	cfargs = ocfa;
-	cfret = ocfr;
-    }
-    if (cc->func) {
-	/* This handles the compctl -K flag. */
-	List list;
-	char **r;
-	int lv = lastval;
-	    
-	/* Get the function. */
-	if ((list = getshfunc(cc->func)) != &dummy_list) {
-	    /* We have it, so build a argument list. */
-	    LinkList args = newlinklist();
-	    int osc = sfcontext;
-		
-	    addlinknode(args, cc->func);
-		
-	    if (delit) {
-		p = dupstrpfx(os, ooffs);
-		untokenize(p);
-		addlinknode(args, p);
-		p = dupstring(os + ooffs);
-		untokenize(p);
-		addlinknode(args, p);
-	    } else {
-		addlinknode(args, lpre);
-		addlinknode(args, lsuf);
-	    }
-		
-	    /* This flag allows us to use read -l and -c. */
-	    if (incompfunc != 1)
-		incompctlfunc = 1;
-	    sfcontext = SFC_COMPLETE;
-	    /* Call the function. */
-	    doshfunc(cc->func, list, args, 0, 1);
-	    sfcontext = osc;
-	    incompctlfunc = 0;
-	    /* And get the result from the reply parameter. */
-	    if ((r = get_user_var("reply")))
-		while (*r)
-		    addmatch(*r++, NULL);
-	}
-	lastval = lv;
-    }
-    if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) {
-	/* Get job names. */
-	int i;
-	char *j;
-
-	for (i = 0; i < MAXJOB; i++)
-	    if ((jobtab[i].stat & STAT_INUSE) &&
-		jobtab[i].procs && jobtab[i].procs->text) {
-		int stopped = jobtab[i].stat & STAT_STOPPED;
-
-		j = dupstring(jobtab[i].procs->text);
-		if ((cc->mask & CC_JOBS) ||
-		    (stopped && (cc->mask & CC_STOPPED)) ||
-		    (!stopped && (cc->mask & CC_RUNNING)))
-		    addmatch(j, NULL);
-	    }
-    }
-    if (cc->str) {
-	/* Get the stuff from a compctl -s. */
-	LinkList foo = newlinklist();
-	LinkNode n;
-	int first = 1, ng = opts[NULLGLOB], oowe = we, oowb = wb;
-	char *tmpbuf;
-
-	opts[NULLGLOB] = 1;
-
-	/* Put the string in the lexer buffer and call the lexer to *
-	 * get the words we have to expand.                        */
-	zleparse = 1;
-	lexsave();
-	tmpbuf = (char *)zhalloc(strlen(cc->str) + 5);
-	sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */
-	inpush(tmpbuf, 0, NULL);
-	strinbeg(0);
-	noaliases = 1;
-	do {
-	    ctxtlex();
-	    if (tok == ENDINPUT || tok == LEXERR)
-		break;
-	    if (!first && tokstr && *tokstr)
-		addlinknode(foo, ztrdup(tokstr));
-	    first = 0;
-	} while (tok != ENDINPUT && tok != LEXERR);
-	noaliases = 0;
-	strinend();
-	inpop();
-	errflag = zleparse = 0;
-	lexrestore();
-	/* Fine, now do full expansion. */
-	prefork(foo, 0);
-	if (!errflag) {
-	    globlist(foo);
-	    if (!errflag)
-		/* And add the resulting words as matches. */
-		for (n = firstnode(foo); n; incnode(n))
-		    addmatch((char *)n->dat, NULL);
-	}
-	opts[NULLGLOB] = ng;
-	we = oowe;
-	wb = oowb;
-    }
-    if (cc->hpat) {
-	/* We have a pattern to take things from the history. */
-	Patprog pprogc = NULL;
-	char *e, *h, hpatsav;
-	int i = addhistnum(curhist,-1,HIST_FOREIGN), n = cc->hnum;
-	Histent he = quietgethistent(i, GETHIST_UPWARD);
-
-	/* Parse the pattern, if it isn't the null string. */
-	if (*(cc->hpat)) {
-	    char *thpat = dupstring(cc->hpat);
-
-	    tokenize(thpat);
-	    pprogc = patcompile(thpat, 0, NULL);
-	}
-	/* n holds the number of history line we have to search. */
-	if (!n)
-	    n = -1;
-
-	/* Now search the history. */
-	while (n-- && he) {
-	    int iwords;
-	    for (iwords = he->nwords - 1; iwords >= 0; iwords--) {
-		h = he->text + he->words[iwords*2];
-		e = he->text + he->words[iwords*2+1];
-		hpatsav = *e;
-		*e = '\0';
-		/* We now have a word from the history, ignore it *
-		 * if it begins with a quote or `$'.              */
-		if (*h != '\'' && *h != '"' && *h != '`' && *h != '$' &&
-		    (!pprogc || pattry(pprogc, h)))
-		    /* Otherwise add it if it was matched. */
-		    addmatch(dupstring(h), NULL);
-		if (hpatsav)
-		    *e = hpatsav;
-	    }
-	    he = up_histent(he);
-	}
-    }
-    if ((t = cc->mask & (CC_ARRAYS | CC_INTVARS | CC_ENVVARS | CC_SCALARS |
-			 CC_READONLYS | CC_SPECIALS | CC_PARAMS)))
-	/* Add various flavours of parameters. */
-	dumphashtable(paramtab, t);
-    if ((t = cc->mask & CC_SHFUNCS))
-	/* Add shell functions. */
-	dumphashtable(shfunctab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
-    if ((t = cc->mask & CC_BUILTINS))
-	/* Add builtins. */
-	dumphashtable(builtintab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
-    if ((t = cc->mask & CC_EXTCMDS)) {
-	/* Add external commands */
-	if (isset(HASHLISTALL))
-	    cmdnamtab->filltable(cmdnamtab);
-	dumphashtable(cmdnamtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
-    }
-    if ((t = cc->mask & CC_RESWDS))
-	/* Add reserved words */
-	dumphashtable(reswdtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
-    if ((t = cc->mask & (CC_ALREG | CC_ALGLOB)))
-	/* Add the two types of aliases. */
-	dumphashtable(aliastab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
-    if (keypm && cc == &cc_dummy) {
-	/* Add the keys of the parameter in keypm. */
-	scanhashtable(keypm->gets.hfn(keypm), 0, 0, PM_UNSET, addhnmatch, 0);
-	keypm = NULL;
-	cc_dummy.suffix = NULL;
-    }
-    if (!errflag && cc->ylist) {
-	/* generate the user-defined display list: if anything fails, *
-	 * we silently allow the normal completion list to be used.   */
-	char **yaptr = NULL, *uv = NULL;
-	List list;
-
-	if (cc->ylist[0] == '$' || cc->ylist[0] == '(') {
-	    /* from variable */
-	    uv = cc->ylist + (cc->ylist[0] == '$');
-	} else if ((list = getshfunc(cc->ylist)) != &dummy_list) {
-	    /* from function:  pass completions as arg list */
-	    LinkList args = newlinklist();
-	    LinkNode ln;
-	    Cmatch m;
-	    int osc = sfcontext;
-
-	    addlinknode(args, cc->ylist);
-	    for (ln = firstnode(matches); ln; ln = nextnode(ln)) {
-		m = (Cmatch) getdata(ln);
-		if (m->ppre) {
-		    char *p = (char *) zhalloc(strlen(m->ppre) + strlen(m->str) +
-					      strlen(m->psuf) + 1);
-
-		    sprintf(p, "%s%s%s", m->ppre, m->str, m->psuf);
-		    addlinknode(args, dupstring(p));
-		} else
-		    addlinknode(args, dupstring(m->str));
-	    }
-
-	    /* No harm in allowing read -l and -c here, too */
-	    if (incompfunc != 1)
-		incompctlfunc = 1;
-	    sfcontext = SFC_COMPLETE;
-	    doshfunc(cc->ylist, list, args, 0, 1);
-	    sfcontext = osc;
-	    incompctlfunc = 0;
-	    uv = "reply";
-	}
-	if (uv)
-	    yaptr = get_user_var(uv);
-	if ((tt = cc->explain)) {
-	    tt = dupstring(tt);
-	    if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
-		singsub(&tt);
-		untokenize(tt);
-	    }
-	    expl->str = tt;
-	    if (cc->gname) {
-		endcmgroup(yaptr);
-		begcmgroup(cc->gname, gflags);
-		addexpl();
-	    } else {
-		addexpl();
-		endcmgroup(yaptr);
-		begcmgroup("default", 0);
-	    }
-	}
-    } else if ((tt = cc->explain)) {
-	tt = dupstring(tt);
-	if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
-	    singsub(&tt);
-	    untokenize(tt);
-	}
-	expl->str = tt;
-	addexpl();
-    }
-    if (cc->subcmd) {
-	/* Handle -l sub-completion. */
-	char **ow = clwords, *os = cmdstr, *ops = NULL;
-	int oldn = clwnum, oldp = clwpos, br;
-	unsigned long occ = ccont;
-	
-	ccont = CC_CCCONT;
-	
-	/* So we restrict the words-array. */
-	if (brange >= clwnum)
-	    brange = clwnum - 1;
-	if (brange < 1)
-	    brange = 1;
-	if (erange >= clwnum)
-	    erange = clwnum - 1;
-	if (erange < 1)
-	    erange = 1;
-	clwnum = erange - brange + 1;
-	clwpos = clwpos - brange;
-	br = brange;
-
-	if (cc->subcmd[0]) {
-	    /* And probably put the command name given to the flag *
-	     * in the array.                                       */
-	    clwpos++;
-	    clwnum++;
-	    incmd = 0;
-	    ops = clwords[br - 1];
-	    clwords[br - 1] = ztrdup(cc->subcmd);
-	    cmdstr = ztrdup(cc->subcmd);
-	    clwords += br - 1;
-	} else {
-	    cmdstr = ztrdup(clwords[br]);
-	    incmd = !clwpos;
-	    clwords += br;
-	}
-	/* Produce the matches. */
-	makecomplistcmd(s, incmd, CFN_FIRST);
-
-	/* And restore the things we changed. */
-	clwords = ow;
-	zsfree(cmdstr);
-	cmdstr = os;
-	clwnum = oldn;
-	clwpos = oldp;
-	if (ops) {
-	    zsfree(clwords[br - 1]);
-	    clwords[br - 1] = ops;
-	}
-	ccont = occ;
-    }
-    if (cc->substr)
-	sep_comp_string(cc->substr, s, offs, 1);
-    uremnode(ccstack, firstnode(ccstack));
-    if (cc->matcher)
-	mstack = mstack->next;
-
-    if (mn == mnum)
-	haspattern = ohp;
-}
-
 /* Invalidate the completion list. */
 
 /**/
@@ -7812,14 +5748,14 @@ addexpl(void)
 
     for (n = firstnode(expls); n; incnode(n)) {
 	e = (Cexpl) getdata(n);
-	if (!strcmp(expl->str, e->str)) {
-	    e->count += expl->count;
-	    e->fcount += expl->fcount;
+	if (!strcmp(curexpl->str, e->str)) {
+	    e->count += curexpl->count;
+	    e->fcount += curexpl->fcount;
 
 	    return;
 	}
     }
-    addlinknode(expls, expl);
+    addlinknode(expls, curexpl);
     newmatches = 1;
 }
 
@@ -7880,7 +5816,6 @@ permmatches(int last)
     Cmgroup g = amatches, n;
     Cmatch *p, *q;
     Cexpl *ep, *eq, e, o;
-    Compctl *cp, *cq;
     LinkList mlist;
     static int fi = 0;
     int nn, nl, ll, gn = 1, mn = 1, rn;
@@ -7922,7 +5857,6 @@ permmatches(int last)
 					   NULL, NULL);
 
 	    g->ccount = 0;
-	    g->ccs = NULL;
 	} LASTALLOC;
 
 	nmatches += g->mcount;
@@ -7966,14 +5900,6 @@ permmatches(int last)
 	} else
 	    n->expls = NULL;
 
-	if ((n->ccount = g->ccount)) {
-	    n->ccs = cp = (Compctl *) ncalloc((n->ccount + 1) *
-					      sizeof(Compctl));
-	    for (cq = g->ccs; *cq; cq++, cp++)
-		*cp = *cq;
-	    *cp = NULL;
-	} else
-	    n->ccs = NULL;
 	n->widths = NULL;
 
 	g = g->next;
@@ -8080,7 +6006,6 @@ freematches(Cmgroup g)
     Cmgroup n;
     Cmatch *m;
     Cexpl *e;
-    Compctl *c;
 
     while (g) {
 	n = g->next;
@@ -8099,14 +6024,6 @@ freematches(Cmgroup g)
 	    }
 	    free(g->expls);
 	}
-	if ((c = g->ccs)) {
-	    while (*c) {
-		if (*c != &cc_dummy)
-		    freecompctl(*c);
-		c++;
-	    }
-	    free(g->ccs);
-	}
 	free(g);
 
 	g = n;
diff --git a/Src/Zle/zleparameter.c b/Src/Zle/zleparameter.c
new file mode 100644
index 000000000..8a5bc0bc2
--- /dev/null
+++ b/Src/Zle/zleparameter.c
@@ -0,0 +1,257 @@
+/*
+ * zleparameter.c - parameter interface to zle internals
+ *
+ * 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 "zleparameter.mdh"
+#include "zleparameter.pro"
+
+/* Empty dummy function for special hash parameters. */
+
+/**/
+static void
+shempty(void)
+{
+}
+
+/* Create a simple special hash parameter. */
+
+/**/
+static Param
+createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan)
+{
+    Param pm;
+    HashTable ht;
+
+    if (!(pm = createparam(name, PM_SPECIAL|PM_HIDE|PM_REMOVABLE|PM_HASHED)))
+	return NULL;
+
+    pm->level = pm->old ? locallevel : 0;
+    pm->gets.hfn = hashgetfn;
+    pm->sets.hfn = hashsetfn;
+    pm->unsetfn = stdunsetfn;
+    pm->u.hash = ht = newhashtable(7, name, NULL);
+
+    ht->hash        = hasher;
+    ht->emptytable  = (TableFunc) shempty;
+    ht->filltable   = NULL;
+    ht->addnode     = (AddNodeFunc) shempty;
+    ht->getnode     = ht->getnode2 = get;
+    ht->removenode  = (RemoveNodeFunc) shempty;
+    ht->disablenode = NULL;
+    ht->enablenode  = NULL;
+    ht->freenode    = (FreeNodeFunc) shempty;
+    ht->printnode   = printparamnode;
+    ht->scantab     = scan;
+
+    return pm;
+}
+
+/* Functions for the zlewidgets special parameter. */
+
+/**/
+static char *
+widgetstr(Widget w)
+{
+    if (w->flags & WIDGET_INT)
+	return dupstring("builtin");
+    if (w->flags & WIDGET_NCOMP) {
+	char *t = (char *) zhalloc(13 + strlen(w->u.comp.wid) +
+				   strlen(w->u.comp.func));
+
+	strcpy(t, "completion:");
+	strcat(t, w->u.comp.wid);
+	strcat(t, ":");
+	strcat(t, w->u.comp.func);
+
+	return t;
+    }
+    return dyncat("user:", w->u.fnnam);
+}
+
+/**/
+static HashNode
+getpmwidgets(HashTable ht, char *name)
+{
+    Param pm = NULL;
+    Thingy th;
+
+    HEAPALLOC {
+	pm = (Param) zhalloc(sizeof(struct param));
+	pm->nam = dupstring(name);
+	pm->flags = PM_SCALAR | PM_READONLY;
+	pm->sets.cfn = NULL;
+	pm->gets.cfn = strgetfn;
+	pm->unsetfn = NULL;
+	pm->ct = 0;
+	pm->env = NULL;
+	pm->ename = NULL;
+	pm->old = NULL;
+	pm->level = 0;
+	if ((th = (Thingy) thingytab->getnode(thingytab, name)) &&
+	    !(th->flags & DISABLED))
+	    pm->u.str = widgetstr(th->widget);
+	else {
+	    pm->u.str = dupstring("");
+	    pm->flags |= PM_UNSET;
+	}
+    } LASTALLOC;
+
+    return (HashNode) pm;
+}
+
+/**/
+static void
+scanpmwidgets(HashTable ht, ScanFunc func, int flags)
+{
+    struct param pm;
+    int i;
+    HashNode hn;
+
+    pm.flags = PM_SCALAR | PM_READONLY;
+    pm.sets.cfn = NULL;
+    pm.gets.cfn = strgetfn;
+    pm.unsetfn = NULL;
+    pm.ct = 0;
+    pm.env = NULL;
+    pm.ename = NULL;
+    pm.old = NULL;
+    pm.level = 0;
+
+    for (i = 0; i < thingytab->hsize; i++)
+	for (hn = thingytab->nodes[i]; hn; hn = hn->next) {
+	    pm.nam = hn->nam;
+	    if (func != scancountparams)
+		pm.u.str = widgetstr(((Thingy) hn)->widget);
+	    func((HashNode) &pm, flags);
+	}
+}
+
+/* Functions for the zlekeymaps special parameter. */
+
+static char **
+keymapsgetfn(Param pm)
+{
+    int i;
+    HashNode hn;
+    char **ret, **p;
+
+    p = ret = (char **) zhalloc((keymapnamtab->ct + 1) * sizeof(char *));
+
+    for (i = 0; i < keymapnamtab->hsize; i++)
+	for (hn = keymapnamtab->nodes[i]; hn; hn = hn->next)
+	    *p++ = dupstring(hn->nam);
+    *p = NULL;
+
+    return ret;
+}
+
+/* Table for defined parameters. */
+
+struct pardef {
+    char *name;
+    int flags;
+    GetNodeFunc getnfn;
+    ScanTabFunc scantfn;
+    void (*hsetfn) _((Param, HashTable));
+    void (*setfn) _((Param, char **));
+    char **(*getfn) _((Param));
+    void (*unsetfn) _((Param, int));
+    Param pm;
+};
+
+static struct pardef partab[] = {
+    { "zlewidgets", PM_READONLY,
+      getpmwidgets, scanpmwidgets, hashsetfn,
+      NULL, NULL, stdunsetfn, NULL },
+    { "zlekeymaps", PM_ARRAY|PM_HIDE|PM_SPECIAL|PM_READONLY,
+      NULL, NULL, NULL,
+      arrsetfn, keymapsgetfn, stdunsetfn, NULL },
+    { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+/**/
+int
+setup_zleparameter(Module m)
+{
+    return 0;
+}
+
+/**/
+int
+boot_zleparameter(Module m)
+{
+    struct pardef *def;
+
+    for (def = partab; def->name; def++) {
+	unsetparam(def->name);
+
+	if (def->getnfn) {
+	    if (!(def->pm = createspecialhash(def->name, def->getnfn,
+					      def->scantfn)))
+		return 1;
+	    def->pm->flags |= def->flags;
+	    if (def->hsetfn)
+		def->pm->sets.hfn = def->hsetfn;
+	} else {
+	    if (!(def->pm = createparam(def->name, def->flags)))
+		return 1;
+	    def->pm->sets.afn = def->setfn;
+	    def->pm->gets.afn = def->getfn;
+	    def->pm->unsetfn = def->unsetfn;
+	}
+    }
+    return 0;
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_zleparameter(Module m)
+{
+    Param pm;
+    struct pardef *def;
+
+    for (def = partab; def->name; def++) {
+	if ((pm = (Param) paramtab->getnode(paramtab, def->name)) &&
+	    pm == def->pm) {
+	    pm->flags &= ~PM_READONLY;
+	    unsetparam_pm(pm, 0, 1);
+	}
+    }
+    return 0;
+}
+
+/**/
+int
+finish_zleparameter(Module m)
+{
+    return 0;
+}
+
+#endif
diff --git a/Src/Zle/zleparameter.mdd b/Src/Zle/zleparameter.mdd
new file mode 100644
index 000000000..f9d528661
--- /dev/null
+++ b/Src/Zle/zleparameter.mdd
@@ -0,0 +1,5 @@
+moddeps="zle"
+
+autoparams="zlewidgets zlekeymaps"
+
+objects="zleparameter.o"