about summary refs log tree commit diff
path: root/Src/Zle
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:11:42 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:11:42 +0000
commit20d67907c95265356b51dbdce8ecc0c1ede9e66b (patch)
tree69b0777db02f96555b3a0587cd630025062a7f09 /Src/Zle
parent2a5a899a55fd2bce10efd01c75a4bec5285aa46c (diff)
downloadzsh-20d67907c95265356b51dbdce8ecc0c1ede9e66b.tar.gz
zsh-20d67907c95265356b51dbdce8ecc0c1ede9e66b.tar.xz
zsh-20d67907c95265356b51dbdce8ecc0c1ede9e66b.zip
zsh-3.1.5-pws-5 zsh-3.1.5-pws-5
Diffstat (limited to 'Src/Zle')
-rw-r--r--Src/Zle/comp1.c44
-rw-r--r--Src/Zle/comp1.export13
-rw-r--r--Src/Zle/compctl.c582
-rw-r--r--Src/Zle/compctl.mdd4
-rw-r--r--Src/Zle/iwidgets.list16
-rw-r--r--Src/Zle/zle.h18
-rw-r--r--Src/Zle/zle_keymap.c4
-rw-r--r--Src/Zle/zle_main.c28
-rw-r--r--Src/Zle/zle_refresh.c105
-rw-r--r--Src/Zle/zle_thingy.c50
-rw-r--r--Src/Zle/zle_tricky.c448
11 files changed, 1152 insertions, 160 deletions
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index a0c013901..42bc92bb2 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -41,7 +41,7 @@ struct compctl cc_compos, cc_default, cc_first, cc_dummy;
 /**/
 Cmlist cmatcher;
 
-/* pointers to functions required by zle */
+/* pointers to functions required by zle and defined by compctl */
 
 /**/
 void (*printcompctlptr) _((char *, Compctl, int, int));
@@ -49,6 +49,24 @@ void (*printcompctlptr) _((char *, Compctl, int, int));
 /**/
 Compctl (*compctl_widgetptr) _((char *, char **));
 
+/**/
+void (*makecompparamsptr) _((void));
+
+/* pointers to functions required by compctl and defined by zle */
+
+/**/
+void (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, int, int, int, int, int, char **));
+
+/**/
+char *(*comp_strptr) _((int*,int*));
+
+/**/
+int (*getcpatptr) _((char *, int, char *, int));
+
+/**/
+void (*makecomplistcallptr) _((Compctl));
+
+
 /* Hash table for completion info for commands */
  
 /**/
@@ -72,6 +90,23 @@ char **clwords;
 /**/
 int incompctlfunc;
 
+/* != 0 if we are in a new style completion function */
+
+/**/
+int incompfunc;
+
+/* global variables for shell parameters in new style completion */
+
+/**/
+long compcurrent,
+     compnmatches;
+
+/**/
+char *compcontext,
+     *compcommand,
+     *compprefix,
+     *compsuffix,
+     *compiprefix;
 
 /* This variable and the functions rembslash() and quotename() came from     *
  * zle_tricky.c, but are now used in compctl.c, too.                         */
@@ -443,6 +478,8 @@ setup_comp1(Module m)
     cc_first.refc = 10000;
     cc_first.mask = 0;
     cc_first.mask2 = CC_CCCONT;
+    compcontext = compcommand = compprefix = compsuffix =
+	compiprefix = NULL;
     return 0;
 }
 
@@ -469,6 +506,11 @@ finish_comp1(Module m)
     deletehashtable(compctltab);
     zfree(clwords, clwsize * sizeof(char *));
     compctlreadptr = fallback_compctlread;
+    zsfree(compcontext);
+    zsfree(compcommand);
+    zsfree(compprefix);
+    zsfree(compiprefix);
+    zsfree(compsuffix);
     return 0;
 }
 
diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export
index 1ac9195df..c90161740 100644
--- a/Src/Zle/comp1.export
+++ b/Src/Zle/comp1.export
@@ -1,4 +1,5 @@
 #!
+addmatchesptr
 cc_compos
 cc_default
 cc_dummy
@@ -8,14 +9,26 @@ clwords
 clwpos
 clwsize
 cmatcher
+compcommand
+compcontext
 compctl_widgetptr
 compctltab
+compcurrent
+compiprefix
+compnmatches
+compprefix
+comp_strptr
+compsuffix
 freecmatcher
 freecmlist
 freecompcond
 freecompctl
+getcpatptr
 incompctlfunc
+incompfunc
 instring
+makecomplistcallptr
+makecompparamsptr
 patcomps
 printcompctlptr
 quotename
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 4d192fef8..1913d3828 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -373,7 +373,7 @@ parse_class(Cpattern p, unsigned char *s, unsigned char e)
 
 /**/
 static int
-get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
+get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
 {
     /* Parse the basic flags for completion:
      * first is a flag that we are not in extended completion,
@@ -394,12 +394,17 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 	if(argv[0] && argv[0][0] == '-')
 	    argv++;
 	*av = argv;
-	freecompctl(cc);
- 	cclist = COMP_REMOVE;
-	return 0;
+	if (cl)
+	    return 1;
+	else {
+	    freecompctl(cc);
+	    cclist = COMP_REMOVE;
+	    return 0;
+	}
     }
 
     memset((void *)&cct, 0, sizeof(cct));
+    cct.mask2 = CC_CCCONT;
 
     /* Loop through the flags until we have no more:        *
      * those with arguments are not properly allocated yet, *
@@ -505,6 +510,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		{
 		    char *p;
 
+		    if (cl) {
+			zerrnam(name, "illegal option -%c", NULL, **argv);
+			return 1;
+		    }
 		    if ((*argv)[1]) {
 			p = (*argv) + 1;
 			*argv = "" - 1;
@@ -516,26 +525,28 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 			p = *++argv;
 			*argv = "" - 1;
 		    }
-		    while (*p) {
-			switch (*p) {
-			case '+':
-			    cct.mask2 |= CC_XORCONT;
-			    break;
-			case 'c':
-			    cct.mask2 |= CC_CCCONT;
-			    break;
-			case '-':
-			    cct.mask2 |= CC_PATCONT;
-			    break;
-			case 'x':
-			    cct.mask2 |= CC_DEFCONT;
-			    break;
-			default:
-			    zwarnnam(name, "invalid retry specification character `%c'",
-				     NULL, *p);
-			    return 1;
-			}
-			p++;
+		    switch (*p) {
+		    case '+':
+			cct.mask2 = CC_XORCONT;
+			break;
+		    case 'n':
+			cct.mask2 = 0;
+			break;
+		    case '-':
+			cct.mask2 = CC_PATCONT;
+			break;
+		    case 'x':
+			cct.mask2 = CC_DEFCONT;
+			break;
+		    default:
+			zwarnnam(name, "invalid retry specification character `%c'",
+				 NULL, *p);
+			return 1;
+		    }
+		    if (p[1]) {
+			zwarnnam(name, "too many retry specification characters: `%s'",
+				 p + 1, 0);
+			return 1;
 		    }
 		}
 		break;
@@ -645,7 +656,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		}
 		break;
 	    case 'l':
-		if ((*argv)[1]) {
+		if (cl) {
+		    zerrnam(name, "illegal option -%c", NULL, **argv);
+		    return 1;
+		} else if ((*argv)[1]) {
 		    cct.subcmd = (*argv) + 1;
 		    *argv = "" - 1;
 		} else if (!argv[1]) {
@@ -745,6 +759,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		*argv = "" - 1;
 		break;
 	    case 'C':
+		if (cl) {
+		    zerrnam(name, "illegal option -%c", NULL, **argv);
+		    return 1;
+		}
 		if (first && !hx) {
 		    cclist |= COMP_COMMAND;
 		} else {
@@ -754,6 +772,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		}
 		break;
 	    case 'D':
+		if (cl) {
+		    zerrnam(name, "illegal option -%c", NULL, **argv);
+		    return 1;
+		}
 		if (first && !hx) {
 		    isdef = 1;
 		    cclist |= COMP_DEFAULT;
@@ -764,7 +786,11 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		}
 		break;
  	    case 'T':
-              if (first && !hx) {
+		if (cl) {
+		    zerrnam(name, "illegal option -%c", NULL, **argv);
+		    return 1;
+		}
+		if (first && !hx) {
  		    cclist |= COMP_FIRST;
  		} else {
  		    zwarnnam(name, "misplaced first completion (-T) flag",
@@ -773,6 +799,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
  		}
  		break;
 	    case 'L':
+		if (cl) {
+		    zerrnam(name, "illegal option -%c", NULL, **argv);
+		    return 1;
+		}
 		if (!first || hx) {
 		    zwarnnam(name, "illegal use of -L flag", NULL, 0);
 		    return 1;
@@ -780,6 +810,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		cclist |= COMP_LIST;
 		break;
 	    case 'x':
+		if (cl) {
+		    zerrnam(name, "extended completion not allowed", NULL, 0);
+		    return 1;
+		}
 		if (!argv[1]) {
 		    zwarnnam(name, "condition expected after -%c", NULL,
 			    **argv);
@@ -811,6 +845,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 
 	if (*++argv && (!ready || ready == 2) &&
 	    **argv == '+' && !argv[0][1]) {
+	    if (cl) {
+		zerrnam(name, "xor'ed completion illegal", NULL, 0);
+		return 1;
+	    }
 	    /* There's an alternative (+) completion:  assign
 	     * what we have so far before moving on to that.
 	     */
@@ -835,6 +873,7 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		cc->xor = (Compctl) zcalloc(sizeof(*cc));
 		cc = cc->xor;
 		memset((void *)&cct, 0, sizeof(cct));
+		cct.mask2 = CC_CCCONT;
 	    }
 	}
     }
@@ -1084,7 +1123,7 @@ get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
 	(*next)->cond = m;
 	argv++;
 	/* End of the condition; get the flags that go with it. */
-	if (get_compctl(name, &argv, *next, 0, isdef))
+	if (get_compctl(name, &argv, *next, 0, isdef, 0))
 	    return 1;
  	if ((!argv || !*argv) && (cclist & COMP_SPECIAL))
  	    /* default, first, or command completion finished */
@@ -1362,17 +1401,16 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
 	    t >>= 1;
 	}
     }
-    if (flags2 & (CC_XORCONT | CC_CCCONT | CC_PATCONT | CC_DEFCONT)) {
+    if (flags2 & (CC_XORCONT | CC_PATCONT | CC_DEFCONT)) {
 	printf(" -t");
 	if (flags2 & CC_XORCONT)
 	    putchar('+');
-	if (flags2 & CC_CCCONT)
-	    putchar('c');
 	if (flags2 & CC_PATCONT)
 	    putchar('-');
 	if (flags2 & CC_DEFCONT)
 	    putchar('x');
-    }
+    } else if (!(flags2 & CC_CCCONT))
+	printf(" -tn");
     /* now flags with arguments */
     printif(cc->mstr, 'M');
     if (flags2 & CC_NOSORT)
@@ -1518,7 +1556,7 @@ bin_compctl(char *name, char **argv, char *ops, int func)
 	    return ret - 1;
 
 	cc = (Compctl) zcalloc(sizeof(*cc));
-	if (get_compctl(name, &argv, cc, 1, 0)) {
+	if (get_compctl(name, &argv, cc, 1, 0, 0)) {
 	    freecompctl(cc);
 	    return 1;
 	}
@@ -1610,7 +1648,7 @@ compctl_widget(char *name, char **argv)
   cclist = 0;
   showmask = 0;
 
-  if (get_compctl(name, &argv, cc, 1, 0)) {
+  if (get_compctl(name, &argv, cc, 1, 0, 0)) {
       freecompctl(cc);
       return NULL;
   }
@@ -1632,8 +1670,478 @@ compctl_widget(char *name, char **argv)
   return cc;
 }
 
+/**/
+static int
+bin_complist(char *name, char **argv, char *ops, int func)
+{
+    Compctl cc;
+    int ret = 0;
+
+    if (!incompfunc) {
+	zerrnam(name, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
+    cc = (Compctl) zcalloc(sizeof(*cc));
+    cclist = 0;
+    showmask = 0;
+
+    if (get_compctl(name, &argv, cc, 1, 0, 1))
+	ret = 1;
+    else if (*argv) {
+	zerrnam(name, "command names illegal", NULL, 0);
+	ret = 1;
+    } else
+	makecomplistcallptr(cc);
+
+    freecompctl(cc);
+    return ret;
+}
+
+/**/
+static int
+bin_compadd(char *name, char **argv, char *ops, int func)
+{
+    char *p, **sp, *e;
+    char *ipre = NULL, *ppre = NULL, *psuf = NULL, *prpre = NULL;
+    char *pre = NULL, *suf = NULL, *group = NULL;
+    int f = 0, q = 0, m = 0, ns = 0, a = 0;
+
+    if (!incompfunc) {
+	zerrnam(name, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
+    for (; *argv && **argv ==  '-'; argv++) {
+	for (p = *argv + 1; *p; p++) {
+	    sp = NULL;
+	    switch (*p) {
+	    case 'q':
+		f |= CMF_REMOVE;
+		break;
+	    case 'Q':
+		q = 1;
+		break;
+	    case 'f':
+		f |= CMF_FILE;
+		break;
+	    case 'n':
+		f |= CMF_NOLIST;
+		break;
+	    case 'U':
+		m = 1;
+		break;
+	    case 'P':
+		sp = &pre;
+		e = "string expected after -%c";
+		break;
+	    case 'S':
+		sp = &suf;
+		e = "string expected after -%c";
+		break;
+	    case 'J':
+		sp = &group;
+		e = "group name expected after -%c";
+		break;
+	    case 'V':
+		if (!group)
+		    ns = 1;
+		sp = &group;
+		e = "group name expected after -%c";
+		break;
+	    case 'i':
+		sp = &ipre;
+		e = "string expected after -%c";
+		break;
+	    case 'p':
+		sp = &ppre;
+		e = "string expected after -%c";
+		break;
+	    case 's':
+		sp = &psuf;
+		e = "string expected after -%c";
+		break;
+	    case 'W':
+		sp = &prpre;
+		e = "string expected after -%c";
+		break;
+	    case 'a':
+		a = 1;
+		break;
+	    case '-':
+		argv++;
+		goto ca_args;
+	    default:
+		zerrnam(name, "bad option: -%c", NULL, *p);
+		return 1;
+	    }
+	    if (sp) {
+		if (*sp) {
+		    zerrnam(name, "doubled option: -%c", NULL, *p);
+		    return 1;
+		}
+		if (p[1]) {
+		    *sp = p + 1;
+		    p = "" - 1;
+		} else if (argv[1]) {
+		    *sp = *++argv;
+		    p = "" - 1;
+		} else {
+		    zerrnam(name, e, NULL, *p);
+		    return 1;
+		}
+	    }
+	}
+    }
+ ca_args:
+    if (!*argv) {
+	zerrnam(name, "missing completions", NULL, 0);
+	return 1;
+    }
+    addmatchesptr(ipre, ppre, psuf, prpre, pre, suf, group,
+		  f, q, m, ns, a, argv);
+    return 0;
+}
+
+#define VAR(X) ((void *) (&(X)))
+static struct compparam {
+    char *name;
+    int type;
+    void *var;
+} compparams[] = {
+    { "CURRENT", PM_INTEGER, VAR(compcurrent) },
+    { "CONTEXT", PM_SCALAR, VAR(compcontext) },
+    { "COMMAND", PM_SCALAR, VAR(compcommand) },
+    { "PREFIX", PM_SCALAR, VAR(compprefix) },
+    { "SUFFIX", PM_SCALAR, VAR(compsuffix) },
+    { "IPREFIX", PM_SCALAR, VAR(compiprefix) },
+    { "NMATCHES", PM_INTEGER, VAR(compnmatches) },
+    { NULL, 0, NULL }
+};
+
+/**/
+void makecompparams(void)
+{
+    struct compparam *cp;
+
+    for (cp = compparams; cp->name; cp++) {
+	Param pm = createparam(cp->name, cp->type | PM_SPECIAL);
+	if (!pm)
+	    pm = (Param) paramtab->getnode(paramtab, cp->name);
+	DPUTS(!pm, "param not set in makecompparams");
+
+	pm->level = locallevel;
+	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;
+	    break;
+	}
+	pm->unsetfn = compunsetfn;
+    }
+}
+
+/**/
+static void
+compunsetfn(Param pm, int exp)
+{
+    if (exp)
+	stdunsetfn(pm, exp);
+}
+
+/**/
+static int
+comp_wrapper(List list, FuncWrap w, char *name)
+{
+    if (!incompfunc)
+	return 1;
+    else {
+	char *octxt, *ocmd, *opre, *osuf, *oipre;
+	long ocur;
+
+	ocur = compcurrent;
+	octxt = dupstring(compcontext);
+	ocmd = dupstring(compcommand);
+	opre = dupstring(compprefix);
+	osuf = dupstring(compsuffix);
+	oipre = dupstring(compiprefix);
+
+	runshfunc(list, w, name);
+
+	compcurrent = ocur;
+	zsfree(compcontext);
+	compcontext = ztrdup(octxt);
+	zsfree(compcommand);
+	compcommand = ztrdup(ocmd);
+	zsfree(compprefix);
+	compprefix = ztrdup(opre);
+	zsfree(compsuffix);
+	compsuffix = ztrdup(osuf);
+	zsfree(compiprefix);
+	compiprefix = ztrdup(oipre);
+
+	return 0;
+    }
+}
+
+/**/
+static void
+ignore_prefix(int l)
+{
+    char *o, sav = compprefix[l];
+
+    compprefix[l] = '\0';
+    o = compiprefix;
+    compiprefix = tricat(o, compprefix, "");
+    zsfree(o);
+    compprefix[l] = sav;
+    o = compprefix;
+    compprefix = ztrdup(o + l);
+    zsfree(o);
+}
+
+/**/
+static int
+comp_check(void)
+{
+    if (!incompfunc) {
+	zerr("condition can only be used in completion function", NULL, 0);
+	return 0;
+    }
+    return 1;
+}
+
+/**/
+static void
+restrict_range(int b, int e)
+{
+    int i = e - b;
+    char **p = (char **) zcalloc((i + 1) * sizeof(char *)), **q, **pp;
+
+    for (q = p, pp = pparams + b + 1; i; i--, q++, pp++)
+	*q = ztrdup(*pp);
+    zsfree(compcommand);
+    compcommand = ztrdup(pparams[b]);
+    freearray(pparams);
+    pparams = p;
+    zsfree(compcontext);
+    if ((compcurrent -= b + 1))
+	compcontext = ztrdup("arg");
+    else
+	compcontext = ztrdup("cmd");
+}
+
+/**/
+static int
+cond_prefix(char **a, int id)
+{
+    if (comp_check())
+	return strpfx(cond_str(a, 0), compprefix);
+    return 0;
+}
+
+/**/
+static int
+cond_iprefix(char **a, int id)
+{
+    if (comp_check()) {
+	char *s = cond_str(a, 0);
+
+	if (strpfx(s, compprefix)) {
+	    ignore_prefix(strlen(s));
+	    return 1;
+	}
+    }
+    return 0;
+}
+
+/**/
+static int
+cond_position(char **a, int id)
+{
+    if (comp_check()) {
+	int b = cond_val(a, 0), e = (a[1] ? cond_val(a, 1) : b);
+	int l = arrlen(pparams), t, i = compcurrent - 1;
+
+	if (b > 0)
+	    b--;
+	if (e > 0)
+	    e--;
+	if (b < 0)
+	    b += l;
+	if (e < 0)
+	    e += l;
+	t = (b >= 0 && e >= 0 && i >= b && i <= e && b <= e);
+
+	if (t && a[1]) {
+	    if (b > l)
+		b = l;
+	    if (e > l)
+		e = l;
+	    restrict_range(b, e);
+	}
+	return t;
+    }
+    return 0;
+}
+
+/**/
+static int
+cond_word(char **a, int id)
+{
+    if (comp_check()) {
+	int o = ((id & 2) ? compcurrent : 0) + cond_val(a, 0);
+	int l = arrlen(pparams);
+	char *s;
+
+	if (o < 0)
+	    o += l;
+
+	o--;
+	if (o < 0 || o >= l)
+	    return 0;
+
+	s = pparams[o];
+	return ((id & 1) ? cond_match(a, 1, s) : !strcmp(s, cond_str(a, 1)));
+    }
+    return 0;
+}
+
+/**/
+static int
+cond_strcl(char **a, int id)
+{
+    if (comp_check()) {
+	char *s;
+	int i;
+
+	if (a[1]) {
+	    s = cond_str(a, 1);
+	    i = cond_val(a, 0);
+	} else {
+	    s = cond_str(a, 0);
+	    i = -1;
+	}
+	if (!getcpatptr) {
+	    zerr("zle not loaded, zle condition not available", NULL, 0);
+	    return 1;
+	}
+	i = getcpatptr(comp_strptr(NULL, NULL), i, s, id);
+	if (i != -1) {
+	    ignore_prefix(i);
+	    return 1;
+	}
+    }
+    return 0;
+}
+
+/**/
+static int
+cond_words(char **a, int id)
+{
+    if (comp_check()) {
+	int b = cond_val(a, 0), e = (a[1] ? cond_val(a, 1) : -1);
+	int l = arrlen(pparams);
+
+	return (l >= b && l <= e);
+    }
+    return 0;
+}
+
+/**/
+static int
+cond_range(char **a, int id)
+{
+    if (comp_check()) {
+	char *s, **p;
+	int i, l = arrlen(pparams), t = 0, b = 0, e = l - 1;
+	Comp c;
+
+	i = compcurrent - 1;
+	if (i < 0 || i >= l)
+	    return 0;
+
+	if (id & 1) {
+	    s = a[0];
+	    singsub(&s);
+	    c = parsereg(s);
+	} else
+	    s = cond_str(a, 0);
+
+	for (i--, p = pparams + i; i >= 0; p--, i--) {
+	    if (((id & 1) ? domatch(*p, c, 0) : !strcmp(*p, s))) {
+		b = i + 1;
+		t = 1;
+		break;
+	    }
+	}
+	if (t && (id & 2)) {
+	    int tt = 0;
+
+	    if (id & 1) {
+		s = a[1];
+		singsub(&s);
+		c = parsereg(s);
+	    } else
+		s = cond_str(a, 1);
+
+	    for (i++, p = pparams + i; i < l; p++, i++) {
+		if (((id & 1) ? domatch(*p, c, 0) : !strcmp(*p, s))) {
+		    e = i - 1;
+		    tt = 1;
+		    break;
+		}
+	    }
+	    if (tt && i < compcurrent)
+		t = 0;
+	}
+	if (e < b)
+	    t = 0;
+	if (t)
+	    restrict_range(b, e);
+	return t;
+    }
+    return 0;
+}
+
+/**/
+static int
+cond_nmatches(char **a, int id)
+{
+    if (comp_check())
+	return compnmatches == cond_val(a, 0);
+    return 0;
+}
+
 static struct builtin bintab[] = {
     BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL),
+    BUILTIN("complist", 0, bin_complist, 1, -1, 0, NULL, NULL),
+    BUILTIN("compadd", 0, bin_compadd, 1, -1, 0, NULL, NULL),
+};
+
+static struct conddef cotab[] = {
+    CONDDEF("prefix", 0, cond_prefix, 1, 1, 0),
+    CONDDEF("iprefix", 0, cond_iprefix, 1, 1, 0),
+    CONDDEF("position", 0, cond_position, 1, 2, 0),
+    CONDDEF("word", 0, cond_word, 2, 2, 0),
+    CONDDEF("mword", 0, cond_word, 2, 2, 1),
+    CONDDEF("current", 0, cond_word, 2, 2, 2),
+    CONDDEF("mcurrent", 0, cond_word, 2, 2, 3),
+    CONDDEF("string", 0, cond_strcl, 1, 2, 0),
+    CONDDEF("class", 0, cond_strcl, 1, 2, 1),
+    CONDDEF("words", 0, cond_words, 1, 2, 0),
+    CONDDEF("between", 0, cond_range, 2, 2, 2),
+    CONDDEF("mbetween", 0, cond_range, 2, 2, 3),
+    CONDDEF("after", 0, cond_range, 1, 1, 0),
+    CONDDEF("mafter", 0, cond_range, 1, 1, 1),
+    CONDDEF("nmatches", 0, cond_nmatches, 1, 1, 0),
+};
+
+static struct funcwrap wrapper[] = {
+    WRAPDEF(comp_wrapper),
 };
 
 /**/
@@ -1643,6 +2151,7 @@ setup_compctl(Module m)
     compctltab->printnode = printcompctlp;
     printcompctlptr = printcompctl;
     compctl_widgetptr = compctl_widget;
+    makecompparamsptr = makecompparams;
     return 0;
 }
 
@@ -1650,7 +2159,9 @@ setup_compctl(Module m)
 int
 boot_compctl(Module m)
 {
-    if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)))
+    if(!(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
+	 addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) |
+	 !addwrapper(m, wrapper)))
 	return 1;
     return 0;
 }
@@ -1662,6 +2173,8 @@ int
 cleanup_compctl(Module m)
 {
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
+    deletewrapper(m, wrapper);
     return 0;
 }
 
@@ -1672,6 +2185,7 @@ finish_compctl(Module m)
     compctltab->printnode = NULL;
     printcompctlptr = NULL;
     compctl_widgetptr = NULL;
+    makecompparamsptr = NULL;
     return 0;
 }
 
diff --git a/Src/Zle/compctl.mdd b/Src/Zle/compctl.mdd
index c83ecda29..48aabe38a 100644
--- a/Src/Zle/compctl.mdd
+++ b/Src/Zle/compctl.mdd
@@ -1,5 +1,7 @@
 moddeps="comp1"
 
-autobins="compctl"
+autobins="compctl complist compadd"
+
+autoprefixconds="prefix iprefix position word mword current mcurrent string class words between mbetween after mafter nmatches"
 
 objects="compctl.o"
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index 52e70fad5..12425d872 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -25,11 +25,11 @@
 "beginning-of-line-hist", beginningoflinehist, 0
 "capitalize-word", capitalizeword, 0
 "clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
-"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
 "copy-prev-word", copyprevword, 0
 "copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX
 "delete-char", deletechar, ZLE_KEEPSUFFIX
-"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
 "delete-word", deleteword, ZLE_KEEPSUFFIX
 "describe-key-briefly", describekeybriefly, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
@@ -48,8 +48,8 @@
 "execute-named-cmd", NULL, 0
 "expand-cmd-path", expandcmdpath, 0
 "expand-history", expandhistory, 0
-"expand-or-complete", expandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
-"expand-or-complete-prefix", expandorcompleteprefix, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"expand-or-complete", expandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
+"expand-or-complete-prefix", expandorcompleteprefix, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
 "expand-word", expandword, 0
 "forward-char", forwardchar, 0
 "forward-word", forwardword, 0
@@ -68,11 +68,11 @@
 "kill-region", killregion, ZLE_KILL | ZLE_KEEPSUFFIX
 "kill-whole-line", killwholeline, ZLE_KILL | ZLE_KEEPSUFFIX
 "kill-word", killword, ZLE_KILL | ZLE_KEEPSUFFIX
-"list-choices", listchoices, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"list-choices", listchoices, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_ISCOMP
 "list-expand", listexpand, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "magic-space", magicspace, 0
-"menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
-"menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
+"menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
 "neg-argument", negargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "overwrite-mode", overwritemode, 0
 "pound-insert", poundinsert, 0
@@ -84,7 +84,7 @@
 "quote-region", quoteregion, 0
 "redisplay", redisplay, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "redo", redo, 0
-"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
 "run-help", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "self-insert", selfinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX
 "self-insert-unmeta", selfinsertunmeta, ZLE_MENUCMP | ZLE_KEEPSUFFIX
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 71a929f87..f12505bd3 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -47,23 +47,29 @@ struct widget {
 	ZleIntFunc fn;	/* pointer to internally implemented widget */
 	char *fnnam;	/* name of the shell function for user-defined widget */
         Compctl cc;     /* for use with a WIDGET_COMP widget */
+	struct {
+	    ZleIntFunc fn; /* internal widget function to call */
+	    char *wid;     /* name of widget to call */
+	    char *func;    /* name of shell function to call */
+	} comp;
     } u;
 };
 
 #define WIDGET_INT	(1<<0)    /* widget is internally implemented */
 #define WIDGET_COMP	(1<<1)	  /* Special completion widget */
-#define ZLE_MENUCMP	(1<<2)    /* DON'T invalidate completion list */
-#define ZLE_YANK	(1<<3)
-#define ZLE_LINEMOVE	(1<<4)    /* command is a line-oriented movement */
-#define ZLE_LASTCOL     (1<<5)    /* command maintains lastcol correctly */
-#define ZLE_KILL	(1<<6)
+#define WIDGET_NCOMP    (1<<2)    /* new style completion widget */
+#define ZLE_MENUCMP	(1<<3)    /* DON'T invalidate completion list */
+#define ZLE_YANK	(1<<4)
+#define ZLE_LINEMOVE	(1<<5)    /* command is a line-oriented movement */
+#define ZLE_LASTCOL     (1<<6)    /* command maintains lastcol correctly */
+#define ZLE_KILL	(1<<7)
 #define ZLE_KEEPSUFFIX	(1<<9)    /* DON'T remove added suffix */
 #define ZLE_USEMENU	(1<<10)   /* Do    ) use menu completion for   */
 #define ZLE_NOMENU	(1<<11)   /* Don't )  widget, else use default */
 #define ZLE_USEGLOB	(1<<12)   /* Do    ) use glob completion for   */
 #define ZLE_NOGLOB	(1<<13)   /* Don't )  widget, else use default */
 #define ZLE_NOTCOMMAND  (1<<14)   /* widget should not alter lastcmd */
-
+#define ZLE_ISCOMP      (1<<15)   /* usable for new style completion */
 /* thingies */
 
 struct thingy {
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 7de96bd03..1ffe6f156 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -598,10 +598,10 @@ bin_bindkey(char *name, char **argv, char *ops, int func)
     int n;
 
     /* select operation and ensure no clashing arguments */
-    for(op = opns; op->o && !ops[op->o]; op++) ;
+    for(op = opns; op->o && !ops[STOUC(op->o)]; op++) ;
     if(op->o)
 	for(opp = op; (++opp)->o; )
-	    if(ops[opp->o]) {
+	    if(ops[STOUC(opp->o)]) {
 		zwarnnam(name, "incompatible operation selection options",
 		    NULL, 0);
 		return 1;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 515405a0d..2c0d3655e 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -443,9 +443,9 @@ zleread(char *lp, char *rp, int ha)
     insmode = unset(OVERSTRIKE);
     eofsent = 0;
     resetneeded = 0;
-    lpptbuf = promptexpand(lp, 1, NULL, NULL);
+    lpromptbuf = promptexpand(lp, 1, NULL, NULL);
     pmpt_attr = txtchange;
-    rpptbuf = promptexpand(rp, 1, NULL, NULL);
+    rpromptbuf = promptexpand(rp, 1, NULL, NULL);
     rpmpt_attr = txtchange;
     histallowed = ha;
     PERMALLOC {
@@ -529,8 +529,8 @@ zleread(char *lp, char *rp, int ha)
 	statusline = NULL;
 	invalidatelist();
 	trashzle();
-	free(lpptbuf);
-	free(rpptbuf);
+	free(lpromptbuf);
+	free(rpromptbuf);
 	zleactive = 0;
 	alarm(0);
     } LASTALLOC;
@@ -565,13 +565,14 @@ execzlefunc(Thingy func)
 	showmsg(msg);
 	zsfree(msg);
 	feep();
-    } else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_COMP)) {
+    } else if((w = func->widget)->flags &
+	      (WIDGET_INT|WIDGET_COMP | WIDGET_NCOMP)) {
 	int wflags = w->flags;
 
 	if(!(wflags & ZLE_KEEPSUFFIX))
 	    removesuffix();
 	if(!(wflags & ZLE_MENUCMP) ||
-	   ((wflags & WIDGET_COMP) && compwidget != w)) {
+	   ((wflags & (WIDGET_COMP|WIDGET_NCOMP)) && compwidget != w)) {
 	    /* If we are doing a special completion, and the widget
 	     * is not the one currently in use for special completion,
 	     * we are starting a new completion.
@@ -586,6 +587,9 @@ execzlefunc(Thingy func)
 	if (wflags & WIDGET_COMP) {
 	    compwidget = w;
 	    completespecial();
+	} else if (wflags & WIDGET_NCOMP) {
+	    compwidget = w;
+	    completecall();
 	} else
 	    w->u.fn();
 	if (!(wflags & ZLE_NOTCOMMAND))
@@ -855,7 +859,7 @@ trashzle(void)
 static struct builtin bintab[] = {
     BUILTIN("bindkey", 0, bin_bindkey, 0, -1, 0, "evaMldDANmrsLR", NULL),
     BUILTIN("vared",   0, bin_vared,   1,  7, 0, NULL,             NULL),
-    BUILTIN("zle",     0, bin_zle,     0, -1, 0, "lDANCLmMgG",     NULL),
+    BUILTIN("zle",     0, bin_zle,     0, -1, 0, "lDANCLmMgGc",    NULL),
 };
 
 /**/
@@ -869,6 +873,11 @@ setup_zle(Module m)
     spaceinlineptr = spaceinline;
     zlereadptr = zleread;
 
+    addmatchesptr = addmatches;
+    comp_strptr = comp_str;
+    getcpatptr = getcpat;
+    makecomplistcallptr = makecomplistcall;
+
     /* initialise the thingies */
     init_thingies();
 
@@ -931,6 +940,11 @@ finish_zle(Module m)
     spaceinlineptr = noop_function_int;
     zlereadptr = fallback_zleread;
 
+    addmatchesptr = NULL;
+    comp_strptr = NULL;
+    getcpatptr = NULL;
+    makecomplistcallptr = NULL;
+
     return 0;
 }
 
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 27e9f2071..c8d6c70a7 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -33,7 +33,7 @@
 /* Expanded prompts */
 
 /**/
-char *lpptbuf, *rpptbuf;
+char *lpromptbuf, *rpromptbuf;
 
 /* Text attributes after displaying prompts */
 
@@ -77,17 +77,17 @@ int cost;
 /* Oct/Nov 94: <mason> some code savagely redesigned to fix several bugs -
    refreshline() & tc_rightcurs() majorly rewritten; zrefresh() fixed -
    I've put my fingers into just about every routine in here -
-   any queries about updates to mason@werple.net.au */
+   any queries about updates to mason@primenet.com.au */
 
 static char **nbuf = NULL,	/* new video buffer line-by-line char array */
     **obuf = NULL;		/* old video buffer line-by-line char array */
 static int more_start,		/* more text before start of screen?	    */
     more_end,			/* more stuff after end of screen?	    */
-    lppth,			/* lines taken up by the prompt		    */
     olnct,			/* previous number of lines		    */
     ovln,			/* previous video cursor position line	    */
-    pptw, rpw,                  /* prompt widths on screen                  */
-    rppth,			/* right prompt height                      */
+    lpromptw, rpromptw,		/* prompt widths on screen                  */
+    lprompth,			/* lines taken up by the prompt		    */
+    rprompth,			/* right prompt height                      */
     vcs, vln,			/* video cursor position column & line	    */
     vmaxln,			/* video maximum number of lines	    */
     winw, winh, rwinh,		/* window width & height		    */
@@ -100,7 +100,6 @@ resetvideo(void)
     int ln;
     static int lwinw = -1, lwinh = -1;	/* last window width & height */
  
-    genprompts();
     winw = columns;  /* terminal width */
     if (termflags & TERM_SHORT)
 	winh = 1;
@@ -132,13 +131,16 @@ resetvideo(void)
 	    *obuf[ln] = '\0';
     }
 
-    if (pptw) {
-    	memset(nbuf[0], ' ', pptw);
-	memset(obuf[0], ' ', pptw);
-	nbuf[0][pptw] = obuf[0][pptw] = '\0';
+    countprompt(lpromptbuf, &lpromptw, &lprompth);
+    countprompt(rpromptbuf, &rpromptw, &rprompth);
+
+    if (lpromptw) {
+    	memset(nbuf[0], ' ', lpromptw);
+	memset(obuf[0], ' ', lpromptw);
+	nbuf[0][lpromptw] = obuf[0][lpromptw] = '\0';
     }
 
-    vcs = pptw;
+    vcs = lpromptw;
     olnct = nlnct = 0;
     if (showinglist > 0)
 	showinglist = -2;
@@ -280,21 +282,25 @@ zrefresh(void)
 	tsetcap(TCSTANDOUTEND, 0);
 	tsetcap(TCUNDERLINEEND, 0);
 
-        if (!clearflag)
+        if (!clearflag) {
             if (tccan(TCCLEAREOD))
                 tcout(TCCLEAREOD);
             else
                 cleareol = 1;   /* request: clear to end of line */
+	}
         if (t0 > -1)
             olnct = t0;
         if (termflags & TERM_SHORT)
             vcs = 0;
-        else if (!clearflag && lpptbuf[0])
-            zputs(lpptbuf, shout);
+        else if (!clearflag && lpromptbuf[0]) {
+            zputs(lpromptbuf, shout);
+	    if (lpromptw == 0)
+		zputs("\n", shout);	/* works with both hasam and !hasam */
+	}
 	if (clearflag) {
 	    zputc('\r', shout);
 	    vcs = 0;
-	    moveto(0, pptw);
+	    moveto(0, lpromptw);
 	}
 	fflush(shout);
 	clearf = clearflag;
@@ -326,7 +332,7 @@ zrefresh(void)
     if (!*nbuf)
 	*nbuf = (char *)zalloc(winw + 2);
 
-    s = (unsigned char *)(nbuf[ln = 0] + pptw);
+    s = (unsigned char *)(nbuf[ln = 0] + lpromptw);
     t = line;
     sen = (unsigned char *)(*nbuf + winw);
     for (; t < line+ll; t++) {
@@ -425,15 +431,16 @@ zrefresh(void)
 
 /* determine whether the right-prompt exists and can fit on the screen */
     if (!more_start)
-	put_rpmpt = rppth == 1 && rpptbuf[0] && !strchr(rpptbuf, '\t') &&
-	    (int)strlen(nbuf[0]) + rpw < winw - 1;
+	put_rpmpt = rprompth == 1 && rpromptbuf[0] &&
+	    !strchr(rpromptbuf, '\t') &&
+	    (int)strlen(nbuf[0]) + rpromptw < winw - 1;
     else {
 /* insert >.... on first line if there is more text before start of screen */
-	memset(nbuf[0], ' ', pptw);
-	t0 = winw - pptw;
+	memset(nbuf[0], ' ', lpromptw);
+	t0 = winw - lpromptw;
 	t0 = t0 > 5 ? 5 : t0;
-	strncpy(nbuf[0] + pptw, ">....", t0);
-	memset(nbuf[0] + pptw + t0, ' ', winw - t0 - pptw);
+	strncpy(nbuf[0] + lpromptw, ">....", t0);
+	memset(nbuf[0] + lpromptw + t0, ' ', winw - t0 - lpromptw);
 	nbuf[0][winw] = nbuf[0][winw + 1] = '\0';
     }
 
@@ -477,8 +484,8 @@ zrefresh(void)
 
     /* output the right-prompt if appropriate */
 	if (put_rpmpt && !ln && !oput_rpmpt) {
-	    moveto(0, winw - 1 - rpw);
-	    zputs(rpptbuf, shout);
+	    moveto(0, winw - 1 - rpromptw);
+	    zputs(rpromptbuf, shout);
 	    vcs = winw - 1;
 	/* reset character attributes to that set by the main prompt */
 	    txtchange = pmpt_attr;
@@ -659,12 +666,12 @@ refreshline(int ln)
 /* 2c: if we're on the first line, start checking at the end of the prompt;
    we shouldn't be doing anything within the prompt */
 
-    if (ln == 0 && pptw) {
-	i = pptw - ccs;
+    if (ln == 0 && lpromptw) {
+	i = lpromptw - ccs;
 	j = strlen(ol);
 	nl += i;
 	ol += (i > j ? j : i);	/* if ol is too short, point it to '\0' */
-	ccs = pptw;
+	ccs = lpromptw;
     }
 
 /* 3: main display loop - write out the buffer using whatever tricks we can */
@@ -815,7 +822,7 @@ moveto(int ln, int cl)
    instead of TCDOWN */
 
     while (ln > vln) {
-	if (vln < vmaxln - 1)
+	if (vln < vmaxln - 1) {
 	    if (ln > vmaxln - 1) {
 		if (tc_downcurs(vmaxln - 1 - vln))
 		    vcs = 0;
@@ -826,6 +833,7 @@ moveto(int ln, int cl)
 		vln = ln;
 		continue;
 	    }
+	}
 	zputc('\r', shout), vcs = 0; /* safety precaution */
 	while (ln > vln) {
 	    zputc('\n', shout);
@@ -893,21 +901,23 @@ tc_rightcurs(int cl)
 
 /* otherwise _carefully_ write the contents of the video buffer.
    if we're anywhere in the prompt, goto the left column and write the whole
-   prompt out unless ztrlen(lpptbuf) == pptw : we can cheat then */
-    if (vln == 0 && i < pptw) {
-	if (strlen(lpptbuf) == pptw)
-	    fputs(lpptbuf + i, shout);
-	else if (tccan(TCRIGHT) && (tclen[TCRIGHT] * ct <= ztrlen(lpptbuf)))
+   prompt out unless ztrlen(lpromptbuf) == lpromptw : we can cheat then */
+    if (vln == 0 && i < lpromptw) {
+	if (strlen(lpromptbuf) == lpromptw)
+	    fputs(lpromptbuf + i, shout);
+	else if (tccan(TCRIGHT) && (tclen[TCRIGHT] * ct <= ztrlen(lpromptbuf)))
 	    /* it is cheaper to send TCRIGHT than reprint the whole prompt */
-	    for (ct = pptw - i; ct--; )
+	    for (ct = lpromptw - i; ct--; )
 		tcout(TCRIGHT);
         else {
 	    if (i != 0)
 		zputc('\r', shout);
-	    tc_upcurs(lppth - 1);
-	    zputs(lpptbuf, shout);
+	    tc_upcurs(lprompth - 1);
+	    zputs(lpromptbuf, shout);
+	    if (lpromptw == 0)
+		zputs("\n", shout);	/* works with both hasam and !hasam */
 	}
-	i = pptw;
+	i = lpromptw;
 	ct = cl - i;
     }
 
@@ -969,7 +979,7 @@ redisplay(void)
 {
     moveto(0, 0);
     zputc('\r', shout);		/* extra care */
-    tc_upcurs(lppth - 1);
+    tc_upcurs(lprompth - 1);
     resetneeded = 1;
     clearflag = 0;
 }
@@ -987,7 +997,7 @@ singlerefresh(void)
 
     nlnct = 1;
 /* generate the new line buffer completely */
-    for (vsiz = 1 + pptw, t0 = 0; t0 != ll; t0++, vsiz++)
+    for (vsiz = 1 + lpromptw, t0 = 0; t0 != ll; t0++, vsiz++)
 	if (line[t0] == '\t')
 	    vsiz = (vsiz | 7) + 1;
 	else if (icntrl(line[t0]))
@@ -1002,9 +1012,10 @@ singlerefresh(void)
 	cs = 0;
     }
 
-    memcpy(vbuf, strchr(lpptbuf, 0) - pptw, pptw); /* only use last part of prompt */
-    vbuf[pptw] = '\0';
-    vp = vbuf + pptw;
+    /* only use last part of prompt */
+    memcpy(vbuf, strchr(lpromptbuf, 0) - lpromptw, lpromptw);
+    vbuf[lpromptw] = '\0';
+    vp = vbuf + lpromptw;
 
     for (t0 = 0; t0 != ll; t0++) {
 	if (line[t0] == '\t')
@@ -1104,13 +1115,3 @@ singmoveto(int pos)
 	    }
     }
 }
-
-/* recheck size of prompts */
-
-/**/
-static void
-genprompts(void)
-{
-    countprompt(lpptbuf, &pptw, &lppth);
-    countprompt(rpptbuf, &rpw, &rppth);
-}
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 68329be65..2e21b5add 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -246,7 +246,10 @@ freewidget(Widget w)
 {
     if ((w->flags & WIDGET_COMP) && w->u.cc)
 	freecompctl(w->u.cc);
-    else if(!(w->flags & WIDGET_INT))
+    else if (w->flags & WIDGET_NCOMP) {
+	zsfree(w->u.comp.wid);
+	zsfree(w->u.comp.func);
+    } else if(!(w->flags & WIDGET_INT))
 	zsfree(w->u.fnnam);
     zfree(w, sizeof(*w));
 }
@@ -337,16 +340,17 @@ bin_zle(char *name, char **args, char *ops, int func)
 	{ 'A', bin_zle_link, 2,  2 },
 	{ 'N', bin_zle_new,  1,  2 },
 	{ 'C', bin_zle_compctl, 1, -1},
+	{ 'c', bin_zle_complete, 3, 3 },
 	{ 0,   bin_zle_call, 0, -1 },
     };
     struct opn const *op, *opp;
     int n;
 
     /* select operation and ensure no clashing arguments */
-    for(op = opns; op->o && !ops[op->o]; op++) ;
+    for(op = opns; op->o && !ops[STOUC(op->o)]; op++) ;
     if(op->o)
 	for(opp = op; (++opp)->o; )
-	    if(ops[opp->o]) {
+	    if(ops[STOUC(opp->o)]) {
 		zerrnam(name, "incompatible operation selection options",
 		    NULL, 0);
 		return 1;
@@ -395,6 +399,11 @@ scanlistwidgets(HashNode hn, int list)
 	if (w->flags & WIDGET_COMP) {
 	    if (printcompctlptr && w->u.cc)
 		printcompctlptr(NULL, w->u.cc, PRINT_LIST, 0);
+	} else if (w->flags & WIDGET_NCOMP) {
+	    fputc(' ', stdout);
+	    quotedzputs(w->u.comp.wid, stdout);
+	    fputc(' ', stdout);
+	    quotedzputs(w->u.comp.func, stdout);
 	} else if(strcmp(t->nam, w->u.fnnam)) {
 	    fputc(' ', stdout);
 	    quotedzputs(w->u.fnnam, stdout);
@@ -405,6 +414,11 @@ scanlistwidgets(HashNode hn, int list)
 	    fputs(" -C", stdout);
 	    if (printcompctlptr && w->u.cc)
 		printcompctlptr(NULL, w->u.cc, PRINT_TYPE, 0);
+	} else if (w->flags & WIDGET_NCOMP) {
+	    fputs(" -c ", stdout);
+	    nicezputs(w->u.comp.wid, stdout);
+	    fputc(' ', stdout);
+	    nicezputs(w->u.comp.func, stdout);
 	} else if(strcmp(t->nam, w->u.fnnam)) {
 	    fputs(" (", stdout);
 	    nicezputs(w->u.fnnam, stdout);
@@ -506,11 +520,39 @@ bin_zle_compctl(char *name, char **args, char *ops, char func)
 
 /**/
 static int
+bin_zle_complete(char *name, char **args, char *ops, char func)
+{
+    Thingy t;
+    Widget w, cw;
+
+    t = rthingy(args[1]);
+    cw = t->widget;
+    unrefthingy(t);
+    if (!(cw->flags & ZLE_ISCOMP)) {
+	zerrnam(name, "invalid widget `%s'", args[1], 0);
+	return 1;
+    }
+    w = zalloc(sizeof(*w));
+    w->flags = WIDGET_NCOMP|ZLE_MENUCMP|ZLE_KEEPSUFFIX;
+    w->first = NULL;
+    w->u.comp.fn = cw->u.fn;
+    w->u.comp.wid = ztrdup(args[1]);
+    w->u.comp.func = ztrdup(args[2]);
+    if (bindwidget(w, rthingy(args[0]))) {
+	freewidget(w);
+	zerrnam(name, "widget name `%s' is protected", args[0], 0);
+	return 1;
+    }
+    return 0;
+}
+
+/**/
+static int
 bin_zle_call(char *name, char **args, char *ops, char func)
 {
     Thingy t;
 
-    if(!zleactive || incompctlfunc) {
+    if(!zleactive || incompctlfunc || incompfunc) {
 	zerrnam(name, "widgets can only be called when ZLE is active",
 	    NULL, 0);
 	return 1;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 8c976449e..a958752ca 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -237,6 +237,15 @@ struct aminfo {
 
 static Aminfo ainfo, fainfo;
 
+/* This contains the name of the function to call if this is for a new  *
+ * style completion. */
+
+static char *compfunc = NULL;
+
+/* The memory heap to use for new style completion generation. */
+
+static Heap compheap;
+
 /* Find out if we have to insert a tab (instead of trying to complete). */
 
 /**/
@@ -274,6 +283,15 @@ completespecial(void)
 
 /**/
 void
+completecall(void)
+{
+    compfunc = compwidget->u.comp.func;
+    compwidget->u.comp.fn();
+    compfunc = NULL;
+}
+
+/**/
+void
 completeword(void)
 {
     usemenu = isset(MENUCOMPLETE);
@@ -408,11 +426,15 @@ reversemenucomplete(void)
 void
 acceptandmenucomplete(void)
 {
+    int sl = suffixlen[' '];
+
     if (!menucmp) {
 	feep();
 	return;
     }
-    cs = menuend + menuinsc;
+    cs = menupos + menulen + menuinsc;
+    if (sl)
+	backdel(sl);
     inststrlen(" ", 1, 1);
     menuinsc = menulen = 0;
     menupos = cs;
@@ -439,6 +461,13 @@ static int lastambig;
 
 static char *cmdstr;
 
+/* This hold the name of the variable we are working on. */
+
+static char *varname;
+
+/* != 0 if we are in a subscript */
+
+static int insubscr;
 
 /* Check if the given string is the name of a parameter and if this *
  * parameter is one worth expanding.                                */
@@ -614,16 +643,13 @@ docomplete(int lst)
 			lst = COMP_EXPAND;
 		    else {
 			int t0, n = 0;
-			char *fc;
 			struct hashnode *hn;
 
 			for (t0 = cmdnamtab->hsize - 1; t0 >= 0; t0--)
 			    for (hn = cmdnamtab->nodes[t0]; hn;
 				 hn = hn->next) {
-				if (strpfx(q, hn->nam) && (fc = findcmd(hn->nam))) {
-				    zsfree(fc);
+				if (strpfx(q, hn->nam) && findcmd(hn->nam, 0))
 				    n++;
-				}
 				if (n == 2)
 				    break;
 			    }
@@ -889,7 +915,7 @@ unmetafy_line(void)
 static char *
 get_comp_string(void)
 {
-    int t0, tt0, i, j, k, cp, rd, sl, ocs;
+    int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins;
     char *s = NULL, *linptr, *tmp, *p, *tt = NULL;
 
     zsfree(brbeg);
@@ -943,13 +969,16 @@ get_comp_string(void)
 	linredir = inredir;
 	zsfree(cmdstr);
 	cmdstr = NULL;
+	zsfree(varname);
+	varname = NULL;
+	insubscr = 0;
 	zleparse = 1;
 	clwpos = -1;
 	lexsave();
 	inpush(dupstrspace((char *) linptr), 0, NULL);
 	strinbeg();
 	stophist = 2;
-	i = tt0 = cp = rd = 0;
+	i = tt0 = cp = rd = ins = oins = 0;
 
 	/* This loop is possibly the wrong way to do this.  It goes through *
 	 * the previously massaged command line using the lexer.  It stores *
@@ -963,8 +992,10 @@ get_comp_string(void)
 	 * this would be to pass the command line through the parser too,   *
 	 * and get the arguments that way.  Maybe in 3.1...                 */
 	do {
-	    lincmd = incmdpos;
-	    linredir = inredir;
+	    lincmd = ((incmdpos && !ins) || (oins == 2 && i == 2) ||
+		      (ins == 3 && i == 1));
+	    linredir = (inredir && !ins);
+	    oins = ins;
 	    /* Get the next token. */
 	    ctxtlex();
 	    if (tok == DINPAR)
@@ -973,7 +1004,9 @@ get_comp_string(void)
 	    /* We reached the end. */
 	    if (tok == ENDINPUT)
 		break;
-	    if (tok == BAR    || tok == AMPER     ||
+	    if ((ins && (tok == DO || tok == SEPER)) ||
+		(ins == 2 && i == 2) ||	(ins == 3 && i == 3) ||
+		tok == BAR    || tok == AMPER     ||
 		tok == BARAMP || tok == AMPERBANG ||
 		((tok == DBAR || tok == DAMPER) && !incond)) {
 		/* This is one of the things that separate commands.  If we  *
@@ -982,11 +1015,13 @@ get_comp_string(void)
 		if (tt)
 		    break;
 		/* Otherwise reset the variables we are collecting data in. */
-		i = tt0 = cp = rd = 0;
+		i = tt0 = cp = rd = ins = 0;
 	    }
-	    if (lincmd && tok == STRING) {
+	    if (lincmd && (tok == STRING || tok == FOR || tok == FOREACH ||
+			   tok == SELECT || tok == REPEAT || tok == CASE)) {
 		/* The lexer says, this token is in command position, so *
 		 * store the token string (to find the right compctl).   */
+		ins = (tok == REPEAT ? 2 : (tok != STRING));
 		zsfree(cmdstr);
 		cmdstr = ztrdup(tokstr);
 		i = 0;
@@ -1004,9 +1039,13 @@ get_comp_string(void)
 		rd = linredir;
 		if (inwhat == IN_NOTHING && incond)
 		    inwhat = IN_COND;
-	    }
+	    } else if (linredir)
+		continue;
 	    if (!tokstr)
 		continue;
+	    /* Hack to allow completion after `repeat n do'. */
+	    if (oins == 2 && !i && !strcmp(tokstr, "do"))
+		ins = 3;
 	    /* We need to store the token strings of all words (for some of *
 	     * the more complicated compctl -x things).  They are stored in *
 	     * the clwords array.  Make this array big enough.              */
@@ -1069,10 +1108,16 @@ get_comp_string(void)
 	    /* We found a simple string. */
 	    s = ztrdup(clwords[clwpos]);
 	} else if (t0 == ENVSTRING) {
+	    char sav;
 	    /* The cursor was inside a parameter assignment. */
 	    for (s = tt; iident(*s); s++);
+	    sav = *s;
+	    *s = '\0';
+	    zsfree(varname);
+	    varname = ztrdup(tt);
+	    *s = sav;
 	    if (skipparens(Inbrack, Outbrack, &s) > 0 || s > tt + cs - wb)
-		s = NULL, inwhat = IN_MATH;
+		s = NULL, inwhat = IN_MATH, insubscr = 1;
 	    else if (*s == '=') {
 		s++;
 		wb += s - tt;
@@ -1110,14 +1155,32 @@ get_comp_string(void)
 	 * foo[_ wrong (note no $).  If we are in a subscript, treat it   *
 	 * as being in math.                                              */
 	if (inwhat != IN_MATH) {
-	    int i = 0;
+	    int i = 0, hn = 0;
+	    char *nb = (*s == String ? s + 1 : NULL), *ne = NULL;
+
 	    for (tt = s; ++tt < s + cs - wb;)
-		if (*tt == Inbrack)
+		if (*tt == String) {
+		    hn = 0;
+		    nb = tt + 1;
+		} else if (*tt == Inbrack) {
 		    i++;
-		else if (i && *tt == Outbrack)
+		    if (nb && !hn) {
+			hn = 1;
+			ne = tt;
+		    }
+		} else if (i && *tt == Outbrack)
 		    i--;
-	    if (i)
+	    if (i) {
 		inwhat = IN_MATH;
+		insubscr = 1;
+		if (hn && nb && ne) {
+		    char sav = *ne;
+		    *ne = '\0';
+		    zsfree(varname);
+		    varname = ztrdup(nb);
+		    *ne = sav;
+		}
+	    }
 	}
 	if (inwhat == IN_MATH) {
 	    /* In mathematical expression, we complete parameter names (even *
@@ -2336,6 +2399,119 @@ instmatch(Cmatch m)
     return r;
 }
 
+/**/
+void
+addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
+	   char *suf, char *group,
+	   int flags, int quote, int menu, int nosort, int alt, char **argv)
+{
+    char *s, *t;
+    int lpl, lsl, i;
+    Aminfo ai = (alt ? fainfo : ainfo);
+    Cmatch cm;
+
+    if (menu && isset(AUTOMENU))
+	usemenu = 1;
+    SWITCHHEAPS(compheap) {
+	HEAPALLOC {
+	    if (ipre)
+		ipre = dupstring(ipre);
+	    if (ppre) {
+		ppre = dupstring(ppre);
+		lpl = strlen(ppre);
+	    } else
+		lpl = 0;
+	    if (psuf) {
+		psuf = dupstring(psuf);
+		lsl = strlen(psuf);
+	    } else
+		lsl = 0;
+	    if (pre)
+		pre = dupstring(pre);
+	    if (suf)
+		suf = dupstring(suf);
+	    if (!prpre && (prpre = ppre)) {
+		singsub(&prpre);
+		untokenize(prpre);
+	    } else
+		prpre = dupstring(prpre);
+	    if (group) {
+		endcmgroup(NULL);
+		begcmgroup(group, nosort);
+		if (nosort)
+		    mgroup->flags |= CGF_NOSORT;
+	    }
+    	    if (ai->pprefix) {
+		if (pre)
+		    ai->pprefix[pfxlen(ai->pprefix, pre)] = '\0';
+		else
+		    ai->pprefix[0] = '\0';
+	    } else
+		ai->pprefix = dupstring(pre ? pre : "");
+
+	    for (; (s = *argv); argv++) {
+		if (ai->firstm) {
+		    if ((i = pfxlen(ai->firstm->str, s)) < ai->prerest)
+			ai->prerest = i;
+		    if ((i = sfxlen(ai->firstm->str, s)) < ai->suflen)
+			ai->suflen = i;
+		}
+		t = s;
+		if (ppre)
+		    t = dyncat(ppre, t);
+		if (ipre && *ipre) {
+		    ai->noipre = 0;
+		    if (ai->icpl > lpl)
+			ai->icpl = lpl;
+		    if (ai->icsl > lsl)
+			ai->icsl = lsl;
+		    if (ai->iaprefix)
+			ai->iaprefix[pfxlen(ai->iaprefix, t)] = '\0';
+		    else
+			ai->iaprefix = dupstring(t);
+		    if (ai->iprefix) {
+			if (strcmp(ipre, ai->iprefix))
+			    ai->iprefix = "";
+		    } else
+			ai->iprefix = dupstring(ipre);
+
+		    t = dyncat(ipre, t);
+		} else
+		    ai->iprefix = "";
+		if (ai->cpl > lpl)
+		    ai->cpl = lpl;
+		if (ai->csl > lsl)
+		    ai->csl = lsl;
+		if (ai->aprefix)
+		    ai->aprefix[pfxlen(ai->aprefix, t)] = '\0';
+		else
+		    ai->aprefix = dupstring(t);
+		mnum++;
+		ai->count++;
+
+		cm = (Cmatch) halloc(sizeof(struct cmatch));
+		cm->ppre = ppre;
+		cm->psuf = psuf;
+		cm->prpre = prpre;
+		if (!quote)
+		    s = quotename(s, NULL, NULL, NULL);
+		cm->str = dupstring(s);
+		cm->ipre = cm->ripre = ipre;
+		cm->pre = pre;
+		cm->suf = suf;
+		cm->flags = flags;
+		cm->brpl = brpl;
+		cm->brsl = brsl;
+		addlinknode((alt ? fmatches : matches), cm);
+
+		if (expl)
+		    expl->fcount++;
+		if (!ai->firstm)
+		    ai->firstm = cm;
+	    }
+	} LASTALLOC;
+    } SWITCHBACKHEAPS;
+}
 
 /* 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     *
@@ -2351,7 +2527,7 @@ addmatch(char *s, char *t)
 {
     int test = 0, sl = strlen(s), pl = rpl, cc = 0, isf = 0;
     int mpl = 0, msl = 0, bpl = brpl, bsl = brsl;
-    char *e = NULL, *tt, *te, *fc, *ms = NULL;
+    char *e = NULL, *tt, *te, *ms = NULL;
     Comp cp = patcomp;
     HashNode hn;
     Param pm;
@@ -2377,6 +2553,8 @@ addmatch(char *s, char *t)
     hn = (HashNode) t;
     pm = (Param) t;
 
+    if (incompfunc)
+	s = dupstring(s);
     if (!addwhat) {
 	test = 1;
     } else if (addwhat == -1 || addwhat == -5 || addwhat == -6 ||
@@ -2427,11 +2605,8 @@ addmatch(char *s, char *t)
 	    }
 	}
 	if (test) {
-	    fc = NULL;
-	    if (addwhat == -7 && !(fc = findcmd(s)))
+	    if (addwhat == -7 && !findcmd(s, 0))
 		return;
-	    if (fc)
-		zsfree(fc);
 	    isf = CMF_FILE;
 
 	    if (addwhat == CC_FILES || addwhat == -6 ||
@@ -2515,7 +2690,6 @@ addmatch(char *s, char *t)
     }
     if (!test)
 	return;
-
     if (!ms && !ispattern && ai->firstm) {
 	if ((test = sl - pfxlen(ai->firstm->str, s)) < ai->prerest)
 	    ai->prerest = test;
@@ -2602,8 +2776,13 @@ addmatch(char *s, char *t)
     cm->str = (ms ? ms : s);
     cm->ipre = (ipre && *ipre ? ipre : NULL);
     cm->ripre = (ripre && *ripre ? ripre : NULL);
-    cm->pre = curcc->prefix;
-    cm->suf = curcc->suffix;
+    if (incompfunc) {
+	cm->pre = dupstring(curcc->prefix);
+	cm->suf = dupstring(curcc->suffix);
+    } else {
+	cm->pre = curcc->prefix;
+	cm->suf = curcc->suffix;
+    }
     cm->flags = mflags | isf;
     cm->brpl = bpl;
     cm->brsl = bsl;
@@ -2729,7 +2908,7 @@ maketildelist(void)
 /* This does the check for compctl -x `n' and `N' patterns. */
 
 /**/
-static int
+int
 getcpat(char *str, int cpatindex, char *cpat, int class)
 {
     char *s, *t, *p;
@@ -2951,6 +3130,8 @@ docompletion(char *s, int lst, int incmd)
 
 	    else if (nmatches == 1) {
 		/* Only one match. */
+		while (!amatches->mcount)
+		    amatches = amatches->next;
 		do_single(amatches->matches[0]);
 		invalidatelist();
 	    }
@@ -3051,7 +3232,98 @@ makecomplist(char *s, int incmd, int lst)
 	ccused = newlinklist();
 	ccstack = newlinklist();
 
-	makecomplistglobal(s, incmd, lst);
+	if (compfunc) {
+	    List list;
+	    int lv = lastval;
+
+	    if ((list = getshfunc(compfunc)) != &dummy_list) {
+		LinkList args = newlinklist();
+		char **p, *tmp;
+		int aadd = 0, usea = 1;
+
+		addlinknode(args, compfunc);
+
+		zsfree(compcontext);
+		zsfree(compcommand);
+		compcommand = "";
+		if (inwhat == IN_MATH) {
+		    if (insubscr) {
+			compcontext = "subscript";
+			compcommand = varname ? varname : "";
+		    } else
+			compcontext = "math";
+		    usea = 0;
+		} else if (lincmd)
+		    compcontext = (insubscr ? "subscript" : "command");
+		else if (linredir)
+		    compcontext = "redirect";
+		else
+		    switch (inwhat) {
+		    case IN_ENV:
+			compcontext = "value";
+			compcommand = varname;
+			usea = 0;
+			break;
+		    case IN_COND:
+			compcontext = "condition";
+			break;
+		    default:
+			if (cmdstr) {
+			    compcontext = "argument";
+			    compcommand = cmdstr;
+			} else {
+			    compcontext = "value";
+			    if (clwords[0])
+				compcommand = clwords[0];
+			}
+			aadd = 1;
+		    }
+		compcontext = ztrdup(compcontext);
+		tmp = quotename(compcommand, NULL, NULL, NULL);
+		untokenize(tmp);
+		compcommand = ztrdup(tmp);
+		if (usea && (!aadd || clwords[0]))
+		    for (p = clwords + aadd; *p; p++) {
+			tmp = dupstring(*p);
+			untokenize(tmp);
+			addlinknode(args, tmp);
+		    }
+		zsfree(compprefix);
+		zsfree(compsuffix);
+		if (unset(COMPLETEINWORD)) {
+		    tmp = quotename(s, NULL, NULL, NULL);
+		    untokenize(tmp);
+		    compprefix = ztrdup(tmp);
+		    compsuffix = ztrdup("");
+		} else {
+		    char *ss = s + offs, sav;
+
+		    tmp = quotename(s, &ss, NULL, NULL);
+		    sav = *ss;
+		    *ss = '\0';
+		    untokenize(tmp);
+		    compprefix = ztrdup(tmp);
+		    *ss = sav;
+		    untokenize(ss);
+		    compsuffix = ztrdup(ss);
+		}
+		zsfree(compiprefix);
+		compiprefix = ztrdup("");
+		compcurrent = (usea ? (clwpos + 1 - aadd) : 1);
+		compnmatches = mnum;
+		incompfunc = 1;
+		startparamscope();
+		makecompparamsptr();
+		NEWHEAPS(compheap) {
+		    doshfunc(compfunc, list, args, 0, 1);
+		} OLDHEAPS;
+		endparamscope();
+		lastcmd = 9;
+		incompfunc = 0;
+	    }
+	    lastval = lv;
+	} else
+	    makecomplistglobal(s, incmd, lst);
 
 	endcmgroup(NULL);
 
@@ -3082,6 +3354,83 @@ makecomplist(char *s, int incmd, int lst)
     return 1;
 }
 
+/* This should probably be moved into tokenize(). */
+
+static char *
+ctokenize(char *p)
+{
+    char *r = p;
+    int bslash = 0;
+
+    tokenize(p);
+
+    for (p = r; *p; p++) {
+	if (*p == '\\')
+	    bslash = 1;
+	else {
+	    if (*p == '$') {
+		if (bslash)
+		    p[-1] = Bnull;
+		else
+		    *p = String;
+	    }
+	    bslash = 0;
+	}
+    }
+    return r;
+}
+
+/**/
+char *
+comp_str(int *ipl, int *pl)
+{
+    char *p = dupstring(compprefix);
+    char *s = dupstring(compsuffix);
+    char *ip = dupstring(compiprefix);
+    char *str;
+    int lp, ls, lip;
+
+    ctokenize(p);
+    remnulargs(p);
+    ctokenize(s);
+    remnulargs(s);
+    ctokenize(ip);
+    remnulargs(ip);
+    ls = strlen(s);
+    lip = strlen(ip);
+    lp = strlen(p);
+    str = halloc(lip + lp + ls + 1);
+    strcpy(str, ip);
+    strcat(str, p);
+    strcat(str, s);
+
+    if (ipl)
+	*ipl = lip;
+    if (pl)
+	*pl = lp;
+
+    return str;
+}
+
+/**/
+void
+makecomplistcall(Compctl cc)
+{
+    SWITCHHEAPS(compheap) {
+	HEAPALLOC {
+	    int ooffs = offs, lip, lp;
+	    char *str = comp_str(&lip, &lp);
+
+	    offs = lip + lp;
+	    cc->refc++;
+	    ccont = 0;
+	    makecomplistor(cc, str, lincmd, lip, 0);
+	    offs = ooffs;
+	    compnmatches = mnum;
+	} LASTALLOC;
+    } SWITCHBACKHEAPS;
+}
+
 /* This function gets the compctls for the given command line and *
  * adds all completions for them. */
 
@@ -3159,7 +3508,7 @@ makecomplistcmd(char *os, int incmd)
     /* If the command string starts with `=', try the path name of the *
      * command. */
     if (cmdstr && cmdstr[0] == Equals) {
-	char *c = findcmd(cmdstr + 1);
+	char *c = findcmd(cmdstr + 1, 1);
 
 	if (c) {
 	    zsfree(cmdstr);
@@ -3191,7 +3540,7 @@ makecomplistpc(char *os, int incmd)
 {
     Patcomp pc;
     Comp pat;
-    char *s = findcmd(cmdstr);
+    char *s = findcmd(cmdstr, 1);
 
     for (pc = patcomps; pc; pc = pc->next) {
 	if ((pat = parsereg(pc->pat)) &&
@@ -3468,12 +3817,12 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 
     ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT));
 
-    if (findnode(ccstack, cc))
+    if (!incompfunc && findnode(ccstack, cc))
 	return;
 
     addlinknode(ccstack, cc);
 
-    if (allccs) {
+    if (!incompfunc && allccs) {
 	if (findnode(allccs, cc)) {
 	    uremnode(ccstack, firstnode(ccstack));
 	    return;
@@ -4107,7 +4456,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    }
 
 	    /* This flag allows us to use read -l and -c. */
-	    incompctlfunc = 1;
+	    if (!incompfunc)
+		incompctlfunc = 1;
 	    sfcontext = SFC_COMPLETE;
 	    /* Call the function. */
 	    doshfunc(cc->func, list, args, 0, 1);
@@ -4126,7 +4476,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	char *j, *jj;
 
 	for (i = 0; i < MAXJOB; i++)
-	    if (jobtab[i].stat & STAT_INUSE) {
+	    if ((jobtab[i].stat & STAT_INUSE) &&
+		jobtab[i].procs && jobtab[i].procs->text) {
 		int stopped = jobtab[i].stat & STAT_STOPPED;
 
 		j = jj = dupstring(jobtab[i].procs->text);
@@ -4274,7 +4625,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    }
 
 	    /* No harm in allowing read -l and -c here, too */
-	    incompctlfunc = 1;
+	    if (!incompfunc)
+		incompctlfunc = 1;
 	    sfcontext = SFC_COMPLETE;
 	    doshfunc(cc->ylist, list, args, 0, 1);
 	    sfcontext = osc;
@@ -4528,10 +4880,12 @@ makearray(LinkList l, int s, int *np, int *nlp)
 	    for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--);
 	    ap = bp;
 	    /* Mark those, that would show the same string in the list. */
-	    for (; bp[1] && !strcmp((*ap)->str, (bp[1])->str); bp++) {
-		(bp[1])->flags |= CMF_NOLIST; nl++;
-	    }
+	    for (; bp[1] && !strcmp((*ap)->str, (bp[1])->str); bp++)
+		(bp[1])->flags |= CMF_NOLIST;
 	}
+	for (ap = rp; *ap; ap++)
+	    if ((*ap)->flags & CMF_NOLIST)
+		nl++;
 	*cp = NULL;
     }
     if (np)
@@ -4991,10 +5345,14 @@ do_single(Cmatch m)
     if (m->suf) {
 	havesuff = 1;
 	menuinsc = ztrlen(m->suf);
-	if (menuwe && (m->flags & CMF_REMOVE)) {
-	    makesuffix(menuinsc);
-	    if (menuinsc == 1)
-		suffixlen[m->suf[0]] = 1;
+	menulen -= menuinsc;
+	if (menuwe) {
+	    menuend += menuinsc;
+	    if (m->flags & CMF_REMOVE) {
+		makesuffix(menuinsc);
+		if (menuinsc == 1)
+		    suffixlen[STOUC(m->suf[0])] = 1;
+	    }
 	}
     } else {
 	/* There is no user-specified suffix, *
@@ -5463,7 +5821,8 @@ listmatches(void)
 		}
 		if (n) {
 		    putc('\n', shout);
-		    p = skipnolist(p + 1);
+		    if (n && nl)
+			p = skipnolist(p + 1);
 		}
 	    }
 	}
@@ -5671,7 +6030,7 @@ expandcmdpath(void)
 	feep();
 	return;
     }
-    str = findcmd(s);
+    str = findcmd(s, 1);
     zsfree(s);
     if (!str) {
 	feep();
@@ -5686,7 +6045,6 @@ expandcmdpath(void)
 	cs += cmdwe - cmdwb + strlen(str);
     if (cs > ll)
 	cs = ll;
-    zsfree(str);
 }
 
 /* Extra function added by AR Iano-Fletcher. */