about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-07-03 13:16:46 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-07-03 13:16:46 +0000
commit7c670f1e6a0e154f0b1a2d4b6eed0e71c9404b56 (patch)
tree9bb85bcd27410e1b752c8550f487165d71dc8aca /Src
parentb09922bb063ddf44c7850b182fec4795fbe1ae90 (diff)
downloadzsh-7c670f1e6a0e154f0b1a2d4b6eed0e71c9404b56.tar.gz
zsh-7c670f1e6a0e154f0b1a2d4b6eed0e71c9404b56.tar.xz
zsh-7c670f1e6a0e154f0b1a2d4b6eed0e71c9404b56.zip
zsh-3.1.5-pws-25 dot-zsh-199907031715
Diffstat (limited to 'Src')
-rw-r--r--Src/Makefile.in4
-rw-r--r--Src/Zle/.distfiles1
-rw-r--r--Src/Zle/comp.h24
-rw-r--r--Src/Zle/compctl.c6
-rw-r--r--Src/Zle/complist.c141
-rw-r--r--Src/Zle/deltochar.c2
-rw-r--r--Src/Zle/iwidgets.list1
-rw-r--r--Src/Zle/zle.export36
-rw-r--r--Src/Zle/zle.h6
-rw-r--r--Src/Zle/zle_hist.c78
-rw-r--r--Src/Zle/zle_keymap.c65
-rw-r--r--Src/Zle/zle_main.c31
-rw-r--r--Src/Zle/zle_misc.c48
-rw-r--r--Src/Zle/zle_move.c35
-rw-r--r--Src/Zle/zle_params.c2
-rw-r--r--Src/Zle/zle_refresh.c2
-rw-r--r--Src/Zle/zle_thingy.c41
-rw-r--r--Src/Zle/zle_tricky.c359
-rw-r--r--Src/Zle/zle_utils.c24
-rw-r--r--Src/Zle/zle_vi.c126
-rw-r--r--Src/Zle/zle_word.c16
-rw-r--r--Src/builtin.c38
-rw-r--r--Src/exec.c80
-rw-r--r--Src/glob.c2
-rw-r--r--Src/hashtable.c1
-rw-r--r--Src/init.c7
-rw-r--r--Src/jobs.c55
-rw-r--r--Src/loop.c2
-rw-r--r--Src/module.c254
-rw-r--r--Src/params.c9
-rw-r--r--Src/prompt.c8
-rw-r--r--Src/signals.c5
-rw-r--r--Src/subst.c11
-rw-r--r--Src/utils.c16
-rw-r--r--Src/xmods.conf1
-rw-r--r--Src/zsh.export5
-rw-r--r--Src/zsh.h40
37 files changed, 1003 insertions, 579 deletions
diff --git a/Src/Makefile.in b/Src/Makefile.in
index e98a696e4..0dcfbdee1 100644
--- a/Src/Makefile.in
+++ b/Src/Makefile.in
@@ -206,9 +206,9 @@ distclean: distclean-modules
 realclean: realclean-modules
 
 # Don't remake Makemod just to delete things, even if it doesn't exist.
-mostlyclean-modules clean-modules distclean-modules realclean-modules:
+mostlyclean-modules clean-modules distclean-modules realclean-modules: modules.index
 	if test -f Makemod; then \
-	  @$(MAKE) -f Makemod $(MAKEDEFS) `echo $@ | sed 's/-modules//'`; \
+	  $(MAKE) -f Makemod $(MAKEDEFS) `echo $@ | sed 's/-modules//'`; \
 	fi; \
 	exit 0
 
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 78c65ffeb..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
@@ -299,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 e0f9a9089..2b426492e 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -2221,7 +2221,8 @@ 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");
@@ -2261,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
index 63bc4c6e3..aac8b410d 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -198,7 +198,7 @@ getcoldef(Listcols c, char *s)
 
 /* This initializes the given terminal color structure. */
 
-static int
+static void
 getcols(Listcols c)
 {
     char *s;
@@ -206,16 +206,17 @@ getcols(Listcols c)
 
     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->cols[COL_MA] = tcstr[TCSTANDOUTBEG]) ||
+	    !c->cols[COL_MA][0])
+	    c->cols[COL_MA] = "";
+	else
+	    c->cols[COL_EC] = tcstr[TCSTANDOUTEND];
+	return;
     }
-    if (!c)
-	return 0;
     /* We have one of the parameters, use it. */
     memset(c, 0, sizeof(*c));
     s = dupstring(s);
@@ -230,48 +231,65 @@ getcols(Listcols c)
     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);
+    return;
+}
 
-	/* 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;
+static int last_col = COL_NO;
+
+static void
+zcputs(Listcols c, int colour)
+{
+    if (colour != last_col
+	&& (last_col < COL_NO
+	    || strcmp(c->cols[last_col], c->cols[colour]))) {
+	fputs(c->cols[COL_LC], shout);
+	fputs(c->cols[colour], shout);
+	fputs(c->cols[COL_RC], shout);
+	last_col = colour;
     }
-    return 0;
+    return;
 }
 
 /* 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)
+static void
+putcolstr(Listcols c, char *n, mode_t m)
 {
+    int colour;
     Extcol e;
 
     for (e = c->exts; e; e = e->next)
-	if (strsfx(e->ext, n))
-	    return e->col;
+	if (strsfx(e->ext, n)) {	/* XXX: unoptimised if used */
+	    if (last_col < COL_NO
+		|| strcmp(c->cols[last_col], e->col)) {
+		fputs(c->cols[COL_LC], shout);
+		fputs(e->col, shout);
+		fputs(c->cols[COL_RC], shout);
+	    }
+	    last_col = COL_NO - 1;
+	    return;
+	}
 
     if (S_ISDIR(m))
-	return c->cols[COL_DI];
+	colour = COL_DI;
     else if (S_ISLNK(m))
-	return c->cols[COL_LN];
+	colour = COL_LN;
     else if (S_ISFIFO(m))
-	return c->cols[COL_PI];
+	colour = COL_PI;
     else if (S_ISSOCK(m))
-	return c->cols[COL_SO];
+	colour = COL_SO;
     else if (S_ISBLK(m))
-	return c->cols[COL_BD];
+	colour = COL_BD;
     else if (S_ISCHR(m))
-	return c->cols[COL_CD];
+	colour = COL_CD;
     else if (S_ISREG(m) && (m & S_IXUGO))
-	return c->cols[COL_EX];
+	colour = COL_EX;
+    else
+	colour = COL_FI;
 
-    return c->cols[COL_FI];
+    zcputs(c, colour);
+    return;
 }
 
 /* Information about the list shown. */
@@ -505,14 +523,15 @@ complistmatches(Hookdef dummy, Chdata dat)
 		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);
+			zcputs(&col, COL_MI);
 			a = longest - 2;
 			while (a--)
 			    putc(' ', shout);
-			fputs(col.cols[COL_EC], shout);
+			if (col.cols[COL_EC])
+			    fputs(col.cols[COL_EC], shout);
+			else
+			    zcputs(&col, COL_NO);
 			break;
 		    }
 		    hasm = 1;
@@ -539,20 +558,18 @@ complistmatches(Hookdef dummy, Chdata dat)
 
 			zt = ztat(pb, &buf, 1);
 			if (cc >= 0)
-			    fputs(col.cols[cc], shout);
+			    zcputs(&col, cc);
 			else if (zt)
-			    fputs(col.cols[COL_NO], shout);
+			    zcputs(&col, COL_NO);
 			else
-			    fputs(getcolstr(&col, pb, buf.st_mode), shout);
-			fputs(col.cols[COL_RC], shout);
+			    putcolstr(&col, pb, buf.st_mode);
 			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);
+			zcputs(&col, cc >= 0 ? cc : COL_NO);
 			nicezputs(m->str, shout);
 			if (of)
 			    putc(' ', shout);
@@ -560,13 +577,17 @@ complistmatches(Hookdef dummy, Chdata dat)
 		    a = longest - niceztrlen(m->str) - 2 - of;
 		    while (a--)
 			putc(' ', shout);
-		    fputs(col.cols[COL_EC], shout);
+		    if (col.cols[COL_EC])
+			fputs(col.cols[COL_EC], shout);
+		    else
+			zcputs(&col, COL_NO);
 		    if (i) {
-			fputs(col.cols[COL_LC], shout);
-			fputs(col.cols[COL_NO], shout);
-			fputs(col.cols[COL_RC], shout);
+			zcputs(&col, COL_NO);
 			fputs("  ", shout);
-			fputs(col.cols[COL_EC], shout);
+			if (col.cols[COL_EC])
+			    fputs(col.cols[COL_EC], shout);
+			else
+			    zcputs(&col, COL_NO);
 		    }
 		    if (--n)
 			for (j = nc; j && *q; j--)
@@ -574,13 +595,14 @@ complistmatches(Hookdef dummy, Chdata dat)
 		    mc++;
 		}
 		if (i > 0) {
-		    fputs(col.cols[COL_LC], shout);
-		    fputs(col.cols[COL_MI], shout);
-		    fputs(col.cols[COL_RC], shout);
+		    zcputs(&col, COL_MI);
 		    a = longest - 2;
 		    while (a--)
 			putc(' ', shout);
-		    fputs(col.cols[COL_EC], shout);
+		    if (col.cols[COL_EC])
+			fputs(col.cols[COL_EC], shout);
+		    else
+			zcputs(&col, COL_NO);
 		}
 		if (n) {
 		    putc('\n', shout);
@@ -627,12 +649,12 @@ domenuselect(Hookdef dummy, Chdata dat)
     Cmgroup *pg;
     Thingy cmd;
     Menustack u = NULL;
-    int i = 0;
+    int i = 0, acc = 0;
     char *s;
 
-    if (getcols(NULL) || (dummy && (!(s = getsparam("SELECTMIN")) ||
-				    (dat && dat->num < atoi(s)))))
-	return 1;
+    if (dummy && (!(s = getsparam("SELECTMIN")) ||
+		  (dat && dat->num < atoi(s))))
+	return 0;
 
     selectlocalmap(mskeymap);
     noselect = 0;
@@ -659,10 +681,12 @@ domenuselect(Hookdef dummy, Chdata dat)
 
     getk:
 
-	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak) ||
-	    cmd == Th(z_acceptline))
+	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak))
+	    break;
+	else if (cmd == Th(z_acceptline)) {
+	    acc = 1;
 	    break;
-	else if (cmd == Th(z_acceptandhold) ||
+	} else if (cmd == Th(z_acceptandhold) ||
 		 cmd == Th(z_acceptandmenucomplete)) {
 	    Menustack s = (Menustack) zhalloc(sizeof(*s));
 
@@ -825,11 +849,16 @@ domenuselect(Hookdef dummy, Chdata dat)
     selectlocalmap(NULL);
     mselect = -1;
     inselect = 0;
+    if (acc) {
+	menucmp = 0;
+	lastambig = 0;
+	do_single(*(minfo.cur));
+    }
     if (!noselect) {
 	showinglist = -2;
 	zrefresh();
     }
-    return noselect;
+    return (!noselect ^ acc);
 }
 
 /* The widget function. */
diff --git a/Src/Zle/deltochar.c b/Src/Zle/deltochar.c
index b9fb34f10..f0df33f95 100644
--- a/Src/Zle/deltochar.c
+++ b/Src/Zle/deltochar.c
@@ -67,8 +67,6 @@ deltochar(char **args)
 	    }
 	}
     }
-    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..e6f469ad0 100644
--- a/Src/Zle/zle.export
+++ b/Src/Zle/zle.export
@@ -1,10 +1,46 @@
 #!
+acceptlast
 addzlefunction
 backdel
 backkill
+bindkey
+clearflag
+clearscreen
 deletezlefunction
+do_menucmp
+do_single
 feep
 foredel
 forekill
 getkey
+getkeycmd
+getzlequery
+lastambig
+linkkeymap
+listshown
+menucmp
+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 8ab86f042..ff515e7c4 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -143,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 66abbe438..f85fa00a6 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -77,10 +77,8 @@ int
 uphistory(char **args)
 {
     int nodups = isset(HISTIGNOREDUPS);
-    if (!zle_goto_hist(histline, -zmult, nodups) && isset(HISTBEEP)) {
-	feep();
+    if (!zle_goto_hist(histline, -zmult, nodups) && isset(HISTBEEP))
 	return 1;
-    }
     return 0;
 }
 
@@ -128,10 +126,8 @@ uplineorhistory(char **args)
 	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
+	if (virangeflag || !histallowed)
 	    return 1;
-	}
 	zmult = n;
 	ret = uphistory(args);
 	zmult = m;
@@ -160,10 +156,8 @@ uplineorsearch(char **args)
 	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
+	if (virangeflag || !histallowed)
 	    return 1;
-	}
 	zmult = n;
 	ret = historysearchbackward(args);
 	zmult = m;
@@ -216,10 +210,8 @@ downlineorhistory(char **args)
 	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
+	if (virangeflag || !histallowed)
 	    return 1;
-	}
 	zmult = n;
 	ret = downhistory(args);
 	zmult = m;
@@ -248,10 +240,8 @@ downlineorsearch(char **args)
 	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
+	if (virangeflag || !histallowed)
 	    return 1;
-	}
 	zmult = n;
 	ret = historysearchforward(args);
 	zmult = m;
@@ -266,10 +256,8 @@ acceptlineanddownhistory(char **args)
 {
     Histent he;
 
-    if (!(he = movehistent(quietgethist(histline), 1, HIST_FOREIGN))) {
-	feep();
+    if (!(he = movehistent(quietgethist(histline), 1, HIST_FOREIGN)))
 	return 1;
-    }
     pushnode(bufstack, ztrdup(ZLETEXT(he)));
     done = 1;
     stackhist = he->histnum;
@@ -281,10 +269,8 @@ int
 downhistory(char **args)
 {
     int nodups = isset(HISTIGNOREDUPS);
-    if (!zle_goto_hist(histline, zmult, nodups) && isset(HISTBEEP)) {
-	feep();
+    if (!zle_goto_hist(histline, zmult, nodups) && isset(HISTBEEP))
 	return 1;
-    }
     return 0;
 }
 
@@ -336,7 +322,6 @@ historysearchbackward(char **args)
 	    }
 	}
     }
-    feep();
     return 1;
 }
 
@@ -385,7 +370,6 @@ historysearchforward(char **args)
 	    }
 	}
     }
-    feep();
     return 1;
 }
 
@@ -404,10 +388,8 @@ beginningofbufferorhistory(char **args)
 int
 beginningofhistory(char **args)
 {
-    if (!zle_goto_hist(firsthist(), 0, 0) && isset(HISTBEEP)) {
-	feep();
+    if (!zle_goto_hist(firsthist(), 0, 0) && isset(HISTBEEP))
 	return 1;
-    }
     return 0;
 }
 
@@ -457,19 +439,15 @@ insertlastword(char **args)
 	zsfree(lastinsert);
 	lastinsert = NULL;
     }
-    if (!(he = quietgethist(evhist)) || !he->nwords) {
-	feep();
+    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();
+    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;
@@ -591,7 +569,6 @@ getline(char **args)
     char *s = (char *)getlinknode(bufstack);
 
     if (!s) {
-	feep();
 	return 1;
     } else {
 	int cc;
@@ -693,7 +670,7 @@ doisearch(char **args, int dir)
     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, savekeys = -1;
+    int hl = histline, savekeys = -1, feep = 0;
     Thingy cmd;
     char *okeymap = curkeymapname;
     static char *previous_search = NULL;
@@ -762,7 +739,7 @@ doisearch(char **args, int dir)
 		    get_isrch_spot(top_spot, &hl, &pos, &cs, &sbptr,
 				   &dir, &nomatch);
 		    if (!nomatch) {
-			feep();
+			feep = 1;
 			nomatch = 1;
 		    }
 		    he = quietgethist(hl);
@@ -803,7 +780,7 @@ doisearch(char **args, int dir)
 	    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)) {
@@ -811,7 +788,7 @@ doisearch(char **args, int dir)
 		get_isrch_spot(--top_spot, &hl, &pos, &cs, &sbptr,
 			       &dir, &nomatch);
 	    else
-		feep();
+		feep = 1;
 	    if (nomatch) {
 		statusline = ibuf;
 		skip_pos = 1;
@@ -878,7 +855,7 @@ doisearch(char **args, int dir)
 		zrefresh();
 	    }
 	    if ((c = getkey(0)) == EOF)
-		feep();
+		feep = 1;
 	    else
 		goto ins;
 	} else {
@@ -896,7 +873,7 @@ doisearch(char **args, int dir)
 	    }
 	ins:
 	    if (sbptr == PATH_MAX) {
-		feep();
+		feep = 1;
 		continue;
 	    }
 	    set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
@@ -907,7 +884,9 @@ doisearch(char **args, int dir)
 	    }
 	    sbuf[sbptr++] = c;
 	}
-	handlefeep();
+	if (feep)
+	    handlefeep(zlenoargs);
+	feep = 0;
     }
     if (sbptr) {
 	zfree(previous_search, previous_search_len);
@@ -957,7 +936,6 @@ infernexthistory(char **args)
 	    return 0;
 	}
     }
-    feep();
     return 1;
 }
 
@@ -976,7 +954,6 @@ vifetchhistory(char **args)
     }
     if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist, 0, 0) &&
 	isset(HISTBEEP)) {
-	feep();
 	return 1;
     }
     return 0;
@@ -992,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;
 
@@ -1045,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)) {
@@ -1062,9 +1039,11 @@ getvisrchstr(void)
 	    }
 	    sbuf[sptr++] = c;
 	} else {
-	    feep();
+	    feep = 1;
 	}
-	handlefeep();
+	if (feep)
+	    handlefeep(zlenoargs);
+	feep = 0;
     }
     statusline = NULL;
     selectkeymap(okeymap, 1);
@@ -1122,10 +1101,8 @@ virepeatsearch(char **args)
     int n = zmult;
     char *s;
 
-    if (!visrchstr) {
-	feep();
+    if (!visrchstr)
 	return 1;
-    }
     if (zmult < 0) {
 	n = -n;
 	visrchsense = -visrchsense;
@@ -1145,7 +1122,6 @@ virepeatsearch(char **args)
 	    }
 	}
     }
-    feep();
     return 1;
 }
 
@@ -1194,7 +1170,6 @@ historybeginningsearchbackward(char **args)
 	    }
 	}
     }
-    feep();
     return 1;
 }
 
@@ -1231,6 +1206,5 @@ historybeginningsearchforward(char **args)
 	    }
 	}
     }
-    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 e2b64cb3c..8f6dfdf75 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -106,13 +106,7 @@ struct modifier zmod;
 /**/
 int prefixflag;
 
-/* != 0 if there is a pending beep (usually indicating an error) */
-
-/**/
-int feepflag;
- 
 /* Number of characters waiting to be read by the ungetkeys mechanism */
-
 /**/
 int kungetct;
 
@@ -507,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);
@@ -535,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, zlenoargs);
+		if (execzlefunc(bindk, zlenoargs))
+		    handlefeep(zlenoargs);
 		handleprefixes();
 		/* for vi mode, make sure the cursor isn't somewhere illegal */
 		if (invicmdmode() && cs > findbol() &&
@@ -573,7 +569,6 @@ zleread(char *lp, char *rp, int flags)
 #endif
 		if (!kungetct)
 		    zrefresh();
-	    handlefeep();
 	}
 	statusline = NULL;
 	invalidatelist();
@@ -613,7 +608,7 @@ execzlefunc(Thingy func, char **args)
 	zsfree(nm);
 	showmsg(msg);
 	zsfree(msg);
-	feep();
+	ret = 1;
     } else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_NCOMP)) {
 	int wflags = w->flags;
 
@@ -646,7 +641,7 @@ execzlefunc(Thingy func, char **args)
 	    zsfree(nm);
 	    showmsg(msg);
 	    zsfree(msg);
-	    feep();
+	    ret = 1;
 	} else {
 	    int osc = sfcontext, osi = movefd(0), olv = lastval;
 	    LinkList largs = NULL;
@@ -953,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, "lDANCLmMgGcR",  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),
 };
 
 /**/
@@ -999,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;
 }
 
@@ -1014,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;
 }
 
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 4d731a21c..a51cdf92e 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -98,7 +98,6 @@ deletechar(char **args)
 	backdel(zmult);
 	return 0;
     }
-    feep();
     return 1;
 }
 
@@ -182,7 +181,6 @@ gosmacstransposechars(char **args)
 	if (cs == ll || line[cs] == '\n' ||
 	    ((cs + 1 == ll || line[cs + 1] == '\n') &&
 	     (!cs || line[cs - 1] == '\n'))) {
-	    feep();
 	    return 1;
 	}
 	cs += (cs == 0 || line[cs - 1] == '\n') ? 2 : 1;
@@ -205,10 +203,8 @@ transposechars(char **args)
 	n = -n;
     while (n--) {
 	if (!(ct = cs) || line[cs - 1] == '\n') {
-	    if (ll == cs || line[cs] == '\n') {
-		feep();
+	    if (ll == cs || line[cs] == '\n')
 		return 1;
-	    }
 	    if (!neg)
 		cs++;
 	    ct++;
@@ -225,10 +221,8 @@ transposechars(char **args)
 	}
 	if (ct == ll || line[ct] == '\n')
 	    ct--;
-	if (ct < 1 || line[ct - 1] == '\n') {
-	    feep();
+	if (ct < 1 || line[ct - 1] == '\n')
 	    return 1;
-	}
 	cc = line[ct - 1];
 	line[ct - 1] = line[ct];
 	line[ct] = cc;
@@ -350,10 +344,8 @@ yank(char **args)
 	return 1;
     if (zmod.flags & MOD_VIBUF)
 	buf = &vibuf[zmod.vibuf];
-    if (!buf->buf) {
-	feep();
+    if (!buf->buf)
 	return 1;
-    }
     mark = cs;
     yankb = cs;
     while (n--) {
@@ -372,10 +364,8 @@ yankpop(char **args)
 {
     int cc;
 
-    if (!(lastcmd & ZLE_YANK) || !kring[kct].buf) {
-	feep();
+    if (!(lastcmd & ZLE_YANK) || !kring[kct].buf)
 	return 1;
-    }
     cs = yankb;
     foredel(yanke - yankb);
     cc = kring[kct].len;
@@ -438,7 +428,6 @@ whatcursorposition(char **args)
 int
 undefinedkey(char **args)
 {
-    feep();
     return 1;
 }
 
@@ -457,10 +446,9 @@ quotedinsert(char **args)
 #ifndef HAS_TIO
     zsetterm();
 #endif
-    if (c < 0) {
-	feep();
+    if (c < 0)
 	return 1;
-    } else
+    else
 	return selfinsert(args);
 }
 
@@ -470,10 +458,8 @@ digitargument(char **args)
 {
     int sign = (zmult < 0) ? -1 : 1;
 
-    if (c < '0' || c > '9') {
-	feep();
+    if (c < '0' || c > '9')
 	return 1;
-    }
 
     if (!(zmod.flags & MOD_TMULT))
 	zmod.tmult = 0;
@@ -493,10 +479,8 @@ digitargument(char **args)
 int
 negargument(char **args)
 {
-    if (zmod.flags & MOD_TMULT) {
-	feep();
+    if (zmod.flags & MOD_TMULT)
 	return 1;
-    }
     zmod.tmult = -1;
     zmod.flags |= MOD_TMULT|MOD_NEG;
     prefixflag = 1;
@@ -655,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;
 
@@ -688,12 +672,12 @@ executenamedcommand(char *prmt)
 	    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) ||
@@ -744,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;
@@ -766,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);
@@ -776,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 e2096f6cf..9189218f1 100644
--- a/Src/Zle/zle_move.c
+++ b/Src/Zle/zle_move.c
@@ -228,7 +228,6 @@ vimatchbracket(char **args)
 
   otog:
     if (cs == ll || line[cs] == '\n') {
-	feep();
 	cs = ocs;
 	return 1;
     }
@@ -273,7 +272,6 @@ vimatchbracket(char **args)
 	    ct++;
     }
     if (cs < 0 || cs >= ll) {
-	feep();
 	cs = ocs;
 	return 1;
     } else if(dir > 0 && virangeflag)
@@ -295,10 +293,8 @@ viforwardchar(char **args)
 	zmult = n;
 	return ret;
     }
-    if (cs >= lim) {
-	feep();
+    if (cs >= lim)
 	return 1;
-    }
     while (n-- && cs < lim)
 	cs++;
     return 0;
@@ -317,10 +313,8 @@ vibackwardchar(char **args)
 	zmult = n;
 	return ret;
     }
-    if (cs == findbol()) {
-	feep();
+    if (cs == findbol())
 	return 1;
-    }
     while (n--) {
 	cs--;
 	if (cs < 0 || line[cs] == '\n') {
@@ -337,14 +331,11 @@ viendofline(char **args)
 {
     int oldcs = cs, n = zmult;
 
-    if (n < 1) {
-	feep();
+    if (n < 1)
 	return 1;
-    }
     while(n--) {
 	if (cs > ll) {
 	    cs = oldcs;
-	    feep();
 	    return 1;
 	}
 	cs = findeol() + 1;
@@ -385,7 +376,7 @@ vifindprevchar(char **args)
 	tailadd = 0;
 	return virepeatfind(args);
     }
-    return 0;
+    return 1;
 }
 
 /**/
@@ -418,10 +409,8 @@ virepeatfind(char **args)
 {
     int ocs = cs, n = zmult;
 
-    if (!vfinddir) {
-	feep();
+    if (!vfinddir)
 	return 1;
-    }
     if (n < 0) {
 	int ret;
 	zmult = -n;
@@ -434,7 +423,6 @@ virepeatfind(char **args)
 	    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 1;
 	}
@@ -480,10 +468,8 @@ visetmark(char **args)
     int ch;
 
     ch = getkey(0);
-    if (ch < 'a' || ch > 'z') {
-	feep();
+    if (ch < 'a' || ch > 'z')
 	return 1;
-    }
     ch -= 'a';
     vimarkcs[ch] = cs;
     vimarkline[ch] = histline;
@@ -500,19 +486,14 @@ vigotomark(char **args)
     if (ch == c)
 	ch = 26;
     else {
-	if (ch < 'a' || ch > 'z') {
-	    feep();
+	if (ch < 'a' || ch > 'z')
 	    return 1;
-	}
 	ch -= 'a';
     }
-    if (!vimarkline[ch]) {
-	feep();
+    if (!vimarkline[ch])
 	return 1;
-    }
     if (curhist != vimarkline[ch] && !zle_goto_hist(vimarkline[ch], 0, 0)) {
 	vimarkline[ch] = 0;
-	feep();
 	return 1;
     }
     cs = vimarkcs[ch];
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index 221440a46..bac399e7d 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -84,7 +84,7 @@ 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");
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index da0b38bfe..dd4310078 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -495,7 +495,7 @@ zrefresh(void)
     /* if old line and new line are different,
        see if we can insert/delete a line to speed up update */
 
-	if (ln < olnct - 1 && !(hasam && vcs == winw) &&
+	if (ln > 0 && ln < olnct - 1 && !(hasam && vcs == winw) &&
 	    nbuf[ln] && obuf[ln] &&
 	    strncmp(nbuf[ln], obuf[ln], 16)) {
 	    if (tccan(TCDELLINE) && obuf[ln + 1] && obuf[ln + 1][0] &&
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 2950d55d9..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,7 +335,7 @@ 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 },
@@ -373,8 +374,21 @@ 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']);
-    return 0;
+    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;
+    }
 }
 
 /**/
@@ -405,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) {
@@ -536,6 +554,7 @@ 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",
@@ -543,6 +562,12 @@ bin_zle_call(char *name, char **args, char *ops, char func)
 	return 1;
     }
 
+    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] == '-') {
@@ -577,14 +602,8 @@ bin_zle_call(char *name, char **args, char *ops, char func)
 	}
 	args++;
     }
-    if (!args[0]) {
-	zwarnnam(name, "wrong number of arguments", NULL, 0);
-	if (saveflag)
-	    zmod = modsave;
-	return 1;
-    }
 
-    t = rthingy(*args++);
+    t = rthingy(wname);
     PERMALLOC {
         ret = execzlefunc(t, args);
     } LASTALLOC;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index bb0a3cac0..f7b243d31 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -98,7 +98,7 @@ static int showagain = 0;
 
 /* The match and group number to insert when starting menucompletion.   */
 
-static int insmnum, insgnum, insgroup;
+static int insmnum, insgnum, insgroup, insspace;
 
 /* This is used to decide when the cursor should be moved to the end of    *
  * the inserted word: 0 - never, 1 - only when a single match is inserted, *
@@ -108,22 +108,13 @@ static int movetoend;
 
 /* != 0 if we are in the middle of a menu completion */
 
-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).       */
-
-static Cmgroup menugrp;
-static Cmatch *menucur;
+/**/
+int menucmp;
 
-/* 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.              */
+/* Information about menucompletion. */
 
-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.     *
@@ -356,7 +347,8 @@ enum { COMP_COMPLETE,
  * on the second tab, a` la bash, and then automenu kicks in when     *
  * lastambig == 2.                                                    */
 
-static int lastambig;
+/**/
+int lastambig;
 
 /* This says what of the state the line is in when completion is started *
  * came from a previous completion. If the FC_LINE bit is set, the       *
@@ -388,7 +380,8 @@ completecall(char **args)
     cfargs = args;
     cfret = 0;
     compfunc = compwidget->u.comp.func;
-    compwidget->u.comp.fn(zlenoargs);
+    if (compwidget->u.comp.fn(zlenoargs) && !cfret)
+	cfret = 1;
     compfunc = NULL;
 
     return cfret;
@@ -446,8 +439,8 @@ spellword(char **args)
 int
 deletecharorlist(char **args)
 {
-    Cmgroup mg = menugrp;
-    Cmatch *mc = menucur;
+    Cmgroup mg = minfo.group;
+    Cmatch *mc = minfo.cur;
     int ret;
 
     usemenu = !!isset(MENUCOMPLETE);
@@ -458,8 +451,8 @@ deletecharorlist(char **args)
     } else
 	ret = docomplete(COMP_LIST_COMPLETE);
 
-    menucur = mc;
-    menugrp = mg;
+    minfo.cur = mc;
+    minfo.group = mg;
     return ret;
 }
 
@@ -522,16 +515,16 @@ reversemenucomplete(char **args)
 	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;
@@ -541,7 +534,8 @@ reversemenucomplete(char **args)
  * 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) {
@@ -557,13 +551,13 @@ 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;
     }
 }
 
@@ -571,10 +565,8 @@ acceptlast(void)
 int
 acceptandmenucomplete(char **args)
 {
-    if (!menucmp) {
-	feep();
+    if (!menucmp)
 	return 1;
-    }
     acceptlast();
     return menucomplete(args);
 }
@@ -807,7 +799,7 @@ static int
 docomplete(int lst)
 {
     char *s, *ol;
-    int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0;;
+    int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0, omc = menucmp;
 
     if (showagain && validlist)
 	showinglist = -2;
@@ -889,7 +881,6 @@ docomplete(int lst)
 	    ll = strlen((char *) line);
 	    cs = ocs;
 	    unmetafy_line();
-	    feep();
 	    return 1;
 	}
 	ocs = cs;
@@ -1062,6 +1053,16 @@ docomplete(int lst)
     popheap();
     zsfree(qword);
     unmetafy_line();
+
+    if (menucmp && !omc) {
+	struct chdata dat;
+
+	dat.matches = amatches;
+	dat.num = nmatches;
+	dat.cur = NULL;
+	if (runhookdef(MENUSTARTHOOK, (void *) &dat))
+	    menucmp = 0;
+    }
     return ret;
 }
 
@@ -1071,7 +1072,7 @@ docomplete(int lst)
  * insert the next completion.                                              */
 
 /**/
-static void
+void
 do_menucmp(int lst)
 {
     /* Just list the matches if the list was requested. */
@@ -1081,16 +1082,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;
 }
@@ -1444,7 +1445,6 @@ get_comp_string(void)
 		lexrestore();
 		goto start;
 	    }
-	    feep();
 	    noaliases = 0;
 	    lexrestore();
 	    LASTALLOC_RETURN NULL;
@@ -1757,11 +1757,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 &&
@@ -1771,8 +1768,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) {
@@ -3824,6 +3819,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++) {
@@ -3849,8 +3848,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,
@@ -4358,7 +4359,6 @@ docompletion(char *s, int lst, int incmd)
 	    foredel(ll);
 	    inststr(origline);
 	    cs = origcs;
-	    feep();
 	    clearlist = 1;
 	    ret = 1;
 	    goto compend;
@@ -4370,17 +4370,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();
 	    }
@@ -4394,7 +4394,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))
@@ -4636,8 +4636,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
@@ -4704,6 +4704,7 @@ callcompfunc(char *s, char *fn)
 		insgroup = 1;
 		insgnum = atoi(m + 1);
 	    }
+	    insspace = (compinsert[strlen(compinsert) - 1] == ' ');
 	} else
 	    useline = usemenu = 0;
 	useexact = (compexact && !strcmp(compexact, "accept"));
@@ -4718,7 +4719,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));
@@ -5635,7 +5636,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]);
@@ -6365,20 +6366,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)))
@@ -6625,7 +6620,8 @@ invalidatelist(void)
     if (validlist)
 	freematches();
     lastambig = menucmp = validlist = showinglist = fromcomp = 0;
-    menucur = NULL;
+    minfo.cur = NULL;
+    minfo.asked = 0;
     compwidget = NULL;
 }
 
@@ -7392,19 +7388,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,  *
@@ -7424,7 +7421,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);
@@ -7473,19 +7471,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' *
@@ -7495,7 +7495,7 @@ do_ambiguous(void)
  * (l)stat().                                                         */
 
 /**/
-static int
+int
 ztat(char *nam, struct stat *buf, int ls)
 {
     char b[PATH_MAX], *p;
@@ -7513,7 +7513,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;
@@ -7526,39 +7526,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;
 	    }
 	}
@@ -7574,11 +7574,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      *
@@ -7614,10 +7614,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)) {
@@ -7627,8 +7627,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) {
@@ -7644,9 +7644,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)) {
@@ -7655,20 +7655,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 || insspace)) {
 	    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 && !minfo.we) || !movetoend)
+	cs = minfo.end;
+    {
+	Cmatch *om = minfo.cur;
+	struct chdata dat;
 
-    if ((menucmp && !menuwe) || !movetoend)
-	cs = menuend;
+	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
@@ -7698,40 +7711,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. */
@@ -7770,7 +7785,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];
@@ -7853,7 +7868,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))
@@ -7868,11 +7884,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 */
@@ -7882,6 +7894,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;
@@ -7966,8 +7994,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);
@@ -7981,7 +8010,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);
@@ -7991,6 +8022,8 @@ listmatches(void)
 	} else
 	    putc('\n', shout);
 	settyinfo(&shttyinfo);
+	if (minfo.cur)
+	    minfo.asked = 1;
     }
 
     /* Now print the matches. */
@@ -8110,6 +8143,7 @@ listmatches(void)
 	    clearflag = 0, putc('\n', shout);
     } else
 	putc('\n', shout);
+    return 0;
 }
 
 /* This is used to print expansions. */
@@ -8119,9 +8153,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;
@@ -8129,12 +8163,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;
@@ -8213,18 +8247,17 @@ magicspace(char **args)
 {
     int ret;
     c = ' ';
-    ret = selfinsert(args);
-    return !doexpandhist();
+    if (!(ret = selfinsert(args)))
+	doexpandhist();
+    return ret;
 }
 
 /**/
 int
 expandhistory(char **args)
 {
-    if (!doexpandhist()) {
-	feep();
+    if (!doexpandhist())
 	return 1;
-    }
     return 0;
 }
 
@@ -8276,10 +8309,8 @@ processcmd(char **args)
     int m = zmult;
 
     s = getcurcmd();
-    if (!s) {
-	feep();
+    if (!s)
 	return 1;
-    }
     zmult = 1;
     pushline(zlenoargs);
     zmult = m;
@@ -8304,16 +8335,12 @@ expandcmdpath(char **args)
     noaliases = 1;
     s = getcurcmd();
     noaliases = na;
-    if (!s || cmdwb < 0 || cmdwe < cmdwb) {
-	feep();
+    if (!s || cmdwb < 0 || cmdwe < cmdwb)
 	return 1;
-    }
     str = findcmd(s, 1);
     zsfree(s);
-    if (!str) {
-	feep();
+    if (!str)
 	return 1;
-    }
     cs = cmdwb;
     foredel(cmdwe - cmdwb);
     spaceinline(strlen(str));
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index cc9c9bf37..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;
 }
 
 /***************/
@@ -559,10 +551,8 @@ undo(char **args)
 {
     handleundo();
     do {
-	if(!curchange->prev) {
-	    feep();
+	if(!curchange->prev)
 	    return 1;
-	}
 	unapplychange(curchange = curchange->prev);
     } while(curchange->flags & CH_PREV);
     setlastline();
@@ -598,10 +588,8 @@ redo(char **args)
 {
     handleundo();
     do {
-	if(!curchange->next) {
-	    feep();
+	if(!curchange->next)
 	    return 1;
-	}
 	applychange(curchange);
 	curchange = curchange->next;
     } while(curchange->prev->flags & CH_NEXT);
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
index 08de69c19..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, zlenoargs);
+	/*
+	 * 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,12 +249,12 @@ dovilinerange(void)
 	    cs = findbol() - 1;
 	if (n != 1) {
 	    cs = pos;
-	    feep();
-	    return;
+	    return 1;
 	}
 	cs++;
     }
     virangeflag = 2;
+    return 0;
 }
 
 /**/
@@ -309,11 +297,12 @@ viinsertbol(char **args)
 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--;
@@ -322,7 +311,7 @@ videlete(char **args)
 	}
     }
     vichgflag = 0;
-    return 0;
+    return ret;
 }
 
 /**/
@@ -341,10 +330,8 @@ videletechar(char **args)
 	return ret;
     }
     /* it is an error to be on the end of line */
-    if (cs == ll || line[cs] == '\n') {
-	feep();
+    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)
@@ -358,16 +345,17 @@ videletechar(char **args)
 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 0;
+    return ret;
 }
 
 /**/
@@ -377,15 +365,11 @@ visubstitute(char **args)
     int n = zmult;
 
     startvichange(1);
-    if (n < 0) {
-	feep();
+    if (n < 0)
 	return 1;
-    }
     /* it is an error to be on the end of line */
-    if (cs == ll || line[cs] == '\n') {
-	feep();
+    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)
@@ -436,10 +420,8 @@ viyankeol(char **args)
     int x = findeol();
 
     startvichange(-1);
-    if (x == cs) {
-	feep();
+    if (x == cs)
 	return 1;
-    }
     cut(cs, x - cs, 0);
     return 0;
 }
@@ -456,7 +438,6 @@ viyankwholeline(char **args)
 	return 1;
     while(n--) {
      if (cs > ll) {
-	feep();
 	cs = oldcs;
 	return 1;
      }
@@ -498,23 +479,18 @@ vireplacechars(char **args)
     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 1;
     }
     /* get key */
     if((ch = vigetkey()) == -1) {
 	vichgflag = 0;
-	feep();
 	return 1;
     }
     /* do change */
@@ -537,7 +513,7 @@ int
 vicmdmode(char **args)
 {
     if (invicmdmode() || selectkeymap("vicmd", 0))
-	feep();
+	return 1;
     undoing = 1;
     vichgflag = 0;
     if (cs != findbol())
@@ -573,7 +549,7 @@ viopenlineabove(char **args)
 int
 vioperswapcase(char **args)
 {
-    int oldcs, c2;
+    int oldcs, c2, ret = 1;
 
     /* get the range */
     startvichange(1);
@@ -589,12 +565,13 @@ vioperswapcase(char **args)
 	}
 	/* go back to the first line of the range */
 	cs = oldcs;
+	ret = 0;
 #if 0
 	vifirstnonblank();
 #endif
     }
     vichgflag = 0;
-    return 0;
+    return ret;
 }
 
 /**/
@@ -602,10 +579,8 @@ int
 virepeatchange(char **args)
 {
     /* make sure we have a change to repeat */
-    if (!vichgbuf || vichgflag) {
-	feep();
+    if (!vichgbuf || vichgflag)
 	return 1;
-    }
     /* restore or update the saved count and buffer */
     if (zmod.flags & MOD_MULT) {
 	lastmod.mult = zmod.mult;
@@ -637,7 +612,6 @@ viindent(char **args)
     vichgflag = 0;
     /* must be a line range */
     if (!vilinerange) {
-	feep();
 	cs = oldcs;
 	return 1;
     }
@@ -669,7 +643,6 @@ viunindent(char **args)
     vichgflag = 0;
     /* must be a line range */
     if (!vilinerange) {
-	feep();
 	cs = oldcs;
 	return 1;
     }
@@ -705,7 +678,6 @@ vibackwarddeletechar(char **args)
     /* 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 1;
     }
     /* Put argument into the acceptable range -- it is not an error to  *
@@ -721,10 +693,8 @@ vibackwarddeletechar(char **args)
 int
 vikillline(char **args)
 {
-    if (viinsbegin > cs) {
-	feep();
+    if (viinsbegin > cs)
 	return 1;
-    }
     backdel(cs - viinsbegin);
     return 0;
 }
@@ -741,10 +711,8 @@ viputbefore(char **args)
 	return 1;
     if (zmod.flags & MOD_VIBUF)
 	buf = &vibuf[zmod.vibuf];
-    if (!buf->buf) {
-	feep();
+    if (!buf->buf)
 	return 1;
-    }
     if(buf->flags & CUTBUFFER_LINE) {
 	cs = findbol();
 	spaceinline(buf->len + 1);
@@ -775,10 +743,8 @@ viputafter(char **args)
 	return 1;
     if (zmod.flags & MOD_VIBUF)
 	buf = &vibuf[zmod.vibuf];
-    if (!buf->buf) {
-	feep();
+    if (!buf->buf)
 	return 1;
-    }
     if(buf->flags & CUTBUFFER_LINE) {
 	cs = findeol();
 	spaceinline(buf->len + 1);
@@ -806,10 +772,8 @@ vijoin(char **args)
     int x;
 
     startvichange(-1);
-    if ((x = findeol()) == ll) {
-	feep();
+    if ((x = findeol()) == ll)
 	return 1;
-    }
     cs = x + 1;
     for (x = 1; cs != ll && iblank(line[cs]); cs++, x++);
     backdel(x);
@@ -866,10 +830,8 @@ visetbuffer(char **args)
 
     if ((zmod.flags & MOD_VIBUF) ||
 	(((ch = getkey(0)) < '1' || ch > '9') &&
-	 (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z'))) {
-	feep();
+	 (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z')))
 	return 1;
-    }
     if (ch >= 'A' && ch <= 'Z')	/* needed in cut() */
 	zmod.flags |= MOD_VIAPP;
     else
@@ -889,7 +851,6 @@ vikilleol(char **args)
     startvichange(-1);
     if (!n) {
 	/* error -- line already empty */
-	feep();
 	return 1;
     }
     /* delete to end of line */
@@ -941,10 +902,9 @@ viquotedinsert(char **args)
     zsetterm();
 #endif
     foredel(1);
-    if(c < 0) {
-	feep();
+    if(c < 0)
 	return 1;
-    } else
+    else
 	return selfinsert(args);
 }
 
diff --git a/Src/Zle/zle_word.c b/Src/Zle/zle_word.c
index e768cbefa..31f83a2df 100644
--- a/Src/Zle/zle_word.c
+++ b/Src/Zle/zle_word.c
@@ -300,10 +300,8 @@ vibackwardkillword(char **args)
     int x = cs, lim = (viinsbegin > findbol()) ? viinsbegin : findbol();
     int n = zmult;
 
-    if (n < 0) {
-	feep();
+    if (n < 0)
 	return 1;
-    }
 /* this taken from "vibackwardword" */
     while (n--) {
 	while ((x > lim) && iblank(line[x - 1]))
@@ -479,22 +477,16 @@ transposewords(char **args)
 	    x = cs;
 	    while (x && line[x - 1] != '\n' && !iword(line[x]))
 		x--;
-	    if (!x || line[x - 1] == '\n') {
-		feep();
+	    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();
+	if (!p3)
 	    return 1;
-	}
 	for (p2 = p3; p2 && !iword(line[p2 - 1]); p2--);
-	if (!p2) {
-	    feep();
+	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);
diff --git a/Src/builtin.c b/Src/builtin.c
index 26bd9d74f..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
 };
 
@@ -1492,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)
@@ -1502,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",
@@ -1519,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;
@@ -1605,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));
@@ -1678,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;
@@ -3074,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;
 }
 
diff --git a/Src/exec.c b/Src/exec.c
index 768706f4c..bc5548f1e 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -302,7 +302,7 @@ static char list_pipe_text[JOBTEXTSIZE];
 static int
 execcursh(Cmd cmd, LinkList args, int flags)
 {
-    if (!list_pipe)
+    if (!list_pipe && thisjob != list_pipe_job)
 	deletejob(jobtab + thisjob);
     cmdpush(CS_CURSH);
     execlist(cmd->u.list, 1, flags & CFLAG_EXEC);
@@ -828,7 +828,7 @@ execpline(Sublist l, int how, int last1)
     int ipipe[2], opipe[2];
     int pj, newjob;
     int old_simple_pline = simple_pline;
-    static int lastwj;
+    static int lastwj, lpforked;
 
     if (!l->left)
 	return lastval = (l->flags & PFLAG_NOT) != 0;
@@ -865,7 +865,7 @@ execpline(Sublist l, int how, int last1)
 	nowait = 0;
 	simple_pline = (l->left->type == END);
     }
-    lastwj = 0;
+    lastwj = lpforked = 0;
     execpline2(l->left, how, opipe[0], ipipe[1], last1);
     pline_level--;
     if (how & Z_ASYNC) {
@@ -890,7 +890,7 @@ execpline(Sublist l, int how, int last1)
 
 	    lastwj = thisjob = newjob;
 
-	    if (list_pipe)
+	    if (list_pipe || (pline_level && !(how & Z_TIMED)))
 		jn->stat |= STAT_NOPRINT;
 
 	    if (nowait) {
@@ -901,6 +901,11 @@ execpline(Sublist l, int how, int last1)
 		    DPUTS(!list_pipe_pid, "invalid list_pipe_pid");
 		    addproc(list_pipe_pid, list_pipe_text);
 
+		    /* If the super-job contains only the sub-shell, the
+		       sub-shell is the group leader. */
+		    if (!jn->procs->next || lpforked == 2)
+			jn->gleader = list_pipe_pid;
+
 		    for (pn = jobtab[jn->other].procs; pn; pn = pn->next)
 			if (WIFSTOPPED(pn->status))
 			    break;
@@ -914,12 +919,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 || pline_level) {
+		if (list_pipe_child) {
 		    jn->stat |= STAT_NOPRINT;
 		    makerunning(jn);
 		}
@@ -930,8 +937,11 @@ execpline(Sublist l, int how, int last1)
 		    jn->stat & STAT_DONE &&
 		    lastval2 & 0200)
 		    killpg(mypgrp, lastval2 & ~0200);
-		if ((list_pipe || last1 || pline_level) &&
-		    !list_pipe_child && jn->stat & STAT_STOPPED) {
+		if (!list_pipe_child && !lpforked && !subsh &&
+		    (list_pipe || last1 || pline_level) &&
+		    ((jn->stat & STAT_STOPPED) ||
+		     (list_pipe_job && pline_level &&
+		      (jobtab[list_pipe_job].stat & STAT_STOPPED)))) {
 		    pid_t pid;
 		    int synch[2];
 
@@ -951,28 +961,41 @@ execpline(Sublist l, int how, int last1)
 		    else if (pid) {
 			char dummy;
 
+			lpforked = 
+			    (killpg(jobtab[list_pipe_job].gleader, 0) == -1 ? 2 : 1);
 			list_pipe_pid = pid;
 			nowait = errflag = 1;
 			breaks = loops;
 			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;
-			if (list_pipe || last1)
+			/* 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) {
+			    if (setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader)
+				== -1) {
+				setpgrp(0L, mypgrp = getpid());
+			    }
+			} else
+			    setpgrp(0L, mypgrp = getpid());
 			close(synch[1]);
 			kill(getpid(), SIGSTOP);
 			list_pipe = 0;
 			list_pipe_child = 1;
+			opts[INTERACTIVE] = 0;
 			break;
 		    }
 		}
@@ -985,12 +1008,11 @@ execpline(Sublist l, int how, int last1)
 
 	    if (list_pipe && (lastval & 0200) && pj >= 0 &&
 		(!(jn->stat & STAT_INUSE) || (jn->stat & STAT_DONE))) {
+		deletejob(jn);
 		jn = jobtab + pj;
-		jn->stat |= STAT_NOPRINT;
-		killjb(jobtab + pj, lastval & ~0200);
+		killjb(jn, lastval & ~0200);
 	    }
-	    if (list_pipe_child || ((list_pipe || pline_level) &&
-				    (jn->stat & STAT_DONE)))
+	    if (list_pipe_child || (list_pipe && (jn->stat & STAT_DONE)))
 		deletejob(jn);
 	    thisjob = pj;
 
@@ -1117,7 +1139,8 @@ void
 untokenize(char *s)
 {
     if (*s) {
-	char *p = s, c;
+	char *p = s;
+	int c;
 
 	while ((c = *s++))
 	    if (itok(c)) {
@@ -1569,8 +1592,10 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 		is_builtin = 1;
 #ifdef DYNAMIC
 		/* autoload the builtin if necessary */
-		if (!((Builtin) hn)->handlerfunc)
+		if (!((Builtin) hn)->handlerfunc) {
 		    load_module(((Builtin) hn)->optstr);
+		    hn = builtintab->getnode(builtintab, cmdarg);
+		}
 #endif
 		break;
 	    }
@@ -2187,15 +2212,14 @@ entersubsh(int how, int cl, int fake)
 	    if (kill(jobtab[list_pipe_job].gleader, 0) == -1 ||
 		setpgrp(0L, jobtab[list_pipe_job].gleader) == -1) {
 		jobtab[list_pipe_job].gleader =
-		    jobtab[thisjob].gleader = mypgrp;
-		setpgrp(0L, mypgrp);
-
+		    jobtab[thisjob].gleader = (list_pipe_child ? mypgrp : getpid());
+		setpgrp(0L, jobtab[list_pipe_job].gleader);
 		if (how & Z_SYNC)
 		    attachtty(jobtab[thisjob].gleader);
 	    }
 	}
 	else if (!jobtab[thisjob].gleader ||
-		 (setpgrp(0L, jobtab[thisjob].gleader) == -1)) {
+		 setpgrp(0L, jobtab[thisjob].gleader) == -1) {
 	    jobtab[thisjob].gleader = getpid();
 	    if (list_pipe_job != thisjob &&
 		!jobtab[list_pipe_job].gleader)
@@ -2783,14 +2807,13 @@ execshfunc(Cmd cmd, Shfunc shf, LinkList args)
     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;
 	printprompt4();
@@ -2869,7 +2892,7 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 {
     char **tab, **x, *oargv0 = NULL;
     int oldzoptind, oldlastval;
-    char saveopts[OPT_SIZE];
+    char saveopts[OPT_SIZE], *oldscriptname;
     int obreaks = breaks;
 
     HEAPALLOC {
@@ -2881,6 +2904,8 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 	starttrapscope();
 
 	tab = pparams;
+	oldscriptname = scriptname;
+	scriptname = name;
 	oldzoptind = zoptind;
 	zoptind = 1;
 
@@ -2923,6 +2948,7 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 	    argzero = oargv0;
 	}
 	zoptind = oldzoptind;
+	scriptname = oldscriptname;
 	pparams = tab;
 
 	if (isset(LOCALOPTIONS)) {
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/init.c b/Src/init.c
index 10286ea87..2da409415 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -235,10 +235,10 @@ parseargs(char **argv)
 		else
 		    dosetopt(optno, action, 1);
               break;
-	    } else if (isspace(**argv)) {
+	    } else if (isspace(STOUC(**argv))) {
 		/* zsh's typtab not yet set, have to use ctype */
 		while (*++*argv)
-		    if (!isspace(**argv)) {
+		    if (!isspace(STOUC(**argv))) {
 			zerr("bad option string: `%s'", args, 0);
 			exit(1);
 		    }
@@ -615,7 +615,8 @@ setupvals(void)
 	prompt2 = ztrdup("%_> ");
     }
     prompt3 = ztrdup("?# ");
-    prompt4 = ztrdup("+ ");
+    prompt4 = (emulation == EMULATE_KSH || emulation == EMULATE_SH)
+	? ztrdup("+ ") : ztrdup("+%N:%i> ");
     sprompt = ztrdup("zsh: correct '%R' to '%r' [nyae]? ");
 
     ifs         = ztrdup(DEFAULT_IFS);
diff --git a/Src/jobs.c b/Src/jobs.c
index 31861e284..b2d7e9af1 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);
@@ -176,13 +180,14 @@ update_job(Job jn)
 		/* If we have `cat foo|while read a; grep $a bar;done'
 		 * and have hit ^Z, the sub-job is stopped, but the
 		 * super-job may still be running, waiting to be stopped
-		 * or to exit. So we have to send it a SIGSTOP. */
+		 * or to exit. So we have to send it a SIGTSTP. */
 		int i;
 
 		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;
 		    }
 	    }
@@ -667,6 +672,8 @@ deletejob(Job jn)
     if (jn->ty)
 	zfree(jn->ty, sizeof(struct ttyinfo));
 
+    if (jn->stat & STAT_WASSUPER)
+	deletejob(jobtab + jn->other);
     jn->gleader = jn->other = 0;
     jn->stat = jn->stty_in_env = 0;
     jn->procs = NULL;
@@ -780,22 +787,38 @@ waitjob(int job, int sig)
 	       what this might be.  --oberon
 
 	    errflag = 0; */
+	    if (subsh) {
+		killjb(jn, SIGCONT);
+		jn->stat &= ~STAT_STOPPED;
+	    }
 	    if (jn->stat & STAT_SUPERJOB) {
 		Job sj = jobtab + jn->other;
-		if (sj->stat & STAT_DONE) {
+		if ((sj->stat & STAT_DONE) || !sj->procs) {
 		    struct process *p;
 		    
 		    for (p = sj->procs; p; p = p->next)
 			if (WIFSIGNALED(p->status)) {
-			    killpg(jn->gleader, WTERMSIG(p->status));
+			    if (jn->gleader != mypgrp && jn->procs->next)
+				killpg(jn->gleader, WTERMSIG(p->status));
+			    else
+				kill(jn->procs->pid, WTERMSIG(p->status));
 			    kill(sj->other, SIGCONT);
 			    kill(sj->other, WTERMSIG(p->status));
 			    break;
 			}
 		    if (!p) {
+			int cp;
+
 			jn->stat &= ~STAT_SUPERJOB;
-			if (WIFEXITED(jn->procs->status))
-			    jn->gleader = mypgrp;
+			jn->stat |= STAT_WASSUPER;
+
+			if ((cp = ((WIFEXITED(jn->procs->status) ||
+				    WIFSIGNALED(jn->procs->status)) &&
+				   killpg(jn->gleader, 0) == -1))) {
+			    Process p;
+			    for (p = jn->procs; p->next; p = p->next);
+			    jn->gleader = p->pid;
+			}
 			/* This deleted the job too early if the parent
 			   shell waited for a command in a list that will
 			   be executed by the sub-shell (e.g.: if we have
@@ -804,6 +827,11 @@ waitjob(int job, int sig)
 			   but the parent shell gets notified for the
 			   sleep.
 			   deletejob(sj); */
+			/* If this super-job contains only the sub-shell,
+			   we have to attach the tty to our process group
+			   (which is shared by the sub-shell) now. */
+			if (!jn->procs->next || cp || jn->procs->pid != jn->gleader)
+			    attachtty(jn->gleader);
 			kill(sj->other, SIGCONT);
 		    }
 		    curjob = jn - jobtab;
@@ -813,7 +841,9 @@ waitjob(int job, int sig)
 
 		    jn->stat |= STAT_STOPPED;
 		    for (p = jn->procs; p; p = p->next)
-			p->status = sj->procs->status;
+			if (p->status == SP_RUNNING ||
+			    (!WIFEXITED(p->status) && !WIFSIGNALED(p->status)))
+			    p->status = sj->procs->status;
 		    curjob = jn - jobtab;
 		    printjob(jn, !!isset(LONGLISTJOBS), 1);
 		    break;
@@ -1232,7 +1262,14 @@ bin_fg(char *name, char **argv, char *ops, int func)
 		fflush(shout);
 		if (func != BIN_WAIT) {		/* fg */
 		    thisjob = job;
-		    attachtty(jobtab[job].gleader);
+		    if ((jobtab[job].stat & STAT_SUPERJOB) &&
+			((!jobtab[job].procs->next ||
+			  WIFEXITED(jobtab[job].procs->status) ||
+			  WIFSIGNALED(jobtab[job].procs->status))) &&
+			jobtab[jobtab[job].other].gleader)
+			attachtty(jobtab[jobtab[job].other].gleader);
+		    else
+			attachtty(jobtab[job].gleader);
 		}
 	    }
 	    if (stopped) {
diff --git a/Src/loop.c b/Src/loop.c
index b35024d6b..16e4ff314 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -378,7 +378,7 @@ execif(Cmd cmd, LinkList args, int flags)
     noerrexit = olderrexit;
 
     if (*t) {
-	cmdpush(s ? CS_ELIFTHEN : CS_IFTHEN);
+	cmdpush(*i ? (s ? CS_ELIFTHEN : CS_IFTHEN) : CS_ELSE);
 	execlist(*t, 1, flags & CFLAG_EXEC);
 	cmdpop();
     } else
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/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/prompt.c b/Src/prompt.c
index ad7cdbc31..89407694e 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -531,6 +531,14 @@ putpromptchar(int doprint, int endchar)
 		if(Rstring)
 		    stradd(Rstring);
 		break;
+	    case 'i':
+		addbufspc(DIGBUFSIZE);
+		sprintf(bp, "%ld", (long)lineno);
+		bp += strlen(bp);
+		break;
+	    case 'N':
+		stradd(scriptname ? scriptname : argzero);
+		break;
 	    case '\0':
 		return 0;
 	    case Meta:
diff --git a/Src/signals.c b/Src/signals.c
index e9851b57a..d29bdd4b0 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -586,7 +586,10 @@ killjb(Job jn, int sig)
  
                 for (pn = jn->procs; pn->next; pn = pn->next)
                     err = kill(pn->pid, sig);
- 
+
+		if (!jobtab[jn->other].procs && pn)
+		    err = kill(pn->pid, sig);
+
                 return err;
             }
  
diff --git a/Src/subst.c b/Src/subst.c
index ab6bf84fb..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));
diff --git a/Src/utils.c b/Src/utils.c
index 16dbe9229..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);
@@ -2133,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);
 }
 
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 9cb186ffa..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
@@ -207,6 +211,7 @@ resetneeded
 restoredir
 reswdtab
 retflag
+runhookdef
 runshfunc
 scancountparams
 scanhashtable
diff --git a/Src/zsh.h b/Src/zsh.h
index 23e84b2ec..88175eea6 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -278,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;
 
 
 /********************************/
@@ -628,10 +629,12 @@ struct job {
 #define STAT_INUSE	(1<<6)	/* this job entry is in use             */
 #define STAT_SUPERJOB	(1<<7)	/* job has a subjob                     */
 #define STAT_SUBJOB	(1<<8)	/* job is a subjob                      */
-#define STAT_CURSH	(1<<9)	/* last command is in current shell     */
-#define STAT_NOSTTY	(1<<10)	/* the tty settings are not inherited   */
+#define STAT_WASSUPER   (1<<9)  /* was a super-job, sub-job needs to be */
+				/* deleted */
+#define STAT_CURSH	(1<<10)	/* last command is in current shell     */
+#define STAT_NOSTTY	(1<<11)	/* the tty settings are not inherited   */
 				/* from this job when it exits.         */
-#define STAT_ATTACH	(1<<11)	/* delay reattaching shell to tty       */
+#define STAT_ATTACH	(1<<12)	/* delay reattaching shell to tty       */
 
 #define SP_RUNNING -1		/* fake status for jobs currently running */
 
@@ -887,6 +890,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 {
@@ -959,12 +978,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)