about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Makefile.in15
-rw-r--r--Src/Zle/comp.h39
-rw-r--r--Src/Zle/comp1.c57
-rw-r--r--Src/Zle/comp1.export15
-rw-r--r--Src/Zle/compctl.c260
-rw-r--r--Src/Zle/zle_hist.c6
-rw-r--r--Src/Zle/zle_main.c4
-rw-r--r--Src/Zle/zle_misc.c6
-rw-r--r--Src/Zle/zle_params.c21
-rw-r--r--Src/Zle/zle_refresh.c8
-rw-r--r--Src/Zle/zle_tricky.c912
-rw-r--r--Src/Zle/zle_word.c2
-rw-r--r--Src/builtin.c12
-rw-r--r--Src/compat.c10
-rw-r--r--Src/glob.c4
-rw-r--r--Src/mem.c18
-rw-r--r--Src/params.c149
-rw-r--r--Src/subst.c4
-rw-r--r--Src/system.h2
-rw-r--r--Src/utils.c6
-rw-r--r--Src/zsh.export6
21 files changed, 1037 insertions, 519 deletions
diff --git a/Src/Makefile.in b/Src/Makefile.in
index 1b5256e16..0babdf47b 100644
--- a/Src/Makefile.in
+++ b/Src/Makefile.in
@@ -112,8 +112,10 @@ FORCE:
 
 # ========== LINKING IN MODULES ==========
 
-modules-bltin:
-	if test @D@ = N; then \
+modules-bltin: Makefile
+	if test -f mymods.conf; then \
+	    cat mymods.conf > $@; \
+	elif test @D@ = N; then \
 	    cat $(sdir)/xmods.conf > $@; \
 	elif test @RTLD_GLOBAL_OK@ != yes; then \
 	    echo comp1 > $@; \
@@ -175,18 +177,21 @@ uninstall.bin-L:
 
 # ========== DEPENDENCIES FOR CLEANUP ==========
 
-@@clean.mk@@
+# Since module cleanup rules depend on Makemod, they come first.  This
+# forces module stuff to get cleaned before Makemod itself gets
+# deleted.
 
 mostlyclean-here:
 	rm -f stamp-modobjs stamp-modobjs.tmp
 
 clean-here:
 	rm -f modules.index.tmp modules.stamp zsh ansi2knr.o ansi2knr
+	rm -f modules.index modules-bltin
 	rm -f libzsh-*.$(DL_EXT)
 
 distclean-here:
 	rm -f TAGS tags
-	rm -f modules.index modules-bltin Makefile
+	rm -f Makefile mymods.conf
 
 mostlyclean: mostlyclean-modules
 clean: clean-modules
@@ -196,6 +201,8 @@ realclean: realclean-modules
 mostlyclean-modules clean-modules distclean-modules realclean-modules: Makemod
 	@$(MAKE) -f Makemod $(MAKEDEFS) `echo $@ | sed 's/-modules//'`
 
+@@clean.mk@@
+
 # ========== RECURSIVE MAKES ==========
 
 install.modules uninstall.modules \
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 76ac67114..d7f3610f1 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -103,6 +103,7 @@ struct compctl {
     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)          */
@@ -284,7 +285,37 @@ struct cline {
 /* Flags for compadd and addmatches(). */
 
 #define CAF_QUOTE    1
-#define CAF_MENU     2
-#define CAF_NOSORT   4
-#define CAF_ALT      8
-#define CAF_MATCH   16
+#define CAF_NOSORT   2
+#define CAF_ALT      4
+#define CAF_MATCH    8
+
+/* Flags for special parameters. */
+
+#define CP_WORDS      (1 <<  0)
+#define CP_CURRENT    (1 <<  1)
+#define CP_PREFIX     (1 <<  2)
+#define CP_SUFFIX     (1 <<  3)
+#define CP_IPREFIX    (1 <<  4)
+#define CP_COMPSTATE  (1 <<  5)
+
+#define CP_REALPARAMS        6
+
+#define CP_NMATCHES   (1 <<  6)
+#define CP_MATCHER    (1 <<  7)
+#define CP_MATCHERSTR (1 <<  8)
+#define CP_MATCHERTOT (1 <<  9)
+#define CP_CONTEXT    (1 << 10)
+#define CP_PARAMETER  (1 << 11)
+#define CP_REDIRECT   (1 << 12)
+#define CP_QUOTE      (1 << 13)
+#define CP_QUOTING    (1 << 14)
+#define CP_RESTORE    (1 << 15)
+#define CP_LIST       (1 << 16)
+#define CP_INSERT     (1 << 17)
+#define CP_EXACT      (1 << 18)
+#define CP_EXACTSTR   (1 << 19)
+#define CP_PATMATCH   (1 << 20)
+
+#define CP_NUM              21
+
+#define CP_ALLMASK    ((1 << CP_NUM) - 1)
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index 72f3cea53..ccccf5a34 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -46,10 +46,13 @@ Cmlist cmatcher;
 /**/
 void (*makecompparamsptr) _((void));
 
+/**/
+void (*comp_setunsetptr) _((int, int));
+
 /* pointers to functions required by compctl and defined by zle */
 
 /**/
-void (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char *, char **));
+int (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char *, char **));
 
 /**/
 char *(*comp_strptr) _((int*, int*, int));
@@ -58,7 +61,7 @@ char *(*comp_strptr) _((int*, int*, int));
 int (*getcpatptr) _((char *, int, char *, int));
 
 /**/
-void (*makecomplistcallptr) _((Compctl));
+int (*makecomplistcallptr) _((Compctl));
 
 /**/
 int (*makecomplistctlptr) _((int));
@@ -96,14 +99,29 @@ int incompfunc;
 /**/
 long compcurrent,
      compnmatches,
-     compmatcher;
+     compmatcher,
+     compmatchertot;
 
 /**/
-char *compcontext,
-     *compcommand,
+char **compwords,
      *compprefix,
      *compsuffix,
-     *compiprefix;
+     *compiprefix,
+     *compmatcherstr,
+     *compcontext,
+     *compparameter,
+     *compredirect,
+     *compquote,
+     *compquoting,
+     *comprestore,
+     *complist,
+     *compinsert,
+     *compexact,
+     *compexactstr,
+     *comppatmatch;
+
+/**/
+Param *comppms;
 
 /* The function rembslash() came from zle_tricky.c, but is now used *
  * in compctl.c, too.                                               */
@@ -154,6 +172,7 @@ freecompctl(Compctl cc)
     zsfree(cc->glob);
     zsfree(cc->str);
     zsfree(cc->func);
+    zsfree(cc->widget);
     zsfree(cc->explain);
     zsfree(cc->ylist);
     zsfree(cc->prefix);
@@ -408,9 +427,14 @@ setup_comp1(Module m)
     cc_first.refc = 10000;
     cc_first.mask = 0;
     cc_first.mask2 = CC_CCCONT;
-    compcontext = compcommand = compprefix = compsuffix =
-	compiprefix = NULL;
+    comppms = NULL;
+    compwords = NULL;
+    compprefix = compsuffix = compiprefix = compmatcherstr = 
+	compcontext = compparameter = compredirect = compquote =
+	compquoting = comprestore = complist = compinsert =
+	compexact = compexactstr = comppatmatch = NULL;
     makecompparamsptr = NULL;
+    comp_setunsetptr = NULL;
     return 0;
 }
 
@@ -437,11 +461,22 @@ finish_comp1(Module m)
     deletehashtable(compctltab);
     zfree(clwords, clwsize * sizeof(char *));
     compctlreadptr = fallback_compctlread;
-    zsfree(compcontext);
-    zsfree(compcommand);
+    freearray(compwords);
     zsfree(compprefix);
-    zsfree(compiprefix);
     zsfree(compsuffix);
+    zsfree(compiprefix);
+    zsfree(compmatcherstr);
+    zsfree(compcontext);
+    zsfree(compparameter);
+    zsfree(compredirect);
+    zsfree(compquote);
+    zsfree(compquoting);
+    zsfree(comprestore);
+    zsfree(complist);
+    zsfree(compinsert);
+    zsfree(compexact);
+    zsfree(compexactstr);
+    zsfree(comppatmatch);
     return 0;
 }
 
diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export
index 278006b6a..35b63e7d3 100644
--- a/Src/Zle/comp1.export
+++ b/Src/Zle/comp1.export
@@ -13,12 +13,27 @@ compcommand
 compcontext
 compctltab
 compcurrent
+compexact
+compexactstr
+compinsert
 compiprefix
+complist
 compmatcher
+compmatcherstr
+compmatchertot
 compnmatches
+compparameter
+comppatmatch
+comppms
 compprefix
+compredirect
+compquote
+compquoting
+comprestore
+comp_setunsetptr
 comp_strptr
 compsuffix
+compwords
 freecmatcher
 freecmlist
 freecompcond
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index f71d67510..b5c8e4b3f 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -140,7 +140,7 @@ get_gmatcher(char *name, char **argv)
 	while (*argv) {
 	    if ((m = parse_cmatcher(name, *argv)) == pcm_err)
 		return 2;
-	    *q = n = (Cmlist) halloc(sizeof(struct cmlist));
+	    *q = n = (Cmlist) zhalloc(sizeof(struct cmlist));
 	    n->next = NULL;
 	    n->matcher = m;
 	    n->str = *argv++;
@@ -578,6 +578,19 @@ 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;
@@ -1197,6 +1210,7 @@ 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);
@@ -1217,6 +1231,7 @@ 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);
@@ -1423,6 +1438,7 @@ 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');
@@ -1644,7 +1660,7 @@ bin_compctl(char *name, char **argv, char *ops, int func)
 
 /**/
 static int
-bin_complist(char *name, char **argv, char *ops, int func)
+bin_compgen(char *name, char **argv, char *ops, int func)
 {
     Compctl cc;
     int ret = 0;
@@ -1663,7 +1679,7 @@ bin_complist(char *name, char **argv, char *ops, int func)
 	zerrnam(name, "command names illegal", NULL, 0);
 	ret = 1;
     } else
-	makecomplistcallptr(cc);
+	ret = makecomplistcallptr(cc);
 
     freecompctl(cc);
     return ret;
@@ -1677,7 +1693,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
     char *ipre = NULL, *ppre = NULL, *psuf = NULL, *prpre = NULL;
     char *pre = NULL, *suf = NULL, *group = NULL, *m = NULL, *rs = NULL;
     char *ign = NULL, *rf = NULL, *expl = NULL;
-    int f = 0, a = 0, dm;
+    int f = 0, a = CAF_MATCH, dm;
     Cmatcher match = NULL;
 
     if (incompfunc != 1) {
@@ -1710,7 +1726,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		f |= CMF_NOLIST;
 		break;
 	    case 'U':
-		a |= CAF_MENU;
+		a &= ~CAF_MATCH;
 		break;
 	    case 'P':
 		sp = &pre;
@@ -1749,9 +1765,6 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 	    case 'a':
 		a |= CAF_ALT;
 		break;
-	    case 'm':
-		a |= CAF_MATCH;
-		break;
 	    case 'M':
 		sp = &m;
 		e = "matching specification expected after -%c";
@@ -1805,11 +1818,11 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 	return 1;
 
     match = cpcmatcher(match);
-    addmatchesptr(ipre, ppre, psuf, prpre, pre, suf, group,
-		  rs, rf, ign, f, a, match, expl, argv);
+    a = addmatchesptr(ipre, ppre, psuf, prpre, pre, suf, group,
+		      rs, rf, ign, f, a, match, expl, argv);
     freecmatcher(match);
 
-    return 0;
+    return a;
 }
 
 /**/
@@ -1824,34 +1837,54 @@ bin_compcall(char *name, char **argv, char *ops, int func)
 			      (ops['D'] ? 0 : CFN_DEFAULT));
 }
 
+/* Definitions for the special parameters. Note that these have to match the
+ * order of the CP_* bits in comp.h */
+
 #define VAR(X) ((void *) (&(X)))
 static struct compparam {
     char *name;
     int type;
     void *var;
 } compparams[] = {
+    { "words", PM_ARRAY, VAR(compwords) },
     { "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) },
-    { "MATCHER", PM_INTEGER, VAR(compmatcher) },
+    { NULL, 0, NULL },
+
+    { "nmatches", PM_INTEGER, VAR(compnmatches) },
+    { "matcher", PM_INTEGER, VAR(compmatcher) },
+    { "matcher_string", PM_SCALAR, VAR(compmatcherstr) },
+    { "total_matchers", PM_INTEGER, VAR(compmatchertot) },
+    { "context", PM_SCALAR, VAR(compcontext) },
+    { "parameter", PM_SCALAR, VAR(compparameter) },
+    { "redirect", PM_SCALAR, VAR(compredirect) },
+    { "quote", PM_SCALAR, VAR(compquote) },
+    { "quoting", PM_SCALAR, VAR(compquoting) },
+    { "restore", PM_SCALAR, VAR(comprestore) },
+    { "list", PM_SCALAR, VAR(complist) },
+    { "insert", PM_SCALAR, VAR(compinsert) },
+    { "exact", PM_SCALAR, VAR(compexact) },
+    { "exact_string", PM_SCALAR, VAR(compexactstr) },
+    { "pattern_match", PM_SCALAR, VAR(comppatmatch) },
     { NULL, 0, NULL }
 };
 
-/**/
-void makecompparams(void)
+#define COMPSTATENAME "compstate"
+
+static struct compparam *
+addcompparams(struct compparam *cp)
 {
-    struct compparam *cp;
+    Param *pp = comppms + (cp - compparams);
 
-    for (cp = compparams; cp->name; cp++) {
+    for (; cp->name; cp++, pp++) {
 	Param pm = createparam(cp->name, cp->type | PM_SPECIAL|PM_REMOVABLE);
 	if (!pm)
 	    pm = (Param) paramtab->getnode(paramtab, cp->name);
-	DPUTS(!pm, "param not set in makecompparams");
+	DPUTS(!pm, "param not set in addcompparams");
 
+	*pp = pm;
 	pm->level = locallevel;
 	pm->u.data = cp->var;
 	switch(PM_TYPE(cp->type)) {
@@ -1862,18 +1895,115 @@ void makecompparams(void)
 	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;
 	}
 	pm->unsetfn = compunsetfn;
     }
+    return cp;
+}
+
+/**/
+void
+makecompparams(void)
+{
+    struct compparam *cp;
+    Param cpm;
+    HashTable tht;
+
+    cp = addcompparams(compparams);
+
+    if (!(cpm = createparam(COMPSTATENAME, PM_SPECIAL|PM_REMOVABLE|PM_HASHED)))
+	cpm = (Param) paramtab->getnode(paramtab, COMPSTATENAME);
+    DPUTS(!cpm, "param not set in makecompparams");
+
+    comppms[cp - compparams] = cpm;
+    tht = paramtab;
+    cpm->level = locallevel;
+    cpm->gets.hfn = get_compstate;
+    cpm->sets.hfn = set_compstate;
+    cpm->unsetfn = compunsetfn;
+    cpm->u.hash = paramtab = newparamtable(17, COMPSTATENAME);
+    addcompparams(cp + 1);
+    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 = compparams + CP_REALPARAMS,
+		 pp = comppms + CP_REALPARAMS; 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)
+			*((long *) cp->var) = getintvalue(&v);
+		    else if ((str = getstrvalue(&v))) {
+			zsfree(*((char **) cp->var));
+			*((char **) cp->var) = ztrdup(str);
+		    }
+		    (*pp)->flags &= ~PM_UNSET;
+
+		    break;
+		}
 }
 
 /**/
 static void
 compunsetfn(Param pm, int exp)
 {
-    if (exp)
-	stdunsetfn(pm, 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 set, int unset)
+{
+    Param *p;
+
+    if (!comppms)
+	return;
+
+    set &= CP_ALLMASK;
+    unset &= CP_ALLMASK;
+    for (p = comppms; set || unset; set >>= 1, unset >>= 1, p++) {
+	if (set & 1)
+	    (*p)->flags &= ~PM_UNSET;
+	if (unset & 1)
+	    (*p)->flags |= PM_UNSET;
+    }
 }
 
 /**/
@@ -1883,29 +2013,51 @@ comp_wrapper(List list, FuncWrap w, char *name)
     if (incompfunc != 1)
 	return 1;
     else {
-	char *octxt, *ocmd, *opre, *osuf, *oipre;
+	char *orest, *opre, *osuf, *oipre, **owords;
 	long ocur;
-
+	int unset = 0, m, sm;
+	Param *pp;
+
+	m = CP_WORDS | CP_CURRENT | CP_PREFIX | CP_SUFFIX | 
+	    CP_IPREFIX | CP_RESTORE;
+	for (pp = comppms, sm = 1; m; pp++, m >>= 1, sm <<= 1) {
+	    if ((m & 1) && ((*pp)->flags & PM_UNSET))
+		unset |= sm;
+	}
+	orest = comprestore;
+	comprestore = ztrdup("auto");
 	ocur = compcurrent;
-	octxt = dupstring(compcontext);
-	ocmd = dupstring(compcommand);
 	opre = dupstring(compprefix);
 	osuf = dupstring(compsuffix);
 	oipre = dupstring(compiprefix);
 
+	HEAPALLOC {
+	    owords = arrdup(compwords);
+	} LASTALLOC;
+
 	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);
+	if (comprestore && !strcmp(comprestore, "auto")) {
+	    compcurrent = ocur;
+	    zsfree(compprefix);
+	    compprefix = ztrdup(opre);
+	    zsfree(compsuffix);
+	    compsuffix = ztrdup(osuf);
+	    zsfree(compiprefix);
+	    compiprefix = ztrdup(oipre);
+	    freearray(compwords);
+	    PERMALLOC {
+		compwords = arrdup(owords);
+	    } LASTALLOC;
+	    comp_setunset(CP_COMPSTATE |
+			  (~unset & (CP_WORDS | CP_CURRENT | CP_PREFIX |
+				     CP_SUFFIX | CP_IPREFIX | CP_RESTORE)),
+			  unset);
+	} else
+	    comp_setunset(CP_COMPSTATE | (~unset & CP_RESTORE),
+			  (unset & CP_RESTORE));
+	zsfree(comprestore);
+	comprestore = orest;
 
 	return 0;
     }
@@ -1942,20 +2094,14 @@ comp_check(void)
 static void
 restrict_range(int b, int e)
 {
-    int i = e - b;
+    int i = e - b + 1;
     char **p = (char **) zcalloc((i + 1) * sizeof(char *)), **q, **pp;
 
-    for (q = p, pp = pparams + b + 1; i; i--, q++, pp++)
+    for (q = p, pp = compwords + b; 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("argument");
-    else
-	compcontext = ztrdup("command");
+    freearray(compwords);
+    compwords = p;
+    compcurrent -= b;
 }
 
 /**/
@@ -1988,7 +2134,7 @@ 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;
+	int l = arrlen(compwords), t, i = compcurrent - 1;
 
 	if (b > 0)
 	    b--;
@@ -2018,7 +2164,7 @@ cond_word(char **a, int id)
 {
     if (comp_check()) {
 	int o = ((id & 2) ? compcurrent : 0) + cond_val(a, 0);
-	int l = arrlen(pparams);
+	int l = arrlen(compwords);
 	char *s;
 
 	if (o < 0)
@@ -2028,7 +2174,7 @@ cond_word(char **a, int id)
 	if (o < 0 || o >= l)
 	    return 0;
 
-	s = pparams[o];
+	s = compwords[o];
 	return ((id & 1) ? cond_match(a, 1, s) : !strcmp(s, cond_str(a, 1)));
     }
     return 0;
@@ -2068,7 +2214,7 @@ 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);
+	int l = arrlen(compwords);
 
 	return (l >= b && l <= e);
     }
@@ -2081,7 +2227,7 @@ cond_range(char **a, int id)
 {
     if (comp_check()) {
 	char *s, **p;
-	int i, l = arrlen(pparams), t = 0, b = 0, e = l - 1;
+	int i, l = arrlen(compwords), t = 0, b = 0, e = l - 1;
 	Comp c;
 
 	i = compcurrent - 1;
@@ -2095,7 +2241,7 @@ cond_range(char **a, int id)
 	} else
 	    s = cond_str(a, 0);
 
-	for (i--, p = pparams + i; i >= 0; p--, i--) {
+	for (i--, p = compwords + i; i >= 0; p--, i--) {
 	    if (((id & 1) ? domatch(*p, c, 0) : !strcmp(*p, s))) {
 		b = i + 1;
 		t = 1;
@@ -2112,7 +2258,7 @@ cond_range(char **a, int id)
 	    } else
 		s = cond_str(a, 1);
 
-	    for (i++, p = pparams + i; i < l; p++, i++) {
+	    for (i++, p = compwords + i; i < l; p++, i++) {
 		if (((id & 1) ? domatch(*p, c, 0) : !strcmp(*p, s))) {
 		    e = i - 1;
 		    tt = 1;
@@ -2151,7 +2297,7 @@ cond_matcher(char **a, int id)
 
 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("compgen", 0, bin_compgen, 1, -1, 0, NULL, NULL),
     BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL),
     BUILTIN("compcall", 0, bin_compcall, 0, 0, 0, "TD", NULL),
 };
@@ -2185,6 +2331,7 @@ setup_compctl(Module m)
 {
     compctltab->printnode = printcompctlp;
     makecompparamsptr = makecompparams;
+    comp_setunsetptr = comp_setunset;
     return 0;
 }
 
@@ -2217,6 +2364,7 @@ finish_compctl(Module m)
 {
     compctltab->printnode = NULL;
     makecompparamsptr = NULL;
+    comp_setunsetptr = NULL;
     return 0;
 }
 
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index a4b3866e5..2def5f03a 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -653,7 +653,7 @@ get_isrch_spot(int num, int *hlp, int *posp, int *csp, int *lenp, int *dirp, int
 static void
 doisearch(int dir)
 {
-    char *s, *ibuf = halloc(80), *sbuf = ibuf + FIRST_SEARCH_CHAR;
+    char *s, *ibuf = zhalloc(80), *sbuf = ibuf + FIRST_SEARCH_CHAR;
     int sbptr = 0, top_spot = 0, pos, sibuf = 80;
     int nomatch = 0, skip_line = 0, skip_pos = 0;
     int odir = dir, sens = zmult == 1 ? 3 : 1;
@@ -936,7 +936,7 @@ static int visrchsense;
 static int
 getvisrchstr(void)
 {
-    char *sbuf = halloc(80);
+    char *sbuf = zhalloc(80);
     int sptr = 1, ret = 0, ssbuf = 80;
     Thingy cmd;
     char *okeymap = curkeymapname;
@@ -1001,7 +1001,7 @@ getvisrchstr(void)
 	    }
 	  ins:
 	    if(sptr == ssbuf - 1) {
-		char *newbuf = halloc(ssbuf *= 2);
+		char *newbuf = zhalloc(ssbuf *= 2);
 		strcpy(newbuf, sbuf);
 		statusline = sbuf = newbuf;
 	    }
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 277d154a6..97d012f7b 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -154,10 +154,14 @@ zsetterm(void)
 #  ifdef OXTABS
     ti.tio.c_oflag &= ~OXTABS;
 #  else
+#   ifdef XTABS
     ti.tio.c_oflag &= ~XTABS;
+#   endif
 #  endif
 # endif
+#ifdef ONLCR
     ti.tio.c_oflag |= ONLCR;
+#endif
     ti.tio.c_cc[VQUIT] =
 # ifdef VDISCARD
 	ti.tio.c_cc[VDISCARD] =
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index ea8874010..1c25cd7eb 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -567,7 +567,7 @@ makequote(char *str, size_t *len)
 	if (*l == '\'')
 	    qtct++;
     *len += 2 + qtct*3;
-    l = ol = (char *)halloc(*len);
+    l = ol = (char *)zhalloc(*len);
     *l++ = '\'';
     for (; str < end; str++)
 	if (*str == '\'') {
@@ -613,7 +613,7 @@ executenamedcommand(char *prmt)
     char *okeymap = curkeymapname;
 
     clearlist = 1;
-    cmdbuf = halloc(l + NAMLEN + 2);
+    cmdbuf = zhalloc(l + NAMLEN + 2);
     strcpy(cmdbuf, prmt);
     statusline = cmdbuf;
     selectkeymap("main", 1);
@@ -794,7 +794,7 @@ makeparamsuffix(int br, int n)
     if(br) {
 	suffixlen['#'] = suffixlen['%'] = suffixlen['?'] = n;
 	suffixlen['-'] = suffixlen['+'] = suffixlen['='] = n;
-	/*{*/ suffixlen['}'] = n;
+	/*{*/ suffixlen['}'] = suffixlen['/'] = n;
     }
 }
 
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index 7e15d3d8b..2a35ac416 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -67,6 +67,8 @@ static struct zleparam {
         zleunsetfn, NULL },
     { "keys", PM_ARRAY | PM_READONLY, NULL, FN(get_keys),
         zleunsetfn, NULL },
+    { "NUMERIC", PM_INTEGER | PM_READONLY, FN(set_numeric), FN(get_numeric),
+        zleunsetfn, NULL },
     { NULL, 0, NULL, NULL, NULL, NULL }
 };
 
@@ -97,6 +99,7 @@ makezleparams(int ro)
 	    case PM_INTEGER:
 		pm->sets.ifn = (void (*) _((Param, long))) zp->setfn;
 		pm->gets.ifn = (long (*) _((Param))) zp->getfn;
+		pm->ct = 10;
 		break;
 	}
 	pm->unsetfn = zp->unsetfn;
@@ -225,9 +228,9 @@ get_keys(Param pm)
 {
     char **r, **q, *p, *k, c;
 
-    r = (char **) halloc((strlen(keybuf) + 1) * sizeof(char *));
+    r = (char **) zhalloc((strlen(keybuf) + 1) * sizeof(char *));
     for (q = r, p = keybuf; (c = *p); q++, p++) {
-	k = *q = (char *) halloc(5);
+	k = *q = (char *) zhalloc(5);
 	if (c & 0x80) {
 	    *k++ = 'M';
 	    *k++ = '-';
@@ -244,3 +247,17 @@ get_keys(Param pm)
 
     return r;
 }
+
+/**/
+static void
+set_numeric(Param pm, long x)
+{
+    zmult = x;
+}
+
+/**/
+static long
+get_numeric(Param pm)
+{
+    return zmult;
+}
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 9e07676e8..b9f39b70b 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -266,7 +266,11 @@ zrefresh(void)
 
 /* Nov 96: <mason>  I haven't checked how complete this is.  sgtty stuff may
    or may not work */
+#if defined(SGTABTYPE)
     oxtabs = ((SGTTYFLAG & SGTABTYPE) == SGTABTYPE);
+#else
+    oxtabs = 0;
+#endif
 
     cleareol = 0;		/* unset */
     more_start = more_end = 0;	/* unset */
@@ -615,7 +619,7 @@ refreshline(int ln)
     if (cleareol 		/* request to clear to end of line */
 	|| !nllen 		/* no line buffer given */
 	|| (ln == 0 && (put_rpmpt != oput_rpmpt))) {	/* prompt changed */
-	p1 = halloc(winw + 2);
+	p1 = zhalloc(winw + 2);
 	if (nllen)
 	    strncpy(p1, nl, nllen);
 	memset(p1 + nllen, ' ', winw - nllen);
@@ -627,7 +631,7 @@ refreshline(int ln)
 	    nl = p1;		/* don't keep the padding for prompt line */
 	nllen = winw;
     } else if (ollen > nllen) { /* make new line at least as long as old */
-	p1 = halloc(ollen + 1);
+	p1 = zhalloc(ollen + 1);
 	strncpy(p1, nl, nllen);
 	memset(p1 + nllen, ' ', ollen - nllen);
 	p1[ollen] = '\0';
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 4b42640e1..f01ffbf1c 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -73,9 +73,10 @@ static int wb, we;
 static int offs;
 
 /* These control the type of completion that will be done.  They are    *
- * affected by the choice of ZLE command and by relevant shell options. */
+ * affected by the choice of ZLE command and by relevant shell options. *
+ * usemenu is set to 2 if we have to start automenu.                    */
 
-static int usemenu, useglob;
+static int usemenu, useglob, useexact, useline, uselist;
 
 /* != 0 if we are in the middle of a menu completion */
 
@@ -125,7 +126,7 @@ static int validlist;
 
 /* This flag is non-zero if we are completing a pattern (with globcomplete) */
 
-static int ispattern;
+static int ispattern, haspattern;
 
 /* 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    *
@@ -168,12 +169,6 @@ static int addwhat;
 
 static char *qword;
 
-/* This is non-zero if we are doing a menu-completion and this is not the *
- * first call (e.g. when automenu is set and menu-completion was entered  *
- * due to this). */
-
-static int amenu;
-
 /* The current group of matches. */
 
 static Cmgroup mgroup;
@@ -322,7 +317,7 @@ completecall(void)
 void
 completeword(void)
 {
-    usemenu = isset(MENUCOMPLETE);
+    usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
     if (c == '\t' && usetab())
 	selfinsert();
@@ -351,7 +346,7 @@ menucomplete(void)
 void
 listchoices(void)
 {
-    usemenu = isset(MENUCOMPLETE);
+    usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
     docomplete(COMP_LIST_COMPLETE);
 }
@@ -371,7 +366,7 @@ deletecharorlist(void)
     Cmgroup mg = menugrp;
     Cmatch *mc = menucur;
 
-    usemenu = isset(MENUCOMPLETE);
+    usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
     if (cs != ll) {
 	fixsuffix();
@@ -398,7 +393,7 @@ expandword(void)
 void
 expandorcomplete(void)
 {
-    usemenu = isset(MENUCOMPLETE);
+    usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
     if (c == '\t' && usetab())
 	selfinsert();
@@ -427,7 +422,7 @@ menuexpandorcomplete(void)
 void
 listexpand(void)
 {
-    usemenu = isset(MENUCOMPLETE);
+    usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
     docomplete(COMP_LIST_EXPAND);
 }
@@ -492,9 +487,9 @@ acceptandmenucomplete(void)
 }
 
 /* These are flags saying if we are completing in the command *
- * position or in a redirection.                              */
+ * position, in a redirection, or in a parameter expansion.   */
 
-static int lincmd, linredir;
+static int lincmd, linredir, ispar;
 
 /* The string for the redirection operator. */
 
@@ -513,10 +508,10 @@ static char *varname;
 
 static int insubscr;
 
-/* 1 if we are completing in a string */
+/* 1 if we are completing in a quoted string (or inside `...`) */
 
 /**/
-int instring;
+int instring, inbackt;
 
 /* Convenience macro for calling bslashquote() (formerly quotename()). *
  * This uses the instring variable above.                              */
@@ -601,6 +596,87 @@ cmphaswilds(char *str)
     return 0;
 }
 
+/* Check if we have to complete a parameter name. */
+
+static char *
+check_param(char *s, int set)
+{
+    char *p;
+
+    ispar = 0;
+    /* Try to find a `$'. */
+    for (p = s + offs; p > s && *p != String; p--);
+    if (*p == String) {
+	/* Handle $$'s */
+	while (p > s && p[-1] == String)
+	    p--;
+	while (p[1] == String && p[2] == String)
+	    p += 2;
+    }
+    if (*p == String &&	p[1] != Inpar && p[1] != Inbrack) {
+	/* This is really a parameter expression (not $(...) or $[...]). */
+	char *b = p + 1, *e = b;
+	int n = 0, br = 1;
+
+	if (*b == Inbrace) {
+	    /* If this is a ${...}, ignore the possible (...) flags. */
+	    b++, br++;
+	    n = skipparens(Inpar, Outpar, &b);
+	}
+
+	/* Ignore the stuff before the parameter name. */
+	for (; *b; b++)
+	    if (*b != '^' && *b != Hat &&
+		*b != '=' && *b != Equals &&
+		*b != '~' && *b != Tilde)
+		break;
+	if (*b == '#' || *b == Pound || *b == '+')
+	    b++;
+
+	e = b;
+	/* Find the end of the name. */
+	if (*e == Quest || *e == Star || *e == String || *e == Qstring ||
+	    *e == '?'   || *e == '*'  || *e == '$'    ||
+	    *e == '-'   || *e == '!'  || *e == '@')
+	    e++;
+	else if (idigit(*e))
+	    while (idigit(*e))
+		e++;
+	else if (iident(*e))
+	    while (iident(*e) ||
+		   (comppatmatch && *comppatmatch &&
+		    (*e == Star || *e == Quest)))
+		e++;
+
+	/* Now make sure that the cursor is inside the name. */
+	if (offs <= e - s && offs >= b - s && n <= 0) {
+	    /* It is. */
+	    /* If we were called from makecomplistflags(), we have to set the
+	     * global variables. */
+	    if (set) {
+		if (br >= 2)
+		    mflags |= CMF_PARBR;
+
+		/* Get the prefix (anything up to the character before the name). */
+		lpsuf = dupstring(quotename(e, NULL, NULL, NULL));
+		*e = '\0';
+		lpsl = strlen(lpsuf);
+		ripre = dupstring(s);
+		ripre[b - s] = '\0';
+		ipre = dupstring(quotename(ripre, NULL, NULL, NULL));
+		untokenize(ipre);
+	    }
+	    /* And adjust wb, we, and offs again. */
+	    offs -= b - s;
+	    wb = cs - offs;
+	    we = wb + e - b;
+	    ispar = (br >= 2 ? 2 : 1);
+	    return b;
+	}
+    }
+    return NULL;
+}
+
 /* The main entry point for completion. */
 
 /**/
@@ -625,9 +701,9 @@ docomplete(int lst)
 
     /* Check if we have to start a menu-completion (via automenu). */
 
-    if ((amenu = (isset(AUTOMENU) && lastambig &&
-		  (!isset(BASHAUTOLIST) || lastambig == 2))))
-	usemenu = 1;
+    if (isset(AUTOMENU) && lastambig &&
+	(!isset(BASHAUTOLIST) || lastambig == 2))
+	usemenu = 2;
 
     /* Expand history references before starting completion.  If anything *
      * changed, do no more.                                               */
@@ -910,7 +986,7 @@ addx(char **ptmp)
 	(instring && (line[cs] == '"' || line[cs] == '\'')) ||
 	(addspace = (comppref && !iblank(line[cs])))) {
 	*ptmp = (char *)line;
-	line = (unsigned char *)halloc(strlen((char *)line) + 3 + addspace);
+	line = (unsigned char *)zhalloc(strlen((char *)line) + 3 + addspace);
 	memcpy(line, *ptmp, cs);
 	line[cs] = 'x';
 	if (addspace)
@@ -999,6 +1075,7 @@ get_comp_string(void)
 	else if (*p == '\\' && p[1] && !(k & 1))
 	    p++;
     instring = (j & 1) ? 2 : (k & 1);
+    inbackt = (i & 1);
     addx(&tmp);
     if (instring) {
 	/* Yes, we are in a string. */
@@ -1010,7 +1087,7 @@ get_comp_string(void)
 	 * What??  Why that??  Well, we want to be able to complete *
 	 * inside strings.  The lexer code gives us no help here,   *
 	 * so we have to cheat.  We remove the quotes, the lexer    *
-	 * will than treat the words in the strings normally and we *
+	 * will then treat the words in the strings normally and we *
 	 * can complete them.                                       *
 	 * This is completely the wrong thing to do, but it's       *
 	 * occasionally useful, and we can't handle quotes properly *
@@ -1551,7 +1628,7 @@ getcline(char *l, int ll, char *w, int wl, int fl)
     if ((r = freecl))
 	freecl = r->next;
     else
-	r = (Cline) halloc(sizeof(*r));
+	r = (Cline) zhalloc(sizeof(*r));
 
     r->next = NULL;
     r->line = l;
@@ -1626,7 +1703,7 @@ add_bmatchers(Cmatcher m)
     for (; m; m = m->next) {
 	if ((!m->flags && m->wlen > 0 && m->llen > 0) ||
 	    (m->flags == CMF_RIGHT && m->wlen == -1 && !m->llen)) {
-	    *q = n = (Cmlist) halloc(sizeof(struct cmlist));
+	    *q = n = (Cmlist) zhalloc(sizeof(struct cmlist));
 	    n->matcher = m;
 	    q = &(n->next);
 	}
@@ -1796,7 +1873,7 @@ join_strs(int la, char *sa, int lb, char *sb)
 	    if (!t)
 		break;
 	} else {
-	    if (rr <= mp->llen) {
+	    if (rr <= 1) {
 		char *or = rs;
 
 		rs = realloc(rs, (rl += 20));
@@ -1850,7 +1927,7 @@ join_ends(Cline o, Cline n, int *olp, int *nlp)
 	} else {
 	    /* Different anchors, see if we can find matching anchors
 	     * further down the lists. */
-	    Cline to, tn;
+	    Cline to, tn = NULL;
 	    int t = 0;
 
 	    /* But first build the common prefix. */
@@ -3337,6 +3414,7 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
 	}
     }
     while (ll && lw) {
+	t = 0;
 	/* First try the matchers. */
 	for (ms = mstack; ms; ms = ms->next) {
 	    for (mp = ms->matcher; mp; mp = mp->next) {
@@ -3578,6 +3656,7 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
 	}
     }
     while (ll && lw) {
+	t = 0;
 	for (ms = mstack; ms; ms = ms->next) {
 	    for (mp = ms->matcher; mp; mp = mp->next) {
 		if (lm == mp)
@@ -3774,7 +3853,7 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl
 		 * in the cline struct in the middle. */
 		pml = strlen(p);
 		sml = strlen(s);
-		r = (char *) halloc(pml + sml + last->llen + 1);
+		r = (char *) zhalloc(pml + sml + last->llen + 1);
 		strcpy(r, p);
 		strncpy(r + pml, last->line, last->llen);
 		strcpy(r + pml + last->llen, s);
@@ -3824,7 +3903,7 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl
 static void
 inststrlen(char *str, int move, int len)
 {
-    if (!len)
+    if (!len || !str)
 	return;
     if (len == -1)
 	len = strlen(str);
@@ -3902,30 +3981,28 @@ instmatch(Cmatch m)
  * the matches. */
 
 /**/
-void
+int
 addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 	   char *suf, char *group, char *rems, char *remf, char *ign,
 	   int flags, int aflags, Cmatcher match, char *exp, char **argv)
 {
-    char *s, *t, *e, *me, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL;
-    int lpl, lsl, i, pl, sl, test, bpl, bsl, llpl, llsl;
-    Aminfo ai;
+    char *s, *t, *e, *me, *ms, *lipre = NULL, *lpre = NULL, *lsuf = NULL;
+    char **aign = NULL;
+    int lpl, lsl, i, pl, sl, test, bpl, bsl, llpl = 0, llsl = 0, nm = mnum;
+    Aminfo ai = NULL;
     Cline lc = NULL;
-    LinkList l;
+    LinkList l = NULL;
     Cmatch cm;
     struct cmlist mst;
     Cmlist oms = mstack;
-
-    /* Use menu-completion (-U)? */
-    if ((aflags & CAF_MENU) && isset(AUTOMENU))
-	usemenu = 1;
+    Comp cp = NULL;
 
     /* Switch back to the heap that was used when the completion widget
      * was invoked. */
     SWITCHHEAPS(compheap) {
 	HEAPALLOC {
 	    if (exp) {
-		expl = (Cexpl) halloc(sizeof(struct cexpl));
+		expl = (Cexpl) zhalloc(sizeof(struct cexpl));
 		expl->count = expl->fcount = 0;
 		expl->str = dupstring(exp);
 	    } else
@@ -3954,9 +4031,30 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 	    if (aflags & CAF_MATCH) {
 		lipre = dupstring(compiprefix);
 		lpre = dupstring(compprefix);
-		llpl = strlen(lpre);
 		lsuf = dupstring(compsuffix);
+		llpl = strlen(lpre);
 		llsl = strlen(lsuf);
+		/* Test if there is an existing -P prefix. */
+		if (pre && *pre) {
+		    pl = pfxlen(pre, lpre);
+		    llpl -= pl;
+		    lpre += pl;
+		}
+		if (comppatmatch && *comppatmatch) {
+		    char *tmp = (char *) zhalloc(2 + llpl + llsl);
+
+		    strcpy(tmp, lpre);
+		    tmp[llpl] = 'x';
+		    strcpy(tmp + llpl + 1, lsuf);
+
+		    tokenize(tmp);
+		    remnulargs(tmp);
+		    if (haswilds(tmp)) {
+			tmp[llpl] = Star;
+			if ((cp = parsereg(tmp)))
+			    haspattern = 1;
+		    }
+		}
 	    }
 	    /* Now duplicate the strings we have from the command line. */
 	    if (ipre)
@@ -3973,50 +4071,71 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		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);
-	    /* Select the group in which to store the matches. */
-	    if (group) {
-		endcmgroup(NULL);
-		begcmgroup(group, (aflags & CAF_NOSORT));
-		if (aflags & CAF_NOSORT)
-		    mgroup->flags |= CGF_NOSORT;
-	    } else {
-		endcmgroup(NULL);
-		begcmgroup("default", 0);
-	    }
-	    /* Select the set of matches. */
-	    if (aflags & CAF_ALT) {
-		l = fmatches;
-		ai = fainfo;
-	    } else {
-		l = matches;
-		ai = ainfo;
+	    if (aflags & CAF_MATCH) {
+		s = ppre ? ppre : "";
+		if (llpl <= lpl && strpfx(lpre, s)) {
+		    llpl = 0;
+		    lpre = "";
+		} else if (llpl > lpl && strpfx(s, lpre)) {
+		    llpl -= lpl;
+		    lpre += lpl;
+		} else
+		    *argv = NULL;
+		s = psuf ? psuf : "";
+		if (llsl <= lsl && strsfx(lsuf, s)) {
+		    llsl = 0;
+		    lsuf = "";
+		} else if (llsl > lsl && strsfx(s, lsuf)) {
+		    lsuf[llsl - lsl] = '\0';
+		    llsl -= lsl;
+		} else
+		    *argv = NULL;
 	    }
-	    if (remf) {
-		remf = dupstring(remf);
-		rems = NULL;
-	    } else if (rems)
-		rems = dupstring(rems);
-	    /* Build the common -P prefix. */
-    	    if (ai->pprefix) {
+	    if (*argv) {
 		if (pre)
-		    ai->pprefix[pfxlen(ai->pprefix, pre)] = '\0';
-		else
-		    ai->pprefix[0] = '\0';
-	    } else
-		ai->pprefix = dupstring(pre ? pre : "");
-
+		    pre = dupstring(pre);
+		if (suf)
+		    suf = dupstring(suf);
+		if (!prpre && (prpre = ppre)) {
+		    singsub(&prpre);
+		    untokenize(prpre);
+		} else
+		    prpre = dupstring(prpre);
+		/* Select the group in which to store the matches. */
+		if (group) {
+		    endcmgroup(NULL);
+		    begcmgroup(group, (aflags & CAF_NOSORT));
+		    if (aflags & CAF_NOSORT)
+			mgroup->flags |= CGF_NOSORT;
+		} else {
+		    endcmgroup(NULL);
+		    begcmgroup("default", 0);
+		}
+		/* Select the set of matches. */
+		if (aflags & CAF_ALT) {
+		    l = fmatches;
+		    ai = fainfo;
+		} else {
+		    l = matches;
+		    ai = ainfo;
+		}
+		if (remf) {
+		    remf = dupstring(remf);
+		    rems = NULL;
+		} else if (rems)
+		    rems = dupstring(rems);
+		/* Build the common -P prefix. */
+		if (ai->pprefix) {
+		    if (pre)
+			ai->pprefix[pfxlen(ai->pprefix, pre)] = '\0';
+		    else
+			ai->pprefix[0] = '\0';
+		} else
+		    ai->pprefix = dupstring(pre ? pre : "");
+	    }
 	    /* Walk through the matches given. */
 	    for (; (s = dupstring(*argv)); argv++) {
-		sl = strlen(s);
+		sl = pl = strlen(s);
 		lc = NULL;
 		ms = NULL;
 		bpl = brpl;
@@ -4042,19 +4161,26 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		}
 		if (aflags & CAF_MATCH) {
 		    /* Do the matching. */
-		    test = (sl >= llpl + llsl &&
-			    strpfx(lpre, s) && strsfx(lsuf, s));
-		    if (!test && mstack &&
-			(ms = comp_match(lpre, lsuf, s,
-					 &lc, (aflags & CAF_QUOTE),
-					 &bpl, &bsl)))
-			test = 1;
-
-		    if (!test)
-			continue;
-		    pl = sl - llsl;
-		    me = s + sl - llsl;
-		    e = s + llpl;
+		    if (cp) {
+			if ((test = domatch(s, cp, 0)))
+			    e = me = s + sl;
+			else
+			    continue;
+		    } else {
+			test = (sl >= llpl + llsl &&
+				strpfx(lpre, s) && strsfx(lsuf, s));
+			if (!test && mstack &&
+			    (ms = comp_match(lpre, lsuf, s,
+					     &lc, (aflags & CAF_QUOTE),
+					     &bpl, &bsl)))
+			    test = 1;
+
+			if (!test)
+			    continue;
+			pl = sl - llsl;
+			me = s + sl - llsl;
+			e = s + llpl;
+		    }
 		} else {
 		    e = s;
 		    me = s + sl;
@@ -4072,14 +4198,15 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		if (!ms) {
 		    if (sl < ai->minlen)
 			ai->minlen = sl;
-		    if (!mstack && ai->firstm &&
+		    if (!cp && !mstack && ai->firstm &&
 			(i = sfxlen(ai->firstm->str, s)) < ai->suflen)
 			ai->suflen = i;
 		}
 		t = s;
 		if (ppre)
 		    t = dyncat(ppre, t);
-		if (!ms && mstack) {
+		lc = NULL;
+		if (!cp && !ms && (mstack || psuf)) {
 		    int bl = ((aflags & CAF_MATCH) ? llpl : 0);
 		    Cline *clp = &lc, tlc;
 		    char *ss = dupstring(s), *ee = me + (ss - s);
@@ -4109,7 +4236,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 			clp = &(tlc->next);
 		    }
 		    *clp = NULL;
-		} else if (mstack) {
+		} else if (!cp && mstack) {
 		    Cline tlc;
 
 		    if (ppre && *ppre) {
@@ -4148,7 +4275,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		    lc = tlc;
 		} else
 		    ai->iprefix = "";
-		if (!ms && !mstack) {
+		if (!ms && !mstack && !lc) {
 		    if ((aflags & CAF_MATCH) || ai->cpl > pl)
 			ai->cpl = pl;
 		    if ((aflags & CAF_MATCH) || ai->csl > lsl)
@@ -4164,12 +4291,12 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		ai->count++;
 
 		/* Finally add the match. */
-		cm = (Cmatch) halloc(sizeof(struct cmatch));
+		cm = (Cmatch) zhalloc(sizeof(struct cmatch));
 		cm->ppre = ppre;
 		cm->psuf = psuf;
 		cm->prpre = prpre;
 		cm->str = (ms ? ms : dupstring(s));
-		cm->ipre = cm->ripre = ipre;
+		cm->ipre = cm->ripre = (ipre && *ipre ? ipre : NULL);
 		cm->pre = pre;
 		cm->suf = suf;
 		cm->flags = flags;
@@ -4179,7 +4306,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		cm->rems = rems;
 		addlinknode(l, cm);
 
-		if (expl) {
+		if (exp) {
 		    if (l == matches)
 			expl->count++;
 		    else
@@ -4188,12 +4315,24 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		if (!ms) {
 		    if (!ai->firstm)
 			ai->firstm = cm;
-		    if ((aflags & CAF_MATCH) && !(e - (s + pl))) {
-			if (!ai->exact)
+		    if (!cp && (aflags & CAF_MATCH) && !(e - (s + pl))) {
+			if (!ai->exact) {
 			    ai->exact = 1;
-			else {
+			    zsfree(compexactstr);
+			    compexactstr = e = (char *) zalloc(lpl + sl + lsl + 1);
+			    if (ppre) {
+				strcpy(e, ppre);
+				e += lpl;
+			    }
+			    strcpy(e, s);
+			    e += sl;
+			    if (psuf)
+				strcpy(e, psuf);
+			    comp_setunsetptr(CP_EXACTSTR, 0);
+			} else {
 			    ai->exact = 2;
 			    cm = NULL;
+			    comp_setunsetptr(0, CP_EXACTSTR);
 			}
 			ai->exactm = cm;
 		    }
@@ -4208,6 +4347,8 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
     /* We switched back to the current heap, now restore the stack of
      * matchers. */
     mstack = oms;
+
+    return (mnum == nm);
 }
 
 /* This adds a match to the list of matches.  The string to add is given   *
@@ -4252,6 +4393,7 @@ addmatch(char *s, char *t)
 
     if (incompfunc)
 	s = dupstring(s);
+    e = s + sl;
     if (!addwhat) {
 	test = 1;
     } else if (addwhat == -1 || addwhat == -5 || addwhat == -6 ||
@@ -4318,7 +4460,7 @@ addmatch(char *s, char *t)
 		e += s - t;
 	    }
 	    if (cc) {
-		tt = (char *)halloc(lppl + lpsl + sl + 1);
+		tt = (char *)zhalloc(lppl + lpsl + sl + 1);
 		tt[0] = '\0';
 		if (lppre)
 		    strcpy(tt, lppre);
@@ -4353,7 +4495,7 @@ addmatch(char *s, char *t)
 		 (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) ||
 		  ((addwhat & CC_EXCMDS)  && !(hn->flags & DISABLED)))) ||
 		((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) {
-	if (sl >= rpl + rsl || mstack) {
+	if (sl >= rpl + rsl || mstack || cp) {
 	    if (cp) {
 		test = domatch(s, patcomp, 0);
 		e = s + sl;
@@ -4413,7 +4555,7 @@ addmatch(char *s, char *t)
     t = s;
     if (lppre)
 	t = dyncat(lppre, t);
-    if (!ms && mstack) {
+    if (!ispattern && !ms && mstack) {
 	Cline *clp = &lc, tlc;
 	char *ss = dupstring(s), *ee = e + (ss - s);
 
@@ -4481,7 +4623,7 @@ addmatch(char *s, char *t)
     ai->count++;
 
     /* Allocate and fill the match structure. */
-    cm = (Cmatch) halloc(sizeof(struct cmatch));
+    cm = (Cmatch) zhalloc(sizeof(struct cmatch));
     if (ispattern) {
 	if (lpsuf && *lpsuf && strsfx(lpsuf, s)) {
 	    s[sl - lpsl] = '\0';
@@ -4532,11 +4674,29 @@ addmatch(char *s, char *t)
 
 	/* Do we have an exact match? More than one? */
 	if (!ispattern && !(e - (s + pl))) {
-	    if (!ai->exact)
+	    if (!ai->exact) {
 		ai->exact = 1;
-	    else {
+		if (incompfunc) {
+		    int lpl = (cm->ppre ? strlen(cm->ppre) : 0);
+		    int lsl = (cm->psuf ? strlen(cm->psuf) : 0);
+
+		    zsfree(compexactstr);
+		    compexactstr = e = (char *) zalloc(lpl + sl + lsl + 1);
+		    if (cm->ppre) {
+			strcpy(e, cm->ppre);
+			e += lpl;
+		    }
+		    strcpy(e, s);
+		    e += sl;
+		    if (cm->psuf)
+			strcpy(e, cm->psuf);
+		    comp_setunsetptr(CP_EXACTSTR, 0);
+		}
+	    } else {
 		ai->exact = 2;
 		cm = NULL;
+		if (incompfunc)
+		    comp_setunsetptr(0, CP_EXACTSTR);
 	    }
 	    ai->exactm = cm;
 	}
@@ -4688,7 +4848,6 @@ dumphashtable(HashTable ht, int 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. */
@@ -4797,7 +4956,7 @@ gen_matches_files(int dirs, int execs, int all)
 			strcpy(p + o, psuf);
 
 			/* Do we have to use globbing? */
-			if (ispattern || (ns && isset(GLOBCOMPLETE))) {
+			if (ispattern || (ns && comppatmatch && *comppatmatch)) {
 			    /* Yes, so append a `*' if needed. */
 			    if (ns) {
 				int tl = strlen(p);
@@ -4847,6 +5006,15 @@ docompletion(char *s, int lst, int incmd)
 	ainfo = fainfo = NULL;
 	matchers = newlinklist();
 
+	useline = (lst != COMP_LIST_COMPLETE);
+	useexact = (isset(RECEXACT) && usemenu != 1);
+	uselist = (useline ?
+		   ((isset(AUTOLIST) && !isset(BASHAUTOLIST)) ? 
+		    (isset(LISTAMBIGUOUS) ? 3 : 2) : 0) : 1);
+	zsfree(comppatmatch);
+	comppatmatch = ztrdup(useglob ? "yes" : "");
+	haspattern = 0;
+
 	/* Make sure we have the completion list and compctl. */
 	if (makecomplist(s, incmd, lst)) {
 	    /* Error condition: feeeeeeeeeeeeep(). */
@@ -4854,23 +5022,26 @@ docompletion(char *s, int lst, int incmd)
 	    clearlist = 1;
 	    goto compend;
 	}
-	if (lst == COMP_LIST_COMPLETE)
+	if (!useline && uselist)
 	    /* All this and the guy only wants to see the list, sigh. */
 	    showinglist = -2;
-	else {
+	else if (useline) {
 	    /* We have matches. */
 	    if (nmatches > 1)
-		/* There are more than one match. */
+		/* There is more than one match. */
 		do_ambiguous();
 
 	    else if (nmatches == 1) {
 		/* Only one match. */
-		while (!amatches->mcount)
-		    amatches = amatches->next;
-		do_single(amatches->matches[0]);
+		Cmgroup m = amatches;
+
+		while (!m->mcount)
+		    m = m->next;
+		do_single(m->matches[0]);
 		invalidatelist();
 	    }
-	}
+	} else
+	    invalidatelist();
 
 	/* Print the explanation strings if needed. */
 	if (!showinglist && validlist && nmatches != 1) {
@@ -4928,33 +5099,47 @@ callcompfunc(char *s, char *fn)
     int lv = lastval;
     
     if ((list = getshfunc(fn)) != &dummy_list) {
-	LinkList args = newlinklist();
 	char **p, *tmp;
-	int aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext;
-	
-	addlinknode(args, fn);
-	
+	int set, aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext;
+	Param *ocpms = comppms;
+
+	comppms = (Param *) zalloc(CP_NUM * sizeof(Param));
+
+	set = -1 & ~(CP_PARAMETER | CP_REDIRECT | CP_QUOTE | CP_QUOTING |
+		     CP_EXACTSTR | (useglob ? 0 : CP_PATMATCH));
 	zsfree(compcontext);
-	zsfree(compcommand);
-	compcommand = "";
-	if (inwhat == IN_MATH) {
+	zsfree(compparameter);
+	zsfree(compredirect);
+	compparameter = compredirect = "";
+	if (ispar)
+	    compcontext = (ispar == 2 ? "brace_parameter" : "parameter");
+	else if (inwhat == IN_MATH) {
 	    if (insubscr) {
 		compcontext = "subscript";
-		compcommand = varname ? varname : "";
+		if (varname) {
+		    compparameter = varname;
+		    set |= CP_PARAMETER;
+		}
 	    } else
 		compcontext = "math";
 	    usea = 0;
-	} else if (lincmd)
-	    compcontext = (insubscr ? "subscript" : "command");
-	else if (linredir) {
+	} else if (lincmd) {
+	    if (insubscr) {
+		compcontext = "subscript";
+		set |= CP_PARAMETER;
+	    } else
+		compcontext = "command";
+	} else if (linredir) {
 	    compcontext = "redirect";
 	    if (rdstr)
-		compcommand = rdstr;
+		compredirect = rdstr;
+	    set |= CP_REDIRECT;
 	} else
 	    switch (inwhat) {
 	    case IN_ENV:
-		compcontext = "value";
-		compcommand = varname;
+		compcontext = "array_value";
+		compparameter = varname;
+		set |= CP_PARAMETER;
 		if (!clwpos) {
 		    clwpos = 1;
 		    zsfree(clwords[1]);
@@ -4966,61 +5151,141 @@ callcompfunc(char *s, char *fn)
 		compcontext = "condition";
 		break;
 	    default:
-		if (cmdstr) {
-		    compcontext = "argument";
-		    compcommand = cmdstr;
-		} else {
+		if (cmdstr)
+		    compcontext = "command";
+		else {
 		    compcontext = "value";
+		    set |= CP_PARAMETER;
 		    if (clwords[0])
-			compcommand = clwords[0];
+			compparameter = clwords[0];
+		    aadd = 1;
 		}
-		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);
+	if (compwords)
+	    freearray(compwords);
+	if (usea && (!aadd || clwords[0])) {
+	    char **q;
+
+	    PERMALLOC {
+		q = compwords = (char **)
+		    zalloc((clwnum - aadd + 1) * sizeof(char *));
+		for (p = clwords + aadd; *p; p++, q++) {
+		    tmp = dupstring(*p);
+		    untokenize(tmp);
+		    *q = ztrdup(tmp);
+		}
+		*q = NULL;
+	    } LASTALLOC;
+	} else
+	    compwords = (char **) zcalloc(sizeof(char *));
+
+	compparameter = ztrdup(compparameter);
+	compredirect = ztrdup(compredirect);
+	zsfree(compquote);
+	zsfree(compquoting);
+	if (instring) {
+	    if (instring == 1) {
+		compquote = ztrdup("\'");
+		compquoting = ztrdup("single");
+	    } else {
+		compquote = ztrdup("\"");
+		compquoting = ztrdup("double");
 	    }
+	    set |= CP_QUOTE | CP_QUOTING;
+	} else if (inbackt) {
+	    compquote = ztrdup("`");
+	    compquoting = ztrdup("backtick");
+	} else {
+	    compquote = ztrdup("");
+	    compquoting = ztrdup("");
+	}
+	untokenize(s = dupstring(s));
 	zsfree(compprefix);
 	zsfree(compsuffix);
 	if (unset(COMPLETEINWORD)) {
-	    tmp = quotename(s, NULL, NULL, NULL);
-	    untokenize(tmp);
-	    compprefix = ztrdup(tmp);
+	    compprefix = ztrdup(s);
 	    compsuffix = ztrdup("");
 	} else {
 	    char *ss = s + offs, sav;
-	    
-	    tmp = quotename(s, &ss, NULL, NULL);
+
 	    sav = *ss;
 	    *ss = '\0';
-	    untokenize(tmp);
-	    compprefix = ztrdup(tmp);
+	    compprefix = ztrdup(s);
 	    *ss = sav;
-	    untokenize(ss);
 	    compsuffix = ztrdup(ss);
 	}
 	zsfree(compiprefix);
 	compiprefix = ztrdup("");
-	compcurrent = (usea ? (clwpos + 1 - aadd) : 1);
+	compcurrent = (usea ? (clwpos + 1 - aadd) : 0);
 	compnmatches = mnum;
+
+	zsfree(complist);
+	switch (uselist) {
+	case 0: complist = ""; set &= ~CP_LIST; break;
+	case 1: complist = "list"; break;
+	case 2: complist = "autolist"; break;
+	case 3: complist = "ambiguous"; break;
+	}
+	complist = ztrdup(complist);
+	zsfree(compinsert);
+	if (useline) {
+	    switch (usemenu) {
+	    case 0: compinsert = "unambiguous"; break;
+	    case 1: compinsert = "menu"; break;
+	    case 2: compinsert = "automenu"; break;
+	    }
+	} else {
+	    compinsert = "";
+	    set &= ~CP_INSERT;
+	}
+	compinsert = ztrdup(compinsert);
+	if (useexact)
+	    compexact = ztrdup("accept");
+	else {
+	    compexact = ztrdup("");
+	    set &= ~CP_EXACT;
+	}
 	incompfunc = 1;
 	startparamscope();
 	makecompparamsptr();
+	comp_setunsetptr(set, ~set);
 	makezleparams(1);
 	sfcontext = SFC_CWIDGET;
 	NEWHEAPS(compheap) {
-	    doshfunc(fn, list, args, 0, 1);
+	    doshfunc(fn, list, NULL, 0, 1);
 	} OLDHEAPS;
 	sfcontext = osc;
 	endparamscope();
 	lastcmd = 0;
 	incompfunc = icf;
+
+	if (!complist)
+	    uselist = 0;
+	else if (!strcmp(complist, "list"))
+	    uselist = 1;
+	else if (!strcmp(complist, "auto") || !strcmp(complist, "autolist"))
+	    uselist = 2;
+	else if (!strcmp(complist, "ambig") || !strcmp(complist, "ambiguous"))
+	    uselist = 3;
+	else
+	    uselist = 0;
+	if (!compinsert)
+	    useline = 0;
+	else if (!strcmp(compinsert, "unambig") ||
+		 !strcmp(compinsert, "unambiguous"))
+	    useline = 1, usemenu = 0;
+	else if (!strcmp(compinsert, "menu"))
+	    useline = 1, usemenu = 1;
+	else if (!strcmp(compinsert, "auto") ||
+		 !strcmp(compinsert, "automenu"))
+	    useline = 1, usemenu = 2;
+	else
+	    useline = usemenu = 0;
+	useexact = (compexact && !strcmp(compexact, "accept"));
+
+	zfree(comppms, CP_NUM * sizeof(Param));
+	comppms = ocpms;
     }
     lastval = lv;
 }
@@ -5047,7 +5312,11 @@ makecomplist(char *s, int incmd, int lst)
 {
     struct cmlist ms;
     Cmlist m;
-    char *os = s;
+    char *p, *os = s;
+
+    /* Inside $... ? */
+    if ((p = check_param(s, 0)))
+	os = s = p;
 
     /* We build a copy of the list of matchers to use to make sure that this
      * works even if a shell function called from the completion code changes
@@ -5055,22 +5324,27 @@ makecomplist(char *s, int incmd, int lst)
 
     if ((m = cmatcher)) {
 	Cmlist mm, *mp = &mm;
+	int n;
 
-	for (; m; m = m->next) {
-	    *mp = (Cmlist) halloc(sizeof(struct cmlist));
+	for (n = 0; m; m = m->next, n++) {
+	    *mp = (Cmlist) zhalloc(sizeof(struct cmlist));
 	    (*mp)->matcher = m->matcher;
 	    (*mp)->next = NULL;
+	    (*mp)->str = dupstring(m->str);
 	    mp = &((*mp)->next);
 	    addlinknode(matchers, m->matcher);
 	    m->matcher->refc++;
 	}
 	m = mm;
-    }
-    compmatcher = 1;
+	compmatcher = 1;
+	compmatchertot = n;
+    } else
+	compmatcher = 0;
 
     /* Walk through the global matchers. */
     for (;;) {
 	bmatchers = NULL;
+	zsfree(compmatcherstr);
 	if (m) {
 	    ms.next = NULL;
 	    ms.matcher = m->matcher;
@@ -5080,9 +5354,11 @@ makecomplist(char *s, int incmd, int lst)
 	     * when building new parts for the string to insert into the 
 	     * line. */
 	    add_bmatchers(m->matcher);
-	} else
+	    compmatcherstr = ztrdup(m->str);
+	} else {
 	    mstack = NULL;
-
+	    compmatcherstr = ztrdup("");
+	}
 	ainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
 	fainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
 
@@ -5140,33 +5416,6 @@ makecomplist(char *s, int incmd, int lst)
 /* 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 == '$' || *p == '=' || *p == '{' || *p == '}') {
-		if (bslash)
-		    p[-1] = Bnull;
-		else
-		    *p = (*p == '$' ? String :
-			  (*p == '=' ? Equals :
-			   (*p == '{' ? Inbrace : Outbrace)));
-	    }
-	    bslash = 0;
-	}
-    }
-    return r;
-}
-
-/**/
 char *
 comp_str(int *ipl, int *pl, int untok)
 {
@@ -5177,17 +5426,17 @@ comp_str(int *ipl, int *pl, int untok)
     int lp, ls, lip;
 
     if (!untok) {
-	ctokenize(p);
+	tokenize(p);
 	remnulargs(p);
-	ctokenize(s);
+	tokenize(s);
 	remnulargs(s);
-	ctokenize(ip);
+	tokenize(ip);
 	remnulargs(ip);
     }
     ls = strlen(s);
     lip = strlen(ip);
     lp = strlen(p);
-    str = halloc(lip + lp + ls + 1);
+    str = zhalloc(lip + lp + ls + 1);
     strcpy(str, ip);
     strcat(str, p);
     strcat(str, s);
@@ -5201,9 +5450,11 @@ comp_str(int *ipl, int *pl, int untok)
 }
 
 /**/
-void
+int
 makecomplistcall(Compctl cc)
 {
+    int nm = mnum;
+
     SWITCHHEAPS(compheap) {
 	HEAPALLOC {
 	    int ooffs = offs, lip, lp;
@@ -5217,6 +5468,8 @@ makecomplistcall(Compctl cc)
 	    compnmatches = mnum;
 	} LASTALLOC;
     } SWITCHBACKHEAPS;
+
+    return (mnum == nm);
 }
 
 /* A simple counter to avoid endless recursion between old and new style *
@@ -5243,24 +5496,20 @@ makecomplistctl(int flags)
 	    char *os = cmdstr, **ow = clwords, **p, **q;
 	    int on = clwnum, op = clwpos;
 
-	    clwnum = arrlen(pparams) + 1;
+	    clwnum = arrlen(compwords);
 	    clwpos = compcurrent - 1;
-	    cmdstr = ztrdup(compcommand);
+	    cmdstr = ztrdup(compwords[0]);
 	    clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
-	    clwords[0] = ztrdup(cmdstr);
-	    for (p = pparams, q = clwords + 1; *p; p++, q++) {
+	    for (p = compwords, q = clwords; *p; p++, q++) {
 		t = dupstring(*p);
-		ctokenize(t);
+		tokenize(t);
 		remnulargs(t);
 		*q = ztrdup(t);
 	    }
 	    *q = NULL;
 	    offs = lip + lp;
 	    incompfunc = 2;
-	    ret = makecomplistglobal(str,
-				     (!clwpos &&
-				      !strcmp(compcontext, "command")),
-				     COMP_COMPLETE, flags);
+	    ret = makecomplistglobal(str, !clwpos, COMP_COMPLETE, flags);
 	    incompfunc = 1;
 	    offs = ooffs;
 	    compnmatches = mnum;
@@ -5499,7 +5748,7 @@ makecomplistext(Compctl occ, char *os, int incmd)
     Compcond or, cc;
     Comp comp;
     int compadd, m = 0, d = 0, t, tt, i, j, a, b;
-    char *sc, *s, *ss;
+    char *sc = NULL, *s, *ss;
 
     /* This loops over the patterns separated by `-'s. */
     for (compc = occ->ext; compc; compc = compc->next) {
@@ -5669,7 +5918,7 @@ findnode(LinkList list, void *dat)
 static void
 makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 {
-    int t, sf1, sf2, ooffs, um = usemenu, delit, ispar = 0;
+    int t, sf1, sf2, ooffs, um = usemenu, delit, oaw;
     char *p, *sd = NULL, *tt, *s1, *s2, *os =  dupstring(s);
     struct cmlist ms;
 
@@ -5678,6 +5927,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     if (incompfunc != 1 && findnode(ccstack, cc))
 	return;
 
+    MUSTUSEHEAP("complistflags");
+
     addlinknode(ccstack, cc);
 
     if (incompfunc != 1 && allccs) {
@@ -5711,7 +5962,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     if (cc->mask2 & CC_NOSORT)
 	mgroup->flags |= CGF_NOSORT;
     if (cc->explain) {
-	expl = (Cexpl) halloc(sizeof(struct cexpl));
+	expl = (Cexpl) zhalloc(sizeof(struct cexpl));
 	expl->count = expl->fcount = 0;
     }
     else
@@ -5781,79 +6032,16 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    s[suffixll - sl] = '\0';
     }
     /* Do we have one of the special characters `~' and `=' at the beginning? */
-    if ((ic = *s) != Tilde && ic != Equals)
+    if (incompfunc || ((ic = *s) != Tilde && ic != Equals))
 	ic = 0;
 
     /* Check if we have to complete a parameter name... */
-
-    /* Try to find a `$'. */
-    for (p = s + offs; p > s && *p != String; p--);
-    if (*p == String) {
-	/* Handle $$'s */
-	while (p > s && p[-1] == String)
-	    p--;
-	while (p[1] == String && p[2] == String)
-	    p += 2;
-    }
-    if (*p == String &&	p[1] != Inpar && p[1] != Inbrack) {
-	/* This is really a parameter expression (not $(...) or $[...]). */
-	char *b = p + 1, *e = b;
-	int n = 0, br = 1;
-
-	if (*b == Inbrace) {
-	    /* If this is a ${...}, ignore the possible (...) flags. */
-	    b++, br++;
-	    n = skipparens(Inpar, Outpar, &b);
-	}
-
-	/* Ignore the stuff before the parameter name. */
-	for (; *b; b++)
-	    if (*b != '^' && *b != Hat &&
-		*b != '=' && *b != Equals &&
-		*b != '~' && *b != Tilde)
-		break;
-	if (*b == '#' || *b == Pound || *b == '+')
-	    b++;
-
-	e = b;
-	/* Find the end of the name. */
-	if (*e == Quest || *e == Star || *e == String || *e == Qstring ||
-	    *e == '?'   || *e == '*'  || *e == '$'    ||
-	    *e == '-'   || *e == '!'  || *e == '@')
-	    e++;
-	else if (idigit(*e))
-	    while (idigit(*e))
-		e++;
-	else if (iident(*e))
-	    while (iident(*e) ||
-		   (useglob && (*e == Star || *e == Quest)))
-		e++;
-
-	/* Now make sure that the cursor is inside the name. */
-	if (offs <= e - s && offs >= b - s && n <= 0) {
-	    /* It is. */
-	    if (br >= 2)
-		mflags |= CMF_PARBR;
-
-	    /* Get the prefix (anything up to the character before the name). */
-	    lpsuf = dupstring(quotename(e, NULL, NULL, NULL));
-	    *e = '\0';
-	    lpsl = strlen(lpsuf);
-	    ripre = dupstring(s);
-	    ripre[b - s] = '\0';
-	    ipre = dupstring(quotename(ripre, NULL, NULL, NULL));
-	    untokenize(ipre);
-	    ispar = 1;
-	    /* And adjust wb, we, and offs again. */
-	    offs -= b - s;
-	    wb = cs - offs;
-	    we = wb + e - b;
-	    s = b;
-	    /* And now make sure that we complete parameter names. */
-	    cc = &cc_dummy;
-	    cc_dummy.refc = 10000;
-	    cc_dummy.mask = CC_PARAMS | CC_ENVVARS;
-	}
+    if (!incompfunc && (p = check_param(s, 1))) {
+	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. */
@@ -5861,12 +6049,13 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	delit = 1;
 	*s = '\0';
 	offs = 0;
-	if (isset(AUTOMENU)) usemenu = 1;
+	if (isset(AUTOMENU))
+	    usemenu = 1;
     }
 
     /* Compute line prefix/suffix. */
     lpl = offs;
-    lpre = halloc(lpl + 1);
+    lpre = zhalloc(lpl + 1);
     memcpy(lpre, s, lpl);
     lpre[lpl] = '\0';
     lsuf = dupstring(s + offs);
@@ -5905,6 +6094,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    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;
@@ -5917,7 +6107,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 
     /* But if we were asked not to do glob completion, we never treat the *
      * thing as a pattern.                                                */
-    if (!useglob)
+    if (!comppatmatch || !*comppatmatch)
 	ispattern = 0;
 
     if (ispattern) {
@@ -5930,6 +6120,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	} else
 	    strcpy(p + rpl, rsuf);
 	patcomp = parsereg(p);
+	haspattern = 1;
     }
     if (!patcomp) {
 	untokenize(rpre);
@@ -5963,6 +6154,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    line[cs] = 0;
 	    lppre = dupstring((char *) (line + wb));
 	    line[cs] = save;
+	    if (brbeg && *brbeg)
+		strcpy(lppre + brpl, lppre + brpl + strlen(brbeg));
 	    if ((p = strrchr(lppre, '/'))) {
 		p[1] = '\0';
 		lppl = strlen(lppre);
@@ -5979,8 +6172,14 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    char save = line[we];
 
 	    line[we] = 0;
-	    lpsuf = strchr(dupstring((char *) (line + cs)), '/');
+	    lpsuf = dupstring((char *) (line + cs));
 	    line[we] = save;
+	    if (brend && *brend) {
+		char *p = lpsuf + brsl - (cs - wb);
+
+		strcpy(p, p + strlen(brend));
+	    }
+	    lpsuf = strchr(lpsuf, '/');
 	    lpsl = (lpsuf ? strlen(lpsuf) : 0);
 	}
 	else {
@@ -5994,7 +6193,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	/* And the suffix. */
 	fsuf = dupstrpfx(rsuf, s2 - rsuf);
 
-	if (useglob && (ispattern & 2)) {
+	if (comppatmatch && *comppatmatch && (ispattern & 2)) {
 	    int t2;
 
 	    /* We have to use globbing, so compute the pattern from *
@@ -6079,10 +6278,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 
 		    if ((pp = get_user_var(cc->withd))) {
 			dirs = npp =
-			    (char**) halloc(sizeof(char *)*(arrlen(pp)+1));
+			    (char**) zhalloc(sizeof(char *)*(arrlen(pp)+1));
 			while (*pp) {
 			    pl = strlen(*pp);
-			    tp = (char *) halloc(strlen(*pp) + tl);
+			    tp = (char *) zhalloc(strlen(*pp) + tl);
 			    strcpy(tp, *pp);
 			    tp[pl] = '/';
 			    strcpy(tp + pl + 1, ppre);
@@ -6098,7 +6297,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 			char *tp;
 			int pl = strlen(cc->withd);
 
-			ta[0] = tp = (char *) halloc(strlen(ppre) + pl + 2);
+			ta[0] = tp = (char *) zhalloc(strlen(ppre) + pl + 2);
 			strcpy(tp, cc->withd);
 			tp[pl] = '/';
 			strcpy(tp + pl + 1, ppre);
@@ -6270,7 +6469,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	if (isset(AUTOCD) && isset(CDABLEVARS))
 	    dumphashtable(paramtab, -4);
     }
-    addwhat = (cc->mask & CC_QUOTEFLAG) ? -2 : CC_QUOTEFLAG;
+    oaw = addwhat = (cc->mask & CC_QUOTEFLAG) ? -2 : CC_QUOTEFLAG;
 
     if (cc->mask & CC_NAMED)
 	/* Add named directories. */
@@ -6278,12 +6477,16 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     if (cc->mask & CC_OPTIONS)
 	/* Add option names. */
 	dumphashtable(optiontab, addwhat);
-    if (cc->mask & CC_VARS)
+    if (cc->mask & CC_VARS) {
 	/* And parameter names. */
 	dumphashtable(paramtab, -9);
-    if (cc->mask & CC_BINDINGS)
+	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).        */
@@ -6293,54 +6496,53 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    while (*usr)
 		addmatch(*usr++, NULL);
     }
-    if (cc->mask & CC_USERS)
+    if (cc->mask & CC_USERS) {
 	/* Add user names. */
 	maketildelist();
+	addwhat = oaw;
+    }
+    if (cc->widget)
+	callcompfunc(os, cc->widget);
     if (cc->func) {
-	if (cc->func[0] == ' ')
-	    /* Temporary hack for access to new style completione. */
-	    callcompfunc(os, cc->func + 1);
-	else {
-	    /* This handles the compctl -K flag. */
-	    List list;
-	    char **r;
-	    int lv = lastval;
+	/* 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);
+	/* Get the function. */
+	if ((list = getshfunc(cc->func)) != &dummy_list) {
+	    /* We have it, so build a argument list. */
+	    LinkList args = newlinklist();
+	    int osc = sfcontext;
 		
-		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);
-		}
+	    addlinknode(args, cc->func);
 		
-		/* 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);
+	    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);
 	    }
-	    lastval = lv;
+		
+	    /* 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. */
@@ -6378,7 +6580,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	 * get the words we have to expand.                        */
 	zleparse = 1;
 	lexsave();
-	tmpbuf = (char *)halloc(strlen(cc->str) + 5);
+	tmpbuf = (char *)zhalloc(strlen(cc->str) + 5);
 	sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */
 	inpush(tmpbuf, 0, NULL);
 	strinbeg();
@@ -6456,9 +6658,12 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     if ((t = cc->mask & CC_BUILTINS))
 	/* Add builtins. */
 	dumphashtable(builtintab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
-    if ((t = cc->mask & CC_EXTCMDS))
+    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)));
@@ -6486,7 +6691,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    for (ln = firstnode(matches); ln; ln = nextnode(ln)) {
 		m = (Cmatch) getdata(ln);
 		if (m->ppre) {
-		    char *p = (char *) halloc(strlen(m->ppre) + strlen(m->str) +
+		    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);
@@ -6654,7 +6859,7 @@ get_user_var(char *nam)
 
 	if ((val = getsparam(nam))) {
 	    arr = (char **)ncalloc(2*sizeof(char *));
-	    arr[0] = val;
+	    arr[0] = (incompfunc ? dupstring(val) : val);
 	    arr[1] = NULL;
 	}
 	return arr;
@@ -6763,7 +6968,10 @@ makearray(LinkList l, int s, int *np, int *nlp)
 	    if ((*ap)->flags & CMF_NOLIST)
 		nl++;
 	*cp = NULL;
-    }
+    } else
+	for (ap = rp; *ap; ap++)
+	    if ((*ap)->flags & CMF_NOLIST)
+		nl++;
     if (np)
 	*np = n;
     if (nlp)
@@ -6795,7 +7003,7 @@ begcmgroup(char *n, int nu)
 	    p = p->next;
 	}
     }
-    mgroup = (Cmgroup) halloc(sizeof(struct cmgroup));
+    mgroup = (Cmgroup) zhalloc(sizeof(struct cmgroup));
     mgroup->name = dupstring(n);
     mgroup->flags = mgroup->lcount = mgroup->mcount = 0;
     mgroup->matches = NULL;
@@ -6858,8 +7066,8 @@ dupmatch(Cmatch m)
     r->ppre = ztrdup(m->ppre);
     r->psuf = ztrdup(m->psuf);
     r->prpre = ztrdup(m->prpre);
-    r->pre = m->pre;
-    r->suf = m->suf;
+    r->pre = ztrdup(m->pre);
+    r->suf = ztrdup(m->suf);
     r->flags = m->flags;
     r->brpl = m->brpl;
     r->brsl = m->brsl;
@@ -6975,6 +7183,8 @@ freematch(Cmatch m)
     zsfree(m->ripre);
     zsfree(m->ppre);
     zsfree(m->psuf);
+    zsfree(m->pre);
+    zsfree(m->suf);
     zsfree(m->prpre);
     zsfree(m->rems);
     zsfree(m->remf);
@@ -7030,15 +7240,14 @@ freematches(void)
 static void
 do_ambiguous(void)
 {
-    int p = (usemenu || ispattern), atend = (cs == we);
+    int p = (usemenu || haspattern), atend = (cs == we);
 
     menucmp = 0;
 
     /* If we have to insert the first match, call do_single().  This is *
      * how REC_EXACT takes effect.  We effectively turn the ambiguous   *
      * completion into an unambiguous one.                              */
-    if (ainfo && ainfo->exact == 1 && isset(RECEXACT) && !(fromcomp & FC_LINE) &&
-	(usemenu == 0 || unset(AUTOMENU))) {
+    if (ainfo && ainfo->exact == 1 && useexact && !(fromcomp & FC_LINE)) {
 	do_single(ainfo->exactm);
 	invalidatelist();
 	return;
@@ -7049,7 +7258,7 @@ do_ambiguous(void)
      * unambiguous prefix.                                               */
     lastambig = 1;
 
-    if(p) {
+    if (p) {
 	/* p is set if we are in a position to start using menu completion *
 	 * due to one of the menu completion options, or due to the        *
 	 * menu-complete-word command, or due to using GLOB_COMPLETE which *
@@ -7152,7 +7361,7 @@ do_ambiguous(void)
 	 * prefix was inserted, return now, bypassing the list-displaying  *
 	 * code.  On the way, invalidate the list and note that we don't   *
 	 * want to enter an AUTO_MENU imediately.                          */
-	if(isset(LISTAMBIGUOUS) && la) {
+	if (uselist == 3 && la) {
 	    int fc = fromcomp;
 
 	    invalidatelist();
@@ -7165,8 +7374,7 @@ do_ambiguous(void)
      * if it is needed.                                                     */
     if (isset(LISTBEEP))
 	feep();
-    if (isset(AUTOLIST) && !isset(BASHAUTOLIST) && !amenu && !showinglist &&
-	smatches >= 2)
+    if (uselist && usemenu != 2 && !showinglist && smatches >= 2)
 	showinglist = -2;
 }
 
@@ -7264,7 +7472,7 @@ do_single(Cmatch m)
 	    struct stat buf;
 
 	    /* Build the path name. */
-	    if (ispattern || ic || m->ripre) {
+	    if (haspattern || ic || m->ripre) {
 		int ne = noerrs;
 
 		noerrs = 1;
@@ -7620,14 +7828,14 @@ listmatches(void)
 	char **pp = g->ylist;
 
 	if ((e = g->expls)) {
-	    if (pnl) {
-		putc('\n', shout);
-		pnl = 0;
-	    }
 	    while (*e) {
 		if ((*e)->count) {
+		    if (pnl) {
+			putc('\n', shout);
+			pnl = 0;
+		    }
 		    printfmt((*e)->str, (*e)->count, 1);
-		    putc('\n', shout);
+		    pnl = 1;
 		}
 		e++;
 	    }
@@ -7670,7 +7878,7 @@ listmatches(void)
 	    }
 	}
 	else if (g->lcount) {
-	    int n = g->lcount, nl = (n + ncols - 1) / ncols, nc = nl, i, j, a;
+	    int n = g->lcount, nl = (n + ncols - 1) / ncols, nc = nl, i, j, a = 0;
 	    Cmatch *q;
 
 	    if (n && pnl) {
@@ -7691,7 +7899,7 @@ listmatches(void)
 			struct stat buf;
 			char *pb;
 
-			pb = (char *) halloc((m->prpre ? strlen(m->prpre) : 0) +
+			pb = (char *) zhalloc((m->prpre ? strlen(m->prpre) : 0) +
 					     3 + strlen(m->str));
 			sprintf(pb, "%s%s", (m->prpre ? m->prpre : "./"),
 				m->str);
diff --git a/Src/Zle/zle_word.c b/Src/Zle/zle_word.c
index afd860066..f446d1769 100644
--- a/Src/Zle/zle_word.c
+++ b/Src/Zle/zle_word.c
@@ -465,7 +465,7 @@ transposewords(void)
 	    return;
 	}
 	for (p1 = p2; p1 && iword(line[p1 - 1]); p1--);
-	pp = temp = (char *)halloc(p4 - p1 + 1);
+	pp = temp = (char *)zhalloc(p4 - p1 + 1);
 	struncpy(&pp, (char *) line + p3, p4 - p3);
 	struncpy(&pp, (char *) line + p2, p3 - p2);
 	struncpy(&pp, (char *) line + p1, p2 - p1);
diff --git a/Src/builtin.c b/Src/builtin.c
index fc9b113ca..c042537f4 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -1072,12 +1072,14 @@ fixdir(char *src)
 	    *dest = '\0';
 	    return;
 	}
-	if (dest > d0 + 1 && src[0] == '.' && src[1] == '.' &&
+	if (src[0] == '.' && src[1] == '.' &&
 	  (src[2] == '\0' || src[2] == '/')) {
-	    /* remove a foo/.. combination */
-	    for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--);
-	    if (dest[-1] != '/')
-		dest--;
+	    if (dest > d0 + 1) {
+		/* remove a foo/.. combination */
+		for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--);
+		if (dest[-1] != '/')
+		    dest--;
+	    }
 	    src++;
 	    while (*++src == '/');
 	} else if (src[0] == '.' && (src[1] == '/' || src[1] == '\0')) {
diff --git a/Src/compat.c b/Src/compat.c
index b1bcbc21b..53ab6b7a3 100644
--- a/Src/compat.c
+++ b/Src/compat.c
@@ -115,7 +115,7 @@ zgetdir(struct dirsav *d)
     struct stat sbuf;
     ino_t pino;
     dev_t pdev;
-#ifndef __CYGWIN__
+#if !defined(__CYGWIN__) && !defined(USE_GETCWD)
     struct dirent *de;
     DIR *dir;
     dev_t dev;
@@ -123,7 +123,7 @@ zgetdir(struct dirsav *d)
     int len;
 #endif
 
-    buf = halloc(bufsiz = PATH_MAX);
+    buf = zhalloc(bufsiz = PATH_MAX);
     pos = bufsiz - 1;
     buf[pos] = '\0';
     strcpy(nbuf, "../");
@@ -142,7 +142,7 @@ zgetdir(struct dirsav *d)
 #ifdef HAVE_FCHDIR
     else
 #endif
-#ifndef __CYGWIN__
+#if !defined(__CYGWIN__) && !defined(USE_GETCWD)
 	holdintr();
 
     for (;;) {
@@ -202,7 +202,7 @@ zgetdir(struct dirsav *d)
 	len = strlen(nbuf + 2);
 	pos -= len;
 	while (pos <= 1) {
-	    char *newbuf = halloc(2*bufsiz);
+	    char *newbuf = zhalloc(2*bufsiz);
 	    memcpy(newbuf + bufsiz, buf, bufsiz);
 	    buf = newbuf;
 	    pos += bufsiz;
@@ -228,7 +228,7 @@ zgetdir(struct dirsav *d)
 	zchdir(buf + pos + 1);
     noholdintr();
 
-#else  /* __CYGWIN__ case */
+#else  /* __CYGWIN__, USE_GETCWD cases */
 
     if (!getcwd(buf, bufsiz)) {
 	if (d) {
diff --git a/Src/glob.c b/Src/glob.c
index 47fa63567..738753377 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -2177,7 +2177,7 @@ get_match_ret(char *s, int b, int e, int fl, char *replstr)
     if (replstr) {
 	if ((fl & SUB_GLOBAL) && repllist) {
 	    /* We are replacing the chunk, just add this to the list */
-	    Repldata rd = (Repldata) halloc(sizeof(*rd));
+	    Repldata rd = (Repldata) zhalloc(sizeof(*rd));
 	    rd->b = b;
 	    rd->e = e;
 	    addlinknode(repllist, rd);
@@ -2481,7 +2481,7 @@ getmatch(char **sp, char *pat, int fl, int n, char *replstr)
 	    i = rd->e;		/* start of next chunk of *sp */
 	}
 	lleft += l - i;	/* final chunk from *sp */
-	start = t = halloc(lleft+1);
+	start = t = zhalloc(lleft+1);
 	i = 0;
 	for (nd = firstnode(repllist); nd; incnode(nd)) {
 	    rd = (Repldata) getdata(nd);
diff --git a/Src/mem.c b/Src/mem.c
index 703920215..ee0f5635f 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -34,7 +34,7 @@
 	There are two ways to allocate memory in zsh.  The first way is
 	to call zalloc/zcalloc, which call malloc/calloc directly.  It
 	is legal to call realloc() or free() on memory allocated this way.
-	The second way is to call halloc/hcalloc, which allocates memory
+	The second way is to call zhalloc/hcalloc, which allocates memory
 	from one of the memory pools on the heap stack.  Such memory pools 
 	will automatically created when the heap allocation routines are
 	called.  To be sure that they are freed at appropriate times
@@ -55,7 +55,7 @@
 	it will all be freed when the pool is destroyed.  In fact,
 	attempting to free this memory may result in a core dump.
 	The pair of pointers ncalloc and alloc may point to either
-	zalloc & zcalloc or halloc & hcalloc; permalloc() sets them to the
+	zalloc & zcalloc or zhalloc & hcalloc; permalloc() sets them to the
 	former, and heapalloc() sets them to the latter. This can be useful.
 	For example, the dupstruct() routine duplicates a syntax tree,
 	allocating the new memory for the tree using alloc().  If you want
@@ -78,7 +78,7 @@
 /**/
 int useheap;
 
-/* Current allocation pointers.  ncalloc() is either zalloc() or halloc(); *
+/* Current allocation pointers.  ncalloc() is either zalloc() or zhalloc(); *
  * alloc() is either zcalloc() or hcalloc().                               */
 
 /**/
@@ -110,7 +110,7 @@ global_heapalloc(void)
     int luh = useheap;
 
     alloc = hcalloc;
-    ncalloc = halloc;
+    ncalloc = zhalloc;
     useheap = 1;
     return luh;
 }
@@ -262,7 +262,7 @@ popheap(void)
 
 /**/
 void *
-halloc(size_t size)
+zhalloc(size_t size)
 {
     Heap h;
     size_t n;
@@ -329,7 +329,7 @@ hrealloc(char *p, size_t old, size_t new)
     if (old == new)
 	return p;
     if (!old && !p)
-	return halloc(new);
+	return zhalloc(new);
 
     /* find the heap with p */
 
@@ -343,7 +343,7 @@ hrealloc(char *p, size_t old, size_t new)
 
     if (p + old < arena(h) + h->used) {
 	if (new > old) {
-	    char *ptr = (char *) halloc(new);
+	    char *ptr = (char *) zhalloc(new);
 	    memcpy(ptr, p, old);
 #ifdef ZSH_MEM_DEBUG
 	    memset(p, 0xff, old);
@@ -380,7 +380,7 @@ hrealloc(char *p, size_t old, size_t new)
 	h->used += new - old;
 	return p;
     } else {
-	char *t = halloc(new);
+	char *t = zhalloc(new);
 	memcpy(t, p, old > new ? new : old);
 	h->used -= old;
 #ifdef ZSH_MEM_DEBUG
@@ -398,7 +398,7 @@ hcalloc(size_t size)
 {
     void *ptr;
 
-    ptr = halloc(size);
+    ptr = zhalloc(size);
     memset(ptr, 0, size);
     return ptr;
 }
diff --git a/Src/params.c b/Src/params.c
index e8182815e..eb50c0b7e 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -680,6 +680,7 @@ static long
 getarg(char **str, int *inv, Value v, int a2, long *w)
 {
     int num = 1, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash;
+    int beg = 0, hasbeg = 0;
     char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt;
     long r = 0;
     Comp c;
@@ -731,6 +732,18 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
 		*t = sav;
 		s = t;
 		break;
+	    case 'b':
+		hasbeg = 1;
+		t = get_strarg(++s);
+		if (!*t)
+		    goto flagerr;
+		sav = *t;
+		*t = '\0';
+		if ((beg = mathevalarg(s + 1, &d)) > 0)
+		    beg--;
+		*t = sav;
+		s = t;
+		break;
 	    case 'p':
 		escapes = 1;
 		break;
@@ -767,14 +780,15 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
     else if (v->isarr & SCANPM_WANTVALS)
 	*inv = 0;
     else {
-	if (ind) {
-	    v->isarr |= SCANPM_WANTKEYS;
-	    v->isarr &= ~SCANPM_WANTVALS;
-	} else if (rev) {
-	    v->isarr |= SCANPM_WANTVALS;
+	if (v->isarr) {
+	    if (ind) {
+		v->isarr |= SCANPM_WANTKEYS;
+		v->isarr &= ~SCANPM_WANTVALS;
+	    } else if (rev)
+		v->isarr |= SCANPM_WANTVALS;
+	    if (!down && ishash)
+		v->isarr &= ~SCANPM_MATCHMANY;
 	}
-	if (!down && ishash)
-	    v->isarr &= ~SCANPM_MATCHMANY;
 	*inv = ind;
     }
 
@@ -869,6 +883,8 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
 	tokenize(s);
 
 	if ((c = parsereg(s))) {
+	    int len;
+
 	    if (v->isarr) {
 		if (ishash) {
 		    scancomp = c;
@@ -887,28 +903,43 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
 		    ta = getarrvalue(v);
 		if (!ta || !*ta)
 		    return 0;
-		if (down)
-		    for (r = -1, p = ta + arrlen(ta) - 1; p >= ta; r--, p--) {
-			if (domatch(*p, c, 0) && !--num)
-			    return r;
-		} else
-		    for (r = 1, p = ta; *p; r++, p++)
-			if (domatch(*p, c, 0) && !--num)
-			    return r;
+		len = arrlen(ta);
+		if (beg < 0)
+		    beg += len;
+		if (beg >= 0 && beg < len) {
+		    if (down) {
+			if (!hasbeg)
+			    beg = len - 1;
+			for (r = 1 + beg, p = ta + beg; p >= ta; r--, p--) {
+			    if (domatch(*p, c, 0) && !--num)
+				return r;
+			}
+		    } else
+			for (r = 1 + beg, p = ta + beg; *p; r++, p++)
+			    if (domatch(*p, c, 0) && !--num)
+				return r;
+		}
 	    } else if (word) {
 		ta = sepsplit(d = s = getstrvalue(v), sep, 1);
-		if (down) {
-		    for (p = ta + (r = arrlen(ta)) - 1; p >= ta; p--, r--)
-			if (domatch(*p, c, 0) && !--num)
-			    break;
-		    if (p < ta)
-			return 0;
-		} else {
-		    for (r = 1, p = ta; *p; r++, p++)
-			if (domatch(*p, c, 0) && !--num)
-			    break;
-		    if (!*p)
-			return 0;
+		len = arrlen(ta);
+		if (beg < 0)
+		    beg += len;
+		if (beg >= 0 && beg < len) {
+		    if (down) {
+			if (!hasbeg)
+			    beg = len - 1;
+			for (r = 1 + beg, p = ta + beg; p >= ta; p--, r--)
+			    if (domatch(*p, c, 0) && !--num)
+				break;
+			if (p < ta)
+			    return 0;
+		    } else {
+			for (r = 1 + beg, p = ta + beg; *p; r++, p++)
+			    if (domatch(*p, c, 0) && !--num)
+				break;
+			if (!*p)
+			    return 0;
+		    }
 		}
 		if (a2)
 		    r++;
@@ -924,35 +955,46 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
 		d = getstrvalue(v);
 		if (!d || !*d)
 		    return 0;
-		if (a2) {
-		    if (down)
-			for (r = -2, t = d + strlen(d) - 1; t >= d; r--, t--) {
-			    sav = *t;
-			    *t = '\0';
-			    if (domatch(d, c, 0) && !--num) {
+		len = strlen(d);
+		if (beg < 0)
+		    beg += len;
+		if (beg >= 0 && beg < len) {
+		    if (a2) {
+			if (down) {
+			    if (!hasbeg)
+				beg = len - 1;
+			    for (r = beg, t = d + beg; t >= d; r--, t--) {
+				sav = *t;
+				*t = '\0';
+				if (domatch(d, c, 0) && !--num) {
+				    *t = sav;
+				    return r;
+				}
 				*t = sav;
-				return r;
 			    }
-			    *t = sav;
-		    } else
-			for (r = 0, t = d; *t; r++, t++) {
-			    sav = *t;
-			    *t = '\0';
-			    if (domatch(d, c, 0) && !--num) {
+			} else
+			    for (r = beg, t = d + beg; *t; r++, t++) {
+				sav = *t;
+				*t = '\0';
+				if (domatch(d, c, 0) && !--num) {
+				    *t = sav;
+				    return r;
+				}
 				*t = sav;
-				return r;
 			    }
-			    *t = sav;
-			}
-		} else {
-		    if (down)
-			for (r = -1, t = d + strlen(d) - 1; t >= d; r--, t--) {
-			    if (domatch(t, c, 0) && !--num)
-				return r;
-		    } else
-			for (r = 1, t = d; *t; r++, t++)
-			    if (domatch(t, c, 0) && !--num)
-				return r;
+		    } else {
+			if (down) {
+			    if (!hasbeg)
+				beg = len - 1;
+			    for (r = beg + 1, t = d + beg; t >= d; r--, t--) {
+				if (domatch(t, c, 0) && !--num)
+				    return r;
+			    }
+			} else
+			    for (r = beg + 1, t = d + beg; *t; r++, t++)
+				if (domatch(t, c, 0) && !--num)
+				    return r;
+		    }
 		}
 		return 0;
 	    }
@@ -1177,7 +1219,8 @@ getstrvalue(Value v)
 	    }
 	    LASTALLOC_RETURN s;
 	case PM_INTEGER:
-	    convbase(s = buf, v->pm->gets.ifn(v->pm), v->pm->ct);
+	    convbase(buf, v->pm->gets.ifn(v->pm), v->pm->ct);
+	    s = dupstring(buf);
 	    break;
 	case PM_SCALAR:
 	    s = v->pm->gets.cfn(v->pm);
diff --git a/Src/subst.c b/Src/subst.c
index 2e9b84718..6445776d1 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -529,7 +529,7 @@ dopadding(char *str, int prenum, int postnum, char *preone, char *postone, char
     if (lr == ls)
 	return str;
 
-    r = ret = (char *)halloc(lr + 1);
+    r = ret = (char *)zhalloc(lr + 1);
 
     if (prenum) {
 	if (postnum) {
@@ -1831,7 +1831,7 @@ modify(char **str, char **ptr)
 		    tc = *tt;
 		    *tt = '\0';
 		    nl = al + strlen(t) + strlen(copy);
-		    ptr1 = tmp = (char *)halloc(nl + 1);
+		    ptr1 = tmp = (char *)zhalloc(nl + 1);
 		    if (all)
 			for (ptr2 = all; *ptr2;)
 			    *ptr1++ = *ptr2++;
diff --git a/Src/system.h b/Src/system.h
index 650690b51..2babafa7a 100644
--- a/Src/system.h
+++ b/Src/system.h
@@ -51,7 +51,7 @@
 #endif
 
 #ifndef HAVE_ALLOCA
-# define alloca halloc
+# define alloca zhalloc
 #else
 # ifdef __GNUC__
 #  define alloca __builtin_alloca
diff --git a/Src/utils.c b/Src/utils.c
index 4ff4a91ba..d010df4e0 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -93,7 +93,7 @@ zerrnam(const char *cmd, const char *fmt, const char *str, int num)
 	    case 'l': {
 		char *s;
 		num = metalen(str, num);
-		s = halloc(num + 1);
+		s = zhalloc(num + 1);
 		memcpy(s, str, num);
 		s[num] = '\0';
 		nicezputs(s, stderr);
@@ -2807,7 +2807,7 @@ metafy(char *buf, int len, int heap)
 	    break;
 	case META_USEHEAP:
 	case META_HEAPDUP:
-	    buf = memcpy(halloc(len + meta + 1), buf, len);
+	    buf = memcpy(zhalloc(len + meta + 1), buf, len);
 	    break;
 	case META_STATIC:
 #ifdef DEBUG
@@ -3372,7 +3372,7 @@ getkeystring(char *s, int *len, int fromwhere, int *misc)
     int meta = 0, control = 0;
 
     if (fromwhere != 4)
-	buf = halloc(strlen(s) + 1);
+	buf = zhalloc(strlen(s) + 1);
     else {
 	buf = s;
 	s += 2;
diff --git a/Src/zsh.export b/Src/zsh.export
index d31e7902e..dcee683f6 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -5,6 +5,8 @@ addconddefs
 addedx
 addhashnode
 addwrapper
+arrvargetfn
+arrvarsetfn
 aliastab
 alloc_stackp
 appstr
@@ -69,6 +71,7 @@ getaparam
 gethashnode
 gethashnode2
 gethparam
+getintvalue
 getiparam
 getkeystring
 getlinknode
@@ -82,7 +85,6 @@ global_heapalloc
 global_permalloc
 globlist
 gotwordptr
-halloc
 hasam
 hashcmd
 hasher
@@ -135,6 +137,7 @@ ncalloc
 new_heaps
 newhashtable
 newlinklist
+newparamtable
 nicechar
 nicezputs
 niceztrdup
@@ -250,6 +253,7 @@ zexit
 zfree
 zgetdir
 zgetenv
+zhalloc
 zjoin
 zleactive
 zleparse