about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-06-27 05:33:04 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-06-27 05:33:04 +0000
commit346825df86466cf151be61b9429ef2c1734e66ea (patch)
treebd3e1ccd947b47f974c62b4113c5276416de2c9f /Src
parenta2159285e80508bb682d90a71270fbddada8bd05 (diff)
downloadzsh-346825df86466cf151be61b9429ef2c1734e66ea.tar.gz
zsh-346825df86466cf151be61b9429ef2c1734e66ea.tar.xz
zsh-346825df86466cf151be61b9429ef2c1734e66ea.zip
zsh-3.1.5-pws-24 zsh-3.1.5-pws-24
Diffstat (limited to 'Src')
-rw-r--r--Src/Modules/parameter.c2
-rw-r--r--Src/Zle/.distfiles1
-rw-r--r--Src/Zle/comp.h25
-rw-r--r--Src/Zle/compctl.c14
-rw-r--r--Src/Zle/complist.c918
-rw-r--r--Src/Zle/complist.mdd3
-rw-r--r--Src/Zle/deltochar.c7
-rw-r--r--Src/Zle/iwidgets.list1
-rw-r--r--Src/Zle/zle.export34
-rw-r--r--Src/Zle/zle.h25
-rw-r--r--Src/Zle/zle_hist.c467
-rw-r--r--Src/Zle/zle_keymap.c65
-rw-r--r--Src/Zle/zle_main.c79
-rw-r--r--Src/Zle/zle_misc.c241
-rw-r--r--Src/Zle/zle_move.c248
-rw-r--r--Src/Zle/zle_params.c22
-rw-r--r--Src/Zle/zle_refresh.c10
-rw-r--r--Src/Zle/zle_thingy.c111
-rw-r--r--Src/Zle/zle_tricky.c602
-rw-r--r--Src/Zle/zle_utils.c45
-rw-r--r--Src/Zle/zle_vi.c371
-rw-r--r--Src/Zle/zle_word.c194
-rw-r--r--Src/builtin.c144
-rw-r--r--Src/exec.c154
-rw-r--r--Src/glob.c2
-rw-r--r--Src/hashtable.c1
-rw-r--r--Src/hist.c9
-rw-r--r--Src/init.c32
-rw-r--r--Src/input.c35
-rw-r--r--Src/jobs.c9
-rw-r--r--Src/lex.c5
-rw-r--r--Src/loop.c21
-rw-r--r--Src/module.c254
-rw-r--r--Src/options.c1
-rw-r--r--Src/params.c9
-rw-r--r--Src/parse.c12
-rw-r--r--Src/signals.c159
-rw-r--r--Src/subst.c13
-rw-r--r--Src/utils.c49
-rw-r--r--Src/xmods.conf1
-rw-r--r--Src/zsh.export6
-rw-r--r--Src/zsh.h47
42 files changed, 3194 insertions, 1254 deletions
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index 4cb7b0aba..908a05ce9 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -326,7 +326,7 @@ setfunction(char *name, char *val)
     val = metafy(val, strlen(val), META_REALLOC);
 
     HEAPALLOC {
-	list = parse_string(val);
+	list = parse_string(val, 1);
     } LASTALLOC;
 
     if (!list || list == &dummy_list) {
diff --git a/Src/Zle/.distfiles b/Src/Zle/.distfiles
index 9dbf27758..add8f8466 100644
--- a/Src/Zle/.distfiles
+++ b/Src/Zle/.distfiles
@@ -2,6 +2,7 @@ DISTFILES_SRC='
     .cvsignore .distfiles .exrc
     comp1.mdd comp.h comp1.c comp1.export
     compctl.mdd compctl.c
+    complist.mdd complist.c
     deltochar.mdd deltochar.c
     zle.mdd iwidgets.list zle.h zle_bindings.c zle_hist.c
     zle_keymap.c zle_main.c zle_misc.c zle_move.c zle_params.c
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index bf3ea816f..8264890df 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -36,6 +36,7 @@ typedef struct patcomp   *Patcomp;
 typedef struct cmatcher  *Cmatcher;
 typedef struct cmlist    *Cmlist;
 typedef struct cpattern  *Cpattern;
+typedef struct menuinfo  *Menuinfo;
 
 /* node for compctl hash table (compctltab) */
 
@@ -266,6 +267,19 @@ struct cpattern {
 #define CFN_FIRST   1
 #define CFN_DEFAULT 2
 
+/* Information about menucompletion stuff. */
+
+struct menuinfo {
+    Cmgroup group;		/* position in the group list */
+    Cmatch *cur;		/* match currently inserted */
+    int pos;			/* begin on line */
+    int len;			/* length of inserted string */
+    int end;			/* end on the line */
+    int we;			/* non-zero if the cursor was at the end */
+    int insc;			/* length of suffix inserted */
+    int asked;			/* we asked if the list should be shown */
+};
+
 /* Flags for compadd and addmatches(). */
 
 #define CAF_QUOTE    1
@@ -286,6 +300,7 @@ struct cadata {
     char *pre;			/* prefix to insert (-P) */
     char *suf;			/* suffix to insert (-S) */
     char *group;		/* name of the group (-[JV]) */
+    char *ylist;		/* display list (-y) */
     char *rems;			/* remove suffix on chars... (-r) */
     char *remf;			/* function to remove suffix (-R) */
     char *ign;			/* ignored suffixes (-F) */
@@ -298,6 +313,16 @@ struct cadata {
     char *dpar;			/* array to delete non-matches in (-D) */
 };
 
+/* Data given to hooks. */
+
+typedef struct chdata *Chdata;
+
+struct chdata {
+    Cmgroup matches;		/* the matches generated */
+    int num;			/* the number of matches */
+    Cmatch cur;			/* current match or NULL */
+};
+
 /* Flags for special parameters. */
 
 #define CPN_WORDS      0
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index c5b24714e..2b426492e 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1727,7 +1727,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
     }
     dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre =
 	dat.pre = dat.suf = dat.group = dat.rems = dat.remf =
-	dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL;
+	dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = dat.ylist = NULL;
     dat.match = NULL;
     dat.flags = 0;
     dat.aflags = CAF_MATCH;
@@ -1779,6 +1779,10 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		sp = &(dat.group);
 		e = "group name expected after -%c";
 		break;
+	    case 'y':
+		sp = &(dat.ylist);
+		e = "string expected after -%c";
+		break;
 	    case 'i':
 		sp = &(dat.ipre);
 		e = "string expected after -%c";
@@ -2217,13 +2221,14 @@ static void
 addcompparams(struct compparam *cp, Param *pp)
 {
     for (; cp->name; cp++, pp++) {
-	Param pm = createparam(cp->name, cp->type | PM_SPECIAL | PM_REMOVABLE);
+	Param pm = createparam(cp->name,
+			       cp->type |PM_SPECIAL|PM_REMOVABLE|PM_LOCAL);
 	if (!pm)
 	    pm = (Param) paramtab->getnode(paramtab, cp->name);
 	DPUTS(!pm, "param not set in addcompparams");
 
 	*pp = pm;
-	pm->level = locallevel;
+	pm->level = locallevel + 1;
 	if ((pm->u.data = cp->var)) {
 	    switch(PM_TYPE(cp->type)) {
 	    case PM_SCALAR:
@@ -2257,7 +2262,8 @@ makecompparams(void)
 
     addcompparams(comprparams, comprpms);
 
-    if (!(cpm = createparam(COMPSTATENAME, PM_SPECIAL|PM_REMOVABLE|PM_HASHED)))
+    if (!(cpm = createparam(COMPSTATENAME,
+			    PM_SPECIAL|PM_REMOVABLE|PM_LOCAL|PM_HASHED)))
 	cpm = (Param) paramtab->getnode(paramtab, COMPSTATENAME);
     DPUTS(!cpm, "param not set in makecompparams");
 
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
new file mode 100644
index 000000000..63bc4c6e3
--- /dev/null
+++ b/Src/Zle/complist.c
@@ -0,0 +1,918 @@
+/*
+ * complist.c - completion listing enhancements
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1999 Sven Wischnowsky
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Sven Wischnowsky and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "complist.mdh"
+#include "complist.pro"
+
+
+/* We use the parameters ZLS_COLORS and ZLS_COLOURS in the same way as
+ * the color ls does. It's just that we don't support the `or' file
+ * type. */
+
+
+static Widget w_menuselect;
+static Keymap mskeymap;
+
+/* Indixes into the terminal string arrays. */
+
+#define COL_NO  0
+#define COL_FI  1
+#define COL_DI  2
+#define COL_LN  3
+#define COL_PI  4
+#define COL_SO  5
+#define COL_BD  6
+#define COL_CD  7
+#define COL_EX  8
+#define COL_MI  9
+#define COL_LC 10
+#define COL_RC 11
+#define COL_EC 12
+#define COL_MA 13
+
+#define NUM_COLS 14
+
+/* Names of the terminal strings. */
+
+static char *colnames[] = {
+    "no", "fi", "di", "ln", "pi", "so", "bd", "cd", "ex", "mi",
+    "lc", "rc", "ec", "ma", NULL
+};
+
+/* Default values. */
+
+static char *defcols[] = {
+    "0", "0", "1;34", "1;36", "33", "1;35", "1;33", "1;33", "1;32", NULL,
+    "\033[", "m", NULL, "7"
+};
+
+/* This describes a terminal string for a filename extension. */
+
+typedef struct extcol *Extcol;
+
+struct extcol {
+    char *ext;			/* the extension */
+    char *col;			/* the terminal color string */
+    Extcol next;		/* the next one in the list */
+};
+
+/* This holds all terminal strings. */
+
+typedef struct listcols *Listcols;
+
+struct listcols {
+    char *cols[NUM_COLS];	/* strings for file types */
+    Extcol exts;		/* strings for extensions */
+};
+
+/* This parses the value of a definition (the part after the `=').
+ * The return value is a pointer to the character after it. */
+
+static char *
+getcolval(char *s)
+{
+    char *p;
+
+    for (p = s; *s && *s != ':'; p++, s++) {
+	if (*s == '\\' && s[1]) {
+	    switch (*++s) {
+	    case 'a': *p = '\007'; break;
+	    case 'n': *p = '\n'; break;
+	    case 'b': *p = '\b'; break;
+	    case 't': *p = '\t'; break;
+	    case 'v': *p = '\v'; break;
+	    case 'f': *p = '\f'; break;
+	    case 'r': *p = '\r'; break;
+	    case 'e': *p = '\033'; break;
+	    case '_': *p = ' '; break;
+	    case '?': *p = '\177'; break;
+	    default:
+		if (*s >= '0' && *s <= '7') {
+		    int i = STOUC(*s);
+
+		    if (*++s >= '0' && *s <= '7') {
+			i = (i * 8) + STOUC(*s);
+			if (*++s >= '0' && *s <= '7')
+			    i = (i * 8) + STOUC(*s);
+		    }
+		    *p = (char) i;
+		} else
+		    *p = *s;
+	    }
+	} else if (*s == '^') {
+	    if ((s[1] >= '@' && s[1] <= '_') ||
+		(s[1] >= 'a' && s[1] <= 'z'))
+		*p = (char) (STOUC(*s) & ~0x60);
+	    else if (s[1] == '?')
+		*p = '\177';
+	    else {
+		*p++ = *s;
+		*p = s[1];
+	    }
+	    s++;
+	} else
+	    *p = *s;
+    }
+    if (p != s)
+	*p = '\0';
+    return s;
+}
+
+/* This parses one definition. Return value is a pointer to the
+ * character after it. */
+
+static char *
+getcoldef(Listcols c, char *s)
+{
+    if (*s == '*') {
+	Extcol ec;
+	char *n, *p;
+
+	/* This is for an extension. */
+
+	n = ++s;
+	while (*s && *s != '=')
+	    s++;
+	if (!*s )
+	    return s;
+	*s++ = '\0';
+	p = getcolval(s);
+	if (*n) {
+	    ec = (Extcol) zhalloc(sizeof(*ec));
+	    ec->ext = n;
+	    ec->col = s;
+	    ec->next = c->exts;
+	    c->exts = ec;
+	}
+	if (*p)
+	    *p++ = '\0';
+	return p;
+    } else {
+	char *n = s, *p, **nn;
+	int i;
+
+	/* This is for a file type. */
+
+	while (*s && *s != '=')
+	    s++;
+	if (!*s)
+	    return s;
+	*s++ = '\0';
+	for (i = 0, nn = colnames; *nn; i++, nn++)
+	    if (!strcmp(n ,*nn))
+		break;
+	p = getcolval(s);
+	if (*nn)
+	    c->cols[i] = s;
+	if (*p)
+	    *p++ = '\0';
+	return p;
+    }
+}
+
+/* This initializes the given terminal color structure. */
+
+static int
+getcols(Listcols c)
+{
+    char *s;
+    int i;
+
+    if (!(s = getsparam("ZLS_COLORS")) &&
+	!(s = getsparam("ZLS_COLOURS"))) {
+	if (!c)
+	    return 1;
+	for (i = 0; i < NUM_COLS; i++)
+	    c->cols[i] = "";
+	
+	c->exts = NULL;
+	return 1;
+    }
+    if (!c)
+	return 0;
+    /* We have one of the parameters, use it. */
+    memset(c, 0, sizeof(*c));
+    s = dupstring(s);
+    while (*s)
+	s = getcoldef(c, s);
+
+    /* Use default values for those that aren't set explicitly. */
+    for (i = 0; i < NUM_COLS; i++)
+	if (!c->cols[i])
+	    c->cols[i] = defcols[i];
+    /* Default for missing files. */
+    if (!c->cols[COL_MI])
+	c->cols[COL_MI] = c->cols[COL_FI];
+
+    if (!c->cols[COL_EC]) {
+	char *e = (char *) zhalloc(strlen(c->cols[COL_LC]) +
+				   strlen(c->cols[COL_NO]) +
+				   strlen(c->cols[COL_RC]) + 1);
+
+	/* If no `ec' was given, we is `<lc><no><rc>' as the default. */
+	strcpy(e, c->cols[COL_LC]);
+	strcat(e, c->cols[COL_NO]);
+	strcat(e, c->cols[COL_RC]);
+	c->cols[COL_EC] = e;
+    }
+    return 0;
+}
+
+/* Get the terminal color string for the file with the given name and
+ * file modes. */
+
+static char *
+getcolstr(Listcols c, char *n, mode_t m)
+{
+    Extcol e;
+
+    for (e = c->exts; e; e = e->next)
+	if (strsfx(e->ext, n))
+	    return e->col;
+
+    if (S_ISDIR(m))
+	return c->cols[COL_DI];
+    else if (S_ISLNK(m))
+	return c->cols[COL_LN];
+    else if (S_ISFIFO(m))
+	return c->cols[COL_PI];
+    else if (S_ISSOCK(m))
+	return c->cols[COL_SO];
+    else if (S_ISBLK(m))
+	return c->cols[COL_BD];
+    else if (S_ISCHR(m))
+	return c->cols[COL_CD];
+    else if (S_ISREG(m) && (m & S_IXUGO))
+	return c->cols[COL_EX];
+
+    return c->cols[COL_FI];
+}
+
+/* Information about the list shown. */
+
+static int noselect, mselect, inselect, mcol, mline, mcols, mlines;
+static Cmatch *mmatch, **mtab;
+static Cmgroup mgroup, *mgtab;
+
+/* List the matches. Most of this is just taken from ilistmatches(),
+ * of course. */
+
+static int
+complistmatches(Hookdef dummy, Chdata dat)
+{
+    Cmgroup amatches = dat->matches, g;
+    Cmatch *p, m;
+    Cexpl *e;
+    int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0, opl = 0;
+    int of = isset(LISTTYPES);
+    int mc, ml = 0, cc, hasm = 0;
+    struct listcols col;
+
+    if (minfo.asked == 2) {
+	showinglist = 0;
+	return (noselect = 1);
+    }
+    getcols(&col);
+
+    /* Set the cursor below the prompt. */
+    if (inselect)
+	clearflag = 0;
+    trashzle();
+    showinglist = listshown = 0;
+
+    clearflag = (isset(USEZLE) && !termflags &&
+		 complastprompt && *complastprompt);
+
+    for (g = amatches; g; g = g->next) {
+	char **pp = g->ylist;
+	int nl = 0, l;
+
+	if (pp) {
+	    /* We have an ylist, lets see, if it contains newlines. */
+	    while (!nl && *pp)
+		nl = !!strchr(*pp++, '\n');
+
+	    pp = g->ylist;
+	    if (nl) {
+		/* Yup, there are newlines, count lines. */
+		char *nlptr, *sptr;
+
+		g->flags |= CGF_LINES;
+		noselect = 1;
+		while ((sptr = *pp)) {
+		    while (sptr && *sptr) {
+			nlines += (nlptr = strchr(sptr, '\n'))
+			    ? 1 + (nlptr-sptr)/columns
+			    : strlen(sptr)/columns;
+			sptr = nlptr ? nlptr+1 : NULL;
+		    }
+		    nlines++;
+		    pp++;
+		}
+		nlines--;
+	    } else {
+		while (*pp) {
+		    if ((l = strlen(*pp)) > longest)
+			longest = l;
+		    nlist++;
+		    pp++;
+		}
+	    }
+	} else {
+	    for (p = g->matches; (m = *p); p++) {
+		if (!(m->flags & CMF_NOLIST)) {
+		    if ((l = niceztrlen(m->str)) > longest)
+			longest = l;
+		    nlist++;
+		} else
+		    noselect = 1;
+	    }
+	}
+	if ((e = g->expls)) {
+	    while (*e) {
+		if ((*e)->count)
+		    nlines += 1 + printfmt((*e)->str, (*e)->count, 0);
+		e++;
+	    }
+	}
+    }
+    longest += 2 + of;
+    if ((ncols = (columns + 1) / longest)) {
+	for (g = amatches; g; g = g->next)
+	    nlines += (g->lcount + ncols - 1) / ncols;
+    } else {
+	ncols = 1;
+	opl = 1;
+	for (g = amatches; g; g = g->next) {
+	    char **pp = g->ylist;
+
+	    if (pp) {
+		if (!(g->flags & CGF_LINES)) {
+		    while (*pp) {
+			nlines += 1 + (strlen(*pp) / columns);
+			pp++;
+		    }
+		}
+	    } else
+		for (p = g->matches; (m = *p); p++)
+		    if (!(m->flags & CMF_NOLIST))
+			nlines += 1 + ((1 + niceztrlen(m->str)) / columns);
+	}
+    }
+
+    /* Maybe we have to ask if the user wants to see the list. */
+    if ((!minfo.cur || !minfo.asked) &&
+	((complistmax && nlist > complistmax) ||
+	 (!complistmax && nlines >= lines))) {
+	int qup;
+	zsetterm();
+	qup = printfmt("zsh: do you wish to see all %n possibilities? ", nlist, 1);
+	fflush(shout);
+	if (getzlequery() != 'y') {
+	    if (clearflag) {
+		putc('\r', shout);
+		tcmultout(TCUP, TCMULTUP, qup);
+		if (tccan(TCCLEAREOD))
+		    tcout(TCCLEAREOD);
+		tcmultout(TCUP, TCMULTUP, nlnct);
+	    } else
+		putc('\n', shout);
+	    noselect = 1;
+	    if (minfo.cur)
+		minfo.asked = 2;
+	    return 1;
+	}
+	if (clearflag) {
+	    putc('\r', shout);
+	    tcmultout(TCUP, TCMULTUP, qup);
+	    if (tccan(TCCLEAREOD))
+		tcout(TCCLEAREOD);
+	} else
+	    putc('\n', shout);
+	settyinfo(&shttyinfo);
+	if (minfo.cur)
+	    minfo.asked = 1;
+    }
+    if (mselect >= 0) {
+	int i;
+
+	i = ncols * nlines;
+	free(mtab);
+	mtab = (Cmatch **) zalloc(i * sizeof(Cmatch **));
+	memset(mtab, 0, i * sizeof(Cmatch **));
+	free(mgtab);
+	mgtab = (Cmgroup *) zalloc(i * sizeof(Cmgroup));
+	memset(mgtab, 0, i * sizeof(Cmgroup));
+	mcols = ncols;
+	mlines = nlines;
+    }
+    /* Now print the matches. */
+    g = amatches;
+    while (g) {
+	char **pp = g->ylist;
+
+	if ((e = g->expls)) {
+	    while (*e) {
+		if ((*e)->count) {
+		    if (pnl) {
+			putc('\n', shout);
+			pnl = 0;
+			ml++;
+		    }
+		    ml += printfmt((*e)->str, (*e)->count, 1);
+		    pnl = 1;
+		}
+		e++;
+	    }
+	}
+	if (pp && *pp) {
+	    if (pnl) {
+		putc('\n', shout);
+		pnl = 0;
+		ml++;
+	    }
+	    if (g->flags & CGF_LINES) {
+		while (*pp) {
+		    zputs(*pp, shout);
+		    if (*++pp)
+			putc('\n', shout);
+		}
+	    } else {
+		int n = g->lcount, nl = (n + ncols - 1) / ncols, nc = nl, i, a;
+		char **pq;
+
+		while (n && nl--) {
+		    i = ncols;
+		    mc = 0;
+		    pq = pp;
+		    while (n && i--) {
+			if (pq - g->ylist >= g->lcount)
+			    break;
+			zputs(*pq, shout);
+			if (i) {
+			    a = longest - strlen(*pq);
+			    while (a--)
+				putc(' ', shout);
+			}
+			pq += nc;
+			n--;
+		    }
+		    if (n) {
+			putc('\n', shout);
+			ml++;
+		    }
+		    pp++;
+		}
+	    }
+	} else if (g->lcount) {
+	    int n = g->lcount, nl = (n + ncols - 1) / ncols, nc = nl, i, j, a = 0;
+	    int zt;
+	    Cmatch *q;
+
+	    if (n && pnl) {
+		putc('\n', shout);
+		pnl = 0;
+		ml++;
+	    }
+	    for (p = skipnolist(g->matches); n && nl--;) {
+		i = ncols;
+		mc = 0;
+		q = p;
+		while (n && i--) {
+		    fputs(col.cols[COL_LC], shout);
+		    if (!(m = *q)) {
+			fputs(col.cols[COL_MI], shout);
+			fputs(col.cols[COL_RC], shout);
+			a = longest - 2;
+			while (a--)
+			    putc(' ', shout);
+			fputs(col.cols[COL_EC], shout);
+			break;
+		    }
+		    hasm = 1;
+		    if (mselect >= 0) {
+			mtab[mc + (ncols * ml)] = q;
+			mgtab[mc + (ncols * ml)] = g;
+		    }
+		    if (m->gnum == mselect) {
+			mcol = mc;
+			mline = ml;
+			mmatch = q;
+			mgroup = g;
+			cc = COL_MA;
+		    } else
+			cc = -1;
+		    if (m->flags & CMF_FILE) {
+			struct stat buf;
+			char *pb;
+
+			pb = (char *) zhalloc((m->prpre ? strlen(m->prpre) : 0) +
+					     3 + strlen(m->str));
+			sprintf(pb, "%s%s", (m->prpre ? m->prpre : "./"),
+				m->str);
+
+			zt = ztat(pb, &buf, 1);
+			if (cc >= 0)
+			    fputs(col.cols[cc], shout);
+			else if (zt)
+			    fputs(col.cols[COL_NO], shout);
+			else
+			    fputs(getcolstr(&col, pb, buf.st_mode), shout);
+			fputs(col.cols[COL_RC], shout);
+			nicezputs(m->str, shout);
+			if (zt)
+			    putc(' ', shout);
+			else
+			    putc(file_type(buf.st_mode), shout);
+		    } else {
+			fputs(col.cols[cc >= 0 ? cc : COL_NO], shout);
+			fputs(col.cols[COL_RC], shout);
+			nicezputs(m->str, shout);
+			if (of)
+			    putc(' ', shout);
+		    }
+		    a = longest - niceztrlen(m->str) - 2 - of;
+		    while (a--)
+			putc(' ', shout);
+		    fputs(col.cols[COL_EC], shout);
+		    if (i) {
+			fputs(col.cols[COL_LC], shout);
+			fputs(col.cols[COL_NO], shout);
+			fputs(col.cols[COL_RC], shout);
+			fputs("  ", shout);
+			fputs(col.cols[COL_EC], shout);
+		    }
+		    if (--n)
+			for (j = nc; j && *q; j--)
+			    q = skipnolist(q + 1);
+		    mc++;
+		}
+		if (i > 0) {
+		    fputs(col.cols[COL_LC], shout);
+		    fputs(col.cols[COL_MI], shout);
+		    fputs(col.cols[COL_RC], shout);
+		    a = longest - 2;
+		    while (a--)
+			putc(' ', shout);
+		    fputs(col.cols[COL_EC], shout);
+		}
+		if (n) {
+		    putc('\n', shout);
+		    ml++;
+		    if (n && nl)
+			p = skipnolist(p + 1);
+		}
+	    }
+	}
+	if (g->lcount)
+	    pnl = 1;
+	g = g->next;
+    }
+
+    if (clearflag) {
+	/* Move the cursor up to the prompt, if always_last_prompt *
+	 * is set and all that...                                  */
+	if ((nlines += nlnct - 1) < lines) {
+	    tcmultout(TCUP, TCMULTUP, nlines);
+	    showinglist = -1;
+	    listshown = 1;
+	} else
+	    clearflag = 0, putc('\n', shout);
+    } else
+	putc('\n', shout);
+    if (!hasm || nlines >= lines)
+	noselect = 1;
+    return noselect;
+}
+
+typedef struct menustack *Menustack;
+
+struct menustack {
+    Menustack prev;
+    char *line;
+    int cs;
+    struct menuinfo info;
+};
+
+static int
+domenuselect(Hookdef dummy, Chdata dat)
+{
+    Cmatch **p;
+    Cmgroup *pg;
+    Thingy cmd;
+    Menustack u = NULL;
+    int i = 0;
+    char *s;
+
+    if (getcols(NULL) || (dummy && (!(s = getsparam("SELECTMIN")) ||
+				    (dat && dat->num < atoi(s)))))
+	return 1;
+
+    selectlocalmap(mskeymap);
+    noselect = 0;
+    mselect = (*(minfo.cur))->gnum;
+    for (;;) {
+	showinglist = -2;
+	zrefresh();
+	inselect = 1;
+	if (noselect)
+	    break;
+	if (!i) {
+	    i = mcols * mlines;
+	    while (i--)
+		if (mtab[i])
+		    break;
+	    if (!i)
+		break;
+	    i = 1;
+	}
+	p = mtab + mcol + (mline * mcols);
+	pg = mgtab + mcol + (mline * mcols);
+	minfo.cur = *p;
+	minfo.group = *pg;
+
+    getk:
+
+	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak) ||
+	    cmd == Th(z_acceptline))
+	    break;
+	else if (cmd == Th(z_acceptandhold) ||
+		 cmd == Th(z_acceptandmenucomplete)) {
+	    Menustack s = (Menustack) zhalloc(sizeof(*s));
+
+	    s->prev = u;
+	    u = s;
+	    s->line = dupstring((char *) line);
+	    s->cs = cs;
+	    memcpy(&(s->info), &minfo, sizeof(struct menuinfo));
+	    acceptlast();
+	    do_menucmp(0);
+	    mselect = (*(minfo.cur))->gnum;
+	    continue;
+	} else if (cmd == Th(z_undo)) {
+	    int l;
+
+	    if (!u)
+		goto getk;
+
+	    cs = 0;
+	    foredel(ll);
+	    spaceinline(l = strlen(u->line));
+	    strncpy((char *) line, u->line, l);
+	    cs = u->cs;
+	    memcpy(&minfo, &(u->info), sizeof(struct menuinfo));
+	    p = &(minfo.cur);
+	    u = u->prev;
+	} else if (cmd == Th(z_redisplay)) {
+	    redisplay(zlenoargs);
+	    continue;
+	} else if (cmd == Th(z_clearscreen)) {
+	    clearscreen(zlenoargs);
+	    continue;
+	} else if (cmd == Th(z_downhistory) ||
+		   cmd == Th(z_downlineorhistory) ||
+		   cmd == Th(z_downlineorsearch) ||
+		   cmd == Th(z_vidownlineorhistory)) {
+	    do {
+		if (mline == mlines - 1) {
+		    p -= mline * mcols;
+		    mline = 0;
+		} else {
+		    mline++;
+		    p += mcols;
+		}
+	    } while (!*p);
+	} else if (cmd == Th(z_uphistory) ||
+		   cmd == Th(z_uplineorhistory) ||
+		   cmd == Th(z_uplineorsearch) ||
+		   cmd == Th(z_viuplineorhistory)) {
+	    do {
+		if (!mline) {
+		    mline = mlines - 1;
+		    p += mline * mcols;
+		} else {
+		    mline--;
+		    p -= mcols;
+		}
+	    } while (!*p);
+	} else if (cmd == Th(z_forwardchar) || cmd == Th(z_viforwardchar)) {
+	    do {
+		if (mcol == mcols - 1) {
+		    p -= mcol;
+		    mcol = 0;
+		} else {
+		    mcol++;
+		    p++;
+		}
+	    } while (!*p);
+	} else if (cmd == Th(z_backwardchar) || cmd == Th(z_vibackwardchar)) {
+	    do {
+		if (!mcol) {
+		    mcol = mcols - 1;
+		    p += mcol;
+		} else {
+		    mcol--;
+		    p--;
+		}
+	    } while (!*p);
+	} else if (cmd == Th(z_beginningofbufferorhistory) ||
+		   cmd == Th(z_beginningofline) ||
+		   cmd == Th(z_beginningoflinehist) ||
+		   cmd == Th(z_vibeginningofline)) {
+	    p -= mcol;
+	    mcol = 0;
+	    while (!*p) {
+		mcol++;
+		p++;
+	    }
+	} else if (cmd == Th(z_endofbufferorhistory) ||
+		   cmd == Th(z_endofline) ||
+		   cmd == Th(z_endoflinehist) ||
+		   cmd == Th(z_viendofline)) {
+	    p += mcols - mcol - 1;
+	    mcol = mcols - 1;
+	    while (!*p) {
+		mcol--;
+		p--;
+	    }
+	} else if (cmd == Th(z_forwardword) ||
+		   cmd == Th(z_emacsforwardword) ||
+		   cmd == Th(z_viforwardword) ||
+		   cmd == Th(z_viforwardwordend)) {
+	    Cmgroup g = *pg;
+	    int ol = mline;
+
+	    do {
+		if (mline == mlines - 1) {
+		    p -= mline * mcols;
+		    pg -= mline * mcols;
+		    mline = 0;
+		} else {
+		    mline++;
+		    p += mcols;
+		    pg += mcols;
+		}
+	    } while (ol != mline && (*pg == g || !*pg));
+	} else if (cmd == Th(z_backwardword) ||
+		   cmd == Th(z_emacsbackwardword) ||
+		   cmd == Th(z_vibackwardword)) {
+	    Cmgroup g = *pg;
+	    int ol = mline;
+
+	    do {
+		if (!mline) {
+		    mline = mlines - 1;
+		    p += mline * mcols;
+		    pg += mline * mcols;
+		} else {
+		    mline--;
+		    p -= mcols;
+		    pg -= mcols;
+		}
+	    } while (ol != mline && (*pg == g || !*pg));
+	} else if (cmd == Th(z_completeword) ||
+		   cmd == Th(z_expandorcomplete) ||
+		   cmd == Th(z_expandorcompleteprefix) ||
+		   cmd == Th(z_menucomplete) ||
+		   cmd == Th(z_menuexpandorcomplete) ||
+		   !strcmp(cmd->nam, "menu-select") ||
+		   !strcmp(cmd->nam, "complete-word") ||
+		   !strcmp(cmd->nam, "expand-or-complete") ||
+		   !strcmp(cmd->nam, "expand-or-complete-prefix") ||
+		   !strcmp(cmd->nam, "menu-complete") ||
+		   !strcmp(cmd->nam, "menu-expand-or-complete")) {
+	    do_menucmp(0);
+	    mselect = (*(minfo.cur))->gnum;
+	    continue;
+	} else if (cmd == Th(z_reversemenucomplete) ||
+		   !strcmp(cmd->nam, "reverse-menu-complete")) {
+	    reversemenucomplete(zlenoargs);
+	    mselect = (*(minfo.cur))->gnum;
+	    continue;
+	} else {
+	    ungetkeycmd();
+	    break;
+	}
+	do_single(**p);
+	mselect = (**p)->gnum;
+    }
+    selectlocalmap(NULL);
+    mselect = -1;
+    inselect = 0;
+    if (!noselect) {
+	showinglist = -2;
+	zrefresh();
+    }
+    return noselect;
+}
+
+/* The widget function. */
+
+static int
+menuselect(char **args)
+{
+    int d = 0;
+
+    if (!minfo.cur) {
+	menucomplete(args);
+	if ((minfo.cur && minfo.asked == 2) || getsparam("ZLS_SELECT"))
+	    return 0;
+	d = 1;
+    }
+    if (minfo.cur && (minfo.asked == 2 || domenuselect(NULL, NULL)) && !d)
+	menucomplete(args);
+
+    return 0;
+}
+
+/**/
+int
+setup_complist(Module m)
+{
+    return 0;
+}
+
+/**/
+int
+boot_complist(Module m)
+{
+    mtab = NULL;
+    mgtab = NULL;
+    mselect = -1;
+    inselect = 0;
+
+    w_menuselect = addzlefunction("menu-select", menuselect,
+                                    ZLE_MENUCMP|ZLE_KEEPSUFFIX|ZLE_ISCOMP);
+    if (!w_menuselect) {
+	zwarnnam(m->nam, "name clash when adding ZLE function `menu-select'",
+		 NULL, 0);
+	return -1;
+    }
+    addhookfunc("list_matches", (Hookfn) complistmatches);
+    addhookfunc("menu_start", (Hookfn) domenuselect);
+    mskeymap = newkeymap(NULL, "menuselect");
+    linkkeymap(mskeymap, "menuselect", 1);
+    bindkey(mskeymap, "\t", refthingy(t_completeword), NULL);
+    bindkey(mskeymap, "\n", refthingy(t_acceptline), NULL);
+    bindkey(mskeymap, "\r", refthingy(t_acceptline), NULL);
+    bindkey(mskeymap, "\33[A",  refthingy(t_uplineorhistory), NULL);
+    bindkey(mskeymap, "\33[B",  refthingy(t_downlineorhistory), NULL);
+    bindkey(mskeymap, "\33[C",  refthingy(t_forwardchar), NULL);
+    bindkey(mskeymap, "\33[D",  refthingy(t_backwardchar), NULL);
+    bindkey(mskeymap, "\33OA",  refthingy(t_uplineorhistory), NULL);
+    bindkey(mskeymap, "\33OB",  refthingy(t_downlineorhistory), NULL);
+    bindkey(mskeymap, "\33OC",  refthingy(t_forwardchar), NULL);
+    bindkey(mskeymap, "\33OD",  refthingy(t_backwardchar), NULL);
+    return 0;
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_complist(Module m)
+{
+    free(mtab);
+    free(mgtab);
+
+    deletezlefunction(w_menuselect);
+    deletehookfunc("list_matches", (Hookfn) complistmatches);
+    deletehookfunc("menu_start", (Hookfn) domenuselect);
+    unlinkkeymap("menuselect", 1);
+    return 0;
+}
+
+/**/
+int
+finish_complist(Module m)
+{
+    return 0;
+}
+
+#endif
diff --git a/Src/Zle/complist.mdd b/Src/Zle/complist.mdd
new file mode 100644
index 000000000..8ea60b0a8
--- /dev/null
+++ b/Src/Zle/complist.mdd
@@ -0,0 +1,3 @@
+moddeps="comp1 zle"
+
+objects="complist.o"
diff --git a/Src/Zle/deltochar.c b/Src/Zle/deltochar.c
index 66a301119..f0df33f95 100644
--- a/Src/Zle/deltochar.c
+++ b/Src/Zle/deltochar.c
@@ -33,8 +33,8 @@
 static Widget w_deletetochar;
 
 /**/
-static void
-deltochar(void)
+static int
+deltochar(char **args)
 {
     int c = getkey(0), dest = cs, ok = 0, n = zmult;
 
@@ -67,8 +67,7 @@ deltochar(void)
 	    }
 	}
     }
-    if (!ok)
-	feep();
+    return !ok;
 }
 
 /**/
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index e0c93bd03..c28acb438 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -19,6 +19,7 @@
 "backward-kill-line", backwardkillline, ZLE_KILL | ZLE_KEEPSUFFIX
 "backward-kill-word", backwardkillword, ZLE_KILL | ZLE_KEEPSUFFIX
 "backward-word", backwardword, 0
+"beep", handlefeep, 0
 "beginning-of-buffer-or-history", beginningofbufferorhistory, 0
 "beginning-of-history", beginningofhistory, 0
 "beginning-of-line", beginningofline, 0
diff --git a/Src/Zle/zle.export b/Src/Zle/zle.export
index ccd5df98a..e0b88cd5c 100644
--- a/Src/Zle/zle.export
+++ b/Src/Zle/zle.export
@@ -1,10 +1,44 @@
 #!
+acceptlast
 addzlefunction
 backdel
 backkill
+bindkey
+clearflag
+clearscreen
 deletezlefunction
+do_menucmp
+do_single
 feep
 foredel
 forekill
 getkey
+getkeycmd
+getzlequery
+linkkeymap
+listshown
+menucomplete
+menucur
+menugrp
+minfo
+newkeymap
+nlnct
+printfmt
+redisplay
+refthingy
+reversemenucomplete
+selectlocalmap
+showinglist
+skipnolist
+spaceinline
+tcmultout
+tcout
+thingies
+trashzle
+ungetkeycmd
+unlinkkeymap
+zlenoargs
 zmod
+zrefresh
+zsetterm
+ztat
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index d5e0ee89c..ff515e7c4 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -38,7 +38,7 @@ typedef struct thingy *Thingy;
 
 /* widgets (ZLE functions) */
 
-typedef void (*ZleIntFunc) _((void));
+typedef int (*ZleIntFunc) _((char **));
 
 struct widget {
     int flags;		/* flags (see below) */
@@ -54,16 +54,17 @@ struct widget {
     } u;
 };
 
-#define WIDGET_INT	(1<<0)    /* widget is internally implemented */
-#define WIDGET_NCOMP    (1<<1)    /* new style completion widget */
-#define ZLE_MENUCMP	(1<<2)    /* DON'T invalidate completion list */
+#define WIDGET_INT	(1<<0)	/* widget is internally implemented */
+#define WIDGET_NCOMP    (1<<1)	/* new style 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_LINEMOVE	(1<<4)	/* command is a line-oriented movement */
+#define ZLE_LASTCOL     (1<<5)	/* command maintains lastcol correctly */
 #define ZLE_KILL	(1<<6)
-#define ZLE_KEEPSUFFIX	(1<<7)    /* DON'T remove added suffix */
-#define ZLE_NOTCOMMAND  (1<<8)    /* widget should not alter lastcmd */
-#define ZLE_ISCOMP      (1<<9)	  /* usable for new style completion */
+#define ZLE_KEEPSUFFIX	(1<<7)	/* DON'T remove added suffix */
+#define ZLE_NOTCOMMAND  (1<<8)	/* widget should not alter lastcmd */
+#define ZLE_ISCOMP      (1<<9)	/* usable for new style completion */
+
 /* thingies */
 
 struct thingy {
@@ -142,3 +143,9 @@ typedef struct cutbuffer *Cutbuffer;
 #define CUTBUFFER_LINE 1   /* for vi: buffer contains whole lines of data */
 
 #define KRINGCT 8   /* number of buffers in the kill ring */
+
+/* Convenience macros for the hooks */
+
+#define LISTMATCHESHOOK (zlehooks + 0)
+#define INSERTMATCHHOOK (zlehooks + 1)
+#define MENUSTARTHOOK   (zlehooks + 2)
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index 2fc55e98c..f85fa00a6 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -73,12 +73,13 @@ forget_edits(void)
 }
 
 /**/
-void
-uphistory(void)
+int
+uphistory(char **args)
 {
     int nodups = isset(HISTIGNOREDUPS);
     if (!zle_goto_hist(histline, -zmult, nodups) && isset(HISTBEEP))
-	feep();
+	return 1;
+    return 0;
 }
 
 /**/
@@ -116,53 +117,53 @@ upline(void)
 }
 
 /**/
-void
-uplineorhistory(void)
+int
+uplineorhistory(char **args)
 {
     int ocs = cs;
     int n = upline();
     if (n) {
-	int m = zmult;
+	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
-	    return;
-	}
+	if (virangeflag || !histallowed)
+	    return 1;
 	zmult = n;
-	uphistory();
+	ret = uphistory(args);
 	zmult = m;
+	return ret;
     }
+    return 0;
 }
 
 /**/
-void
-viuplineorhistory(void)
+int
+viuplineorhistory(char **args)
 {
     int col = lastcol;
-    uplineorhistory();
+    uplineorhistory(args);
     lastcol = col;
-    vifirstnonblank();
+    return vifirstnonblank(args);
 }
 
 /**/
-void
-uplineorsearch(void)
+int
+uplineorsearch(char **args)
 {
     int ocs = cs;
     int n = upline();
     if (n) {
-	int m = zmult;
+	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
-	    return;
-	}
+	if (virangeflag || !histallowed)
+	    return 1;
 	zmult = n;
-	historysearchbackward();
+	ret = historysearchbackward(args);
 	zmult = m;
+	return ret;
     }
+    return 0;
 }
 
 /**/
@@ -200,202 +201,220 @@ downline(void)
 }
 
 /**/
-void
-downlineorhistory(void)
+int
+downlineorhistory(char **args)
 {
     int ocs = cs;
     int n = downline();
     if (n) {
-	int m = zmult;
+	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
-	    return;
-	}
+	if (virangeflag || !histallowed)
+	    return 1;
 	zmult = n;
-	downhistory();
+	ret = downhistory(args);
 	zmult = m;
+	return ret;
     }
+    return 0;
 }
 
 /**/
-void
-vidownlineorhistory(void)
+int
+vidownlineorhistory(char **args)
 {
     int col = lastcol;
-    downlineorhistory();
+    downlineorhistory(args);
     lastcol = col;
-    vifirstnonblank();
+    return vifirstnonblank(zlenoargs);
 }
 
 /**/
-void
-downlineorsearch(void)
+int
+downlineorsearch(char **args)
 {
     int ocs = cs;
     int n = downline();
     if (n) {
-	int m = zmult;
+	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
-	    return;
-	}
+	if (virangeflag || !histallowed)
+	    return 1;
 	zmult = n;
-	historysearchforward();
+	ret = historysearchforward(args);
 	zmult = m;
+	return ret;
     }
+    return 0;
 }
 
 /**/
-void
-acceptlineanddownhistory(void)
+int
+acceptlineanddownhistory(char **args)
 {
     Histent he;
 
-    if (!(he = movehistent(quietgethist(histline), 1, HIST_FOREIGN))) {
-	feep();
-	return;
-    }
+    if (!(he = movehistent(quietgethist(histline), 1, HIST_FOREIGN)))
+	return 1;
     pushnode(bufstack, ztrdup(ZLETEXT(he)));
     done = 1;
     stackhist = he->histnum;
+    return 0;
 }
 
 /**/
-void
-downhistory(void)
+int
+downhistory(char **args)
 {
     int nodups = isset(HISTIGNOREDUPS);
     if (!zle_goto_hist(histline, zmult, nodups) && isset(HISTBEEP))
-	feep();
+	return 1;
+    return 0;
 }
 
 static int histpos, srch_hl, srch_cs = -1;
 static char *srch_str;
 
 /**/
-void
-historysearchbackward(void)
+int
+historysearchbackward(char **args)
 {
     Histent he;
-    int n = zmult;
-    char *s;
+    int n = zmult, hp;
+    char *s, *str;
 
     if (zmult < 0) {
+	int ret;
 	zmult = -n;
-	historysearchforward();
+	ret = historysearchforward(args);
 	zmult = n;
-	return;
+	return ret;
     }
-    if (histline == curhist || histline != srch_hl || cs != srch_cs || mark != 0
-     || memcmp(srch_str, line, histpos) != 0) {
-	zfree(srch_str, histpos);
-	for (histpos = 0; histpos < ll && !iblank(line[histpos]); histpos++) ;
-	if (histpos < ll)
-	    histpos++;
-	srch_str = zalloc(histpos);
-	memcpy(srch_str, line, histpos);
+    if ((str = *args))
+	hp = strlen(str);
+    else {
+	if (histline == curhist || histline != srch_hl || cs != srch_cs ||
+	    mark != 0 || memcmp(srch_str, line, histpos) != 0) {
+	    zfree(srch_str, histpos);
+	    for (histpos = 0; histpos < ll && !iblank(line[histpos]); histpos++) ;
+	    if (histpos < ll)
+		histpos++;
+	    srch_str = zalloc(histpos);
+	    memcpy(srch_str, line, histpos);
+	}
+	str = srch_str;
+	hp = histpos;
     }
     he = quietgethist(histline);
     while ((he = movehistent(he, -1, hist_skip_flags))) {
 	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
 	    continue;
 	s = ZLETEXT(he);
-	if (metadiffer(s, srch_str, histpos) < 0 &&
-	    metadiffer(s, srch_str, ll)) {
+	if (metadiffer(s, str, hp) < 0 &&
+	    (*args || metadiffer(s, str, ll))) {
 	    if (--n <= 0) {
 		zle_setline(he);
 		srch_hl = histline;
 		srch_cs = cs;
-		return;
+		return 0;
 	    }
 	}
     }
-    feep();
+    return 1;
 }
 
 /**/
-void
-historysearchforward(void)
+int
+historysearchforward(char **args)
 {
     Histent he;
-    int n = zmult;
-    char *s;
+    int n = zmult, hp;
+    char *s, *str;
 
     if (zmult < 0) {
+	int ret;
 	zmult = -n;
-	historysearchbackward();
+	ret = historysearchbackward(args);
 	zmult = n;
-	return;
+	return ret;
     }
-    if (histline == curhist || histline != srch_hl || cs != srch_cs || mark != 0
-     || memcmp(srch_str, line, histpos) != 0) {
-	zfree(srch_str, histpos);
-	for (histpos = 0; histpos < ll && !iblank(line[histpos]); histpos++) ;
-	if (histpos < ll)
-	    histpos++;
-	srch_str = zalloc(histpos);
-	memcpy(srch_str, line, histpos);
+    if ((str = *args))
+	hp = strlen(str);
+    else {
+	if (histline == curhist || histline != srch_hl || cs != srch_cs ||
+	    mark != 0 || memcmp(srch_str, line, histpos) != 0) {
+	    zfree(srch_str, histpos);
+	    for (histpos = 0; histpos < ll && !iblank(line[histpos]); histpos++) ;
+	    if (histpos < ll)
+		histpos++;
+	    srch_str = zalloc(histpos);
+	    memcpy(srch_str, line, histpos);
+	}
+	str = srch_str;
+	hp = histpos;
     }
     he = quietgethist(histline);
     while ((he = movehistent(he, 1, hist_skip_flags))) {
 	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
 	    continue;
 	s = ZLETEXT(he);
-	if (metadiffer(s, srch_str, histpos) < (he->histnum == curhist) &&
-	    metadiffer(s, srch_str, ll)) {
+	if (metadiffer(s, str, hp) < (he->histnum == curhist) &&
+	    (*args || metadiffer(s, str, ll))) {
 	    if (--n <= 0) {
 		zle_setline(he);
 		srch_hl = histline;
 		srch_cs = cs;
-		return;
+		return 0;
 	    }
 	}
     }
-    feep();
+    return 1;
 }
 
 /**/
-void
-beginningofbufferorhistory(void)
+int
+beginningofbufferorhistory(char **args)
 {
     if (findbol())
 	cs = 0;
     else
-	beginningofhistory();
+	return beginningofhistory(args);
+    return 0;
 }
 
 /**/
-void
-beginningofhistory(void)
+int
+beginningofhistory(char **args)
 {
     if (!zle_goto_hist(firsthist(), 0, 0) && isset(HISTBEEP))
-	feep();
+	return 1;
+    return 0;
 }
 
 /**/
-void
-endofbufferorhistory(void)
+int
+endofbufferorhistory(char **args)
 {
     if (findeol() != ll)
 	cs = ll;
     else
-	endofhistory();
+	return endofhistory(args);
+    return 0;
 }
 
 /**/
-void
-endofhistory(void)
+int
+endofhistory(char **args)
 {
     zle_goto_hist(curhist, 0, 0);
+    return 0;
 }
 
 /**/
-void
-insertlastword(void)
+int
+insertlastword(char **args)
 {
     int n;
     char *s, *t;
@@ -420,19 +439,15 @@ insertlastword(void)
 	zsfree(lastinsert);
 	lastinsert = NULL;
     }
-    if (!(he = quietgethist(evhist)) || !he->nwords) {
-	feep();
-	return;
-    }
+    if (!(he = quietgethist(evhist)) || !he->nwords)
+	return 1;
     if (zmult > 0) {
 	n = he->nwords - (zmult - 1);
     } else {
 	n = 1 - zmult;
     }
-    if (n < 1 || n > he->nwords) {
-	feep();
-	return;
-    }
+    if (n < 1 || n > he->nwords)
+	return 1;
     s = he->text + he->words[2*n-2];
     t = he->text + he->words[2*n-1];
     save = *t;
@@ -446,6 +461,7 @@ insertlastword(void)
     doinsert(s);
     zmult = n;
     *t = save;
+    return 0;
 }
 
 /**/
@@ -461,15 +477,15 @@ zle_setline(Histent he)
 }
 
 /**/
-void
-setlocalhistory(void)
+int
+setlocalhistory(char **args)
 {
     if (zmod.flags & MOD_MULT) {
 	hist_skip_flags = zmult? HIST_FOREIGN : 0;
-    }
-    else {
+    } else {
 	hist_skip_flags ^= HIST_FOREIGN;
     }
+    return 0;
 }
 
 /**/
@@ -489,13 +505,13 @@ zle_goto_hist(int ev, int n, int skipdups)
 }
 
 /**/
-void
-pushline(void)
+int
+pushline(char **args)
 {
     int n = zmult;
 
     if (n < 0)
-	return;
+	return 1;
     pushnode(bufstack, metafy((char *) line, ll, META_DUP));
     while (--n)
 	pushnode(bufstack, ztrdup(""));
@@ -503,18 +519,19 @@ pushline(void)
     *line = '\0';
     ll = cs = 0;
     clearlist = 1;
+    return 0;
 }
 
 /**/
-void
-pushlineoredit(void)
+int
+pushlineoredit(char **args)
 {
-    int ics;
+    int ics, ret;
     unsigned char *s;
     char *hline = hgetline();
 
     if (zmult < 0)
-	return;
+	return 1;
     if (hline && *hline) {
 	ics = ztrlen(hline);
 	sizeline(ics + ll + 1);
@@ -524,34 +541,36 @@ pushlineoredit(void)
 	ll += ics;
 	cs += ics;
     }
-    pushline();
+    ret = pushline(args);
     if (!isfirstln)
 	errflag = done = 1;
     clearlist = 1;
+    return ret;
 }
 
 /**/
-void
-pushinput(void)
+int
+pushinput(char **args)
 {
-    int i;
+    int i, ret;
 
     if (zmult < 0)
-	return;
+	return 1;
     zmult += i = !isfirstln;
-    pushlineoredit();
+    ret = pushlineoredit(args);
     zmult -= i;
+    return ret;
 }
 
 /**/
-void
-getline(void)
+int
+getline(char **args)
 {
     char *s = (char *)getlinknode(bufstack);
 
-    if (!s)
-	feep();
-    else {
+    if (!s) {
+	return 1;
+    } else {
 	int cc;
 
 	unmetafy(s, &cc);
@@ -561,20 +580,23 @@ getline(void)
 	free(s);
 	clearlist = 1;
     }
+    return 0;
 }
 
 /**/
-void
-historyincrementalsearchbackward(void)
+int
+historyincrementalsearchbackward(char **args)
 {
-    doisearch(-1);
+    doisearch(args, -1);
+    return 0;
 }
 
 /**/
-void
-historyincrementalsearchforward(void)
+int
+historyincrementalsearchforward(char **args)
 {
-    doisearch(1);
+    doisearch(args, 1);
+    return 0;
 }
 
 static struct isrch_spot {
@@ -642,13 +664,13 @@ get_isrch_spot(int num, int *hlp, int *posp, int *csp, int *lenp, int *dirp, int
 
 /**/
 static void
-doisearch(int dir)
+doisearch(char **args, int dir)
 {
     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;
-    int hl = histline;
+    int hl = histline, savekeys = -1, feep = 0;
     Thingy cmd;
     char *okeymap = curkeymapname;
     static char *previous_search = NULL;
@@ -657,6 +679,14 @@ doisearch(int dir)
 
     clearlist = 1;
 
+    if (*args) {
+	int len;
+	char *arg;
+	savekeys = kungetct;
+	arg = getkeystring(*args, &len, 2, NULL);
+	ungetkeys(arg, len);
+    }
+
     strcpy(ibuf, ISEARCH_PROMPT);
     memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
     remember_edits();
@@ -709,7 +739,7 @@ doisearch(int dir)
 		    get_isrch_spot(top_spot, &hl, &pos, &cs, &sbptr,
 				   &dir, &nomatch);
 		    if (!nomatch) {
-			feep();
+			feep = 1;
 			nomatch = 1;
 		    }
 		    he = quietgethist(hl);
@@ -743,14 +773,14 @@ doisearch(int dir)
 	    break;
 	}
 	if(cmd == Th(z_clearscreen)) {
-	    clearscreen();
+	    clearscreen(zlenoargs);
 	    goto ref;
 	} else if(cmd == Th(z_redisplay)) {
-	    redisplay();
+	    redisplay(zlenoargs);
 	    goto ref;
 	} else if(cmd == Th(z_vicmdmode)) {
 	    if(selectkeymap(invicmdmode() ? "main" : "vicmd", 0))
-		feep();
+		feep = 1;
 	    goto ref;
 	} else if(cmd == Th(z_vibackwarddeletechar) ||
 	    	cmd == Th(z_backwarddeletechar)) {
@@ -758,7 +788,7 @@ doisearch(int dir)
 		get_isrch_spot(--top_spot, &hl, &pos, &cs, &sbptr,
 			       &dir, &nomatch);
 	    else
-		feep();
+		feep = 1;
 	    if (nomatch) {
 		statusline = ibuf;
 		skip_pos = 1;
@@ -773,16 +803,16 @@ doisearch(int dir)
 	    memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
 	    continue;
 	} else if(cmd == Th(z_acceptandhold)) {
-	    acceptandhold();
+	    acceptandhold(zlenoargs);
 	    break;
 	} else if(cmd == Th(z_acceptandinfernexthistory)) {
-	    acceptandinfernexthistory();
+	    acceptandinfernexthistory(zlenoargs);
 	    break;
 	} else if(cmd == Th(z_acceptlineanddownhistory)) {
-	    acceptlineanddownhistory();
+	    acceptlineanddownhistory(zlenoargs);
 	    break;
 	} else if(cmd == Th(z_acceptline)) {
-	    acceptline();
+	    acceptline(zlenoargs);
 	    break;
 	} else if(cmd == Th(z_historyincrementalsearchbackward)) {
 	    set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
@@ -825,7 +855,7 @@ doisearch(int dir)
 		zrefresh();
 	    }
 	    if ((c = getkey(0)) == EOF)
-		feep();
+		feep = 1;
 	    else
 		goto ins;
 	} else {
@@ -843,7 +873,7 @@ doisearch(int dir)
 	    }
 	ins:
 	    if (sbptr == PATH_MAX) {
-		feep();
+		feep = 1;
 		continue;
 	    }
 	    set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
@@ -854,7 +884,9 @@ doisearch(int dir)
 	    }
 	    sbuf[sbptr++] = c;
 	}
-	handlefeep();
+	if (feep)
+	    handlefeep(zlenoargs);
+	feep = 0;
     }
     if (sbptr) {
 	zfree(previous_search, previous_search_len);
@@ -863,11 +895,17 @@ doisearch(int dir)
     }
     statusline = NULL;
     selectkeymap(okeymap, 1);
+    /*
+     * Don't allow unused characters provided as a string to the
+     * widget to overflow and be used as separated commands.
+     */
+    if (savekeys >= 0 && kungetct > savekeys)
+	kungetct = savekeys;
 }
 
 /**/
-void
-acceptandinfernexthistory(void)
+int
+acceptandinfernexthistory(char **args)
 {
     Histent he;
 
@@ -878,14 +916,15 @@ acceptandinfernexthistory(void)
 	    he = movehistent(he, 1, HIST_FOREIGN);
 	    pushnode(bufstack, ztrdup(ZLETEXT(he)));
 	    stackhist = he->histnum;
-	    return;
+	    return 0;
 	}
     }
+    return 1;
 }
 
 /**/
-void
-infernexthistory(void)
+int
+infernexthistory(char **args)
 {
     Histent he;
 
@@ -894,28 +933,30 @@ infernexthistory(void)
 	if (!metadiffer(ZLETEXT(he), (char *) line, ll)) {
 	    he = movehistent(he, 1, HIST_FOREIGN);
 	    zle_setline(he);
-	    return;
+	    return 0;
 	}
     }
-    feep();
+    return 1;
 }
 
 /**/
-void
-vifetchhistory(void)
+int
+vifetchhistory(char **args)
 {
     if (zmult < 0)
-	return;
+	return 1;
     if (histline == curhist) {
 	if (!(zmod.flags & MOD_MULT)) {
 	    cs = ll;
 	    cs = findbol();
-	    return;
+	    return 0;
 	}
     }
     if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist, 0, 0) &&
-	isset(HISTBEEP))
-	feep();
+	isset(HISTBEEP)) {
+	return 1;
+    }
+    return 0;
 }
 
 /* the last vi search */
@@ -928,7 +969,7 @@ static int
 getvisrchstr(void)
 {
     char *sbuf = zhalloc(80);
-    int sptr = 1, ret = 0, ssbuf = 80;
+    int sptr = 1, ret = 0, ssbuf = 80, feep = 0;
     Thingy cmd;
     char *okeymap = curkeymapname;
 
@@ -953,9 +994,9 @@ getvisrchstr(void)
 	    cmd = Th(z_selfinsert);
 	}
 	if(cmd == Th(z_redisplay)) {
-	    redisplay();
+	    redisplay(zlenoargs);
 	} else if(cmd == Th(z_clearscreen)) {
-	    clearscreen();
+	    clearscreen(zlenoargs);
 	} else if(cmd == Th(z_acceptline) ||
 	    	cmd == Th(z_vicmdmode)) {
 	    sbuf[sptr] = 0;
@@ -981,7 +1022,7 @@ getvisrchstr(void)
 		zrefresh();
 	    }
 	    if ((c = getkey(0)) == EOF)
-		feep();
+		feep = 1;
 	    else
 		goto ins;
 	} else if(cmd == Th(z_selfinsertunmeta) || cmd == Th(z_selfinsert)) {
@@ -998,9 +1039,11 @@ getvisrchstr(void)
 	    }
 	    sbuf[sptr++] = c;
 	} else {
-	    feep();
+	    feep = 1;
 	}
-	handlefeep();
+	if (feep)
+	    handlefeep(zlenoargs);
+	feep = 0;
     }
     statusline = NULL;
     selectkeymap(okeymap, 1);
@@ -1008,36 +1051,58 @@ getvisrchstr(void)
 }
 
 /**/
-void
-vihistorysearchforward(void)
+int
+vihistorysearchforward(char **args)
 {
+    if (*args) {
+	int ose = visrchsense, ret;
+	char *ost = visrchstr;
+
+	visrchsense = 1;
+	visrchstr = *args;
+	ret = virepeatsearch(zlenoargs);
+	visrchsense = ose;
+	visrchstr = ost;
+	return ret;
+    }
     visrchsense = 1;
     if (getvisrchstr())
-	virepeatsearch();
+	return virepeatsearch(zlenoargs);
+    return 1;
 }
 
 /**/
-void
-vihistorysearchbackward(void)
+int
+vihistorysearchbackward(char **args)
 {
+    if (*args) {
+	int ose = visrchsense, ret;
+	char *ost = visrchstr;
+
+	visrchsense = -1;
+	visrchstr = *args;
+	ret = virepeatsearch(zlenoargs);
+	visrchsense = ose;
+	visrchstr = ost;
+	return ret;
+    }
     visrchsense = -1;
     if (getvisrchstr())
-	virepeatsearch();
+	return virepeatsearch(zlenoargs);
+    return 1;
 }
 
 /**/
-void
-virepeatsearch(void)
+int
+virepeatsearch(char **args)
 {
     Histent he;
     int t0;
     int n = zmult;
     char *s;
 
-    if (!visrchstr) {
-	feep();
-	return;
-    }
+    if (!visrchstr)
+	return 1;
     if (zmult < 0) {
 	n = -n;
 	visrchsense = -visrchsense;
@@ -1053,20 +1118,22 @@ virepeatsearch(void)
 			      : hstrnstr(s, 0, visrchstr, t0, 1, 1) != 0)) {
 	    if (--n <= 0) {
 		zle_setline(he);
-		return;
+		return 0;
 	    }
 	}
     }
-    feep();
+    return 1;
 }
 
 /**/
-void
-virevrepeatsearch(void)
+int
+virevrepeatsearch(char **args)
 {
+    int ret;
     visrchsense = -visrchsense;
-    virepeatsearch();
+    ret = virepeatsearch(args);
     visrchsense = -visrchsense;
+    return ret;
 }
 
 /* Extra function added by A.R. Iano-Fletcher.	*/
@@ -1074,8 +1141,8 @@ virevrepeatsearch(void)
 /* history-beginning-search-backward */
 
 /**/
-void
-historybeginningsearchbackward(void)
+int
+historybeginningsearchbackward(char **args)
 {
     Histent he;
     int cpos = cs;		/* save cursor position */
@@ -1083,10 +1150,11 @@ historybeginningsearchbackward(void)
     char *s;
 
     if (zmult < 0) {
+	int ret;
 	zmult = -n;
-	historybeginningsearchforward();
+	ret = historybeginningsearchforward(args);
 	zmult = n;
-	return;
+	return ret;
     }
     he = quietgethist(histline);
     while ((he = movehistent(he, -1, hist_skip_flags))) {
@@ -1098,19 +1166,19 @@ historybeginningsearchbackward(void)
 	    if (--n <= 0) {
 		zle_setline(he);
 		cs = cpos;
-		return;
+		return 0;
 	    }
 	}
     }
-    feep();
+    return 1;
 }
 
 /* Extra function added by A.R. Iano-Fletcher.	*/
 
 /* history-beginning-search-forward */
 /**/
-void
-historybeginningsearchforward(void)
+int
+historybeginningsearchforward(char **args)
 {
     Histent he;
     int cpos = cs;		/* save cursor position */
@@ -1118,10 +1186,11 @@ historybeginningsearchforward(void)
     char *s;
 
     if (zmult < 0) {
+	int ret;
 	zmult = -n;
-	historybeginningsearchbackward();
+	ret = historybeginningsearchbackward(args);
 	zmult = n;
-	return;
+	return ret;
     }
     he = quietgethist(histline);
     while ((he = movehistent(he, 1, hist_skip_flags))) {
@@ -1133,9 +1202,9 @@ historybeginningsearchforward(void)
 	    if (--n <= 0) {
 		zle_setline(he);
 		cs = cpos;
-		return;
+		return 0;
 	    }
 	}
     }
-    feep();
+    return 1;
 }
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 7504ed809..28bc96b64 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -98,7 +98,7 @@ struct bindstate {
 /* currently selected keymap, and its name */
 
 /**/
-Keymap curkeymap;
+Keymap curkeymap, localkeymap;
 /**/
 char *curkeymapname;
 
@@ -216,7 +216,7 @@ freekeynode(HashNode hn)
 static HashTable copyto;
 
 /**/
-static Keymap
+Keymap
 newkeymap(Keymap tocopy, char *kmname)
 {
     Keymap km = zcalloc(sizeof(*km));
@@ -250,7 +250,7 @@ scancopykeys(HashNode hn, int flags)
 }
 
 /**/
-static void
+void
 deletekeymap(Keymap km)
 {
     int i;
@@ -322,21 +322,21 @@ openkeymap(char *name)
 }
 
 /**/
-static int
-unlinkkeymap(char *name)
+int
+unlinkkeymap(char *name, int ignm)
 {
     KeymapName n = (KeymapName) keymapnamtab->getnode(keymapnamtab, name);
     if(!n)
 	return 2;
-    if(n->flags & KMN_IMMORTAL)
+    if(!ignm && (n->flags & KMN_IMMORTAL))
 	return 1;
     keymapnamtab->freenode(keymapnamtab->removenode(keymapnamtab, name));
     return 0;
 }
 
 /**/
-static int
-linkkeymap(Keymap km, char *name)
+int
+linkkeymap(Keymap km, char *name, int imm)
 {
     KeymapName n = (KeymapName) keymapnamtab->getnode(keymapnamtab, name);
     if(n) {
@@ -347,9 +347,12 @@ linkkeymap(Keymap km, char *name)
 	if(!--n->keymap->rc)
 	    deletekeymap(n->keymap);
 	n->keymap = km;
-    } else
-	keymapnamtab->addnode(keymapnamtab, ztrdup(name),
-	    makekeymapnamnode(km));
+    } else {
+	n = makekeymapnamnode(km);
+	if (imm)
+	    n->flags |= KMN_IMMORTAL;
+	keymapnamtab->addnode(keymapnamtab, ztrdup(name), n);
+    }
     km->rc++;
     return 0;
 }
@@ -379,6 +382,15 @@ selectkeymap(char *name, int fb)
     return 0;
 }
 
+/* Select a local key map. */
+
+/**/
+void
+selectlocalmap(Keymap m)
+{
+    localkeymap = m;
+}
+
 /* Reopen the currently selected keymap, in case it got deleted.  This *
  * should be called after doing anything that might have run an        *
  * arbitrary user-specified command.                                   */
@@ -642,7 +654,7 @@ bin_bindkey(char *name, char **argv, char *ops, int func)
 	    return 1;
 	}
 	if(ops['e'] || ops['v'])
-	    linkkeymap(km, "main");
+	    linkkeymap(km, "main", 0);
     } else {
 	kmname = NULL;
 	km = NULL;
@@ -715,7 +727,7 @@ bin_bindkey_del(char *name, char *kmname, Keymap km, char **argv, char *ops, cha
     int ret = 0;
 
     do {
-	int r = unlinkkeymap(*argv);
+	int r = unlinkkeymap(*argv, 0);
 	if(r == 1)
 	    zwarnnam(name, "keymap name `%s' is protected", *argv, 0);
 	else if(r == 2)
@@ -735,7 +747,7 @@ bin_bindkey_link(char *name, char *kmname, Keymap km, char **argv, char *ops, ch
     if(!km) {
 	zwarnnam(name, "no such keymap `%s'", argv[0], 0);
 	return 1;
-    } else if(linkkeymap(km, argv[1])) {
+    } else if(linkkeymap(km, argv[1], 0)) {
 	zwarnnam(name, "keymap name `%s' is protected", argv[1], 0);
 	return 1;
     }
@@ -762,7 +774,7 @@ bin_bindkey_new(char *name, char *kmname, Keymap km, char **argv, char *ops, cha
 	}
     } else
 	km = NULL;
-    linkkeymap(newkeymap(km, argv[0]), argv[0]);
+    linkkeymap(newkeymap(km, argv[0]), argv[0], 0);
     return 0;
 }
 
@@ -1108,20 +1120,18 @@ default_bindings(void)
      * will be linked to the "emacs" keymap, except that if VISUAL *
      * or EDITOR contain the string "vi" then it will be linked to *
      * the "viins" keymap.                                         */
-    linkkeymap(vmap, "viins");
-    linkkeymap(emap, "emacs");
-    linkkeymap(amap, "vicmd");
-    linkkeymap(smap, ".safe");
+    linkkeymap(vmap, "viins", 0);
+    linkkeymap(emap, "emacs", 0);
+    linkkeymap(amap, "vicmd", 0);
+    linkkeymap(smap, ".safe", 1);
     if (((ed = zgetenv("VISUAL")) && strstr(ed, "vi")) ||
 	((ed = zgetenv("EDITOR")) && strstr(ed, "vi")))
-	linkkeymap(vmap, "main");
+	linkkeymap(vmap, "main", 0);
     else
-	linkkeymap(emap, "main");
+	linkkeymap(emap, "main", 0);
 
     /* the .safe map cannot be modified or deleted */
     smap->flags |= KM_IMMUTABLE;
-    ((KeymapName) keymapnamtab->getnode(keymapnamtab, ".safe"))->flags
-	|= KMN_IMMORTAL;
 }
 
 /*************************/
@@ -1142,7 +1152,12 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
     keybuf[0] = 0;
     while((c = getkeybuf(!!lastlen)) != EOF) {
 	char *s;
-	Thingy f = keybind(km, keybuf, &s);
+	Thingy f;
+	int loc = 1;
+
+	if (!localkeymap ||
+	    (f = keybind(localkeymap, keybuf, &s)) == t_undefinedkey)
+	    loc = 0, f = keybind(km, keybuf, &s);
 
 	if(f != t_undefinedkey) {
 	    lastlen = keybuflen;
@@ -1150,7 +1165,7 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
 	    str = s;
 	    lastc = c;
 	}
-	if(!keyisprefix(km, keybuf))
+	if(!keyisprefix((loc ? localkeymap : km), keybuf))
 	    break;
     }
     if(!lastlen && keybuflen)
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 13f3cc402..8f6dfdf75 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -106,10 +106,12 @@ struct modifier zmod;
 /**/
 int prefixflag;
 
-/* != 0 if there is a pending beep (usually indicating an error) */
+/* Number of characters waiting to be read by the ungetkeys mechanism */
+/**/
+int kungetct;
 
 /**/
-int feepflag;
+char *zlenoargs[1] = { NULL };
 
 #ifdef FIONREAD
 static int delayzsetterm;
@@ -262,7 +264,7 @@ zsetterm(void)
 }
 
 static char *kungetbuf;
-static int kungetct, kungetsz;
+static int kungetsz;
 
 /**/
 void
@@ -499,6 +501,7 @@ zleread(char *lp, char *rp, int flags)
 	viinsbegin = 0;
 	statusline = NULL;
 	selectkeymap("main", 1);
+	selectlocalmap(NULL);
 	fixsuffix();
 	if ((s = (unsigned char *)getlinknode(bufstack))) {
 	    setline((char *)s);
@@ -527,20 +530,21 @@ zleread(char *lp, char *rp, int flags)
 	lastcol = -1;
 	initmodifier(&zmod);
 	prefixflag = 0;
-	feepflag = 0;
 	zrefresh();
 	while (!done && !errflag) {
 
 	    statusline = NULL;
 	    vilinerange = 0;
 	    reselectkeymap();
+	    selectlocalmap(NULL);
 	    bindk = getkeycmd();
 	    if (!ll && isfirstln && c == eofchar) {
 		eofsent = 1;
 		break;
 	    }
 	    if (bindk) {
-		execzlefunc(bindk);
+		if (execzlefunc(bindk, zlenoargs))
+		    handlefeep(zlenoargs);
 		handleprefixes();
 		/* for vi mode, make sure the cursor isn't somewhere illegal */
 		if (invicmdmode() && cs > findbol() &&
@@ -565,7 +569,6 @@ zleread(char *lp, char *rp, int flags)
 #endif
 		if (!kungetct)
 		    zrefresh();
-	    handlefeep();
 	}
 	statusline = NULL;
 	invalidatelist();
@@ -591,10 +594,10 @@ zleread(char *lp, char *rp, int flags)
 /* execute a widget */
 
 /**/
-void
-execzlefunc(Thingy func)
+int
+execzlefunc(Thingy func, char **args)
 {
-    int r = 0;
+    int r = 0, ret = 0;
     Widget w;
 
     if(func->flags & DISABLED) {
@@ -605,7 +608,7 @@ execzlefunc(Thingy func)
 	zsfree(nm);
 	showmsg(msg);
 	zsfree(msg);
-	feep();
+	ret = 1;
     } else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_NCOMP)) {
 	int wflags = w->flags;
 
@@ -621,9 +624,9 @@ execzlefunc(Thingy func)
 	    lastcol = -1;
 	if (wflags & WIDGET_NCOMP) {
 	    compwidget = w;
-	    completecall();
+	    ret = completecall(args);
 	} else
-	    w->u.fn();
+	    ret = w->u.fn(args);
 	if (!(wflags & ZLE_NOTCOMMAND))
 	    lastcmd = wflags;
 	r = 1;
@@ -638,14 +641,23 @@ execzlefunc(Thingy func)
 	    zsfree(nm);
 	    showmsg(msg);
 	    zsfree(msg);
-	    feep();
+	    ret = 1;
 	} else {
-	    int osc = sfcontext, osi = movefd(0);
-
+	    int osc = sfcontext, osi = movefd(0), olv = lastval;
+	    LinkList largs = NULL;
+
+	    if (*args) {
+		largs = newlinklist();
+		addlinknode(largs, dupstring(w->u.fnnam));
+		while (*args)
+		    addlinknode(largs, dupstring(*args++));
+	    }
 	    startparamscope();
 	    makezleparams(0);
 	    sfcontext = SFC_WIDGET;
-	    doshfunc(w->u.fnnam, l, NULL, 0, 1);
+	    doshfunc(w->u.fnnam, l, largs, 0, 0);
+	    ret = lastval;
+	    lastval = olv;
 	    sfcontext = osc;
 	    endparamscope();
 	    lastcmd = 0;
@@ -658,6 +670,7 @@ execzlefunc(Thingy func)
 	refthingy(func);
 	lbindk = func;
     }
+    return ret;
 }
 
 /* initialise command modifiers */
@@ -826,14 +839,14 @@ bin_vared(char *name, char **args, char *ops, int func)
 }
 
 /**/
-void
-describekeybriefly(void)
+int
+describekeybriefly(char **args)
 {
     char *seq, *str, *msg, *is;
     Thingy func;
 
     if (statusline)
-	return;
+	return 1;
     clearlist = 1;
     statusline = "Describe key briefly: _";
     statusll = strlen(statusline);
@@ -841,7 +854,7 @@ describekeybriefly(void)
     seq = getkeymapcmd(curkeymap, &func, &str);
     statusline = NULL;
     if(!*seq)
-	return;
+	return 1;
     msg = bindztrdup(seq);
     msg = appstr(msg, " is ");
     if (!func)
@@ -852,6 +865,7 @@ describekeybriefly(void)
     zsfree(is);
     showmsg(msg);
     zsfree(msg);
+    return 0;
 }
 
 #define MAXFOUND 4
@@ -882,13 +896,13 @@ scanfindfunc(char *seq, Thingy func, char *str, void *magic)
 }
 
 /**/
-void
-whereis(void)
+int
+whereis(char **args)
 {
     struct findfunc ff;
 
     if (!(ff.func = executenamedcommand("Where is: ")))
-	return;
+	return 1;
     ff.found = 0;
     ff.msg = niceztrdup(ff.func->nam);
     scankeymap(curkeymap, 1, scanfindfunc, &ff);
@@ -898,6 +912,7 @@ whereis(void)
 	ff.msg = appstr(ff.msg, " et al");
     showmsg(ff.msg);
     zsfree(ff.msg);
+    return 0;
 }
 
 /**/
@@ -933,7 +948,17 @@ 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, "lDANCLmMgGc",    NULL),
+    BUILTIN("zle",     0, bin_zle,     0, -1, 0, "lDANCLmMgGcRa",  NULL),
+};
+
+/* The order of the entries in this table has to match the *HOOK
+ * macros in zle.h */
+
+/**/
+struct hookdef zlehooks[] = {
+    HOOKDEF("list_matches", ilistmatches, 0),
+    HOOKDEF("insert_match", NULL, HOOKF_ALL),
+    HOOKDEF("menu_start", NULL, HOOKF_ALL),
 };
 
 /**/
@@ -955,6 +980,8 @@ setup_zle(Module m)
     unambig_dataptr = unambig_data;
     set_comp_sepptr = set_comp_sep;
 
+    getkeyptr = getkey;
+
     /* initialise the thingies */
     init_thingies();
     lbindk = NULL;
@@ -977,6 +1004,7 @@ int
 boot_zle(Module m)
 {
     addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    addhookdefs(m->nam, zlehooks, sizeof(zlehooks)/sizeof(*zlehooks));
     return 0;
 }
 
@@ -992,6 +1020,7 @@ cleanup_zle(Module m)
 	return 1;
     }
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    deletehookdefs(m->nam, zlehooks, sizeof(zlehooks)/sizeof(*zlehooks));
     return 0;
 }
 
@@ -1031,6 +1060,8 @@ finish_zle(Module m)
     unambig_dataptr = NULL;
     set_comp_sepptr = NULL;
 
+    getkeyptr = NULL;
+
     return 0;
 }
 
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index e2ccfbc46..a51cdf92e 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -57,8 +57,8 @@ doinsert(char *str)
 }
 
 /**/
-void
-selfinsert(void)
+int
+selfinsert(char **args)
 {
     char s[3], *p = s;
 
@@ -69,56 +69,61 @@ selfinsert(void)
     *p++ = c;
     *p = 0;
     doinsert(s);
+    return 0;
 }
 
 /**/
-void
-selfinsertunmeta(void)
+int
+selfinsertunmeta(char **args)
 {
     c &= 0x7f;
     if (c == '\r')
 	c = '\n';
-    selfinsert();
+    return selfinsert(args);
 }
 
 /**/
-void
-deletechar(void)
+int
+deletechar(char **args)
 {
     if (zmult < 0) {
+	int ret;
 	zmult = -zmult;
-	backwarddeletechar();
+	ret = backwarddeletechar(args);
 	zmult = -zmult;
-	return;
+	return ret;
     }
     if (cs + zmult <= ll) {
 	cs += zmult;
 	backdel(zmult);
-    } else
-	feep();
+	return 0;
+    }
+    return 1;
 }
 
 /**/
-void
-backwarddeletechar(void)
+int
+backwarddeletechar(char **args)
 {
     if (zmult < 0) {
+	int ret;
 	zmult = -zmult;
-	deletechar();
+	ret = deletechar(args);
 	zmult = -zmult;
-	return;
+	return ret;
     }
     backdel(zmult > cs ? cs : zmult);
+    return 0;
 }
 
 /**/
-void
-killwholeline(void)
+int
+killwholeline(char **args)
 {
     int i, fg, n = zmult;
 
     if (n < 0)
-	return;
+	return 1;
     while (n--) {
 	if ((fg = (cs && cs == ll)))
 	    cs--;
@@ -128,28 +133,31 @@ killwholeline(void)
 	forekill(i - cs + (i != ll), fg);
     }
     clearlist = 1;
+    return 0;
 }
 
 /**/
-void
-killbuffer(void)
+int
+killbuffer(char **args)
 {
     cs = 0;
     forekill(ll, 0);
     clearlist = 1;
+    return 0;
 }
 
 /**/
-void
-backwardkillline(void)
+int
+backwardkillline(char **args)
 {
     int i = 0, n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	killline();
+	ret = killline(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	if (cs && line[cs - 1] == '\n')
@@ -160,11 +168,12 @@ backwardkillline(void)
     }
     forekill(i, 1);
     clearlist = 1;
+    return 0;
 }
 
 /**/
-void
-gosmacstransposechars(void)
+int
+gosmacstransposechars(char **args)
 {
     int cc;
 
@@ -172,19 +181,19 @@ gosmacstransposechars(void)
 	if (cs == ll || line[cs] == '\n' ||
 	    ((cs + 1 == ll || line[cs + 1] == '\n') &&
 	     (!cs || line[cs - 1] == '\n'))) {
-	    feep();
-	    return;
+	    return 1;
 	}
 	cs += (cs == 0 || line[cs - 1] == '\n') ? 2 : 1;
     }
     cc = line[cs - 2];
     line[cs - 2] = line[cs - 1];
     line[cs - 1] = cc;
+    return 0;
 }
 
 /**/
-void
-transposechars(void)
+int
+transposechars(char **args)
 {
     int cc, ct;
     int n = zmult;
@@ -194,10 +203,8 @@ transposechars(void)
 	n = -n;
     while (n--) {
 	if (!(ct = cs) || line[cs - 1] == '\n') {
-	    if (ll == cs || line[cs] == '\n') {
-		feep();
-		return;
-	    }
+	    if (ll == cs || line[cs] == '\n')
+		return 1;
 	    if (!neg)
 		cs++;
 	    ct++;
@@ -214,29 +221,28 @@ transposechars(void)
 	}
 	if (ct == ll || line[ct] == '\n')
 	    ct--;
-	if (ct < 1 || line[ct - 1] == '\n') {
-	    feep();
-	    return;
-	}
+	if (ct < 1 || line[ct - 1] == '\n')
+	    return 1;
 	cc = line[ct - 1];
 	line[ct - 1] = line[ct];
 	line[ct] = cc;
     }
+    return 0;
 }
 
 /**/
-void
-poundinsert(void)
+int
+poundinsert(char **args)
 {
     cs = 0;
-    vifirstnonblank();
+    vifirstnonblank(zlenoargs);
     if (line[cs] != '#') {
 	spaceinline(1);
 	line[cs] = '#';
 	cs = findeol();
 	while(cs != ll) {
 	    cs++;
-	    vifirstnonblank();
+	    vifirstnonblank(zlenoargs);
 	    spaceinline(1);
 	    line[cs] = '#';
 	    cs = findeol();
@@ -246,42 +252,46 @@ poundinsert(void)
 	cs = findeol();
 	while(cs != ll) {
 	    cs++;
-	    vifirstnonblank();
+	    vifirstnonblank(zlenoargs);
 	    if(line[cs] == '#')
 		foredel(1);
 	    cs = findeol();
 	}
     }
     done = 1;
+    return 0;
 }
 
 /**/
-void
-acceptline(void)
+int
+acceptline(char **args)
 {
     done = 1;
+    return 0;
 }
 
 /**/
-void
-acceptandhold(void)
+int
+acceptandhold(char **args)
 {
     pushnode(bufstack, metafy((char *)line, ll, META_DUP));
     stackcs = cs;
     done = 1;
+    return 0;
 }
 
 /**/
-void
-killline(void)
+int
+killline(char **args)
 {
     int i = 0, n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	backwardkillline();
+	ret = backwardkillline(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	if (line[cs] == '\n')
@@ -292,11 +302,12 @@ killline(void)
     }
     backkill(i, 0);
     clearlist = 1;
+    return 0;
 }
 
 /**/
-void
-killregion(void)
+int
+killregion(char **args)
 {
     if (mark > ll)
 	mark = ll;
@@ -304,11 +315,12 @@ killregion(void)
 	forekill(mark - cs, 0);
     else
 	backkill(cs - mark, 1);
+    return 0;
 }
 
 /**/
-void
-copyregionaskill(void)
+int
+copyregionaskill(char **args)
 {
     if (mark > ll)
 	mark = ll;
@@ -316,25 +328,24 @@ copyregionaskill(void)
 	cut(cs, mark - cs, 0);
     else
 	cut(mark, cs - mark, 1);
+    return 0;
 }
 
 static int kct, yankb, yanke;
 
 /**/
-void
-yank(void)
+int
+yank(char **args)
 {
     Cutbuffer buf = &cutbuf;
     int n = zmult;
 
     if (n < 0)
-	return;
+	return 1;
     if (zmod.flags & MOD_VIBUF)
 	buf = &vibuf[zmod.vibuf];
-    if (!buf->buf) {
-	feep();
-	return;
-    }
+    if (!buf->buf)
+	return 1;
     mark = cs;
     yankb = cs;
     while (n--) {
@@ -344,18 +355,17 @@ yank(void)
 	cs += buf->len;
 	yanke = cs;
     }
+    return 0;
 }
 
 /**/
-void
-yankpop(void)
+int
+yankpop(char **args)
 {
     int cc;
 
-    if (!(lastcmd & ZLE_YANK) || !kring[kct].buf) {
-	feep();
-	return;
-    }
+    if (!(lastcmd & ZLE_YANK) || !kring[kct].buf)
+	return 1;
     cs = yankb;
     foredel(yanke - yankb);
     cc = kring[kct].len;
@@ -364,17 +374,20 @@ yankpop(void)
     cs += cc;
     yanke = cs;
     kct = (kct + KRINGCT - 1) % KRINGCT;
+    return 0;
 }
 
 /**/
-void
-overwritemode(void)
+int
+overwritemode(char **args)
 {
     insmode ^= 1;
+    return 0;
 }
+
 /**/
-void
-whatcursorposition(void)
+int
+whatcursorposition(char **args)
 {
     char msg[100];
     char *s = msg;
@@ -408,18 +421,19 @@ whatcursorposition(void)
     sprintf(s, "  point %d of %d(%d%%)  column %d", cs+1, ll+1,
 	    ll ? 100 * cs / ll : 0, cs - bol);
     showmsg(msg);
+    return 0;
 }
 
 /**/
-void
-undefinedkey(void)
+int
+undefinedkey(char **args)
 {
-    feep();
+    return 1;
 }
 
 /**/
-void
-quotedinsert(void)
+int
+quotedinsert(char **args)
 {
 #ifndef HAS_TIO
     struct sgttyb sob;
@@ -433,17 +447,20 @@ quotedinsert(void)
     zsetterm();
 #endif
     if (c < 0)
-	feep();
+	return 1;
     else
-	selfinsert();
+	return selfinsert(args);
 }
 
 /**/
-void
-digitargument(void)
+int
+digitargument(char **args)
 {
     int sign = (zmult < 0) ? -1 : 1;
 
+    if (c < '0' || c > '9')
+	return 1;
+
     if (!(zmod.flags & MOD_TMULT))
 	zmod.tmult = 0;
     if (zmod.flags & MOD_NEG) {
@@ -455,26 +472,31 @@ digitargument(void)
 	zmod.tmult = zmod.tmult * 10 + sign * (c & 0xf);
     zmod.flags |= MOD_TMULT;
     prefixflag = 1;
+    return 0;
 }
 
 /**/
-void
-negargument(void)
+int
+negargument(char **args)
 {
-    if(zmod.flags & MOD_TMULT) {
-	feep();
-	return;
-    }
+    if (zmod.flags & MOD_TMULT)
+	return 1;
     zmod.tmult = -1;
     zmod.flags |= MOD_TMULT|MOD_NEG;
     prefixflag = 1;
+    return 0;
 }
 
 /**/
-void
-universalargument(void)
+int
+universalargument(char **args)
 {
     int digcnt = 0, pref = 0, minus = 1, gotk;
+    if (*args) {
+	zmod.mult = atoi(*args);
+	zmod.flags |= MOD_MULT;
+	return 0;
+    }
     while ((gotk = getkey(0)) != EOF) {
 	if (gotk == '-' && !digcnt) {
 	    minus = -1;
@@ -493,11 +515,12 @@ universalargument(void)
 	zmod.tmult *= 4;
     zmod.flags |= MOD_TMULT;
     prefixflag = 1;
+    return 0;
 }
 
 /**/
-void
-copyprevword(void)
+int
+copyprevword(char **args)
 {
     int len, t0;
 
@@ -513,18 +536,20 @@ copyprevword(void)
     spaceinline(len);
     memcpy((char *)&line[cs], (char *)&line[t0], len);
     cs += len;
+    return 0;
 }
 
 /**/
-void
-sendbreak(void)
+int
+sendbreak(char **args)
 {
     errflag = 1;
+    return 1;
 }
 
 /**/
-void
-quoteregion(void)
+int
+quoteregion(char **args)
 {
     char *str;
     size_t len;
@@ -544,11 +569,12 @@ quoteregion(void)
     memcpy((char *)&line[cs], str, len);
     mark = cs;
     cs += len;
+    return 0;
 }
 
 /**/
-void
-quoteline(void)
+int
+quoteline(char **args)
 {
     char *str;
     size_t len = ll;
@@ -557,6 +583,7 @@ quoteline(void)
     sizeline(len);
     memcpy(line, str, len);
     cs = ll = len;
+    return 0;
 }
 
 /**/
@@ -612,7 +639,7 @@ Thingy
 executenamedcommand(char *prmt)
 {
     Thingy cmd;
-    int len, l = strlen(prmt), ols = listshown;
+    int len, l = strlen(prmt), ols = listshown, feep = 0;
     char *ptr;
     char *okeymap = curkeymapname;
 
@@ -637,20 +664,20 @@ executenamedcommand(char *prmt)
 	    return NULL;
 	}
 	if(cmd == Th(z_clearscreen)) {
-	    clearscreen();
+	    clearscreen(zlenoargs);
 	} else if(cmd == Th(z_redisplay)) {
-	    redisplay();
+	    redisplay(zlenoargs);
 	} else if(cmd == Th(z_viquotedinsert)) {
 	    *ptr = '^';
 	    zrefresh();
 	    c = getkey(0);
 	    if(c == EOF || !c || len == NAMLEN)
-		feep();
+		feep = 1;
 	    else
 		*ptr++ = c, len++;
 	} else if(cmd == Th(z_quotedinsert)) {
 	    if((c = getkey(0)) == EOF || !c || len == NAMLEN)
-		feep();
+		feep = 1;
 	    else
 		*ptr++ = c, len++;
 	} else if(cmd == Th(z_backwarddeletechar) ||
@@ -701,7 +728,7 @@ executenamedcommand(char *prmt)
 		    scanhashtable(thingytab, 1, 0, DISABLED, scancompcmd, 0);
 		} LASTALLOC;
 		if (empty(cmdll))
-		    feep();
+		    feep = 1;
 		else if (cmd == Th(z_listchoices) ||
 		    cmd == Th(z_deletecharorlist)) {
 		    int zmultsav = zmult;
@@ -723,7 +750,7 @@ executenamedcommand(char *prmt)
 			!(isset(LISTAMBIGUOUS) && cmdambig > len)) {
 			int zmultsav = zmult;
 			if (isset(LISTBEEP))
-			    feep();
+			    feep = 1;
 			statusll = l + cmdambig + 1;
 			zmult = 1;
 			listlist(cmdll);
@@ -733,12 +760,14 @@ executenamedcommand(char *prmt)
 		}
 	    } else {
 		if (len == NAMLEN || icntrl(c) || cmd != Th(z_selfinsert))
-		    feep();
+		    feep = 1;
 		else
 		    *ptr++ = c, len++;
 	    }
 	}
-	handlefeep();
+	if (feep)
+	    handlefeep(zlenoargs);
+	feep = 0;
     }
 }
 
diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c
index aec1c1767..9189218f1 100644
--- a/Src/Zle/zle_move.c
+++ b/Src/Zle/zle_move.c
@@ -33,64 +33,69 @@
 static int vimarkcs[27], vimarkline[27];
 
 /**/
-void
-beginningofline(void)
+int
+beginningofline(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	endofline();
+	ret = endofline(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	if (cs == 0)
-	    return;
+	    return 0;
 	if (line[cs - 1] == '\n')
 	    if (!--cs)
-		return;
+		return 0;
 	while (cs && line[cs - 1] != '\n')
 	    cs--;
     }
+    return 0;
 }
 
 /**/
-void
-endofline(void)
+int
+endofline(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	beginningofline();
+	ret = beginningofline(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	if (cs >= ll) {
 	    cs = ll;
-	    return;
+	    return 0;
 	}
 	if (line[cs] == '\n')
 	    if (++cs == ll)
-		return;
+		return 0;
 	while (cs != ll && line[cs] != '\n')
 	    cs++;
     }
+    return 0;
 }
 
 /**/
-void
-beginningoflinehist(void)
+int
+beginningoflinehist(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	endoflinehist();
+	ret = endoflinehist(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n) {
 	if (cs == 0)
@@ -103,26 +108,29 @@ beginningoflinehist(void)
 	n--;
     }
     if (n) {
-	int m = zmult;
+	int m = zmult, ret;
 
 	zmult = n;
-	uphistory();
+	ret = uphistory(args);
 	zmult = m;
 	cs = 0;
+	return ret;
     }
+    return 0;
 }
 
 /**/
-void
-endoflinehist(void)
+int
+endoflinehist(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	beginningoflinehist();
+	ret = beginningoflinehist(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n) {
 	if (cs >= ll) {
@@ -137,46 +145,51 @@ endoflinehist(void)
 	n--;
     }
     if (n) {
-	int m = zmult;
+	int m = zmult, ret;
 
 	zmult = n;
-	downhistory();
+	ret = downhistory(args);
 	zmult = m;
+	return ret;
     }
+    return 0;
 }
 
 /**/
-void
-forwardchar(void)
+int
+forwardchar(char **args)
 {
     cs += zmult;
     if (cs > ll)
 	cs = ll;
     if (cs < 0)
 	cs = 0;
+    return 0;
 }
 
 /**/
-void
-backwardchar(void)
+int
+backwardchar(char **args)
 {
     cs -= zmult;
     if (cs > ll)
 	cs = ll;
     if (cs < 0)
 	cs = 0;
+    return 0;
 }
 
 /**/
-void
-setmarkcommand(void)
+int
+setmarkcommand(char **args)
 {
     mark = cs;
+    return 0;
 }
 
 /**/
-void
-exchangepointandmark(void)
+int
+exchangepointandmark(char **args)
 {
     int x;
 
@@ -185,11 +198,12 @@ exchangepointandmark(void)
     cs = x;
     if (cs > ll)
 	cs = ll;
+    return 0;
 }
 
 /**/
-void
-vigotocolumn(void)
+int
+vigotocolumn(char **args)
 {
     int x, y;
 
@@ -202,20 +216,20 @@ vigotocolumn(void)
 	cs = y;
     if (cs < x)
 	cs = x;
+    return 0;
 }
 
 /**/
-void
-vimatchbracket(void)
+int
+vimatchbracket(char **args)
 {
     int ocs = cs, dir, ct;
     unsigned char oth, me;
 
   otog:
     if (cs == ll || line[cs] == '\n') {
-	feep();
 	cs = ocs;
-	return;
+	return 1;
     }
     switch (me = line[cs]) {
     case '{':
@@ -258,49 +272,49 @@ vimatchbracket(void)
 	    ct++;
     }
     if (cs < 0 || cs >= ll) {
-	feep();
 	cs = ocs;
+	return 1;
     } else if(dir > 0 && virangeflag)
 	cs++;
+    return 0;
 }
 
 /**/
-void
-viforwardchar(void)
+int
+viforwardchar(char **args)
 {
     int lim = findeol() - invicmdmode();
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	vibackwardchar();
+	ret = vibackwardchar(args);
 	zmult = n;
-	return;
-    }
-    if (cs >= lim) {
-	feep();
-	return;
+	return ret;
     }
+    if (cs >= lim)
+	return 1;
     while (n-- && cs < lim)
 	cs++;
+    return 0;
 }
 
 /**/
-void
-vibackwardchar(void)
+int
+vibackwardchar(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	viforwardchar();
+	ret = viforwardchar(args);
 	zmult = n;
-	return;
-    }
-    if (cs == findbol()) {
-	feep();
-	return;
+	return ret;
     }
+    if (cs == findbol())
+	return 1;
     while (n--) {
 	cs--;
 	if (cs < 0 || line[cs] == '\n') {
@@ -308,157 +322,163 @@ vibackwardchar(void)
 	    break;
 	}
     }
+    return 0;
 }
 
 /**/
-void
-viendofline(void)
+int
+viendofline(char **args)
 {
     int oldcs = cs, n = zmult;
 
-    if (n < 1) {
-	feep();
-	return;
-    }
+    if (n < 1)
+	return 1;
     while(n--) {
 	if (cs > ll) {
 	    cs = oldcs;
-	    feep();
-	    return;
+	    return 1;
 	}
 	cs = findeol() + 1;
     }
     cs--;
     lastcol = 1<<30;
+    return 0;
 }
 
 /**/
-void
-vibeginningofline(void)
+int
+vibeginningofline(char **args)
 {
     cs = findbol();
+    return 0;
 }
 
 static int vfindchar, vfinddir, tailadd;
 
 /**/
-void
-vifindnextchar(void)
+int
+vifindnextchar(char **args)
 {
     if ((vfindchar = vigetkey()) != -1) {
 	vfinddir = 1;
 	tailadd = 0;
-	virepeatfind();
+	return virepeatfind(args);
     }
+    return 1;
 }
 
 /**/
-void
-vifindprevchar(void)
+int
+vifindprevchar(char **args)
 {
     if ((vfindchar = vigetkey()) != -1) {
 	vfinddir = -1;
 	tailadd = 0;
-	virepeatfind();
+	return virepeatfind(args);
     }
+    return 1;
 }
 
 /**/
-void
-vifindnextcharskip(void)
+int
+vifindnextcharskip(char **args)
 {
     if ((vfindchar = vigetkey()) != -1) {
 	vfinddir = 1;
 	tailadd = -1;
-	virepeatfind();
+	return virepeatfind(args);
     }
+    return 1;
 }
 
 /**/
-void
-vifindprevcharskip(void)
+int
+vifindprevcharskip(char **args)
 {
     if ((vfindchar = vigetkey()) != -1) {
 	vfinddir = -1;
 	tailadd = 1;
-	virepeatfind();
+	return virepeatfind(args);
     }
+    return 1;
 }
 
 /**/
-void
-virepeatfind(void)
+int
+virepeatfind(char **args)
 {
     int ocs = cs, n = zmult;
 
-    if (!vfinddir) {
-	feep();
-	return;
-    }
+    if (!vfinddir)
+	return 1;
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	virevrepeatfind();
+	ret = virevrepeatfind(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	do
 	    cs += vfinddir;
 	while (cs >= 0 && cs < ll && line[cs] != vfindchar && line[cs] != '\n');
 	if (cs < 0 || cs >= ll || line[cs] == '\n') {
-	    feep();
 	    cs = ocs;
-	    return;
+	    return 1;
 	}
     }
     cs += tailadd;
     if (vfinddir == 1 && virangeflag)
 	cs++;
+    return 0;
 }
 
 /**/
-void
-virevrepeatfind(void)
+int
+virevrepeatfind(char **args)
 {
+    int ret;
+
     if (zmult < 0) {
 	zmult = -zmult;
-	virepeatfind();
+	ret = virepeatfind(args);
 	zmult = -zmult;
-	return;
+	return ret;
     }
     vfinddir = -vfinddir;
-    virepeatfind();
+    ret = virepeatfind(args);
     vfinddir = -vfinddir;
+    return ret;
 }
 
 /**/
-void
-vifirstnonblank(void)
+int
+vifirstnonblank(char **args)
 {
     cs = findbol();
     while (cs != ll && iblank(line[cs]))
 	cs++;
+    return 0;
 }
 
 /**/
-void
-visetmark(void)
+int
+visetmark(char **args)
 {
     int ch;
 
     ch = getkey(0);
-    if (ch < 'a' || ch > 'z') {
-	feep();
-	return;
-    }
+    if (ch < 'a' || ch > 'z')
+	return 1;
     ch -= 'a';
     vimarkcs[ch] = cs;
     vimarkline[ch] = histline;
+    return 0;
 }
 
 /**/
-void
-vigotomark(void)
+int
+vigotomark(char **args)
 {
     int ch;
 
@@ -466,30 +486,26 @@ vigotomark(void)
     if (ch == c)
 	ch = 26;
     else {
-	if (ch < 'a' || ch > 'z') {
-	    feep();
-	    return;
-	}
+	if (ch < 'a' || ch > 'z')
+	    return 1;
 	ch -= 'a';
     }
-    if (!vimarkline[ch]) {
-	feep();
-	return;
-    }
+    if (!vimarkline[ch])
+	return 1;
     if (curhist != vimarkline[ch] && !zle_goto_hist(vimarkline[ch], 0, 0)) {
 	vimarkline[ch] = 0;
-	feep();
-	return;
+	return 1;
     }
     cs = vimarkcs[ch];
     if (cs > ll)
 	cs = ll;
+    return 0;
 }
 
 /**/
-void
-vigotomarkline(void)
+int
+vigotomarkline(char **args)
 {
-    vigotomark();
-    vifirstnonblank();
+    vigotomark(args);
+    return vifirstnonblank(zlenoargs);
 }
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index 5c4feef50..bac399e7d 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -69,8 +69,8 @@ static struct zleparam {
         zleunsetfn, NULL },
     { "keys", PM_ARRAY | PM_READONLY, NULL, FN(get_keys),
         zleunsetfn, NULL },
-    { "NUMERIC", PM_INTEGER, FN(set_numeric), FN(get_numeric),
-        zleunsetfn, NULL },
+    { "NUMERIC", PM_INTEGER | PM_UNSET, FN(set_numeric), FN(get_numeric),
+        unset_numeric, NULL },
     { "HISTNO", PM_INTEGER | PM_READONLY, NULL, FN(get_histno),
         zleunsetfn, NULL },
     { NULL, 0, NULL, NULL, NULL, NULL }
@@ -84,12 +84,12 @@ makezleparams(int ro)
 
     for(zp = zleparams; zp->name; zp++) {
 	Param pm = createparam(zp->name, (zp->type |PM_SPECIAL|PM_REMOVABLE|
-					  (ro ? PM_READONLY : 0)));
+					  PM_LOCAL|(ro ? PM_READONLY : 0)));
 	if (!pm)
 	    pm = (Param) paramtab->getnode(paramtab, zp->name);
 	DPUTS(!pm, "param not set in makezleparams");
 
-	pm->level = locallevel;
+	pm->level = locallevel + 1;
 	pm->u.data = zp->data;
 	switch(PM_TYPE(zp->type)) {
 	    case PM_SCALAR:
@@ -107,6 +107,8 @@ makezleparams(int ro)
 		break;
 	}
 	pm->unsetfn = zp->unsetfn;
+	if ((zp->type & PM_UNSET) && (zmod.flags & MOD_MULT))
+	    pm->flags &= ~PM_UNSET;
     }
 }
 
@@ -267,6 +269,7 @@ static void
 set_numeric(Param pm, zlong x)
 {
     zmult = x;
+    zmod.flags = MOD_MULT;
 }
 
 /**/
@@ -277,6 +280,17 @@ get_numeric(Param pm)
 }
 
 /**/
+static void
+unset_numeric(Param pm, int exp)
+{
+    if (exp) {
+	stdunsetfn(pm, exp);
+	zmod.flags = 0;
+	zmult = 1;
+    }
+}
+
+/**/
 static zlong
 get_histno(Param pm)
 {
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index ac18f52f0..da0b38bfe 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -1004,23 +1004,25 @@ tcoutarg(int cap, int arg)
 }
 
 /**/
-void
-clearscreen(void)
+int
+clearscreen(char **args)
 {
     tcout(TCCLEARSCREEN);
     resetneeded = 1;
     clearflag = 0;
+    return 0;
 }
 
 /**/
-void
-redisplay(void)
+int
+redisplay(char **args)
 {
     moveto(0, 0);
     zputc('\r', shout);		/* extra care */
     tc_upcurs(lprompth - 1);
     resetneeded = 1;
     clearflag = 0;
+    return 0;
 }
 
 /**/
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index dbf982899..cf01f2fc1 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -318,9 +318,10 @@ deletezlefunction(Widget w)
 /*
  * The available operations are:
  *
- *   -l   list user-defined widgets (no arguments)
+ *   -l   list widgets/test for existence
  *   -D   delete widget names
  *   -A   link the two named widgets (2 arguments)
+ *   -C   create completion widget (3 arguments)
  *   -N   create new user-defined widget (1 or 2 arguments)
  *        invoke a widget (1 argument)
  */
@@ -334,12 +335,12 @@ bin_zle(char *name, char **args, char *ops, int func)
 	int (*func) _((char *, char **, char *, char));
 	int min, max;
     } const opns[] = {
-	{ 'l', bin_zle_list, 0,  0 },
+	{ 'l', bin_zle_list, 0, -1 },
 	{ 'D', bin_zle_del,  1, -1 },
 	{ 'A', bin_zle_link, 2,  2 },
 	{ 'N', bin_zle_new,  1,  2 },
 	{ 'C', bin_zle_complete, 3, 3 },
-	{ 'c', bin_zle_complete, 3, 3 },
+	{ 'R', bin_zle_refresh, 0, 1 },
 	{ 0,   bin_zle_call, 0, -1 },
     };
     struct opn const *op, *opp;
@@ -357,10 +358,6 @@ bin_zle(char *name, char **args, char *ops, int func)
 
     /* check number of arguments */
     for(n = 0; args[n]; n++) ;
-    if(!op->o && n != 1 && n != 2) {
-	zerrnam(name, "wrong number of arguments", NULL, 0);
-	return 1;
-    }
     if(n < op->min) {
 	zerrnam(name, "not enough arguments for -%c", NULL, op->o);
 	return 1;
@@ -377,7 +374,41 @@ bin_zle(char *name, char **args, char *ops, int func)
 static int
 bin_zle_list(char *name, char **args, char *ops, char func)
 {
-    scanhashtable(thingytab, 1, 0, DISABLED, scanlistwidgets, ops['L']);
+    if (!*args) {
+	scanhashtable(thingytab, 1, 0, DISABLED, scanlistwidgets,
+		      (ops['a'] ? -1 : ops['L']));
+	return 0;
+    } else {
+	int ret = 0;
+	Thingy t;
+
+	for (; *args && !ret; args++) {
+	    if (!(t = (Thingy) thingytab->getnode2(thingytab, *args)) ||
+		(!ops['a'] && (t->widget->flags & WIDGET_INT)))
+		ret = 1;
+	}
+	return ret;
+    }
+}
+
+/**/
+static int
+bin_zle_refresh(char *name, char **args, char *ops, char func)
+{
+    char *s = statusline;
+    int sl = statusll;
+
+    if (*args) {
+	statusline = *args;
+	statusll = strlen(statusline);
+    } else {
+	statusline = NULL;
+	statusll = 0;
+    }
+    zrefresh();
+
+    statusline = s;
+    statusll = sl;
     return 0;
 }
 
@@ -388,6 +419,10 @@ scanlistwidgets(HashNode hn, int list)
     Thingy t = (Thingy) hn;
     Widget w = t->widget;
 
+    if(list < 0) {
+	printf("%s\n", hn->nam);
+	return;
+    }
     if(w->flags & WIDGET_INT)
 	return;
     if(list) {
@@ -490,7 +525,8 @@ bin_zle_complete(char *name, char **args, char *ops, char func)
 	return 1;
     }
 #endif
-    t = rthingy(args[1]);
+    
+    t = rthingy((args[1][0] == '.') ? args[1] : dyncat(".", args[1]));
     cw = t->widget;
     unrefthingy(t);
     if (!cw || !(cw->flags & ZLE_ISCOMP)) {
@@ -517,31 +553,64 @@ bin_zle_call(char *name, char **args, char *ops, char func)
 {
     Thingy t;
     struct modifier modsave;
+    int ret, saveflag = 0;
+    char *wname = *args++;
 
     if(!zleactive || incompctlfunc || incompfunc) {
 	zerrnam(name, "widgets can only be called when ZLE is active",
 	    NULL, 0);
 	return 1;
     }
-    if (args[1]) {
-	modsave = zmod;
-	if (isdigit(*args[1])) {
-	    zmod.mult = atoi(args[1]);
-	    zmod.flags |= MOD_MULT;
+
+    if (!wname) {
+	zwarnnam(name, "wrong number of arguments", NULL, 0);
+	if (saveflag)
+	    zmod = modsave;
+	return 1;
+    }
+    while (*args && **args == '-') {
+	char *num;
+	if (!args[0][1] || args[0][1] == '-') {
+	    args++;
+	    break;
 	}
-	else {
-	    zmod.mult = 1;
-	    zmod.flags &= ~MOD_MULT;
+	while (*++(*args)) {
+	    switch (**args) {
+	    case 'n':
+		num = args[0][1] ? args[0]+1 : args[1];
+		if (!num) {
+		    zwarnnam(name, "number expected after -%c", NULL, **args);
+		    return 1;
+		}
+		if (!args[0][1])
+		    args++;
+		modsave = zmod;
+		saveflag = 1;
+		zmod.mult = atoi(num);
+		zmod.flags |= MOD_MULT;
+		break;
+	    case 'N':
+		modsave = zmod;
+		saveflag = 1;
+		zmod.mult = 1;
+		zmod.flags &= ~MOD_MULT;
+		break;
+	    default:
+		zwarnnam(name, "unknown option: %s", *args, 0);
+		return 1;
+	    }
 	}
+	args++;
     }
-    t = rthingy(args[0]);
+
+    t = rthingy(wname);
     PERMALLOC {
-      execzlefunc(t);
+        ret = execzlefunc(t, args);
     } LASTALLOC;
     unrefthingy(t);
-    if (args[1])
+    if (saveflag)
 	zmod = modsave;
-    return 0;
+    return ret;
 }
 
 /*******************/
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 8c2ae7bb6..65e1aeae9 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -110,20 +110,10 @@ static int movetoend;
 
 static int menucmp;
 
-/* Pointers to the current position in the groups list and in the menu-    *
- * completion array (the one that was put in the command line last).       */
+/* Information about menucompletion. */
 
-static Cmgroup menugrp;
-static Cmatch *menucur;
-
-/* menupos is the point (in the command line) where the menu-completion   *
- * strings are inserted.  menulen is the length of the string that was    *
- * inserted last.  menuend is the end position of this string in the      *
- * command line.  menuwe is non-zero if the cursor was at the end of the  *
- * word (meaning that suffixes should go before the cursor).  menuinsc is *
- * the length of any suffix that has been temporarily added.              */
-
-static int menupos, menulen, menuend, menuwe, menuinsc;
+/**/
+struct menuinfo minfo;
 
 /* This is for completion inside a brace expansion. brbeg and brend hold  *
  * strings that were temporarily removed from the string to complete.     *
@@ -376,157 +366,174 @@ static int lastend;
 #define FC_LINE   1
 #define FC_INWORD 2
 
+/* Arguments for and return value of completion widget. */
+
+static char **cfargs;
+static int cfret;
+
 /**/
-void
-completecall(void)
+int
+completecall(char **args)
 {
+    cfargs = args;
+    cfret = 0;
     compfunc = compwidget->u.comp.func;
-    compwidget->u.comp.fn();
+    if (compwidget->u.comp.fn(zlenoargs) && !cfret)
+	cfret = 1;
     compfunc = NULL;
+
+    return cfret;
 }
 
 /**/
-void
-completeword(void)
+int
+completeword(char **args)
 {
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
     if (c == '\t' && usetab())
-	selfinsert();
+	return selfinsert(args);
     else {
+	int ret;
 	if (lastambig == 1 && isset(BASHAUTOLIST) && !usemenu && !menucmp) {
-	    docomplete(COMP_LIST_COMPLETE);
+	    ret = docomplete(COMP_LIST_COMPLETE);
 	    lastambig = 2;
 	} else
-	    docomplete(COMP_COMPLETE);
+	    ret = docomplete(COMP_COMPLETE);
+	return ret;
     }
 }
 
 /**/
-void
-menucomplete(void)
+int
+menucomplete(char **args)
 {
     usemenu = 1;
     useglob = isset(GLOBCOMPLETE);
     if (c == '\t' && usetab())
-	selfinsert();
+	return selfinsert(args);
     else
-	docomplete(COMP_COMPLETE);
+	return docomplete(COMP_COMPLETE);
 }
 
 /**/
-void
-listchoices(void)
+int
+listchoices(char **args)
 {
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
-    docomplete(COMP_LIST_COMPLETE);
+    return docomplete(COMP_LIST_COMPLETE);
 }
 
 /**/
-void
-spellword(void)
+int
+spellword(char **args)
 {
     usemenu = useglob = 0;
-    docomplete(COMP_SPELL);
+    return docomplete(COMP_SPELL);
 }
 
 /**/
-void
-deletecharorlist(void)
+int
+deletecharorlist(char **args)
 {
-    Cmgroup mg = menugrp;
-    Cmatch *mc = menucur;
+    Cmgroup mg = minfo.group;
+    Cmatch *mc = minfo.cur;
+    int ret;
 
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
     if (cs != ll) {
 	fixsuffix();
-	deletechar();
+	ret = deletechar(args);
     } else
-	docomplete(COMP_LIST_COMPLETE);
+	ret = docomplete(COMP_LIST_COMPLETE);
 
-    menucur = mc;
-    menugrp = mg;
+    minfo.cur = mc;
+    minfo.group = mg;
+    return ret;
 }
 
 /**/
-void
-expandword(void)
+int
+expandword(char **args)
 {
     usemenu = useglob = 0;
     if (c == '\t' && usetab())
-	selfinsert();
+	return selfinsert(args);
     else
-	docomplete(COMP_EXPAND);
+	return docomplete(COMP_EXPAND);
 }
 
 /**/
-void
-expandorcomplete(void)
+int
+expandorcomplete(char **args)
 {
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
     if (c == '\t' && usetab())
-	selfinsert();
+	return selfinsert(args);
     else {
+	int ret;
 	if (lastambig == 1 && isset(BASHAUTOLIST) && !usemenu && !menucmp) {
-	    docomplete(COMP_LIST_COMPLETE);
+	    ret = docomplete(COMP_LIST_COMPLETE);
 	    lastambig = 2;
 	} else
-	    docomplete(COMP_EXPAND_COMPLETE);
+	    ret = docomplete(COMP_EXPAND_COMPLETE);
+	return ret;
     }
 }
 
 /**/
-void
-menuexpandorcomplete(void)
+int
+menuexpandorcomplete(char **args)
 {
     usemenu = 1;
     useglob = isset(GLOBCOMPLETE);
     if (c == '\t' && usetab())
-	selfinsert();
+	return selfinsert(args);
     else
-	docomplete(COMP_EXPAND_COMPLETE);
+	return docomplete(COMP_EXPAND_COMPLETE);
 }
 
 /**/
-void
-listexpand(void)
+int
+listexpand(char **args)
 {
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
-    docomplete(COMP_LIST_EXPAND);
+    return docomplete(COMP_LIST_EXPAND);
 }
 
 /**/
-void
-reversemenucomplete(void)
+int
+reversemenucomplete(char **args)
 {
-    if (!menucmp) {
-	menucomplete();
-	return;
-    }
+    if (!menucmp)
+	return menucomplete(args);
+
     HEAPALLOC {
-	if (menucur == menugrp->matches) {
+	if (minfo.cur == (minfo.group)->matches) {
 	    do {
-		if (!(menugrp = menugrp->prev))
-		    menugrp = lmatches;
-	    } while (!menugrp->mcount);
-	    menucur = menugrp->matches + menugrp->mcount - 1;
+		if (!(minfo.group = (minfo.group)->prev))
+		    minfo.group = lmatches;
+	    } while (!(minfo.group)->mcount);
+	    minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1;
 	} else
-	    menucur--;
+	    minfo.cur--;
 	metafy_line();
-	do_single(*menucur);
+	do_single(*(minfo.cur));
 	unmetafy_line();
     } LASTALLOC;
+    return 0;
 }
 
 /* Accepts the current completion and starts a new arg, *
  * with the next completions. This gives you a way to   *
  * accept several selections from the list of matches.  */
 
-static void
+/**/
+void
 acceptlast(void)
 {
     if (brbeg && *brbeg) {
@@ -542,26 +549,24 @@ acceptlast(void)
 	brbeg[l] = ',';
 	brbeg[l + 1] = '\0';
     } else {
-	cs = menupos + menulen + menuinsc;
+	cs = minfo.pos + minfo.len + minfo.insc;
 	iremovesuffix(' ', 1);
 
 	inststrlen(" ", 1, 1);
-	menuinsc = menulen = 0;
-	menupos = cs;
-	menuwe = 1;
+	minfo.insc = minfo.len = 0;
+	minfo.pos = cs;
+	minfo.we = 1;
     }
 }
 
 /**/
-void
-acceptandmenucomplete(void)
+int
+acceptandmenucomplete(char **args)
 {
-    if (!menucmp) {
-	feep();
-	return;
-    }
+    if (!menucmp)
+	return 1;
     acceptlast();
-    menucomplete();
+    return menucomplete(args);
 }
 
 /* These are flags saying if we are completing in the command *
@@ -788,11 +793,11 @@ check_param(char *s, int set, int test)
 /* The main entry point for completion. */
 
 /**/
-static void
+static int
 docomplete(int lst)
 {
     char *s, *ol;
-    int olst = lst, chl = 0, ne = noerrs, ocs;
+    int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0, omc = menucmp;
 
     if (showagain && validlist)
 	showinglist = -2;
@@ -803,7 +808,7 @@ docomplete(int lst)
     if (menucmp && lst != COMP_LIST_EXPAND && 
 	(!compwidget || compwidget == lastcompwidget)) {
 	do_menucmp(lst);
-	return;
+	return 0;
     }
     lastcompwidget = compwidget;
 
@@ -823,7 +828,7 @@ docomplete(int lst)
      * changed, do no more.                                               */
 
     if (doexpandhist())
-	return;
+	return 0;
 
     metafy_line();
 
@@ -874,8 +879,7 @@ docomplete(int lst)
 	    ll = strlen((char *) line);
 	    cs = ocs;
 	    unmetafy_line();
-	    feep();
-	    return;
+	    return 1;
 	}
 	ocs = cs;
 	cs = 0;
@@ -985,7 +989,7 @@ docomplete(int lst)
 	    inwhat = IN_CMD;
 
 	if (lst == COMP_SPELL) {
-	    char *x, *q;
+	    char *x, *q, *ox;
 
 	    for (q = s; *q; q++)
 		if (INULL(*q))
@@ -993,10 +997,11 @@ docomplete(int lst)
 	    cs = wb;
 	    foredel(we - wb);
 	    HEAPALLOC {
-		untokenize(x = dupstring(s));
+		untokenize(x = ox = dupstring(s));
 		if (*s == Tilde || *s == Equals || *s == String)
 		    *x = *s;
 		spckword(&x, 0, lincmd, 0);
+		ret = !strcmp(x, ox);
 	    } LASTALLOC;
 	    untokenize(x);
 	    inststr(x);
@@ -1032,18 +1037,30 @@ docomplete(int lst)
 				p++, skipparens(Inbrace, Outbrace, &p);
 			}
 		}
-		docompletion(s, lst, lincmd);
-	    }
+		ret = docompletion(s, lst, lincmd);
+	    } else
+		ret = !strcmp(ol, (char *) line);
 	} else
 	    /* Just do completion. */
-	    docompletion(s, lst, lincmd);
+	    ret = docompletion(s, lst, lincmd);
 	zsfree(s);
-    }
+    } else
+	ret = 1;
     /* Reset the lexer state, pop the heap. */
     lexrestore();
     popheap();
     zsfree(qword);
     unmetafy_line();
+
+    if (menucmp && !omc) {
+	struct chdata dat;
+
+	dat.matches = amatches;
+	dat.num = nmatches;
+	dat.cur = NULL;
+	runhookdef(MENUSTARTHOOK, (void *) &dat);
+    }
+    return ret;
 }
 
 /* Do completion, given that we are in the middle of a menu completion.  We *
@@ -1052,7 +1069,7 @@ docomplete(int lst)
  * insert the next completion.                                              */
 
 /**/
-static void
+void
 do_menucmp(int lst)
 {
     /* Just list the matches if the list was requested. */
@@ -1062,16 +1079,16 @@ do_menucmp(int lst)
     }
     /* Otherwise go to the next match in the array... */
     HEAPALLOC {
-	if (!*++menucur) {
+	if (!*++(minfo.cur)) {
 	    do {
-		if (!(menugrp = menugrp->next))
-		    menugrp = amatches;
-	    } while (!menugrp->mcount);
-	    menucur = menugrp->matches;
+		if (!(minfo.group = (minfo.group)->next))
+		    minfo.group = amatches;
+	    } while (!(minfo.group)->mcount);
+	    minfo.cur = minfo.group->matches;
 	}
 	/* ... and insert it into the command line. */
 	metafy_line();
-	do_single(*menucur);
+	do_single(*(minfo.cur));
 	unmetafy_line();
     } LASTALLOC;
 }
@@ -1425,7 +1442,6 @@ get_comp_string(void)
 		lexrestore();
 		goto start;
 	    }
-	    feep();
 	    noaliases = 0;
 	    lexrestore();
 	    LASTALLOC_RETURN NULL;
@@ -1484,11 +1500,14 @@ get_comp_string(void)
 			if (lev)
 			    lev--;
 		    }
-		wb++;
 		p = (char *) line + wb;
+		wb++;
 		if (wb && (*p == '[' || *p == '(') &&
-		    !skipparens(*p, (*p == '[' ? ']' : ')'), &p))
-			we = p - (char *) line;
+		    !skipparens(*p, (*p == '[' ? ']' : ')'), &p)) {
+			we = (p - (char *) line) - 1;
+			if (insubscr == 2)
+			    insubscr = 3;
+		}
 	    } else {
 		/* In mathematical expression, we complete parameter names  *
 		 * (even if they don't have a `$' in front of them).  So we *
@@ -1513,9 +1532,10 @@ get_comp_string(void)
 		varname = ztrdup((char *) line + i + 1);
 		line[wb - 1] = sav;
 		if ((keypm = (Param) paramtab->getnode(paramtab, varname)) &&
-		    (keypm->flags & PM_HASHED))
-		    insubscr = 2;
-		else
+		    (keypm->flags & PM_HASHED)) {
+		    if (insubscr != 3)
+			insubscr = 2;
+		} else
 		    insubscr = 1;
 	    }
 	}
@@ -1734,11 +1754,8 @@ doexpansion(char *s, int lst, int olst, int explincmd)
 	}
 	if (errflag)
 	    goto end;
-	if (empty(vl) || !*(char *)peekfirst(vl)) {
-	    if (!noerrs)
-		feep();
+	if (empty(vl) || !*(char *)peekfirst(vl))
 	    goto end;
-	}
 	if (peekfirst(vl) == (void *) ss ||
 		(olst == COMP_EXPAND_COMPLETE &&
 		 !nextnode(firstnode(vl)) && *s == Tilde &&
@@ -1748,8 +1765,6 @@ doexpansion(char *s, int lst, int olst, int explincmd)
 	     * expandorcomplete was called, otherwise, just beep.     */
 	    if (lst == COMP_EXPAND_COMPLETE)
 		docompletion(s, COMP_COMPLETE, explincmd);
-	    else
-		feep();
 	    goto end;
 	}
 	if (lst == COMP_LIST_EXPAND) {
@@ -3776,9 +3791,10 @@ addmatches(Cadata dat, char **argv)
 		} else
 		    dat->prpre = dupstring(dat->prpre);
 		/* Select the group in which to store the matches. */
-		if (dat->group) {
+		if (dat->group || dat->ylist) {
 		    endcmgroup(NULL);
-		    begcmgroup(dat->group, (dat->aflags & CAF_NOSORT));
+		    begcmgroup((dat->ylist ? NULL : dat->group),
+			       (dat->aflags & CAF_NOSORT));
 		    if (dat->aflags & CAF_NOSORT)
 			mgroup->flags |= CGF_NOSORT;
 		} else {
@@ -3800,6 +3816,10 @@ addmatches(Cadata dat, char **argv)
 		    lpre = quotename(lpre, NULL);
 		    lsuf = quotename(lsuf, NULL);
 		}
+		if (dat->ppre)
+		    dat->ppre = quotename(dat->ppre, NULL);
+		if (dat->psuf)
+		    dat->psuf = quotename(dat->psuf, NULL);
 	    }
 	    /* Walk through the matches given. */
 	    for (; (s = *argv); argv++) {
@@ -3825,8 +3845,10 @@ addmatches(Cadata dat, char **argv)
 		    }
 		}
 		if (!(dat->aflags & CAF_MATCH)) {
-		    ms = ((dat->aflags & CAF_QUOTE) ? dupstring(s) :
-			  quotename(s, NULL));
+		    if (dat->aflags & CAF_QUOTE)
+			ms = dupstring(s);
+		    else
+			sl = strlen(ms = quotename(s, NULL));
 		    lc = bld_parts(ms, sl, -1, NULL);
 		    isexact = 0;
 		} else if (!(ms = comp_match(lpre, lsuf, s, cp, &lc,
@@ -3865,6 +3887,10 @@ addmatches(Cadata dat, char **argv)
 		set_param(dat->opar, oparl);
 	    if (dat->dpar)
 		set_param(dat->dpar, dparl);
+	    if (dat->ylist) {
+		endcmgroup(get_user_var(dat->ylist));
+		begcmgroup("default", 0);
+	    }
 	} LASTALLOC;
     } SWITCHBACKHEAPS;
 
@@ -4288,9 +4314,11 @@ gen_matches_files(int dirs, int execs, int all)
 }
 
 /**/
-static void
+static int
 docompletion(char *s, int lst, int incmd)
 {
+    int ret = 0;
+
     HEAPALLOC {
 	char *opm;
 	LinkNode n;
@@ -4328,8 +4356,8 @@ docompletion(char *s, int lst, int incmd)
 	    foredel(ll);
 	    inststr(origline);
 	    cs = origcs;
-	    feep();
 	    clearlist = 1;
+	    ret = 1;
 	    goto compend;
 	}
 	if (comppatmatch && *comppatmatch && comppatmatch != opm)
@@ -4339,17 +4367,17 @@ docompletion(char *s, int lst, int incmd)
 	    showinglist = -2;
 	else if (useline) {
 	    /* We have matches. */
-	    if (nmatches > 1)
+	    if (nmatches > 1) {
 		/* There is more than one match. */
-		    do_ambiguous();
-
-	    else if (nmatches == 1) {
+		ret = do_ambiguous();
+	    } else if (nmatches == 1) {
 		/* Only one match. */
 		Cmgroup m = amatches;
 
 		while (!m->mcount)
 		    m = m->next;
-		menucur = NULL;
+		minfo.cur = NULL;
+		minfo.asked = 0;
 		do_single(m->matches[0]);
 		invalidatelist();
 	    }
@@ -4363,7 +4391,7 @@ docompletion(char *s, int lst, int incmd)
 	    int up = 0, tr = 1, nn = 0;
 
 	    if (!nmatches)
-		feep();
+		ret = 1;
 
 	    while (g) {
 		if ((e = g->expls))
@@ -4404,6 +4432,7 @@ docompletion(char *s, int lst, int incmd)
 	    cs = ll;
 	popheap();
     } LASTALLOC;
+    return ret;
 }
 
 /* This calls the given function for new style completion. */
@@ -4604,8 +4633,8 @@ callcompfunc(char *s, char *fn)
 	    else
 		compoldlist = "yes";
 	    kset |= CP_OLDLIST;
-	    if (menucur) {
-		sprintf(buf, "%d", (*menucur)->gnum);
+	    if (minfo.cur) {
+		sprintf(buf, "%d", (*(minfo.cur))->gnum);
 		compoldins = buf;
 		kset |= CP_OLDINS;
 	    } else
@@ -4623,7 +4652,20 @@ callcompfunc(char *s, char *fn)
 	makezleparams(1);
 	sfcontext = SFC_CWIDGET;
 	NEWHEAPS(compheap) {
-	    doshfunc(fn, list, NULL, 0, 1);
+	    LinkList largs = NULL;
+	    int olv = lastval;
+
+	    if (*cfargs) {
+		char **p = cfargs;
+
+		largs = newlinklist();
+		addlinknode(largs, dupstring(fn));
+		while (*p)
+		    addlinknode(largs, dupstring(*p++));
+	    }
+	    doshfunc(fn, list, largs, 0, 0);
+	    cfret = lastval;
+	    lastval = olv;
 	} OLDHEAPS;
 	sfcontext = osc;
 	endparamscope();
@@ -4673,7 +4715,7 @@ callcompfunc(char *s, char *fn)
 	    movetoend = 2;
 
 	oldlist = (hasperm && compoldlist && !strcmp(compoldlist, "keep"));
-	oldins = (hasperm && menucur &&
+	oldins = (hasperm && minfo.cur &&
 		  compoldins && !strcmp(compoldins, "keep"));
 
 	zfree(comprpms, CP_REALPARAMS * sizeof(Param));
@@ -4710,7 +4752,7 @@ makecomplist(char *s, int incmd, int lst)
     int onm = nmatches, osi = movefd(0);
 
     /* Inside $... ? */
-    if (compfunc && (p = check_param(s, 0, NULL)))
+    if (compfunc && (p = check_param(s, 0, 0)))
 	os = s = p;
 
     /* We build a copy of the list of matchers to use to make sure that this
@@ -5281,10 +5323,10 @@ makecomplistglobal(char *os, int incmd, int lst, int flags)
         cc = &cc_default;
 	keypm = NULL;
     } else if (linwhat == IN_MATH) {
-	if (insubscr == 2) {
+	if (insubscr >= 2) {
 	    /* Inside subscript of assoc array, complete keys. */
 	    cc_dummy.mask = 0;
-	    cc_dummy.suffix = "]";
+	    cc_dummy.suffix = (insubscr == 2 ? "]" : "");
 	} else {
 	    /* Other math environment, complete paramete names. */
 	    keypm = NULL;
@@ -5590,7 +5632,7 @@ makecomplistext(Compctl occ, char *os, int incmd)
 		    case CCT_RANGEPAT:
 			if (cc->type == CCT_RANGEPAT)
 			    tokenize(sc = dupstring(cc->u.l.a[i]));
-			for (j = clwpos; j; j--) {
+			for (j = clwpos - 1; j > 0; j--) {
 			    untokenize(s = ztrdup(clwords[j]));
 			    if (cc->type == CCT_RANGESTR)
 				sc = rembslash(cc->u.l.a[i]);
@@ -5791,7 +5833,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	ic = 0;
 
     /* Check if we have to complete a parameter name... */
-    if (!incompfunc && (p = check_param(s, 1, NULL))) {
+    if (!incompfunc && (p = check_param(s, 1, 0))) {
 	s = p;
 	/* And now make sure that we complete parameter names. */
 	cc = &cc_dummy;
@@ -6267,8 +6309,15 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	maketildelist();
 	addwhat = oaw;
     }
-    if (cc->widget)
+    if (cc->widget) {
+	char **ocfa = cfargs;
+	int ocfr = cfret;
+
+	cfargs = zlenoargs;
 	callcompfunc(os, cc->widget);
+	cfargs = ocfa;
+	cfret = ocfr;
+    }
     if (cc->func) {
 	/* This handles the compctl -K flag. */
 	List list;
@@ -6313,20 +6362,14 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) {
 	/* Get job names. */
 	int i;
-	char *j, *jj;
+	char *j;
 
 	for (i = 0; i < MAXJOB; i++)
 	    if ((jobtab[i].stat & STAT_INUSE) &&
 		jobtab[i].procs && jobtab[i].procs->text) {
 		int stopped = jobtab[i].stat & STAT_STOPPED;
 
-		j = jj = dupstring(jobtab[i].procs->text);
-		/* Find the first word. */
-		for (; *jj; jj++)
-		    if (*jj == ' ') {
-			*jj = '\0';
-			break;
-		    }
+		j = dupstring(jobtab[i].procs->text);
 		if ((cc->mask & CC_JOBS) ||
 		    (stopped && (cc->mask & CC_STOPPED)) ||
 		    (!stopped && (cc->mask & CC_RUNNING)))
@@ -6507,7 +6550,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     if (cc->subcmd) {
 	/* Handle -l sub-completion. */
 	char **ow = clwords, *os = cmdstr, *ops = NULL;
-	int oldn = clwnum, oldp = clwpos;
+	int oldn = clwnum, oldp = clwpos, br;
 	unsigned long occ = ccont;
 	
 	ccont = CC_CCCONT;
@@ -6523,21 +6566,22 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    erange = 1;
 	clwnum = erange - brange + 1;
 	clwpos = clwpos - brange;
-	
+	br = brange;
+
 	if (cc->subcmd[0]) {
 	    /* And probably put the command name given to the flag *
 	     * in the array.                                       */
 	    clwpos++;
 	    clwnum++;
 	    incmd = 0;
-	    ops = clwords[brange - 1];
-	    clwords[brange - 1] = cc->subcmd;
+	    ops = clwords[br - 1];
+	    clwords[br - 1] = ztrdup(cc->subcmd);
 	    cmdstr = ztrdup(cc->subcmd);
-	    clwords += brange - 1;
+	    clwords += br - 1;
 	} else {
-	    cmdstr = ztrdup(clwords[brange]);
+	    cmdstr = ztrdup(clwords[br]);
 	    incmd = !clwpos;
-	    clwords += brange;
+	    clwords += br;
 	}
 	/* Produce the matches. */
 	makecomplistcmd(s, incmd, CFN_FIRST);
@@ -6548,8 +6592,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	cmdstr = os;
 	clwnum = oldn;
 	clwpos = oldp;
-	if (ops)
-	    clwords[brange - 1] = ops;
+	if (ops) {
+	    zsfree(clwords[br - 1]);
+	    clwords[br - 1] = ops;
+	}
 	ccont = occ;
     }
     if (cc->substr)
@@ -6570,7 +6616,8 @@ invalidatelist(void)
     if (validlist)
 	freematches();
     lastambig = menucmp = validlist = showinglist = fromcomp = 0;
-    menucur = NULL;
+    minfo.cur = NULL;
+    minfo.asked = 0;
     compwidget = NULL;
 }
 
@@ -7337,19 +7384,20 @@ instmatch(Cmatch m, int *scs)
 /* Handle the case were we found more than one match. */
 
 /**/
-static void
+static int
 do_ambiguous(void)
 {
+    int ret = 0;
     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 && useexact && !(fromcomp & FC_LINE)) {
-	menucur = NULL;
+	minfo.cur = NULL;
 	do_single(ainfo->exactm);
 	invalidatelist();
-	return;
+	return ret;
     }
     /* Setting lastambig here means that the completion is ambiguous and *
      * AUTO_MENU might want to start a menu completion next time round,  *
@@ -7369,7 +7417,8 @@ do_ambiguous(void)
 	int atend = (cs == we), oll = ll, la, eq, tcs;
 	VARARR(char, oline, ll);
 
-	menucur = NULL;
+	minfo.cur = NULL;
+	minfo.asked = 0;
 
 	/* Copy the line buffer to be able to easily test if it changed. */
 	memcpy(oline, line, ll);
@@ -7418,19 +7467,21 @@ do_ambiguous(void)
 	    fromcomp = fc;
 	    lastambig = 0;
 	    clearlist = 1;
-	    return;
+	    return ret;
 	}
     } else
-	return;
+	return ret;
 
     /* At this point, we might want a completion listing.  Show the listing *
      * if it is needed.                                                     */
     if (isset(LISTBEEP))
-	feep();
-    if (uselist && usemenu != 2 &&
-	(!showinglist || (usemenu == 3 && !oldlist)) &&
+	ret = 1;
+    if (uselist && (usemenu != 2 || (!showinglist && !oldlist)) &&
+	((!showinglist && (!listshown || !oldlist)) ||
+	 (usemenu == 3 && !oldlist)) &&
 	(smatches >= 2 || (compforcelist && *compforcelist)))
 	showinglist = -2;
+    return ret;
 }
 
 /* This is a stat that ignores backslashes in the filename.  The `ls' *
@@ -7440,7 +7491,7 @@ do_ambiguous(void)
  * (l)stat().                                                         */
 
 /**/
-static int
+int
 ztat(char *nam, struct stat *buf, int ls)
 {
     char b[PATH_MAX], *p;
@@ -7458,7 +7509,7 @@ ztat(char *nam, struct stat *buf, int ls)
 /* Insert a single match in the command line. */
 
 /**/
-static void
+void
 do_single(Cmatch m)
 {
     int l, sr = 0, scs;
@@ -7471,39 +7522,39 @@ do_single(Cmatch m)
 
     fixsuffix();
 
-    if (!menucur) {
+    if (!minfo.cur) {
 	/* We are currently not in a menu-completion, *
 	 * so set the position variables.             */
-	menupos = wb;
-	menuwe = (movetoend >= 2 || (movetoend == 1 && !menucmp));
-	menuend = we;
+	minfo.pos = wb;
+	minfo.we = (movetoend >= 2 || (movetoend == 1 && !menucmp));
+	minfo.end = we;
     }
     /* If we are already in a menu-completion or if we have done a *
      * glob completion, we have to delete some of the stuff on the *
      * command line.                                               */
-    if (menucur)
-	l = menulen + menuinsc;
+    if (minfo.cur)
+	l = minfo.len + minfo.insc;
     else
 	l = we - wb;
 
-    menuinsc = 0;
-    cs = menupos;
+    minfo.insc = 0;
+    cs = minfo.pos;
     foredel(l);
 
     /* And then we insert the new string. */
-    menulen = instmatch(m, &scs);
-    menuend = cs;
-    cs = menupos + menulen;
+    minfo.len = instmatch(m, &scs);
+    minfo.end = cs;
+    cs = minfo.pos + minfo.len;
 
     if (m->suf) {
 	havesuff = 1;
-	menuinsc = ztrlen(m->suf);
-	menulen -= menuinsc;
-	if (menuwe) {
-	    menuend += menuinsc;
+	minfo.insc = ztrlen(m->suf);
+	minfo.len -= minfo.insc;
+	if (minfo.we) {
+	    minfo.end += minfo.insc;
 	    if (m->flags & CMF_REMOVE) {
-		makesuffixstr(m->remf, m->rems, menuinsc);
-		if (menuinsc == 1)
+		makesuffixstr(m->remf, m->rems, minfo.insc);
+		if (minfo.insc == 1)
 		    suffixlen[STOUC(m->suf[0])] = 1;
 	    }
 	}
@@ -7519,11 +7570,11 @@ do_single(Cmatch m)
 	    cs += eparq;
 	    for (pq = parq; pq; pq--)
 		inststrlen("\"", 1, 1);
-	    menuinsc += parq;
+	    minfo.insc += parq;
 	    inststrlen("}", 1, 1);
-	    menuinsc++;
-	    if (menuwe)
-		menuend += menuinsc;
+	    minfo.insc++;
+	    if (minfo.we)
+		minfo.end += minfo.insc;
 	}
 	if ((m->flags & CMF_FILE) || (m->ripre && isset(AUTOPARAMSLASH))) {
 	    /* If we have a filename or we completed a parameter name      *
@@ -7559,10 +7610,10 @@ do_single(Cmatch m)
 		/* It is a directory, so add the slash. */
 		havesuff = 1;
 		inststrlen("/", 1, 1);
-		menuinsc++;
-		if (menuwe)
-		    menuend++;
-		if (!menucmp || menuwe) {
+		minfo.insc++;
+		if (minfo.we)
+		    minfo.end++;
+		if (!menucmp || minfo.we) {
 		    if (m->remf || m->rems)
 			makesuffixstr(m->remf, m->rems, 1);
 		    else if (isset(AUTOREMOVESLASH)) {
@@ -7572,8 +7623,8 @@ do_single(Cmatch m)
 		}
 	    }
 	}
-	if (!menuinsc)
-	    cs = menupos + menulen;
+	if (!minfo.insc)
+	    cs = minfo.pos + minfo.len;
     }
     /* If completing in a brace expansion... */
     if (brbeg) {
@@ -7589,9 +7640,9 @@ do_single(Cmatch m)
 	    cs = scs;
 	    havesuff = 1;
 	    inststrlen(",", 1, 1);
-	    menuinsc++;
+	    minfo.insc++;
 	    makesuffix(1);
-	    if ((!menucmp || menuwe) && isset(AUTOPARAMKEYS))
+	    if ((!menucmp || minfo.we) && isset(AUTOPARAMKEYS))
 		suffixlen[','] = suffixlen['}'] = 1;
 	}
     } else if (!havesuff && (!(m->flags & CMF_FILE) || !sr)) {
@@ -7600,20 +7651,33 @@ do_single(Cmatch m)
 	 * the string doesn't name an existing file.             */
 	if (m->autoq && (!m->isuf || m->isuf[0] != m->autoq)) {
 	    inststrlen(&(m->autoq), 1, 1);
-	    menuinsc++;
+	    minfo.insc++;
 	}
-	if (!menucmp) {
+	if (!menucmp && usemenu != 3) {
 	    inststrlen(" ", 1, 1);
-	    menuinsc++;
-	    if (menuwe)
+	    minfo.insc++;
+	    if (minfo.we)
 		makesuffix(1);
 	}
     }
-    if (menuwe && m->ripre && isset(AUTOPARAMKEYS))
-	makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), menuinsc - parq);
+    if (minfo.we && m->ripre && isset(AUTOPARAMKEYS))
+	makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), minfo.insc - parq);
 
-    if ((menucmp && !menuwe) || !movetoend)
-	cs = menuend;
+    if ((menucmp && !minfo.we) || !movetoend)
+	cs = minfo.end;
+    {
+	Cmatch *om = minfo.cur;
+	struct chdata dat;
+
+	dat.matches = amatches;
+	dat.num = nmatches;
+	dat.cur = m;
+
+	if (menucmp)
+	    minfo.cur = &m;
+	runhookdef(INSERTMATCHHOOK, (void *) &dat);
+	minfo.cur = om;
+    }
 }
 
 /* This maps the value in v into the range [0,m-1], decrementing v
@@ -7643,40 +7707,42 @@ do_ambig_menu(void)
 
     if (usemenu != 3) {
 	menucmp = 1;
-	menucur = NULL;
+	minfo.cur = NULL;
     } else {
 	if (oldlist) {
 	    if (oldins)
 		acceptlast();
 	} else
-	    menucur = NULL;
+	    minfo.cur = NULL;
     }
     if (insgroup) {
 	insgnum = comp_mod(insgnum, permgnum);
-	for (menugrp = amatches;
-	     menugrp && menugrp->num != insgnum + 1;
-	     menugrp = menugrp->next);
-	if (!menugrp || !menugrp->mcount) {
-	    menucur = NULL;
+	for (minfo.group = amatches;
+	     minfo.group && (minfo.group)->num != insgnum + 1;
+	     minfo.group = (minfo.group)->next);
+	if (!minfo.group || !(minfo.group)->mcount) {
+	    minfo.cur = NULL;
+	    minfo.asked = 0;
 	    return;
 	}
-	insmnum = comp_mod(insmnum, menugrp->mcount);
+	insmnum = comp_mod(insmnum, (minfo.group)->mcount);
     } else {
 	int c = 0;
 
 	insmnum = comp_mod(insmnum, permmnum);
-	for (menugrp = amatches;
-	     menugrp && (c += menugrp->mcount) <= insmnum;
-	     menugrp = menugrp->next)
-	    insmnum -= menugrp->mcount;
-	if (!menugrp) {
-	    menucur = NULL;
+	for (minfo.group = amatches;
+	     minfo.group && (c += (minfo.group)->mcount) <= insmnum;
+	     minfo.group = (minfo.group)->next)
+	    insmnum -= (minfo.group)->mcount;
+	if (!minfo.group) {
+	    minfo.cur = NULL;
+	    minfo.asked = 0;
 	    return;
 	}
     }
-    mc = menugrp->matches + insmnum;
+    mc = (minfo.group)->matches + insmnum;
     do_single(*mc);
-    menucur = mc;
+    minfo.cur = mc;
 }
 
 /* Return the length of the common prefix of s and t. */
@@ -7715,7 +7781,7 @@ sfxlen(char *s, char *t)
  * It returns the number of lines printed.       */
 
 /**/
-static int
+int
 printfmt(char *fmt, int n, int dopr)
 {
     char *p = fmt, nc[DIGBUFSIZE];
@@ -7798,7 +7864,8 @@ printfmt(char *fmt, int n, int dopr)
 
 /* This skips over matches that are not to be listed. */
 
-static Cmatch *
+/**/
+Cmatch *
 skipnolist(Cmatch *p)
 {
     while (*p && ((*p)->flags & CMF_NOLIST))
@@ -7813,11 +7880,7 @@ skipnolist(Cmatch *p)
 void
 listmatches(void)
 {
-    Cmgroup g;
-    Cmatch *p, m;
-    Cexpl *e;
-    int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0;
-    int of = isset(LISTTYPES), opl = 0;
+    struct chdata dat;
 
 #ifdef DEBUG
     /* Sanity check */
@@ -7827,6 +7890,22 @@ listmatches(void)
     }
 #endif
 
+    dat.matches = amatches;
+    dat.num = nmatches;
+    dat.cur = NULL;
+    runhookdef(LISTMATCHESHOOK, (void *) &dat);
+}
+
+/**/
+int
+ilistmatches(Hookdef dummy, Chdata dat)
+{
+    Cmgroup g;
+    Cmatch *p, m;
+    Cexpl *e;
+    int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0;
+    int of = isset(LISTTYPES), opl = 0;
+
     /* Set the cursor below the prompt. */
     trashzle();
     showinglist = listshown = 0;
@@ -7911,8 +7990,9 @@ listmatches(void)
     }
 
     /* Maybe we have to ask if the user wants to see the list. */
-    if ((complistmax && nlist > complistmax) ||
-	(!complistmax && nlines >= lines)) {
+    if ((!minfo.cur || !minfo.asked) &&
+	((complistmax && nlist > complistmax) ||
+	 (!complistmax && nlines >= lines))) {
 	int qup;
 	zsetterm();
 	qup = printfmt("zsh: do you wish to see all %n possibilities? ", nlist, 1);
@@ -7926,7 +8006,9 @@ listmatches(void)
 		tcmultout(TCUP, TCMULTUP, nlnct);
 	    } else
 		putc('\n', shout);
-	    return;
+	    if (minfo.cur)
+		minfo.asked = 2;
+	    return 0;
 	}
 	if (clearflag) {
 	    putc('\r', shout);
@@ -7936,6 +8018,8 @@ listmatches(void)
 	} else
 	    putc('\n', shout);
 	settyinfo(&shttyinfo);
+	if (minfo.cur)
+	    minfo.asked = 1;
     }
 
     /* Now print the matches. */
@@ -7956,7 +8040,7 @@ listmatches(void)
 		e++;
 	    }
 	}
-	if (pp) {
+	if (pp && *pp) {
 	    if (pnl) {
 		putc('\n', shout);
 		pnl = 0;
@@ -8055,6 +8139,7 @@ listmatches(void)
 	    clearflag = 0, putc('\n', shout);
     } else
 	putc('\n', shout);
+    return 0;
 }
 
 /* This is used to print expansions. */
@@ -8064,9 +8149,9 @@ void
 listlist(LinkList l)
 {
     struct cmgroup dg;
-    Cmgroup am = amatches;
     int vl = validlist, sm = smatches;
     char *oclp = complastprompt;
+    Cmgroup am = amatches;
 
     if (listshown)
 	showagain = 1;
@@ -8074,12 +8159,12 @@ listlist(LinkList l)
     complastprompt = ((zmult == 1) == !!isset(ALWAYSLASTPROMPT) ? "yes" : NULL);
     smatches = 1;
     validlist = 1;
-    amatches = &dg;
     memset(&dg, 0, sizeof(struct cmgroup));
     dg.ylist = (char **) makearray(l, 1, &(dg.lcount), NULL);
-    listmatches();
-
+    amatches = &dg;
+    ilistmatches(NULL, NULL);
     amatches = am;
+
     validlist = vl;
     smatches = sm;
     complastprompt = oclp;
@@ -8153,20 +8238,22 @@ doexpandhist(void)
 }
 
 /**/
-void
-magicspace(void)
+int
+magicspace(char **args)
 {
+    int ret;
     c = ' ';
-    selfinsert();
-    doexpandhist();
+    ret = selfinsert(args);
+    return !doexpandhist();
 }
 
 /**/
-void
-expandhistory(void)
+int
+expandhistory(char **args)
 {
     if (!doexpandhist())
-	feep();
+	return 1;
+    return 0;
 }
 
 static int cmdwb, cmdwe;
@@ -8210,19 +8297,17 @@ getcurcmd(void)
 }
 
 /**/
-void
-processcmd(void)
+int
+processcmd(char **args)
 {
     char *s;
     int m = zmult;
 
     s = getcurcmd();
-    if (!s) {
-	feep();
-	return;
-    }
+    if (!s)
+	return 1;
     zmult = 1;
-    pushline();
+    pushline(zlenoargs);
     zmult = m;
     inststr(bindk->nam);
     inststr(" ");
@@ -8232,11 +8317,12 @@ processcmd(void)
     } LASTALLOC;
     zsfree(s);
     done = 1;
+    return 0;
 }
 
 /**/
-void
-expandcmdpath(void)
+int
+expandcmdpath(char **args)
 {
     int oldcs = cs, na = noaliases;
     char *s, *str;
@@ -8244,16 +8330,12 @@ expandcmdpath(void)
     noaliases = 1;
     s = getcurcmd();
     noaliases = na;
-    if (!s || cmdwb < 0 || cmdwe < cmdwb) {
-	feep();
-	return;
-    }
+    if (!s || cmdwb < 0 || cmdwe < cmdwb)
+	return 1;
     str = findcmd(s, 1);
     zsfree(s);
-    if (!str) {
-	feep();
-	return;
-    }
+    if (!str)
+	return 1;
     cs = cmdwb;
     foredel(cmdwe - cmdwb);
     spaceinline(strlen(str));
@@ -8263,16 +8345,20 @@ expandcmdpath(void)
 	cs += cmdwe - cmdwb + strlen(str);
     if (cs > ll)
 	cs = ll;
+    return 0;
 }
 
 /* Extra function added by AR Iano-Fletcher. */
 /* This is a expand/complete in the vein of wash. */
 
 /**/
-void
-expandorcompleteprefix(void)
+int
+expandorcompleteprefix(char **args)
 {
+    int ret;
+
     comppref = 1;
-    expandorcomplete();
+    ret = expandorcomplete(args);
     comppref = 0;
+    return ret;
 }
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 8524fd21e..bd2f39a06 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -409,19 +409,11 @@ showmsg(char const *msg)
 /* handle the error flag */
 
 /**/
-void
-feep(void)
-{
-    feepflag = 1;
-}
-
-/**/
-void
-handlefeep(void)
+int
+handlefeep(char **args)
 {
-    if(feepflag)
-	zbeep();
-    feepflag = 0;
+    zbeep();
+    return 0;
 }
 
 /***************/
@@ -554,18 +546,17 @@ setlastline(void)
 /* move backwards through the change list */
 
 /**/
-void
-undo(void)
+int
+undo(char **args)
 {
     handleundo();
     do {
-	if(!curchange->prev) {
-	    feep();
-	    return;
-	}
+	if(!curchange->prev)
+	    return 1;
 	unapplychange(curchange = curchange->prev);
     } while(curchange->flags & CH_PREV);
     setlastline();
+    return 0;
 }
 
 /**/
@@ -592,19 +583,18 @@ unapplychange(struct change *ch)
 /* move forwards through the change list */
 
 /**/
-void
-redo(void)
+int
+redo(char **args)
 {
     handleundo();
     do {
-	if(!curchange->next) {
-	    feep();
-	    return;
-	}
+	if(!curchange->next)
+	    return 1;
 	applychange(curchange);
 	curchange = curchange->next;
     } while(curchange->prev->flags & CH_NEXT);
     setlastline();
+    return 0;
 }
 
 /**/
@@ -631,8 +621,8 @@ applychange(struct change *ch)
 /* vi undo: toggle between the end of the undo list and the preceding point */
 
 /**/
-void
-viundochange(void)
+int
+viundochange(char **args)
 {
     handleundo();
     if(curchange->next) {
@@ -641,6 +631,7 @@ viundochange(void)
 	    curchange = curchange->next;
 	} while(curchange->next);
 	setlastline();
+	return 0;
     } else
-	undo();
+	return undo(args);
 }
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
index 5b1548e25..71f766739 100644
--- a/Src/Zle/zle_vi.c
+++ b/Src/Zle/zle_vi.c
@@ -102,10 +102,8 @@ vigetkey(void)
     char m[3], *str;
     Thingy cmd;
 
-    if((c = getkey(0)) == EOF) {
-	feep();
+    if((c = getkey(0)) == EOF)
 	return -1;
-    }
 
     m[0] = c;
     metafy(m, 1, META_NOALLOC);
@@ -115,13 +113,10 @@ vigetkey(void)
 	cmd = t_undefinedkey;
 
     if (!cmd || cmd == Th(z_sendbreak)) {
-	feep();
 	return -1;
     } else if (cmd == Th(z_quotedinsert)) {
-	if ((c = getkey(0)) == EOF) {
-	    feep();
+	if ((c = getkey(0)) == EOF)
 	    return -1;
-	}
     } else if(cmd == Th(z_viquotedinsert)) {
 	char sav = line[cs];
 
@@ -129,10 +124,8 @@ vigetkey(void)
 	zrefresh();
 	c = getkey(0);
 	line[cs] = sav;
-	if(c == EOF) {
-	    feep();
+	if(c == EOF)
 	    return -1;
-	}
     } else if (cmd == Th(z_vicmdmode))
 	return -1;
     return c;
@@ -142,7 +135,7 @@ vigetkey(void)
 static int
 getvirange(int wf)
 {
-    int pos = cs;
+    int pos = cs, ret = 0;
     int mult1 = zmult, hist1 = histline;
     Thingy k2;
 
@@ -168,39 +161,37 @@ getvirange(int wf)
 		k2 == Th(z_sendbreak)) {
 	    wordflag = 0;
 	    virangeflag = 0;
-	    feep();
 	    return -1;
 	}
-	if(k2 == bindk)
-	    /* The command key is repeated: a number of lines is used. */
-	    dovilinerange();
-	else
-	    execzlefunc(k2);
+	/*
+	 * With k2 == bindk, the command key is repeated:
+	 * a number of lines is used.  If the function used
+	 * returns 1, we fail.
+	 */
+	if ((k2 == bindk) ? dovilinerange() : execzlefunc(k2, zlenoargs))
+	    ret = -1;
 	if(vichgrepeat)
 	    zmult = mult1;
 	else
 	    zmult = mult1 * zmod.tmult;
-    } while(prefixflag);
+    } while(prefixflag && !ret);
     wordflag = 0;
     virangeflag = 0;
 
     /* It is an error to use a non-movement command to delimit the *
      * range.  We here reject the case where the command modified  *
      * the line, or selected a different history line.             */
-    if(histline != hist1 || ll != lastll || memcmp(line, lastline, ll)) {
+    if (histline != hist1 || ll != lastll || memcmp(line, lastline, ll)) {
 	histline = hist1;
 	memcpy(line, lastline, ll = lastll);
 	cs = pos;
-	feep();
 	return -1;
     }
 
     /* Can't handle an empty file.  Also, if the movement command *
      * failed, or didn't move, it is an error.                    */
-    if (!ll || (cs == pos && virangeflag != 2)) {
-	feep();
+    if (!ll || (cs == pos && virangeflag != 2) || ret == -1)
 	return -1;
-    }
 
     /* vi-match-bracket changes the value of virangeflag when *
      * moving to the opening bracket, meaning that we need to *
@@ -233,7 +224,7 @@ getvirange(int wf)
 }
 
 /**/
-static void
+static int
 dovilinerange(void)
 {
     int pos = cs, n = zmult;
@@ -243,17 +234,14 @@ dovilinerange(void)
      * downward, otherwise upward.  The repeat count gives the    *
      * number of lines.                                           */
     vilinerange = 1;
-    if (!n) {
-	feep();
-	return;
-    }
+    if (!n)
+	return 1;
     if (n > 0) {
 	while(n-- && cs <= ll)
 	    cs = findeol() + 1;
 	if (n != -1) {
 	    cs = pos;
-	    feep();
-	    return;
+	    return 1;
 	}
 	cs--;
     } else {
@@ -261,123 +249,127 @@ dovilinerange(void)
 	    cs = findbol() - 1;
 	if (n != 1) {
 	    cs = pos;
-	    feep();
-	    return;
+	    return 1;
 	}
 	cs++;
     }
     virangeflag = 2;
+    return 0;
 }
 
 /**/
-void
-viaddnext(void)
+int
+viaddnext(char **args)
 {
     if (cs != findeol())
 	cs++;
     startvitext(1);
+    return 0;
 }
 
 /**/
-void
-viaddeol(void)
+int
+viaddeol(char **args)
 {
     cs = findeol();
     startvitext(1);
+    return 0;
 }
 
 /**/
-void
-viinsert(void)
+int
+viinsert(char **args)
 {
     startvitext(1);
+    return 0;
 }
 
 /**/
-void
-viinsertbol(void)
+int
+viinsertbol(char **args)
 {
-    vifirstnonblank();
+    vifirstnonblank(zlenoargs);
     startvitext(1);
+    return 0;
 }
 
 /**/
-void
-videlete(void)
+int
+videlete(char **args)
 {
-    int c2;
+    int c2, ret = 1;
 
     startvichange(1);
     if ((c2 = getvirange(0)) != -1) {
 	forekill(c2 - cs, 0);
+	ret = 0;
 	if (vilinerange && ll) {
 	    if (cs == ll)
 		cs--;
 	    foredel(1);
-	    vifirstnonblank();
+	    vifirstnonblank(zlenoargs);
 	}
     }
     vichgflag = 0;
+    return ret;
 }
 
 /**/
-void
-videletechar(void)
+int
+videletechar(char **args)
 {
     int n = zmult;
 
     startvichange(-1);
     /* handle negative argument */
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	vibackwarddeletechar();
+	ret = vibackwarddeletechar(args);
 	zmult = n;
-	return;
+	return ret;
     }
     /* it is an error to be on the end of line */
-    if (cs == ll || line[cs] == '\n') {
-	feep();
-	return;
-    }
+    if (cs == ll || line[cs] == '\n')
+	return 1;
     /* Put argument into the acceptable range -- it is not an error to  *
      * specify a greater count than the number of available characters. */
     if (n > findeol() - cs)
 	n = findeol() - cs;
     /* do the deletion */
     forekill(n, 0);
+    return 0;
 }
 
 /**/
-void
-vichange(void)
+int
+vichange(char **args)
 {
-    int c2;
+    int c2, ret = 1;
 
     startvichange(1);
     if ((c2 = getvirange(1)) != -1) {
+	ret = 0;
 	forekill(c2 - cs, 0);
 	selectkeymap("main", 1);
 	viinsbegin = cs;
 	undoing = 0;
     }
+    return ret;
 }
 
 /**/
-void
-visubstitute(void)
+int
+visubstitute(char **args)
 {
     int n = zmult;
 
     startvichange(1);
-    if (n < 0) {
-	feep();
-	return;
-    }
+    if (n < 0)
+	return 1;
     /* it is an error to be on the end of line */
-    if (cs == ll || line[cs] == '\n') {
-	feep();
-	return;
-    }
+    if (cs == ll || line[cs] == '\n')
+	return 1;
     /* Put argument into the acceptable range -- it is not an error to  *
      * specify a greater count than the number of available characters. */
     if (n > findeol() - cs)
@@ -385,79 +377,84 @@ visubstitute(void)
     /* do the substitution */
     forekill(n, 0);
     startvitext(1);
+    return 0;
 }
 
 /**/
-void
-vichangeeol(void)
+int
+vichangeeol(char **args)
 {
     forekill(findeol() - cs, 0);
     startvitext(1);
+    return 0;
 }
 
 /**/
-void
-vichangewholeline(void)
+int
+vichangewholeline(char **args)
 {
-    vifirstnonblank();
-    vichangeeol();
+    vifirstnonblank(args);
+    return vichangeeol(zlenoargs);
 }
 
 /**/
-void
-viyank(void)
+int
+viyank(char **args)
 {
-    int oldcs = cs, c2;
+    int oldcs = cs, c2, ret = 1;
 
     startvichange(1);
-    if ((c2 = getvirange(0)) != -1)
+    if ((c2 = getvirange(0)) != -1) {
 	cut(cs, c2 - cs, 0);
+	ret = 0;
+    }
     vichgflag = 0;
     cs = oldcs;
+    return ret;
 }
 
 /**/
-void
-viyankeol(void)
+int
+viyankeol(char **args)
 {
     int x = findeol();
 
     startvichange(-1);
-    if (x == cs) {
-	feep();
-	return;
-    }
+    if (x == cs)
+	return 1;
     cut(cs, x - cs, 0);
+    return 0;
 }
 
 /**/
-void
-viyankwholeline(void)
+int
+viyankwholeline(char **args)
 {
     int bol = findbol(), oldcs = cs;
     int n = zmult;
 
     startvichange(-1);
     if (n < 1)
-	return;
+	return 1;
     while(n--) {
      if (cs > ll) {
-	feep();
 	cs = oldcs;
-	return;
+	return 1;
      }
      cs = findeol() + 1;
     }
     vilinerange = 1;
     cut(bol, cs - bol - 1, 0);
     cs = oldcs;
+    return 0;
 }
 
 /**/
-void
-vireplace(void)
+int
+vireplace(char **args)
 {
     startvitext(0);
+    return 0;
 }
 
 /* vi-replace-chars has some oddities relating to vi-repeat-change.  In *
@@ -474,32 +471,27 @@ vireplace(void)
  * without a rewrite of the repeat code.                                */
 
 /**/
-void
-vireplacechars(void)
+int
+vireplacechars(char **args)
 {
     int ch, n = zmult;
 
     startvichange(1);
     /* check argument range */
     if (n < 1 || n + cs > findeol()) {
-	if(vichgrepeat) {
-	    int ofeep = feepflag;
+	if(vichgrepeat)
 	    vigetkey();
-	    feepflag = ofeep;
-	}
 	if(vichgflag) {
 	    free(vichgbuf);
 	    vichgbuf = NULL;
 	    vichgflag = 0;
 	}
-	feep();
-	return;
+	return 1;
     }
     /* get key */
     if((ch = vigetkey()) == -1) {
 	vichgflag = 0;
-	feep();
-	return;
+	return 1;
     }
     /* do change */
     if (ch == '\r' || ch == '\n') {
@@ -513,47 +505,51 @@ vireplacechars(void)
 	cs--;
     }
     vichgflag = 0;
+    return 0;
 }
 
 /**/
-void
-vicmdmode(void)
+int
+vicmdmode(char **args)
 {
     if (invicmdmode() || selectkeymap("vicmd", 0))
-	feep();
+	return 1;
     undoing = 1;
     vichgflag = 0;
     if (cs != findbol())
 	cs--;
+    return 0;
 }
 
 /**/
-void
-viopenlinebelow(void)
+int
+viopenlinebelow(char **args)
 {
     cs = findeol();
     spaceinline(1);
     line[cs++] = '\n';
     startvitext(1);
     clearlist = 1;
+    return 0;
 }
 
 /**/
-void
-viopenlineabove(void)
+int
+viopenlineabove(char **args)
 {
     cs = findbol();
     spaceinline(1);
     line[cs] = '\n';
     startvitext(1);
     clearlist = 1;
+    return 0;
 }
 
 /**/
-void
-vioperswapcase(void)
+int
+vioperswapcase(char **args)
 {
-    int oldcs, c2;
+    int oldcs, c2, ret = 1;
 
     /* get the range */
     startvichange(1);
@@ -569,22 +565,22 @@ vioperswapcase(void)
 	}
 	/* go back to the first line of the range */
 	cs = oldcs;
+	ret = 0;
 #if 0
 	vifirstnonblank();
 #endif
     }
     vichgflag = 0;
+    return ret;
 }
 
 /**/
-void
-virepeatchange(void)
+int
+virepeatchange(char **args)
 {
     /* make sure we have a change to repeat */
-    if (!vichgbuf || vichgflag) {
-	feep();
-	return;
-    }
+    if (!vichgbuf || vichgflag)
+	return 1;
     /* restore or update the saved count and buffer */
     if (zmod.flags & MOD_MULT) {
 	lastmod.mult = zmod.mult;
@@ -598,11 +594,12 @@ virepeatchange(void)
     /* repeat the command */
     inrepeat = 1;
     ungetkeys(vichgbuf, vichgbufptr);
+    return 0;
 }
 
 /**/
-void
-viindent(void)
+int
+viindent(char **args)
 {
     int oldcs = cs, c2;
 
@@ -610,14 +607,13 @@ viindent(void)
     startvichange(1);
     if ((c2 = getvirange(0)) == -1) {
 	vichgflag = 0;
-	return;
+	return 1;
     }
     vichgflag = 0;
     /* must be a line range */
     if (!vilinerange) {
-	feep();
 	cs = oldcs;
-	return;
+	return 1;
     }
     oldcs = cs;
     /* add a tab to the beginning of each line within range */
@@ -628,12 +624,13 @@ viindent(void)
     }
     /* go back to the first line of the range */
     cs = oldcs;
-    vifirstnonblank();
+    vifirstnonblank(zlenoargs);
+    return 0;
 }
 
 /**/
-void
-viunindent(void)
+int
+viunindent(char **args)
 {
     int oldcs = cs, c2;
 
@@ -641,14 +638,13 @@ viunindent(void)
     startvichange(1);
     if ((c2 = getvirange(0)) == -1) {
 	vichgflag = 0;
-	return;
+	return 1;
     }
     vichgflag = 0;
     /* must be a line range */
     if (!vilinerange) {
-	feep();
 	cs = oldcs;
-	return;
+	return 1;
     }
     oldcs = cs;
     /* remove a tab from the beginning of each line within range */
@@ -659,12 +655,13 @@ viunindent(void)
     }
     /* go back to the first line of the range */
     cs = oldcs;
-    vifirstnonblank();
+    vifirstnonblank(zlenoargs);
+    return 0;
 }
 
 /**/
-void
-vibackwarddeletechar(void)
+int
+vibackwarddeletechar(char **args)
 {
     int n = zmult;
 
@@ -672,16 +669,16 @@ vibackwarddeletechar(void)
 	startvichange(-1);
     /* handle negative argument */
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	videletechar();
+	ret = videletechar(args);
 	zmult = n;
-	return;
+	return ret;
     }
     /* It is an error to be at the beginning of the line, or (in *
      * insert mode) to delete past the beginning of insertion.   */
     if ((!invicmdmode() && cs - n < viinsbegin) || cs == findbol()) {
-	feep();
-	return;
+	return 1;
     }
     /* Put argument into the acceptable range -- it is not an error to  *
      * specify a greater count than the number of available characters. */
@@ -689,41 +686,39 @@ vibackwarddeletechar(void)
 	n = cs - findbol();
     /* do the deletion */
     backkill(n, 1);
+    return 0;
 }
 
 /**/
-void
-vikillline(void)
+int
+vikillline(char **args)
 {
-    if (viinsbegin > cs) {
-	feep();
-	return;
-    }
+    if (viinsbegin > cs)
+	return 1;
     backdel(cs - viinsbegin);
+    return 0;
 }
 
 /**/
-void
-viputbefore(void)
+int
+viputbefore(char **args)
 {
     Cutbuffer buf = &cutbuf;
     int n = zmult;
 
     startvichange(-1);
     if (n < 0)
-	return;
+	return 1;
     if (zmod.flags & MOD_VIBUF)
 	buf = &vibuf[zmod.vibuf];
-    if (!buf->buf) {
-	feep();
-	return;
-    }
+    if (!buf->buf)
+	return 1;
     if(buf->flags & CUTBUFFER_LINE) {
 	cs = findbol();
 	spaceinline(buf->len + 1);
 	memcpy((char *)line + cs, buf->buf, buf->len);
 	line[cs + buf->len] = '\n';
-	vifirstnonblank();
+	vifirstnonblank(zlenoargs);
     } else {
 	while (n--) {
 	    spaceinline(buf->len);
@@ -733,30 +728,29 @@ viputbefore(void)
 	if (cs)
 	    cs--;
     }
+    return 0;
 }
 
 /**/
-void
-viputafter(void)
+int
+viputafter(char **args)
 {
     Cutbuffer buf = &cutbuf;
     int n = zmult;
 
     startvichange(-1);
     if (n < 0)
-	return;
+	return 1;
     if (zmod.flags & MOD_VIBUF)
 	buf = &vibuf[zmod.vibuf];
-    if (!buf->buf) {
-	feep();
-	return;
-    }
+    if (!buf->buf)
+	return 1;
     if(buf->flags & CUTBUFFER_LINE) {
 	cs = findeol();
 	spaceinline(buf->len + 1);
 	line[cs++] = '\n';
 	memcpy((char *)line + cs, buf->buf, buf->len);
-	vifirstnonblank();
+	vifirstnonblank(zlenoargs);
     } else {
 	if (cs != findeol())
 	    cs++;
@@ -768,20 +762,18 @@ viputafter(void)
 	if (cs)
 	    cs--;
     }
-
+    return 0;
 }
 
 /**/
-void
-vijoin(void)
+int
+vijoin(char **args)
 {
     int x;
 
     startvichange(-1);
-    if ((x = findeol()) == ll) {
-	feep();
-	return;
-    }
+    if ((x = findeol()) == ll)
+	return 1;
     cs = x + 1;
     for (x = 1; cs != ll && iblank(line[cs]); cs++, x++);
     backdel(x);
@@ -791,17 +783,18 @@ vijoin(void)
 	spaceinline(1);
 	line[cs] = ' ';
     }
+    return 0;
 }
 
 /**/
-void
-viswapcase(void)
+int
+viswapcase(char **args)
 {
     int eol, n = zmult;
 
     startvichange(-1);
     if (n < 1)
-	return;
+	return 1;
     eol = findeol();
     while (cs < eol && n--) {
 	if (islower(line[cs]))
@@ -812,11 +805,12 @@ viswapcase(void)
     }
     if (cs && cs == eol)
 	cs--;
+    return 0;
 }
 
 /**/
-void
-vicapslockpanic(void)
+int
+vicapslockpanic(char **args)
 {
     clearlist = 1;
     zbeep();
@@ -825,20 +819,19 @@ vicapslockpanic(void)
     zrefresh();
     while (!islower(getkey(0)));
     statusline = NULL;
+    return 0;
 }
 
 /**/
-void
-visetbuffer(void)
+int
+visetbuffer(char **args)
 {
     int ch;
 
     if ((zmod.flags & MOD_VIBUF) ||
 	(((ch = getkey(0)) < '1' || ch > '9') &&
-	 (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z'))) {
-	feep();
-	return;
-    }
+	 (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z')))
+	return 1;
     if (ch >= 'A' && ch <= 'Z')	/* needed in cut() */
 	zmod.flags |= MOD_VIAPP;
     else
@@ -846,32 +839,33 @@ visetbuffer(void)
     zmod.vibuf = tulower(ch) + (idigit(ch) ? -'1' + 26 : -'a');
     zmod.flags |= MOD_VIBUF;
     prefixflag = 1;
+    return 0;
 }
 
 /**/
-void
-vikilleol(void)
+int
+vikilleol(char **args)
 {
     int n = findeol() - cs;
 
     startvichange(-1);
     if (!n) {
 	/* error -- line already empty */
-	feep();
-	return;
+	return 1;
     }
     /* delete to end of line */
     forekill(findeol() - cs, 0);
+    return 0;
 }
 
 /**/
-void
-vipoundinsert(void)
+int
+vipoundinsert(char **args)
 {
     int oldcs = cs;
 
     startvichange(-1);
-    vifirstnonblank();
+    vifirstnonblank(zlenoargs);
     if(line[cs] != '#') {
 	spaceinline(1);
 	line[cs] = '#';
@@ -884,11 +878,12 @@ vipoundinsert(void)
 	    viinsbegin--;
 	cs = oldcs - (cs < oldcs);
     }
+    return 0;
 }
 
 /**/
-void
-viquotedinsert(void)
+int
+viquotedinsert(char **args)
 {
 #ifndef HAS_TIO
     struct sgttyb sob;
@@ -908,23 +903,23 @@ viquotedinsert(void)
 #endif
     foredel(1);
     if(c < 0)
-	feep();
+	return 1;
     else
-	selfinsert();
+	return selfinsert(args);
 }
 
 /* the 0 key in vi: continue a repeat count in the manner of      *
  * digit-argument if possible, otherwise do vi-beginning-of-line. */
 
 /**/
-void
-vidigitorbeginningofline(void)
+int
+vidigitorbeginningofline(char **args)
 {
     if(zmod.flags & MOD_TMULT)
-	digitargument();
+	return digitargument(args);
     else {
 	removesuffix();
 	invalidatelist();
-	vibeginningofline();
+	return vibeginningofline(args);
     }
 }
diff --git a/Src/Zle/zle_word.c b/Src/Zle/zle_word.c
index f446d1769..31f83a2df 100644
--- a/Src/Zle/zle_word.c
+++ b/Src/Zle/zle_word.c
@@ -31,38 +31,41 @@
 #include "zle_word.pro"
 
 /**/
-void
-forwardword(void)
+int
+forwardword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	backwardword();
+	ret = backwardword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (cs != ll && iword(line[cs]))
 	    cs++;
 	if (wordflag && !n)
-	    return;
+	    return 0;
 	while (cs != ll && !iword(line[cs]))
 	    cs++;
     }
+    return 0;
 }
 
 /**/
-void
-viforwardword(void)
+int
+viforwardword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	backwardword();
+	ret = backwardword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	if (iident(line[cs]))
@@ -72,64 +75,69 @@ viforwardword(void)
 	    while (cs != ll && !iident(line[cs]) && !iblank(line[cs]))
 		cs++;
 	if (wordflag && !n)
-	    return;
+	    return 0;
 	while (cs != ll && (iblank(line[cs]) || line[cs] == '\n'))
 	    cs++;
     }
+    return 0;
 }
 
 /**/
-void
-viforwardblankword(void)
+int
+viforwardblankword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	vibackwardblankword();
+	ret = vibackwardblankword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (cs != ll && !iblank(line[cs]))
 	    cs++;
 	if (wordflag && !n)
-	    return;
+	    return 0;
 	while (cs != ll && iblank(line[cs]))
 	    cs++;
     }
+    return 0;
 }
 
 /**/
-void
-emacsforwardword(void)
+int
+emacsforwardword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	emacsbackwardword();
+	ret = emacsbackwardword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (cs != ll && !iword(line[cs]))
 	    cs++;
 	if (wordflag && !n)
-	    return;
+	    return 0;
 	while (cs != ll && iword(line[cs]))
 	    cs++;
     }
+    return 0;
 }
 
 /**/
-void
-viforwardblankwordend(void)
+int
+viforwardblankwordend(char **args)
 {
     int n = zmult;
 
     if (n < 0)
-	return;
+	return 1;
     while (n--) {
 	while (cs != ll && iblank(line[cs + 1]))
 	    cs++;
@@ -138,19 +146,21 @@ viforwardblankwordend(void)
     }
     if (cs != ll && virangeflag)
 	cs++;
+    return 0;
 }
 
 /**/
-void
-viforwardwordend(void)
+int
+viforwardwordend(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	backwardword();
+	ret = backwardword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	if (iblank(line[cs + 1]))
@@ -165,19 +175,21 @@ viforwardwordend(void)
     }
     if (cs != ll && virangeflag)
 	cs++;
+    return 0;
 }
 
 /**/
-void
-backwardword(void)
+int
+backwardword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	forwardword();
+	ret = forwardword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (cs && !iword(line[cs - 1]))
@@ -185,19 +197,21 @@ backwardword(void)
 	while (cs && iword(line[cs - 1]))
 	    cs--;
     }
+    return 0;
 }
 
 /**/
-void
-vibackwardword(void)
+int
+vibackwardword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	backwardword();
+	ret = backwardword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (cs && iblank(line[cs - 1]))
@@ -209,19 +223,21 @@ vibackwardword(void)
 	    while (cs && !iident(line[cs - 1]) && !iblank(line[cs - 1]))
 		cs--;
     }
+    return 0;
 }
 
 /**/
-void
-vibackwardblankword(void)
+int
+vibackwardblankword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	viforwardblankword();
+	ret = viforwardblankword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (cs && iblank(line[cs - 1]))
@@ -229,19 +245,21 @@ vibackwardblankword(void)
 	while (cs && !iblank(line[cs - 1]))
 	    cs--;
     }
+    return 0;
 }
 
 /**/
-void
-emacsbackwardword(void)
+int
+emacsbackwardword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	emacsforwardword();
+	ret = emacsforwardword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (cs && !iword(line[cs - 1]))
@@ -249,19 +267,21 @@ emacsbackwardword(void)
 	while (cs && iword(line[cs - 1]))
 	    cs--;
     }
+    return 0;
 }
 
 /**/
-void
-backwarddeleteword(void)
+int
+backwarddeleteword(char **args)
 {
     int x = cs, n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	deleteword();
+	ret = deleteword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (x && !iword(line[x - 1]))
@@ -270,19 +290,18 @@ backwarddeleteword(void)
 	    x--;
     }
     backdel(cs - x);
+    return 0;
 }
 
 /**/
-void
-vibackwardkillword(void)
+int
+vibackwardkillword(char **args)
 {
     int x = cs, lim = (viinsbegin > findbol()) ? viinsbegin : findbol();
     int n = zmult;
 
-    if (n < 0) {
-	feep();
-	return;
-    }
+    if (n < 0)
+	return 1;
 /* this taken from "vibackwardword" */
     while (n--) {
 	while ((x > lim) && iblank(line[x - 1]))
@@ -295,20 +314,22 @@ vibackwardkillword(void)
 		x--;
     }
     backkill(cs - x, 1);
+    return 0;
 }
 
 /**/
-void
-backwardkillword(void)
+int
+backwardkillword(char **args)
 {
     int x = cs;
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	killword();
+	ret = killword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (x && !iword(line[x - 1]))
@@ -317,11 +338,12 @@ backwardkillword(void)
 	    x--;
     }
     backkill(cs - x, 1);
+    return 0;
 }
 
 /**/
-void
-upcaseword(void)
+int
+upcaseword(char **args)
 {
     int n = zmult;
     int neg = n < 0, ocs = cs;
@@ -338,11 +360,12 @@ upcaseword(void)
     }
     if (neg)
 	cs = ocs;
+    return 0;
 }
 
 /**/
-void
-downcaseword(void)
+int
+downcaseword(char **args)
 {
     int n = zmult;
     int neg = n < 0, ocs = cs;
@@ -359,11 +382,12 @@ downcaseword(void)
     }
     if (neg)
 	cs = ocs;
+    return 0;
 }
 
 /**/
-void
-capitalizeword(void)
+int
+capitalizeword(char **args)
 {
     int first, n = zmult;
     int neg = n < 0, ocs = cs;
@@ -384,20 +408,22 @@ capitalizeword(void)
     }
     if (neg)
 	cs = ocs;
+    return 0;
 }
 
 /**/
-void
-deleteword(void)
+int
+deleteword(char **args)
 {
     int x = cs;
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	backwarddeleteword();
+	ret = backwarddeleteword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (x != ll && !iword(line[x]))
@@ -406,20 +432,22 @@ deleteword(void)
 	    x++;
     }
     foredel(x - cs);
+    return 0;
 }
 
 /**/
-void
-killword(void)
+int
+killword(char **args)
 {
     int x = cs;
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	backwardkillword();
+	ret = backwardkillword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (x != ll && !iword(line[x]))
@@ -428,11 +456,12 @@ killword(void)
 	    x++;
     }
     forekill(x - cs, 0);
+    return 0;
 }
 
 /**/
-void
-transposewords(void)
+int
+transposewords(char **args)
 {
     int p1, p2, p3, p4, x = cs;
     char *temp, *pp;
@@ -448,22 +477,16 @@ transposewords(void)
 	    x = cs;
 	    while (x && line[x - 1] != '\n' && !iword(line[x]))
 		x--;
-	    if (!x || line[x - 1] == '\n') {
-		feep();
-		return;
-	    }
+	    if (!x || line[x - 1] == '\n')
+		return 1;
 	}
 	for (p4 = x; p4 != ll && iword(line[p4]); p4++);
 	for (p3 = p4; p3 && iword(line[p3 - 1]); p3--);
-	if (!p3) {
-	    feep();
-	    return;
-	}
+	if (!p3)
+	    return 1;
 	for (p2 = p3; p2 && !iword(line[p2 - 1]); p2--);
-	if (!p2) {
-	    feep();
-	    return;
-	}
+	if (!p2)
+	    return 1;
 	for (p1 = p2; p1 && iword(line[p1 - 1]); p1--);
 	pp = temp = (char *)zhalloc(p4 - p1 + 1);
 	struncpy(&pp, (char *) line + p3, p4 - p3);
@@ -474,4 +497,5 @@ transposewords(void)
     }
     if (neg)
 	cs = ocs;
+    return 0;
 }
diff --git a/Src/builtin.c b/Src/builtin.c
index d74d9cd88..8fe112cb5 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -50,7 +50,7 @@ static struct builtin builtins[] =
     BUILTIN("cd", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
     BUILTIN("chdir", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
     BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
-    BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafilrtux", NULL),
+    BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafgilrtux", NULL),
     BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "v", NULL),
     BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmr", NULL),
     BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
@@ -60,7 +60,7 @@ static struct builtin builtins[] =
     BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmr", NULL),
     BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
     BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
-    BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "LRTUZafilrtu", "x"),
+    BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "LRTUZafilrtu", "xg"),
     BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
     BUILTIN("fc", BINF_FCOPTS, bin_fc, 0, -1, BIN_FC, "nlreIRWAdDfEim", NULL),
     BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
@@ -74,7 +74,7 @@ static struct builtin builtins[] =
 #endif
 
     BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "nrdDfEim", "l"),
-    BUILTIN("integer", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "lrtux", "i"),
+    BUILTIN("integer", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "glrtux", "i"),
     BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
     BUILTIN("kill", 0, bin_kill, 0, -1, 0, NULL, NULL),
     BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
@@ -93,7 +93,7 @@ static struct builtin builtins[] =
     BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
     BUILTIN("r", BINF_R, bin_fc, 0, -1, BIN_FC, "nrl", NULL),
     BUILTIN("read", 0, bin_read, 0, -1, 0, "rzu0123456789pkqecnAlE", NULL),
-    BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafiltux", "r"),
+    BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafgiltux", "r"),
     BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "dfv", "r"),
     BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
     BUILTIN("set", BINF_PSPECIAL, bin_set, 0, -1, 0, NULL, NULL),
@@ -107,7 +107,7 @@ static struct builtin builtins[] =
     BUILTIN("trap", BINF_PSPECIAL, bin_trap, 0, -1, 0, NULL, NULL),
     BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
     BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsw", "v"),
-    BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafilrtuxm", NULL),
+    BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafgilrtuxm", NULL),
     BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
     BUILTIN("unalias", 0, bin_unhash, 1, -1, 0, "m", "a"),
     BUILTIN("unfunction", 0, bin_unhash, 1, -1, 0, "m", "f"),
@@ -120,7 +120,9 @@ static struct builtin builtins[] =
     BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsw", "c"),
 
 #ifdef DYNAMIC
-    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "ILabcdipu", NULL),
+    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "ILabcdipue", NULL),
+#else
+    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "e", NULL),
 #endif
 };
 
@@ -351,7 +353,8 @@ execbuiltin(LinkList args, Builtin bn)
 
     /* display execution trace information, if required */
     if (xtr) {
-	fprintf(stderr, "%s%s", (prompt4) ? prompt4 : "", name);
+	printprompt4();
+	fprintf(stderr, "%s", name);
 	if (xarg)
 	    fprintf(stderr, " %s", xarg);
 	while (*oargv)
@@ -1349,7 +1352,7 @@ fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment
 	fclistdone = 1;
 
     ent = gethistent(first, first < last? GETHIST_DOWNWARD : GETHIST_UPWARD);
-    if (!ent || ent->histnum < first || ent->histnum > last) {
+    if (!ent || (first < last? ent->histnum > last : ent->histnum < last)) {
 	if (first == last)
 	    zwarnnam("fc", "no such event: %d", NULL, first);
 	else
@@ -1491,8 +1494,14 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 {
     int usepm, tc, keeplocal = 0;
 
-    /* use the existing pm? */
-    usepm = pm && !(pm->flags & (PM_UNSET | PM_AUTOLOAD));
+    /*
+     * Do we use the existing pm?  Note that this isn't the end of the
+     * story, because if we try and create a new pm at the same
+     * locallevel as an unset one we use the pm struct anyway: that's
+     * handled in createparam().  Here we just avoid using it for the
+     * present tests if it's unset.
+     */
+    usepm = pm && !(pm->flags & PM_UNSET);
 
     /* Always use an existing pm if special at current locallevel */
     if (pm && (pm->flags & PM_SPECIAL) && pm->level == locallevel)
@@ -1501,15 +1510,15 @@ typeset_single(char *cname, char *pname, Param pm, int func,
     /*
      * Don't use a non-special existing param if
      *   - the local level has changed, and
-     *   - the function is not `export'.
+     *   - we are really locallizing the parameter
      */
     if (usepm && !(pm->flags & PM_SPECIAL) &&
-	locallevel != pm->level && func != BIN_EXPORT)
+	locallevel != pm->level && (on & PM_LOCAL))
 	usepm = 0;
 
     /* attempting a type conversion, or making a tied colonarray? */
     if ((tc = usepm && (((off & pm->flags) | (on & ~pm->flags)) &
-			(PM_INTEGER|PM_HASHED|PM_ARRAY|PM_TIED))))
+			(PM_INTEGER|PM_HASHED|PM_ARRAY|PM_TIED|PM_AUTOLOAD))))
 	usepm = 0;
     if (tc && (pm->flags & PM_SPECIAL)) {
 	zerrnam(cname, "%s: can't change type of a special parameter",
@@ -1518,6 +1527,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
     }
 
     if (usepm) {
+	on &= ~PM_LOCAL;
 	if (!on && !roff && !value) {
 	    paramtab->printnode((HashNode)pm, 0);
 	    return pm;
@@ -1604,7 +1614,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 
     if (keeplocal)
 	pm->level = keeplocal;
-    else if (func != BIN_EXPORT)
+    else if (on & PM_LOCAL)
 	pm->level = locallevel;
     if (value && !(pm->flags & (PM_ARRAY|PM_HASHED)))
 	setsparam(pname, ztrdup(value));
@@ -1677,6 +1687,9 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	return 0;
     }
 
+    if (!ops['g'])
+	on |= PM_LOCAL;
+
     if (on & PM_TIED) {
 	Param apm;
 	struct asgment asg0;
@@ -3073,7 +3086,7 @@ bin_emulate(char *nam, char **argv, char *ops, int func)
 {
     emulate(*argv, ops['R']);
     if (ops['L'])
-	dosetopt(LOCALOPTIONS, 1, 0);
+	opts[LOCALOPTIONS] = opts[LOCALTRAPS] = 1;
     return 0;
 }
 
@@ -3121,7 +3134,7 @@ bin_read(char *name, char **args, char *ops, int func)
     char *reply, *readpmpt;
     int bsiz, c = 0, gotnl = 0, al = 0, first, nchars = 1, bslash;
     int haso = 0;	/* true if /dev/tty has been opened specially */
-    int isem = !strcmp(term, "emacs");
+    int isem = !strcmp(term, "emacs"), izle = zleactive && getkeyptr;
     char *buf, *bptr, *firstarg, *zbuforig;
     LinkList readll = newlinklist();
 
@@ -3146,33 +3159,37 @@ bin_read(char *name, char **args, char *ops, int func)
 	return compctlread(name, args, ops, reply);
 
     if ((ops['k'] && !ops['u'] && !ops['p']) || ops['q']) {
-	if (SHTTY == -1) {
-	    /* need to open /dev/tty specially */
-	    SHTTY = open("/dev/tty", O_RDWR|O_NOCTTY);
-	    haso = 1;
-	}
-	/* We should have a SHTTY opened by now. */
-	if (SHTTY == -1) {
-	    /* Unfortunately, we didn't. */
-	    fprintf(stderr, "not interactive and can't open terminal\n");
-	    fflush(stderr);
-	    return 1;
+	if (!zleactive) {
+	    if (SHTTY == -1) {
+		/* need to open /dev/tty specially */
+		SHTTY = open("/dev/tty", O_RDWR|O_NOCTTY);
+		haso = 1;
+	    }
+	    /* We should have a SHTTY opened by now. */
+	    if (SHTTY == -1) {
+		/* Unfortunately, we didn't. */
+		fprintf(stderr, "not interactive and can't open terminal\n");
+		fflush(stderr);
+		return 1;
+	    }
+	    if (unset(INTERACTIVE))
+		gettyinfo(&shttyinfo);
+	    /* attach to the tty */
+	    attachtty(mypgrp);
+	    if (!isem && ops['k'])
+		setcbreak();
+	    readfd = SHTTY;
 	}
-	if (unset(INTERACTIVE))
-	    gettyinfo(&shttyinfo);
-	/* attach to the tty */
-	attachtty(mypgrp);
-	if (!isem && ops['k'])
-	    setcbreak();
-	readfd = SHTTY;
     } else if (ops['u'] && !ops['p']) {
 	/* -u means take input from the specified file descriptor. *
 	 * -up means take input from the coprocess.                */
 	for (readfd = 9; readfd && !ops[readfd + '0']; --readfd);
-    } else if (ops['p'])
+	izle = 0;
+    } else if (ops['p']) {
 	readfd = coprocin;
-    else
-	readfd = 0;
+	izle = 0;
+    } else
+	readfd = izle = 0;
 
     /* handle prompt */
     if (firstarg) {
@@ -3196,18 +3213,25 @@ bin_read(char *name, char **args, char *ops, int func)
 	bptr = buf = (char *)zalloc(nchars+1);
 
 	do {
-	    /* If read returns 0, is end of file */
-	    if ((val = read(readfd, bptr, nchars)) <= 0)
-		break;
+	    if (izle) {
+		if ((val = getkeyptr(0)) < 0)
+		    break;
+		*bptr++ = (char) val;
+		nchars--;
+	    } else {
+		/* If read returns 0, is end of file */
+		if ((val = read(readfd, bptr, nchars)) <= 0)
+		    break;
 	    
-	    /* decrement number of characters read from number required */
-	    nchars -= val;
+		/* decrement number of characters read from number required */
+		nchars -= val;
 
-	    /* increment pointer past read characters */
-	    bptr += val;
+		/* increment pointer past read characters */
+		bptr += val;
+	    }
 	} while (nchars > 0);
 	
-	if (!ops['u'] && !ops['p']) {
+	if (!izle && !ops['u'] && !ops['p']) {
 	    /* dispose of result appropriately, etc. */
 	    if (isem)
 		while (val > 0 && read(SHTTY, &d, 1) == 1 && d != '\n');
@@ -3236,14 +3260,19 @@ bin_read(char *name, char **args, char *ops, int func)
 	readbuf[1] = '\0';
 
 	/* get, and store, reply */
-	readbuf[0] = ((char)getquery(NULL, 0)) == 'y' ? 'y' : 'n';
+	if (izle) {
+	    int key = getkeyptr(0);
 
-	/* dispose of result appropriately, etc. */
-	if (haso) {
-	    close(SHTTY);
-	    SHTTY = -1;
-	}
+	    readbuf[0] = (key == 'y' ? 'y' : 'n');
+	} else {
+	    readbuf[0] = ((char)getquery(NULL, 0)) == 'y' ? 'y' : 'n';
 
+	    /* dispose of result appropriately, etc. */
+	    if (haso) {
+		close(SHTTY);
+		SHTTY = -1;
+	    }
+	}
 	if (ops['e'] || ops['E'])
 	    printf("%s\n", readbuf);
 	if (!ops['e'])
@@ -3265,7 +3294,7 @@ bin_read(char *name, char **args, char *ops, int func)
 	buf = bptr = (char *)zalloc(bsiz = 64);
 	/* get input, a character at a time */
 	while (!gotnl) {
-	    c = zread();
+	    c = zread(izle);
 	    /* \ at the end of a line indicates a continuation *
 	     * line, except in raw mode (-r option)            */
 	    if (bslash && c == '\n') {
@@ -3355,7 +3384,7 @@ bin_read(char *name, char **args, char *ops, int func)
     bslash = 0;
     if (!gotnl)
 	for (;;) {
-	    c = zread();
+	    c = zread(izle);
 	    /* \ at the end of a line introduces a continuation line, except in
 	    raw mode (-r option) */
 	    if (bslash && c == '\n') {
@@ -3419,10 +3448,15 @@ bin_read(char *name, char **args, char *ops, int func)
 
 /**/
 static int
-zread(void)
+zread(int izle)
 {
     char cc, retry = 0;
 
+    if (izle) {
+	int c = getkeyptr(0);
+
+	return (c < 0 ? EOF : c);
+    }
     /* use zbuf if possible */
     if (zbuf) {
 	/* If zbuf points to anything, it points to the next character in the
@@ -3618,7 +3652,7 @@ bin_trap(char *name, char **argv, char *ops, int func)
     arg = *argv++;
     if (!*arg)
 	l = NULL;
-    else if (!(l = parse_string(arg))) {
+    else if (!(l = parse_string(arg, 0))) {
 	zwarnnam(name, "couldn't parse trap command", NULL, 0);
 	return 1;
     }
diff --git a/Src/exec.c b/Src/exec.c
index 5cfe3e3ef..5ae4f0d33 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -133,14 +133,17 @@ static int doneps4;
 
 /**/
 List
-parse_string(char *s)
+parse_string(char *s, int ln)
 {
     List l;
+    int oldlineno = lineno;
 
     lexsave();
-    inpush(s, 0, NULL);
+    inpush(s, (ln ? INP_LINENO : 0), NULL);
     strinbeg(0);
+    lineno = ln ? 1 : -1;
     l = parse_list();
+    lineno = oldlineno;
     strinend();
     inpop();
     lexrestore();
@@ -301,7 +304,9 @@ execcursh(Cmd cmd, LinkList args, int flags)
 {
     if (!list_pipe)
 	deletejob(jobtab + thisjob);
+    cmdpush(CS_CURSH);
     execlist(cmd->u.list, 1, flags & CFLAG_EXEC);
+    cmdpop();
 
     return lastval;
 }
@@ -354,7 +359,9 @@ zexecve(char *pth, char **argv)
 		    if (execvebuf[1] == '!') {
 			for (t0 = 0; t0 != ct; t0++)
 			    if (execvebuf[t0] == '\n')
-				execvebuf[t0] = '\0';
+				break;
+			while (inblank(execvebuf[t0]))
+			    execvebuf[t0--] = '\0';
 			execvebuf[POUNDBANGLIMIT] = '\0';
 			for (ptr = execvebuf + 2; *ptr && *ptr == ' '; ptr++);
 			for (ptr2 = ptr; *ptr && *ptr != ' '; ptr++);
@@ -676,7 +683,7 @@ execstring(char *s, int dont_change_job, int exiting)
     List list;
 
     pushheap();
-    if ((list = parse_string(s)))
+    if ((list = parse_string(s, 0)))
 	execlist(list, dont_change_job, exiting);
     popheap();
 }
@@ -694,8 +701,8 @@ execlist(List list, int dont_change_job, int exiting)
 {
     Sublist slist;
     static int donetrap;
-    int ret, cj;
-    int old_pline_level, old_list_pipe;
+    int ret, cj, csp;
+    int old_pline_level, old_list_pipe, oldlineno;
     /*
      * ERREXIT only forces the shell to exit if the last command in a &&
      * or || fails.  This is the case even if an earlier command is a
@@ -707,6 +714,7 @@ execlist(List list, int dont_change_job, int exiting)
     cj = thisjob;
     old_pline_level = pline_level;
     old_list_pipe = list_pipe;
+    oldlineno = lineno;
 
     if (sourcelevel && unset(SHINSTDIN))
 	pline_level = list_pipe = 0;
@@ -718,6 +726,7 @@ execlist(List list, int dont_change_job, int exiting)
 	 * called once for each sublist that fails.          */
 	donetrap = 0;
 	slist = list->left;
+	csp = cmdsp;
 
 	/* Loop through code followed by &&, ||, or end of sublist. */
 	while (slist) {
@@ -744,6 +753,7 @@ execlist(List list, int dont_change_job, int exiting)
 			goto sublist_done;
 		    }
 		}
+		cmdpush(CS_CMDAND);
 		break;
 	    case ORNEXT:
 		/* If the return code is zero, we skip pipelines until *
@@ -760,12 +770,14 @@ execlist(List list, int dont_change_job, int exiting)
 			goto sublist_done;
 		     }
 		}
+		cmdpush(CS_CMDOR);
 		break;
 	    }
 	    slist = slist->right;
 	}
 sublist_done:
 
+	cmdsp = csp;
 	noerrexit = oldnoerrexit;
 
 	if (sigtrapped[SIGDEBUG])
@@ -794,6 +806,7 @@ sublist_done:
 
     pline_level = old_pline_level;
     list_pipe = old_list_pipe;
+    lineno = oldlineno;
     if (dont_change_job)
 	thisjob = cj;
 }
@@ -888,6 +901,9 @@ execpline(Sublist l, int how, int last1)
 		    DPUTS(!list_pipe_pid, "invalid list_pipe_pid");
 		    addproc(list_pipe_pid, list_pipe_text);
 
+		    if (!jn->procs->next)
+			jn->gleader = mypgrp;
+
 		    for (pn = jobtab[jn->other].procs; pn; pn = pn->next)
 			if (WIFSTOPPED(pn->status))
 			    break;
@@ -901,12 +917,14 @@ execpline(Sublist l, int how, int last1)
 		    jn->stat |= STAT_STOPPED | STAT_CHANGED;
 		    printjob(jn, !!isset(LONGLISTJOBS), 1);
 		}
-		else
+		else if (newjob != list_pipe_job)
 		    deletejob(jn);
+		else
+		    lastwj = -1;
 	    }
 
 	    for (; !nowait;) {
-		if (list_pipe_child) {
+		if (list_pipe_child || pline_level) {
 		    jn->stat |= STAT_NOPRINT;
 		    makerunning(jn);
 		}
@@ -917,8 +935,11 @@ execpline(Sublist l, int how, int last1)
 		    jn->stat & STAT_DONE &&
 		    lastval2 & 0200)
 		    killpg(mypgrp, lastval2 & ~0200);
-		if ((list_pipe || last1) && !list_pipe_child &&
-		    jn->stat & STAT_STOPPED) {
+		if ((list_pipe || last1 || pline_level) &&
+		    !list_pipe_child && 
+		    ((jn->stat & STAT_STOPPED) ||
+		     (list_pipe_job && pline_level &&
+		      (jobtab[list_pipe_job].stat & STAT_STOPPED)))) {
 		    pid_t pid;
 		    int synch[2];
 
@@ -944,21 +965,28 @@ execpline(Sublist l, int how, int last1)
 			close(synch[1]);
 			read(synch[0], &dummy, 1);
 			close(synch[0]);
-			jobtab[list_pipe_job].other = newjob;
-			jobtab[list_pipe_job].stat |= STAT_SUPERJOB;
-			jn->stat |= STAT_SUBJOB | STAT_NOPRINT;
-			jn->other = pid;
-			killpg(jobtab[list_pipe_job].gleader, SIGSTOP);
+			/* If this job has finished, we leave it as a
+			 * normal (non-super-) job. */
+			if (!(jn->stat & STAT_DONE)) {
+			    jobtab[list_pipe_job].other = newjob;
+			    jobtab[list_pipe_job].stat |= STAT_SUPERJOB;
+			    jn->stat |= STAT_SUBJOB | STAT_NOPRINT;
+			    jn->other = pid;
+			}
+			if ((list_pipe || last1) && jobtab[list_pipe_job].procs)
+			    killpg(jobtab[list_pipe_job].gleader, SIGSTOP);
 			break;
 		    }
 		    else {
 			close(synch[0]);
 			entersubsh(Z_ASYNC, 0, 0);
-			setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader);
+			if (jobtab[list_pipe_job].procs)
+			    setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader);
 			close(synch[1]);
 			kill(getpid(), SIGSTOP);
 			list_pipe = 0;
 			list_pipe_child = 1;
+			opts[INTERACTIVE] = 0;
 			break;
 		    }
 		}
@@ -975,7 +1003,8 @@ execpline(Sublist l, int how, int last1)
 		jn->stat |= STAT_NOPRINT;
 		killjb(jobtab + pj, lastval & ~0200);
 	    }
-	    if (list_pipe_child || (list_pipe && (jn->stat & STAT_DONE)))
+	    if (list_pipe_child || ((list_pipe || pline_level) &&
+				    (jn->stat & STAT_DONE)))
 		deletejob(jn);
 	    thisjob = pj;
 
@@ -998,19 +1027,19 @@ execpline2(Pline pline, int how, int input, int output, int last1)
 {
     pid_t pid;
     int pipes[2];
-    int oldlineno;
 
     if (breaks || retflag)
 	return;
 
-    oldlineno = lineno;
-    lineno = pline->left->lineno;
+    if (pline->left->lineno >= 0)
+	lineno = pline->left->lineno;
 
-    if (pline_level == 1)
+    if (pline_level == 1) {
 	if (!sfcontext)
 	    strcpy(list_pipe_text, getjobtext((void *) pline->left));
 	else
 	    list_pipe_text[0] = '\0';
+    }
     if (pline->type == END)
 	execcmd(pline->left, input, output, how, last1 ? 1 : 2);
     else {
@@ -1054,15 +1083,15 @@ execpline2(Pline pline, int how, int input, int output, int last1)
 	if (pline->right) {
 	    /* if another execpline() is invoked because the command is *
 	     * a list it must know that we're already in a pipeline     */
+	    cmdpush(CS_PIPE);
 	    list_pipe = 1;
 	    execpline2(pline->right, how, pipes[0], output, last1);
 	    list_pipe = old_list_pipe;
+	    cmdpop();
 	    zclose(pipes[0]);
 	    subsh_close = -1;
 	}
     }
-
-    lineno = oldlineno;
 }
 
 /* make the argv array */
@@ -1079,7 +1108,7 @@ makecline(LinkList list)
     argv = 2 + (char **) ncalloc((countlinknodes(list) + 4) * sizeof(char *));
     if (isset(XTRACE)) {
 	if (!doneps4)
-	    fprintf(stderr, "%s", (prompt4) ? prompt4 : "");
+	    printprompt4();
 
 	for (node = firstnode(list); node; incnode(node)) {
 	    *ptr++ = (char *)getdata(node);
@@ -1283,7 +1312,7 @@ addvars(LinkList l, int export)
 
     xtr = isset(XTRACE);
     if (xtr && nonempty(l)) {
-	fprintf(stderr, "%s", prompt4 ? prompt4 : "");
+	printprompt4();
 	doneps4 = 1;
     }
 
@@ -2179,8 +2208,9 @@ entersubsh(int how, int cl, int fake)
 		    attachtty(jobtab[thisjob].gleader);
 	    }
 	}
-	else if (!jobtab[thisjob].gleader ||
-		 (setpgrp(0L, jobtab[thisjob].gleader) == -1)) {
+	else if (!(list_pipe || list_pipe_child || pline_level > 1) &&
+		 (!jobtab[thisjob].gleader ||
+		  setpgrp(0L, jobtab[thisjob].gleader) == -1)) {
 	    jobtab[thisjob].gleader = getpid();
 	    if (list_pipe_job != thisjob &&
 		!jobtab[list_pipe_job].gleader)
@@ -2322,7 +2352,7 @@ getoutput(char *cmd, int qt)
     Cmd c;
     Redir r;
 
-    if (!(list = parse_string(cmd)))
+    if (!(list = parse_string(cmd, 0)))
 	return NULL;
     if (list != &dummy_list && !list->right && !list->left->flags &&
 	list->left->type == END && list->left->left->type == END &&
@@ -2451,7 +2481,7 @@ parsecmd(char *cmd)
 	return NULL;
     }
     *str = '\0';
-    if (str[1] || !(list = parse_string(cmd + 2))) {
+    if (str[1] || !(list = parse_string(cmd + 2, 0))) {
 	zerr("parse error in process substitution", NULL, 0);
 	return NULL;
     }
@@ -2661,7 +2691,8 @@ execcond(Cmd cmd, LinkList args, int flags)
 {
     int stat;
     if (isset(XTRACE)) {
-	fprintf(stderr, "%s[[", prompt4 ? prompt4 : "");
+	printprompt4();
+	fprintf(stderr, "[[");
 	tracingcond++;
     }
     stat = !evalcond(cmd->u.cond);
@@ -2682,8 +2713,10 @@ execarith(Cmd cmd, LinkList args, int flags)
     char *e;
     zlong val = 0;
 
-    if (isset(XTRACE))
-	fprintf(stderr, "%s((", prompt4 ? prompt4 : "");
+    if (isset(XTRACE)) {
+	printprompt4();
+	fprintf(stderr, "((");
+    }
     if (args)
 	while ((e = (char *) ugetnode(args))) {
 	    if (isset(XTRACE))
@@ -2759,21 +2792,22 @@ static void
 execshfunc(Cmd cmd, Shfunc shf, LinkList args)
 {
     LinkList last_file_list = NULL;
+    unsigned char *ocs;
+    int ocsp;
 
     if (errflag)
 	return;
 
-    if (!list_pipe) {
+    if (!list_pipe && thisjob != list_pipe_job) {
 	/* Without this deletejob the process table *
 	 * would be filled by a recursive function. */
 	last_file_list = jobtab[thisjob].filelist;
 	jobtab[thisjob].filelist = NULL;
 	deletejob(jobtab + thisjob);
     }
-
     if (isset(XTRACE)) {
 	LinkNode lptr;
-	fprintf(stderr, "%s", prompt4 ? prompt4 : prompt4);
+	printprompt4();
 	if (args)
 	    for (lptr = firstnode(args); lptr; incnode(lptr)) {
 		if (lptr != firstnode(args))
@@ -2783,8 +2817,14 @@ execshfunc(Cmd cmd, Shfunc shf, LinkList args)
 	fputc('\n', stderr);
 	fflush(stderr);
     }
-
+    ocs = cmdstack;
+    ocsp = cmdsp;
+    cmdstack = (unsigned char *) zalloc(CMDSTACKSZ);
+    cmdsp = 0;
     doshfunc(shf->nam, shf->funcdef, args, shf->flags, 0);
+    free(cmdstack);
+    cmdstack = ocs;
+    cmdsp = ocsp;
 
     if (!list_pipe)
 	deletefilelist(last_file_list);
@@ -2842,9 +2882,8 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
  * was executed.                                            */
 {
     char **tab, **x, *oargv0 = NULL;
-    int xexittr, newexittr, oldzoptind, oldlastval;
-    void *xexitfn, *newexitfn;
-    char saveopts[OPT_SIZE];
+    int oldzoptind, oldlastval;
+    char saveopts[OPT_SIZE], *oldscriptname;
     int obreaks = breaks;
 
     HEAPALLOC {
@@ -2852,14 +2891,12 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 	if (trapreturn < 0)
 	    trapreturn--;
 	oldlastval = lastval;
-	xexittr = sigtrapped[SIGEXIT];
-	if (xexittr & ZSIG_FUNC)
-	    xexitfn = shfunctab->removenode(shfunctab, "TRAPEXIT");
-	else
-	    xexitfn = sigfuncs[SIGEXIT];
-	sigtrapped[SIGEXIT] = 0;
-	sigfuncs[SIGEXIT] = NULL;
+
+	starttrapscope();
+
 	tab = pparams;
+	oldscriptname = scriptname;
+	scriptname = name;
 	oldzoptind = zoptind;
 	zoptind = 1;
 
@@ -2902,6 +2939,7 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 	    argzero = oargv0;
 	}
 	zoptind = oldzoptind;
+	scriptname = oldscriptname;
 	pparams = tab;
 
 	if (isset(LOCALOPTIONS)) {
@@ -2916,27 +2954,7 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 	    opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS];
 	}
 
-	/*
-	 * The trap '...' EXIT runs in the environment of the caller,
-	 * so remember it here but run it after resetting the
-	 * traps for the parent.
-	 */
-	newexittr = sigtrapped[SIGEXIT];
-	newexitfn = sigfuncs[SIGEXIT];
-	if (newexittr & ZSIG_FUNC)
-	    shfunctab->removenode(shfunctab, "TRAPEXIT");
-
-	sigtrapped[SIGEXIT] = xexittr;
-	if (xexittr & ZSIG_FUNC) {
-	    shfunctab->addnode(shfunctab, ztrdup("TRAPEXIT"), xexitfn);
-	    sigfuncs[SIGEXIT] = ((Shfunc) xexitfn)->funcdef;
-	} else
-	    sigfuncs[SIGEXIT] = (List) xexitfn;
-
-	if (newexitfn) {
-	    dotrapargs(SIGEXIT, &newexittr, newexitfn);
-	    freestruct(newexitfn);
-	}
+	endtrapscope();
 
 	if (trapreturn < -1)
 	    trapreturn++;
@@ -3008,7 +3026,7 @@ getfpfunc(char *s)
 		    d[len] = '\0';
 		    d = metafy(d, len, META_REALLOC);
 		    HEAPALLOC {
-			r = parse_string(d);
+			r = parse_string(d, 1);
 		    } LASTALLOC;
 		    return r;
 		} else
diff --git a/Src/glob.c b/Src/glob.c
index 93b497632..ea4980b8b 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -1993,7 +1993,7 @@ dyncat(char *s1, char *s2)
     char *ptr;
     int l1 = strlen(s1);
 
-    ptr = (char *)ncalloc(l1 + strlen(s2) + 1);
+    ptr = (char *)zhalloc(l1 + strlen(s2) + 1);
     strcpy(ptr, s1);
     strcpy(ptr + l1, s2);
     return ptr;
diff --git a/Src/hashtable.c b/Src/hashtable.c
index e80461b4e..c4c0b00ac 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -133,6 +133,7 @@ deletehashtable(HashTable ht)
 	ht->last->next = ht->next;
     else
 	firstht = ht->next;
+    zsfree(ht->tablename);
 #endif /* ZSH_HASH_DEBUG */
     zfree(ht->nodes, ht->hsize * sizeof(HashNode));
     zfree(ht, sizeof(*ht));
diff --git a/Src/hist.c b/Src/hist.c
index 49dac724c..edf74009e 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -888,9 +888,14 @@ prepnexthistent(int histnum)
     else {
 	he = hist_ring->down;
 	if (isset(HISTEXPIREDUPSFIRST) && !(he->flags & HIST_DUP)) {
+	    int max_unique_ct = getiparam("SAVEHIST");
 	    do {
+		if (max_unique_ct-- <= 0) {
+		    he = hist_ring->down;
+		    break;
+		}
 		he = he->down;
-	    } while (he != hist_ring->down && !(he->flags & HIST_DUP)) ;
+	    } while (he != hist_ring->down && !(he->flags & HIST_DUP));
 	    if (he != hist_ring->down) {
 		he->up->down = he->down;
 		he->down->up = he->up;
@@ -989,7 +994,7 @@ hend(void)
 	}
 #endif
 	/* get rid of pesky \n which we've already nulled out */
-	if (!chline[chwords[chwordpos-2]])
+	if (chwordpos > 1 && !chline[chwords[chwordpos-2]])
 	    chwordpos -= 2;
 	/* strip superfluous blanks, if desired */
 	if (isset(HISTREDUCEBLANKS))
diff --git a/Src/init.c b/Src/init.c
index 89cbf2a8b..51c2b88a5 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -75,6 +75,11 @@ int tclen[TC_COUNT];
 /**/
 int tclines, tccolumns, hasam;
 
+/* Pointer to read-key function from zle */
+
+/**/
+int (*getkeyptr) _((int));
+
 #ifdef DEBUG
 /* depth of allocation type stack */
 
@@ -194,6 +199,7 @@ parseargs(char **argv)
 
     /* loop through command line options (begins with "-" or "+") */
     while (*argv && (**argv == '-' || **argv == '+')) {
+	char *args = *argv;
 	action = (**argv == '-');
 	if(!argv[0][1])
 	    *argv = "--";
@@ -228,6 +234,14 @@ parseargs(char **argv)
 		    restricted = action;
 		else
 		    dosetopt(optno, action, 1);
+              break;
+	    } else if (isspace(**argv)) {
+		/* zsh's typtab not yet set, have to use ctype */
+		while (*++*argv)
+		    if (!isspace(**argv)) {
+			zerr("bad option string: `%s'", args, 0);
+			exit(1);
+		    }
 		break;
 	    } else {
 	    	if (!(optno = optlookupc(**argv))) {
@@ -376,7 +390,16 @@ init_io(void)
 #ifdef JOB_CONTROL
     /* If interactive, make the shell the foreground process */
     if (opts[MONITOR] && interact && (SHTTY != -1)) {
-	attachtty(GETPGRP());
+      /* Since we now sometimes execute programs in the process group
+       * of the parent shell even when using job-control, we have to
+       * make sure that we run in our own process group. Otherwise if
+       * we are called from a program that doesn't put us in our own
+       * group a SIGTSTP that we ignore might stop our parent process.
+       * Instead of the two calls below we once had:
+       *   attachtty(GETPGRP());
+       */
+	attachtty(getpid());
+	setpgrp(0L, 0L);
 	if ((mypgrp = GETPGRP()) > 0) {
 	    while ((ttpgrp = gettygrp()) != -1 && ttpgrp != mypgrp) {
 		sleep(1);
@@ -530,12 +553,15 @@ setupvals(void)
     int i;
 #endif
 
+    getkeyptr = NULL;
+
+    lineno = 1;
     noeval = 0;
     curhist = 0;
     histsiz = DEFAULT_HISTSIZE;
     inithist();
 
-    cmdstack = (unsigned char *) zalloc(256);
+    cmdstack = (unsigned char *) zalloc(CMDSTACKSZ);
     cmdsp = 0;
 
     bangchar = '!';
@@ -869,7 +895,7 @@ source(char *s)
     SHIN = tempfd;
     bshin = fdopen(SHIN, "r");
     subsh  = 0;
-    lineno = 0;
+    lineno = 1;
     loops  = 0;
     dosetopt(SHINSTDIN, 0, 1);
     scriptname = s;
diff --git a/Src/input.c b/Src/input.c
index b4a6fe22d..d05c75c0b 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -186,6 +186,8 @@ ingetc(void)
 	    inbufct--;
 	    if (itok(lastc = STOUC(*inbufptr++)))
 		continue;
+	    if (((inbufflags & INP_LINENO) || !strin) && lastc == '\n')
+		lineno++;
 	    return lastc;
 	}
 
@@ -279,23 +281,20 @@ inputline(void)
 	zputs(ingetcline, stderr);
 	fflush(stderr);
     }
-    if (*ingetcline && ingetcline[strlen(ingetcline) - 1] == '\n') {
-	/* We've now read a complete line. */
-	lineno++;
-	if (interact && isset(SUNKEYBOARDHACK) && isset(SHINSTDIN) &&
-	    SHTTY != -1 && *ingetcline && ingetcline[1] &&
-	    ingetcline[strlen(ingetcline) - 2] == '`') {
-	    /* Junk an unmatched "`" at the end of the line. */
-	    int ct;
-	    char *ptr;
-
-	    for (ct = 0, ptr = ingetcline; *ptr; ptr++)
-		if (*ptr == '`')
-		    ct++;
-	    if (ct & 1) {
-		ptr[-2] = '\n';
-		ptr[-1] = '\0';
-	    }
+    if (*ingetcline && ingetcline[strlen(ingetcline) - 1] == '\n' &&
+	interact && isset(SUNKEYBOARDHACK) && isset(SHINSTDIN) &&
+	SHTTY != -1 && *ingetcline && ingetcline[1] &&
+	ingetcline[strlen(ingetcline) - 2] == '`') {
+	/* Junk an unmatched "`" at the end of the line. */
+	int ct;
+	char *ptr;
+
+	for (ct = 0, ptr = ingetcline; *ptr; ptr++)
+	    if (*ptr == '`')
+		ct++;
+	if (ct & 1) {
+	    ptr[-2] = '\n';
+	    ptr[-1] = '\0';
 	}
     }
     isfirstch = 1;
@@ -359,6 +358,8 @@ inungetc(int c)
 	    inbufptr--;
 	    inbufct++;
 	    inbufleft++;
+	    if (((inbufflags & INP_LINENO) || !strin) && c == '\n')
+		lineno--;
 	}
 #ifdef DEBUG
         else if (!(inbufflags & INP_CONT)) {
diff --git a/Src/jobs.c b/Src/jobs.c
index 31861e284..a50b84fd7 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -92,9 +92,13 @@ makerunning(Job jn)
 
     jn->stat &= ~STAT_STOPPED;
     for (pn = jn->procs; pn; pn = pn->next)
+#if 0
 	if (WIFSTOPPED(pn->status) && 
 	    (!(jn->stat & STAT_SUPERJOB) || pn->next))
 	    pn->status = SP_RUNNING;
+#endif
+        if (WIFSTOPPED(pn->status))
+	    pn->status = SP_RUNNING;
 
     if (jn->stat & STAT_SUPERJOB)
 	makerunning(jobtab + jn->other);
@@ -181,8 +185,9 @@ update_job(Job jn)
 
 		for (i = 1; i < MAXJOB; i++)
 		    if ((jobtab[i].stat & STAT_SUPERJOB) &&
-			jobtab[i].other == job) {
-			killpg(jobtab[i].gleader, SIGSTOP);
+			jobtab[i].other == job &&
+			jobtab[i].gleader) {
+			killpg(jobtab[i].gleader, SIGTSTP);
 			break;
 		    }
 	    }
diff --git a/Src/lex.c b/Src/lex.c
index 8dc836329..33b6598b9 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -219,7 +219,7 @@ lexsave(void)
     ls->hlinesz = hlinesz;
     ls->cstack = cmdstack;
     ls->csp = cmdsp;
-    cmdstack = (unsigned char *)zalloc(256);
+    cmdstack = (unsigned char *)zalloc(CMDSTACKSZ);
     ls->tok = tok;
     ls->isnewlin = isnewlin;
     ls->tokstr = tokstr;
@@ -1150,7 +1150,7 @@ gettokstr(int c, int sub)
 	    cmdpush(CS_BQUOTE);
 	    SETPARBEGIN
 	    inquote = 0;
-	    while ((c = hgetc()) != '`' && !lexstop)
+	    while ((c = hgetc()) != '`' && !lexstop) {
 		if (c == '\\') {
 		    c = hgetc();
 		    if (c != '\n') {
@@ -1171,6 +1171,7 @@ gettokstr(int c, int sub)
 			    ALLOWHIST
 		    }
 		}
+	    }
 	    if (inquote)
 		ALLOWHIST
 	    cmdpop();
diff --git a/Src/loop.c b/Src/loop.c
index 07a7a56b0..16e4ff314 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -71,6 +71,7 @@ execfor(Cmd cmd, LinkList args, int flags)
     lastval = 0;
     loops++;
     pushheap();
+    cmdpush(CS_FOR);
     for (;;) {
 	if (node->condition) {
 	    str = dupstring(node->condition);
@@ -119,6 +120,7 @@ execfor(Cmd cmd, LinkList args, int flags)
 	freeheap();
     }
     popheap();
+    cmdpop();
     loops--;
     return lastval;
 }
@@ -147,6 +149,7 @@ execselect(Cmd cmd, LinkList args, int flags)
     loops++;
     lastval = 0;
     pushheap();
+    cmdpush(CS_SELECT);
     inp = fdopen(dup((SHTTY == -1) ? 0 : SHTTY), "r");
     more = selectlist(args, 0);
     for (;;) {
@@ -201,6 +204,7 @@ execselect(Cmd cmd, LinkList args, int flags)
 	    break;
     }
   done:
+    cmdpop();
     popheap();
     fclose(inp);
     loops--;
@@ -279,6 +283,7 @@ execwhile(Cmd cmd, LinkList args, int flags)
     node = cmd->u.whilecmd;
     oldval = 0;
     pushheap();
+    cmdpush(node->cond ? CS_UNTIL : CS_WHILE);
     loops++;
     for (;;) {
 	noerrexit = 1;
@@ -304,6 +309,7 @@ execwhile(Cmd cmd, LinkList args, int flags)
 	}
 	oldval = lastval;
     }
+    cmdpop();
     popheap();
     loops--;
     return lastval;
@@ -322,6 +328,7 @@ execrepeat(Cmd cmd, LinkList args, int flags)
     }
     count = atoi(peekfirst(args));
     pushheap();
+    cmdpush(CS_REPEAT);
     loops++;
     while (count--) {
 	execlist(cmd->u.list, 1, 0);
@@ -337,6 +344,7 @@ execrepeat(Cmd cmd, LinkList args, int flags)
 	    break;
 	}
     }
+    cmdpop();
     popheap();
     loops--;
     return lastval;
@@ -347,7 +355,7 @@ int
 execif(Cmd cmd, LinkList args, int flags)
 {
     struct ifcmd *node;
-    int olderrexit;
+    int olderrexit, s = 0;
     List *i, *t;
 
     olderrexit = noerrexit;
@@ -358,17 +366,22 @@ execif(Cmd cmd, LinkList args, int flags)
     if (!noerrexit)
 	noerrexit = 1;
     while (*i) {
+	cmdpush(s ? CS_ELIF : CS_IF);
 	execlist(*i, 1, 0);
+	cmdpop();
 	if (!lastval)
 	    break;
+	s = 1;
 	i++;
 	t++;
     }
     noerrexit = olderrexit;
 
-    if (*t)
+    if (*t) {
+	cmdpush(*i ? (s ? CS_ELIFTHEN : CS_IFTHEN) : CS_ELSE);
 	execlist(*t, 1, flags & CFLAG_EXEC);
-    else
+	cmdpop();
+    } else
 	lastval = 0;
 
     return lastval;
@@ -393,6 +406,7 @@ execcase(Cmd cmd, LinkList args, int flags)
     lastval = 0;
 
     if (node) {
+	cmdpush(CS_CASE);
 	while (*p) {
 	    char *pat = dupstring(*p + 1);
 	    singsub(&pat);
@@ -405,6 +419,7 @@ execcase(Cmd cmd, LinkList args, int flags)
 	    p++;
 	    l++;
 	}
+	cmdpop();
     }
     return lastval;
 }
diff --git a/Src/module.c b/Src/module.c
index fa7dd2774..ce926c27a 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -709,7 +709,14 @@ bin_zmodload(char *nam, char **args, char *ops, int func)
 	zwarnnam(nam, "what do you want to unload?", NULL, 0);
 	return 1;
     }
-    if (ops['d'])
+    if (ops['e'] && (ops['I'] || ops['L'] || ops['a'] || ops['d'] ||
+		     ops['i'] || ops['u'])) {
+	zwarnnam(nam, "-e cannot be combined with other options", NULL, 0);
+	return 1;
+    }
+    if (ops['e'])
+	return bin_zmodload_exist(nam, args, ops);
+    else if (ops['d'])
 	return bin_zmodload_dep(nam, args, ops);
     else if ((ops['a'] || ops['b']) && !(ops['c'] || ops['p']))
 	return bin_zmodload_auto(nam, args, ops);
@@ -727,6 +734,46 @@ bin_zmodload(char *nam, char **args, char *ops, int func)
 
 /**/
 static int
+bin_zmodload_exist(char *nam, char **args, char *ops)
+{
+    LinkNode node;
+    Module m;
+
+    if (!*args) {
+	for (node = firstnode(bltinmodules); node; incnode(node)) {
+	    nicezputs((char *) getdata(node), stdout);
+	    putchar('\n');
+	}
+	for (node = firstnode(modules); node; incnode(node)) {
+	    m = (Module) getdata(node);
+	    if (m->handle && !(m->flags & MOD_UNLOAD)) {
+		nicezputs(m->nam, stdout);
+		putchar('\n');
+	    }
+	}
+	return 0;
+    } else {
+	int ret = 0, f;
+
+	for (; !ret && *args; args++) {
+	    f = 0;
+	    for (node = firstnode(bltinmodules);
+		 !f && node; incnode(node))
+		f = !strcmp(*args, (char *) getdata(node));
+	    for (node = firstnode(modules);
+		 !f && node; incnode(node)) {
+		m = (Module) getdata(node);
+		if (m->handle && !(m->flags & MOD_UNLOAD))
+		    f = !strcmp(*args, m->nam);
+	    }
+	    ret = !f;
+	}
+	return ret;
+    }
+}
+
+/**/
+static int
 bin_zmodload_dep(char *nam, char **args, char *ops)
 {
     LinkNode node;
@@ -1116,6 +1163,37 @@ bin_zmodload_load(char *nam, char **args, char *ops)
 }
 
 /**/
+#else /* DYNAMIC */
+
+/* This is the version for shells without dynamic linking. */
+
+/**/
+int
+bin_zmodload(char *nam, char **args, char *ops, int func)
+{
+    /* We understand only the -e option. */
+
+    if (ops['e']) {
+	LinkNode node;
+
+	if (!*args) {
+	    for (node = firstnode(bltinmodules); node; incnode(node)) {
+		nicezputs((char *) getdata(node), stdout);
+		putchar('\n');
+	    }
+	} else {
+	    for (; *args; args++)
+		for (node = firstnode(bltinmodules); node; incnode(node))
+		    if (strcmp(*args, (char *) getdata(node)))
+			return 1;
+	}
+	return 0;
+    }
+    /* Otherwise we return 1 -- different from the dynamic version. */
+    return 1;
+}
+
+/**/
 #endif /* DYNAMIC */
 
 /* The list of module-defined conditions. */
@@ -1206,6 +1284,180 @@ addconddefs(char const *nam, Conddef c, int size)
     return hadf ? hads : 1;
 }
 
+/* This list of hook functions defined. */
+
+/**/
+Hookdef hooktab;
+
+/* Find a hook definition given the name. */
+
+/**/
+Hookdef
+gethookdef(char *n)
+{
+    Hookdef p;
+
+    for (p = hooktab; p; p = p->next)
+	if (!strcmp(n, p->name))
+	    return p;
+    return NULL;
+}
+
+/* This adds the given hook definition. The return value is zero on      *
+ * success and 1 on failure.                                             */
+
+/**/
+int
+addhookdef(Hookdef h)
+{
+    if (gethookdef(h->name))
+	return 1;
+
+    h->next = hooktab;
+    hooktab = h;
+    PERMALLOC {
+	h->funcs = newlinklist();
+    } LASTALLOC;
+
+    return 0;
+}
+
+/* This adds multiple hook definitions. This is like addbuiltins(). */
+
+/**/
+int
+addhookdefs(char const *nam, Hookdef h, int size)
+{
+    int hads = 0, hadf = 0;
+
+    while (size--) {
+	if (addhookdef(h)) {
+	    zwarnnam(nam, "name clash when adding condition `%s'", h->name, 0);
+	    hadf = 1;
+	} else
+	    hads = 2;
+	h++;
+    }
+    return hadf ? hads : 1;
+}
+
+/* Delete hook definitions. */
+
+/**/
+int
+deletehookdef(Hookdef h)
+{
+    Hookdef p, q;
+
+    for (p = hooktab, q = NULL; p && p != h; q = p, p = p->next);
+
+    if (!p)
+	return 1;
+
+    if (q)
+	q->next = p->next;
+    else
+	hooktab = p->next;
+    freelinklist(p->funcs, NULL);
+    return 0;
+}
+
+/**/
+int
+deletehookdefs(char const *nam, Hookdef h, int size)
+{
+    while (size--) {
+	deletehookdef(h);
+	h++;
+    }
+    return 1;
+}
+
+/* Add a function to a hook. */
+
+/**/
+int
+addhookdeffunc(Hookdef h, Hookfn f)
+{
+    PERMALLOC {
+	addlinknode(h->funcs, (void *) f);
+    } LASTALLOC;
+    return 0;
+}
+
+/**/
+int
+addhookfunc(char *n, Hookfn f)
+{
+    Hookdef h = gethookdef(n);
+
+    if (h)
+	return addhookdeffunc(h, f);
+    return 1;
+}
+
+/* Delete a function from a hook. */
+
+/**/
+int
+deletehookdeffunc(Hookdef h, Hookfn f)
+{
+    LinkNode p;
+
+    for (p = firstnode(h->funcs); p; incnode(p))
+	if (f == (Hookfn) getdata(p)) {
+	    remnode(h->funcs, p);
+	    return 0;
+	}
+    return 1;
+}
+
+/**/
+int
+deletehookfunc(char *n, Hookfn f)
+{
+    Hookdef h = gethookdef(n);
+
+    if (h)
+	return deletehookdeffunc(h, f);
+    return 1;
+}
+
+/* Run the function(s) for a hook. */
+
+/**/
+int
+runhookdef(Hookdef h, void *d)
+{
+    if (empty(h->funcs)) {
+	if (h->def)
+	    return h->def(h, d);
+	return 0;
+    } else if (h->flags & HOOKF_ALL) {
+	LinkNode p;
+	int r;
+
+	for (p = firstnode(h->funcs); p; incnode(p))
+	    if ((r = ((Hookfn) getdata(p))(h, d)))
+		return r;
+	if (h->def)
+	    return h->def(h, d);
+	return 0;
+    } else
+	return ((Hookfn) getdata(lastnode(h->funcs)))(h, d);
+}
+
+/**/
+int
+runhook(char *n, void *d)
+{
+    Hookdef h = gethookdef(n);
+
+    if (h)
+	return runhookdef(h, d);
+    return 0;
+}
+
 /* This adds the given parameter definition. The return value is zero on *
  * success and 1 on failure. */
 
diff --git a/Src/options.c b/Src/options.c
index 504ccdf7e..2eb73690e 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -143,6 +143,7 @@ static struct optname optns[] = {
 {NULL, "listbeep",	      OPT_ALL,			 LISTBEEP},
 {NULL, "listtypes",	      OPT_ALL,			 LISTTYPES},
 {NULL, "localoptions",	      OPT_EMULATE|OPT_KSH,	 LOCALOPTIONS},
+{NULL, "localtraps",	      OPT_EMULATE|OPT_KSH,	 LOCALTRAPS},
 {NULL, "login",		      OPT_SPECIAL,		 LOGINSHELL},
 {NULL, "longlistjobs",	      0,			 LONGLISTJOBS},
 {NULL, "magicequalsubst",     0,			 MAGICEQUALSUBST},
diff --git a/Src/params.c b/Src/params.c
index b9f3d33a0..d71cfb46e 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -588,7 +588,7 @@ createparam(char *name, int flags)
 
 	DPUTS(oldpm && oldpm->level > locallevel,
 	      "BUG:  old local parameter not deleteed");
-	if (oldpm && oldpm->level == locallevel) {
+	if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) {
 	    if (!(oldpm->flags & PM_UNSET) || (oldpm->flags & PM_SPECIAL)) {
 		oldpm->flags &= ~PM_UNSET;
 		return NULL;
@@ -616,7 +616,7 @@ createparam(char *name, int flags)
 	pm = (Param) alloc(sizeof *pm);
 	pm->nam = nulstring;
     }
-    pm->flags = flags;
+    pm->flags = flags & ~PM_LOCAL;
 
     if(!(pm->flags & PM_SPECIAL))
 	assigngetset(pm);
@@ -1898,7 +1898,7 @@ arrhashsetfn(Param pm, char **val)
      * since that could cause trouble for special hashes.  This way, *
      * it's up to pm->sets.hfn() what to do.                         */
     int alen = arrlen(val);
-    HashTable opmtab = paramtab, ht;
+    HashTable opmtab = paramtab, ht = 0;
     char **aptr = val;
     Value v = (Value) hcalloc(sizeof *v);
     v->b = -1;
@@ -1909,7 +1909,8 @@ arrhashsetfn(Param pm, char **val)
 	     NULL, 0);
 	return;
     }
-    ht = paramtab = newparamtable(17, pm->nam);
+    if (alen)
+	ht = paramtab = newparamtable(17, pm->nam);
     while (*aptr) {
 	/* The parameter name is ztrdup'd... */
 	v->pm = createparam(*aptr, PM_SCALAR|PM_UNSET);
diff --git a/Src/parse.c b/Src/parse.c
index 520181750..626ffc982 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -890,6 +890,8 @@ par_subsh(Cmd c)
 static void
 par_funcdef(Cmd c)
 {
+    int oldlineno = lineno;
+    lineno = 0;
     nocorrect = 1;
     incmdpos = 0;
     yylex();
@@ -912,13 +914,17 @@ par_funcdef(Cmd c)
     if (tok == INBRACE) {
 	yylex();
 	c->u.list = par_list();
-	if (tok != OUTBRACE)
+	if (tok != OUTBRACE) {
+	    lineno += oldlineno;
 	    YYERRORV;
+	}
 	yylex();
     } else if (unset(SHORTLOOPS)) {
+	lineno += oldlineno;
 	YYERRORV;
     } else
 	c->u.list = par_list1();
+    lineno += oldlineno;
 }
 
 /*
@@ -1023,6 +1029,8 @@ par_simple(Cmd c)
 		c->redir = newlinklist();
 	    par_redir(c->redir);
 	} else if (tok == INOUTPAR) {
+	    int oldlineno = lineno;
+	    lineno = 0;
 	    incmdpos = 1;
 	    cmdpush(CS_FUNCDEF);
 	    yylex();
@@ -1033,6 +1041,7 @@ par_simple(Cmd c)
 		c->u.list = par_list();
 		if (tok != OUTBRACE) {
 		    cmdpop();
+		    lineno += oldlineno;
 		    YYERROR;
 		}
 		yylex();
@@ -1051,6 +1060,7 @@ par_simple(Cmd c)
 	    }
 	    cmdpop();
 	    c->type = FUNCDEF;
+	    lineno += oldlineno;
 	} else
 	    break;
 	isnull = 0;
diff --git a/Src/signals.c b/Src/signals.c
index f69da0140..e9851b57a 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -602,6 +602,57 @@ killjb(Job jn, int sig)
     return err;
 }
 
+/*
+ * List for saving traps.  We don't usually have that many traps
+ * at once, so just use a linked list.
+ */
+struct savetrap {
+    int sig, flags, local;
+    void *list;
+};
+
+static LinkList savetraps;
+
+/* Flag to unsettrap not to free the structs, which we're keeping */
+
+/**/
+int notrapfree;
+
+/*
+ * Save the current trap and unset it.
+ */
+
+static void
+dosavetrap(int sig, int level)
+{
+    struct savetrap *st;
+    st = (struct savetrap *)zalloc(sizeof(*st));
+    st->sig = sig;
+    st->local = level;
+    notrapfree++;
+    if ((st->flags = sigtrapped[sig]) & ZSIG_FUNC) {
+	/*
+	 * Get the old function: this assumes we haven't added
+	 * the new one yet.
+	 */
+	char func[20];
+	sprintf(func, "TRAP%s", sigs[sig]);
+	st->list = shfunctab->removenode(shfunctab, func);
+    } else {
+	st->list = sigfuncs[sig];
+	unsettrap(sig);
+    }
+    notrapfree--;
+    PERMALLOC {
+	if (!savetraps)
+	    savetraps = newlinklist();
+	/*
+	 * Put this at the front of the list
+	 */
+	insertlinknode(savetraps, (LinkNode)savetraps, st);
+    } LASTALLOC;
+}
+
 /**/
 int
 settrap(int sig, List l)
@@ -612,7 +663,15 @@ settrap(int sig, List l)
         zerr("can't trap SIG%s in interactive shells", sigs[sig], 0);
         return 1;
     }
-    if (sigfuncs[sig])
+    /*
+     * Note that we save the trap here even if there isn't an existing
+     * one, to aid in removing this one.  However, if there's
+     * already one at the current locallevel we just overwrite it.
+     */
+    if (isset(LOCALTRAPS) && locallevel &&
+	(!sigtrapped[sig] || locallevel > (sigtrapped[sig] >> ZSIG_SHIFT))) {
+	dosavetrap(sig, locallevel);
+    } else if (sigfuncs[sig])
 	unsettrap(sig);
     sigfuncs[sig] = l;
     if (!l) {
@@ -632,6 +691,12 @@ settrap(int sig, List l)
             sig != SIGCHLD)
             install_handler(sig);
     }
+    /*
+     * Note that introducing the locallevel does not affect whether
+     * sigtrapped[sig] is zero or not, i.e. a test without a mask
+     * works just the same.
+     */
+    sigtrapped[sig] |= (locallevel << ZSIG_SHIFT);
     return 0;
 }
 
@@ -645,6 +710,19 @@ unsettrap(int sig)
 	(jobbing && (sig == SIGTTOU || sig == SIGTSTP || sig == SIGTTIN))) {
         return;
     }
+    if (isset(LOCALTRAPS) && locallevel &&
+	sigtrapped[sig] && locallevel > (sigtrapped[sig] >> ZSIG_SHIFT)) {
+	/*
+	 * This calls unsettrap recursively to do any dirty work, so
+	 * make sure this bit doesn't happen:  a bit messy, but hard
+	 * to avoid.
+	 */
+	int oldlt = opts[LOCALTRAPS];
+	opts[LOCALTRAPS] = 0;
+	dosavetrap(sig, locallevel);
+	opts[LOCALTRAPS] = oldlt;
+	return;
+    }
     sigtrapped[sig] = 0;
     if (sig == SIGINT && interact) {
 	/* PWS 1995/05/16:  added test for interactive, also noholdintr() *
@@ -659,6 +737,8 @@ unsettrap(int sig)
 #endif
              sig != SIGCHLD)
         signal_default(sig);
+    if (notrapfree)
+	return;
     if (trapped & ZSIG_FUNC) {
 	char func[20];
 	HashNode hn;
@@ -672,6 +752,83 @@ unsettrap(int sig)
     }
 }
 
+/**/
+void
+starttrapscope(void)
+{
+    /*
+     * SIGEXIT needs to be restored at the current locallevel,
+     * so give it the next higher one.
+     */
+    if (sigtrapped[SIGEXIT])
+	dosavetrap(SIGEXIT, locallevel+1);
+}
+
+/*
+ * Reset traps after the end of a function: must be called after
+ * endparamscope() so that the locallevel has been decremented.
+ */
+
+/**/
+void
+endtrapscope(void)
+{
+    LinkNode ln;
+    struct savetrap *st;
+    int exittr;
+    void *exitfn = NULL;
+
+    /*
+     * Remember the exit trap, but don't run it until
+     * after all the other traps have been put back.
+     */
+    if ((exittr = sigtrapped[SIGEXIT])) {
+	notrapfree++;
+	if (exittr & ZSIG_FUNC) {
+	    exitfn = shfunctab->removenode(shfunctab, "TRAPEXIT");
+	} else {
+	    exitfn = sigfuncs[SIGEXIT];
+	    unsettrap(SIGEXIT);
+	}
+	notrapfree--;
+    }
+
+    if (savetraps) {
+	while ((ln = firstnode(savetraps)) &&
+	       (st = (struct savetrap *) ln->dat) &&
+	       st->local > locallevel) {
+	    int sig = st->sig;
+
+	    remnode(savetraps, ln);
+
+	    if (sigtrapped[sig])
+		unsettrap(sig);
+	    if (st->flags) {
+		List list = (st->flags & ZSIG_FUNC) ?
+		    ((Shfunc) st->list)->funcdef : (List) st->list;
+		/* prevent settrap from saving this */
+		int oldlt = opts[LOCALTRAPS];
+		opts[LOCALTRAPS] = 0;
+		settrap(sig, list);
+		opts[LOCALTRAPS] = oldlt;
+		if ((sigtrapped[sig] = st->flags) & ZSIG_FUNC)
+		    shfunctab->addnode(shfunctab, ((Shfunc)st->list)->nam,
+				       (Shfunc) st->list);
+	    }
+	    zfree(st, sizeof(*st));
+	}
+    }
+
+    if (exittr) {
+	dotrapargs(SIGEXIT, &exittr, (exittr & ZSIG_FUNC) ?
+		   ((Shfunc)exitfn)->funcdef : (List) exitfn);
+	if (exittr & ZSIG_FUNC)
+	    shfunctab->freenode((HashNode)exitfn);
+	else
+	    freestruct(exitfn);
+    }
+}
+
 /* Execute a trap function for a given signal, possibly
  * with non-standard sigtrapped & sigfuncs values
  */
diff --git a/Src/subst.c b/Src/subst.c
index d85d73f58..18d815404 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -1328,10 +1328,15 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 			else
 			    t = aval;
 		    } else if (!isarr) {
-			arr[0] = val;
-			arr[1] = NULL;
+			if (!*val && arrasg > 1) {
+			    arr[0] = NULL;
+			    l = 0;
+			} else {
+			    arr[0] = val;
+			    arr[1] = NULL;
+			    l = 1;
+			}
 			t = aval = arr;
-			l = 1;
 		    } else
 			l = arrlen(aval), t = aval;
 		    p = a = zalloc(sizeof(char *) * (l + 1));
@@ -1944,6 +1949,8 @@ dstackent(char ch, int val)
     else
 	for (end=NULL, n=firstnode(dirstack); n && val; val--, n=nextnode(n));
     if (n == end) {
+	if (backwards && !val)
+	    return pwd;
 	if (isset(NOMATCH))
 	    zerr("not enough directory stack entries.", NULL, 0);
 	return NULL;
diff --git a/Src/utils.c b/Src/utils.c
index ba4f3dc9d..a409ab03c 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -62,9 +62,10 @@ zerr(const char *fmt, const char *str, int num)
     /*
      * scriptname is set when sourcing scripts, so that we get the
      * correct name instead of the generic name of whatever
-     * program/script is running.
+     * program/script is running.  It's also set in shell functions,
+     * so test locallevel, too.
      */
-    nicezputs(isset(SHINSTDIN) ? "zsh" :
+    nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" :
 	      scriptname ? scriptname : argzero, stderr);
     fputs(": ", stderr);
     zerrnam(NULL, fmt, str, num);
@@ -79,7 +80,7 @@ zerrnam(const char *cmd, const char *fmt, const char *str, int num)
 	    return;
 	errflag = 1;
 	trashzle();
-	if(unset(SHINSTDIN)) {
+	if (unset(SHINSTDIN) || locallevel) {
 	    nicezputs(scriptname ? scriptname : argzero, stderr);
 	    fputs(": ", stderr);
 	}
@@ -133,7 +134,7 @@ zerrnam(const char *cmd, const char *fmt, const char *str, int num)
 	    putc(*fmt == Meta ? *++fmt ^ 32 : *fmt, stderr);
 	    fmt++;
 	}
-    if (unset(SHINSTDIN) && lineno)
+    if ((unset(SHINSTDIN) || locallevel) && lineno)
 	fprintf(stderr, " [%ld]\n", (long)lineno);
     else
 	putc('\n', stderr);
@@ -774,6 +775,23 @@ checkmailpath(char **s)
     }
 }
 
+/* This prints the XTRACE prompt. */
+
+/**/
+void
+printprompt4(void)
+{
+    if (prompt4) {
+	int l;
+	char *s = dupstring(prompt4);
+
+	unmetafy(s, &l);
+	s = unmetafy(promptexpand(metafy(s, l, META_NOALLOC), 0, NULL, NULL), &l);
+
+	fprintf(stderr, "%s", s);
+    }
+}
+
 /**/
 void
 freestr(void *a)
@@ -2116,7 +2134,12 @@ mkarray(char *s)
 void
 zbeep(void)
 {
-    if (isset(BEEP))
+    char *vb;
+    if ((vb = getsparam("ZBEEP"))) {
+	int len;
+	vb = getkeystring(vb, &len, 2, NULL);
+	write(SHTTY, vb, len);
+    } else if (isset(BEEP))
 	write(SHTTY, "\07", 1);
 }
 
@@ -3150,6 +3173,22 @@ dquotedzputs(char const *s, FILE *stream)
 }
 #endif
 
+/*
+ * Decode a key string, turning it into the literal characters.
+ * The length is returned in len.
+ * fromwhere determines how the processing works.
+ *   0:  Don't handle keystring, just print-like escapes.
+ *       Expects misc to be present.
+ *   1:  Handle Emacs-like \C-X arguments etc., but not ^X
+ *       Expects misc to be present.
+ *   2:  Handle ^X as well as emacs-like keys; don't handle \c
+ *       for no newlines.
+ *   3:  As 1, but don't handle \c.
+ *   4:  Do $'...' quoting.  Overwrites the existing string instead of
+ *       zhalloc'ing 
+ *   5:  As 2, but \- is special.  Expects misc to be defined.
+ */
+
 /**/
 char *
 getkeystring(char *s, int *len, int fromwhere, int *misc)
diff --git a/Src/xmods.conf b/Src/xmods.conf
index c36105721..6b7f1701a 100644
--- a/Src/xmods.conf
+++ b/Src/xmods.conf
@@ -3,3 +3,4 @@ comp1
 zle
 compctl
 sched
+complist
diff --git a/Src/zsh.export b/Src/zsh.export
index 72779e24a..5a3a94c70 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -5,6 +5,8 @@ addconddefs
 addedx
 addhashnode
 addhistnum
+addhookdefs
+addhookfunc
 addparamdefs
 addwrapper
 arrvargetfn
@@ -43,6 +45,8 @@ current_limits
 deletebuiltins
 deleteconddefs
 deletehashtable
+deletehookdefs
+deletehookfunc
 deleteparamdefs
 deleteparamtable
 deletewrapper
@@ -83,6 +87,7 @@ gethashnode2
 gethparam
 getintvalue
 getiparam
+getkeyptr
 getkeystring
 getlinknode
 getpermtext
@@ -206,6 +211,7 @@ resetneeded
 restoredir
 reswdtab
 retflag
+runhookdef
 runshfunc
 scancountparams
 scanhashtable
diff --git a/Src/zsh.h b/Src/zsh.h
index e7b6e5ae0..45691039e 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -226,6 +226,7 @@ enum {
 #define INP_HIST      (1<<2)	/* expanding history                       */
 #define INP_CONT      (1<<3)	/* continue onto previously stacked input  */
 #define INP_ALCONT    (1<<4)	/* stack is continued from alias expn.     */
+#define INP_LINENO    (1<<5)    /* update line number                      */
 
 /* Flags for metafy */
 #define META_REALLOC	0
@@ -277,8 +278,9 @@ typedef struct heapstack *Heapstack;
 typedef struct histent   *Histent;
 typedef struct forcmd    *Forcmd;
 typedef struct autofn    *AutoFn;
+typedef struct hookdef   *Hookdef;
 
-typedef struct asgment  *Asgment;
+typedef struct asgment   *Asgment;
 
 
 /********************************/
@@ -886,6 +888,22 @@ struct module {
 #define MOD_UNLOAD  (1<<1)
 #define MOD_SETUP   (1<<2)
 
+/* C-function hooks */
+
+typedef int (*Hookfn) _((Hookdef, void *));
+
+struct hookdef {
+    Hookdef next;
+    char *name;
+    Hookfn def;
+    int flags;
+    LinkList funcs;
+};
+
+#define HOOKF_ALL 1
+
+#define HOOKDEF(name, func, flags) { NULL, name, (Hookfn) func, flags, NULL }
+
 /* node used in parameter hash table (paramtab) */
 
 struct param {
@@ -958,12 +976,13 @@ struct param {
 #define PM_UNALIASED	(1<<11)	/* do not expand aliases when autoloading     */
 
 #define PM_TIED 	(1<<12)	/* array tied to colon-path or v.v. */
-#define PM_SPECIAL	(1<<13) /* special builtin parameter                  */
-#define PM_DONTIMPORT	(1<<14)	/* do not import this variable                */
-#define PM_RESTRICTED	(1<<15) /* cannot be changed in restricted mode       */
-#define PM_UNSET	(1<<16)	/* has null value                             */
-#define PM_REMOVABLE	(1<<17)	/* special can be removed from paramtab */
-#define PM_AUTOLOAD     (1<<18) /* autoloaded from module */
+#define PM_LOCAL	(1<<13) /* this parameter will be made local */
+#define PM_SPECIAL	(1<<14) /* special builtin parameter                  */
+#define PM_DONTIMPORT	(1<<15)	/* do not import this variable                */
+#define PM_RESTRICTED	(1<<16) /* cannot be changed in restricted mode       */
+#define PM_UNSET	(1<<17)	/* has null value                             */
+#define PM_REMOVABLE	(1<<18)	/* special can be removed from paramtab */
+#define PM_AUTOLOAD     (1<<19) /* autoloaded from module */
 
 /* Flags for extracting elements of arrays and associative arrays */
 #define SCANPM_WANTVALS   (1<<0)
@@ -1201,6 +1220,7 @@ enum {
     LISTBEEP,
     LISTTYPES,
     LOCALOPTIONS,
+    LOCALTRAPS,
     LOGINSHELL,
     LONGLISTJOBS,
     MAGICEQUALSUBST,
@@ -1358,7 +1378,8 @@ struct ttyinfo {
 /* Definitions for the %_ prompt escape */
 /****************************************/
 
-#define cmdpush(X) if (!(cmdsp >= 0 && cmdsp < 256)) {;} else cmdstack[cmdsp++]=(X)
+#define CMDSTACKSZ 256
+#define cmdpush(X) if (!(cmdsp >= 0 && cmdsp < CMDSTACKSZ)) {;} else cmdstack[cmdsp++]=(X)
 #ifdef DEBUG
 # define cmdpop()  if (cmdsp <= 0) { \
 			fputs("BUG: cmdstack empty\n", stderr); \
@@ -1491,9 +1512,13 @@ struct heap {
 
 /* These used in the sigtrapped[] array */
 
-#define ZSIG_TRAPPED	(1<<0)
-#define ZSIG_IGNORED	(1<<1)
-#define ZSIG_FUNC	(1<<2)
+#define ZSIG_TRAPPED	(1<<0)	/* Signal is trapped */
+#define ZSIG_IGNORED	(1<<1)	/* Signal is ignored */
+#define ZSIG_FUNC	(1<<2)	/* Trap is a function, not an eval list */
+/* Mask to get the above flags */
+#define ZSIG_MASK	(ZSIG_TRAPPED|ZSIG_IGNORED|ZSIG_FUNC)
+/* No. of bits to shift local level when storing in sigtrapped */
+#define ZSIG_SHIFT	3
 
 /**********************************/
 /* Flags to third argument of zle */