about summary refs log tree commit diff
path: root/Src/Zle
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Zle')
-rw-r--r--Src/Zle/comp1.c3
-rw-r--r--Src/Zle/iwidgets.list1
-rw-r--r--Src/Zle/zle_hist.c318
-rw-r--r--Src/Zle/zle_keymap.c2
-rw-r--r--Src/Zle/zle_main.c42
-rw-r--r--Src/Zle/zle_move.c15
-rw-r--r--Src/Zle/zle_thingy.c17
-rw-r--r--Src/Zle/zle_tricky.c63
-rw-r--r--Src/Zle/zle_utils.c12
9 files changed, 265 insertions, 208 deletions
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index c51aad297..f32e5f5c0 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -47,7 +47,7 @@ Cmlist cmatcher;
 void (*makecompparamsptr) _((void));
 
 /**/
-void (*comp_setunsetptr) _((int, int));
+void (*comp_setunsetptr) _((unsigned int, unsigned int));
 
 /* pointers to functions required by compctl and defined by zle */
 
@@ -147,6 +147,7 @@ createcompctltable(void)
     compctltab->hash        = hasher;
     compctltab->emptytable  = emptyhashtable;
     compctltab->filltable   = NULL;
+    compctltab->cmpnodes    = strcmp;
     compctltab->addnode     = addhashnode;
     compctltab->getnode     = gethashnode2;
     compctltab->getnode2    = gethashnode2;
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index 61ad0e24a..e0c93bd03 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -91,6 +91,7 @@
 "send-break", sendbreak, 0
 "set-mark-command", setmarkcommand, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "spell-word", spellword, 0
+"set-local-history", setlocalhistory, 0
 "transpose-chars", transposechars, 0
 "transpose-words", transposewords, 0
 "undefined-key", undefinedkey, 0
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index 0064d4ef0..d2b9a3233 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -47,27 +47,16 @@ int lastcol;
 /**/
 int histline;
 
-/* the last line in the history (the current one), metafied */
-
-/**/
-char *curhistline;
+#define ZLETEXT(X) ((X)->zle_text ? (X)->zle_text : (X)->text)
 
 /**/
 void
 remember_edits(void)
 {
-    if (histline == curhist) {
-	zsfree(curhistline);
-	curhistline = metafy((char *) line, ll, META_DUP);
-    }
-    else {
-	Histent ent = gethistent(histline);
-
-	if (metadiffer(ent->zle_text ? ent->zle_text : ent->text,
-		       (char *) line, ll)) {
-	    zsfree(ent->zle_text);
-	    ent->zle_text = metafy((char *) line, ll, META_DUP);
-	}
+    Histent ent = quietgethist(histline);
+    if (metadiffer(ZLETEXT(ent), (char *) line, ll)) {
+	zsfree(ent->zle_text);
+	ent->zle_text = metafy((char *) line, ll, META_DUP);
     }
 }
 
@@ -75,11 +64,11 @@ remember_edits(void)
 void
 forget_edits(void)
 {
-    int i;
+    Histent he;
 
-    for (i = 0; i < histentct; i++) {
-	zsfree(histentarr[i].zle_text);
-	histentarr[i].zle_text = NULL;
+    for (he = hist_ring; he; he = up_histent(he)) {
+	zsfree(he->zle_text);
+	he->zle_text = NULL;
     }
 }
 
@@ -87,16 +76,12 @@ forget_edits(void)
 void
 uphistory(void)
 {
-    if (zmult < 0) {
-	zmult = -zmult;
-	downhistory();
-	zmult = -zmult;
-    } else if(!zle_goto_hist(histline - zmult) && isset(HISTBEEP))
+    if (!zle_goto_hist(histline, -zmult) && isset(HISTBEEP))
 	feep();
 }
 
 /**/
-int
+static int
 upline(void)
 {
     int n = zmult;
@@ -159,7 +144,6 @@ viuplineorhistory(void)
     vifirstnonblank();
 }
 
-
 /**/
 void
 uplineorsearch(void)
@@ -181,7 +165,7 @@ uplineorsearch(void)
 }
 
 /**/
-int
+static int
 downline(void)
 {
     int n = zmult;
@@ -268,26 +252,22 @@ downlineorsearch(void)
 void
 acceptlineanddownhistory(void)
 {
-    char *s;
+    Histent he;
 
-    if (!(s = zle_get_event(histline + 1))) {
+    if (!(he = movehistent(quietgethist(histline), 1, HIST_FOREIGN))) {
 	feep();
 	return;
     }
-    pushnode(bufstack, ztrdup(s));
+    pushnode(bufstack, ztrdup(ZLETEXT(he)));
     done = 1;
-    stackhist = histline + 1;
+    stackhist = he->histnum;
 }
 
 /**/
 void
 downhistory(void)
 {
-    if (zmult < 0) {
-	zmult = -zmult;
-	uphistory();
-	zmult = -zmult;
-    } else if(!zle_goto_hist(histline + zmult) && isset(HISTBEEP))
+    if (!zle_goto_hist(histline, zmult) && isset(HISTBEEP))
 	feep();
 }
 
@@ -298,7 +278,7 @@ static char *srch_str;
 void
 historysearchbackward(void)
 {
-    int hl = histline;
+    Histent he;
     int n = zmult;
     char *s;
 
@@ -308,7 +288,7 @@ historysearchbackward(void)
 	zmult = n;
 	return;
     }
-    if (hl == curhist || hl != srch_hl || cs != srch_cs || mark != 0
+    if (histline == curhist || histline != srch_hl || cs != srch_cs || mark != 0
      || memcmp(srch_str, line, histpos) != 0) {
 	zfree(srch_str, histpos);
 	for (histpos = 0; histpos < ll && !iblank(line[histpos]); histpos++) ;
@@ -317,28 +297,29 @@ historysearchbackward(void)
 	srch_str = zalloc(histpos);
 	memcpy(srch_str, line, histpos);
     }
-    for (;;) {
-	hl--;
-	if (!(s = zle_get_event(hl))) {
-	    feep();
-	    return;
-	}
+    he = quietgethist(histline);
+    while ((he = movehistent(he, -1, hist_skip_flags))) {
+	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
+	    continue;
+	s = ZLETEXT(he);
 	if (metadiffer(s, srch_str, histpos) < 0 &&
 	    metadiffer(s, srch_str, ll)) {
-	    if (--n <= 0)
-		break;
+	    if (--n <= 0) {
+		zle_setline(he);
+		srch_hl = histline;
+		srch_cs = cs;
+		return;
+	    }
 	}
     }
-    zle_goto_hist(hl);
-    srch_hl = hl;
-    srch_cs = cs;
+    feep();
 }
 
 /**/
 void
 historysearchforward(void)
 {
-    int hl = histline;
+    Histent he;
     int n = zmult;
     char *s;
 
@@ -348,7 +329,7 @@ historysearchforward(void)
 	zmult = n;
 	return;
     }
-    if (hl == curhist || hl != srch_hl || cs != srch_cs || mark != 0
+    if (histline == curhist || histline != srch_hl || cs != srch_cs || mark != 0
      || memcmp(srch_str, line, histpos) != 0) {
 	zfree(srch_str, histpos);
 	for (histpos = 0; histpos < ll && !iblank(line[histpos]); histpos++) ;
@@ -357,20 +338,22 @@ historysearchforward(void)
 	srch_str = zalloc(histpos);
 	memcpy(srch_str, line, histpos);
     }
-    for (;;) {
-	hl++;
-	if (!(s = zle_get_event(hl))) {
-	    feep();
-	    return;
+    he = quietgethist(histline);
+    while ((he = movehistent(he, 1, hist_skip_flags))) {
+	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
+	    continue;
+	s = ZLETEXT(he);
+	if (metadiffer(s, srch_str, histpos) < (he->histnum == curhist) &&
+	    metadiffer(s, srch_str, ll)) {
+	    if (--n <= 0) {
+		zle_setline(he);
+		srch_hl = histline;
+		srch_cs = cs;
+		return;
+	    }
 	}
-	if (metadiffer(s, srch_str, histpos) < (hl == curhist) &&
-	    metadiffer(s, srch_str, ll))
-	    if (--n <= 0)
-		break;
     }
-    zle_goto_hist(hl);
-    srch_hl = hl;
-    srch_cs = cs;
+    feep();
 }
 
 /**/
@@ -387,7 +370,7 @@ beginningofbufferorhistory(void)
 void
 beginningofhistory(void)
 {
-    if (!zle_goto_hist(firsthist()) && isset(HISTBEEP))
+    if (!zle_goto_hist(firsthist(), 0) && isset(HISTBEEP))
 	feep();
 }
 
@@ -405,7 +388,7 @@ endofbufferorhistory(void)
 void
 endofhistory(void)
 {
-    zle_goto_hist(curhist);
+    zle_goto_hist(curhist, 0);
 }
 
 /**/
@@ -419,7 +402,7 @@ insertlastword(void)
 /* multiple calls will now search back through the history, pem */
     static char *lastinsert;
     static int lasthist, lastpos;
-    int evhist = curhist - 1, save;
+    int evhist = addhistnum(curhist, -1, HIST_FOREIGN), save;
 
     if (lastinsert) {
 	int lastlen = ztrlen(lastinsert);
@@ -428,7 +411,7 @@ insertlastword(void)
 	if (lastpos <= pos &&
 	    lastlen == pos - lastpos &&
 	    memcmp(lastinsert, (char *)&line[lastpos], lastlen) == 0) {
-	    evhist = --lasthist;
+	    evhist = addhistnum(lasthist, -1, HIST_FOREIGN);
 	    cs = lastpos;
 	    foredel(pos - cs);
 	}
@@ -464,41 +447,37 @@ insertlastword(void)
 }
 
 /**/
-char *
-qgetevent(int ev)
+void
+zle_setline(Histent he)
 {
-    return ((ev == curhist) ? curhistline : quietgetevent(ev));
+    remember_edits();
+    mkundoent();
+    histline = he->histnum;
+    setline(ZLETEXT(he));
+    setlastline();
+    clearlist = 1;
 }
 
 /**/
-char *
-zle_get_event(int ev)
+void
+setlocalhistory(void)
 {
-    Histent ent;
-
-    if (ev == curhist)
-	return curhistline;
-    if (! (ent = quietgethist(ev)))
-	return NULL;
-    if (ent->zle_text)
-	return ent->zle_text;
-    return ent->text;
+    if (zmod.flags & MOD_MULT) {
+	hist_skip_flags = zmult? HIST_FOREIGN : 0;
+    }
+    else {
+	hist_skip_flags ^= HIST_FOREIGN;
+    }
 }
 
 /**/
-static int
-zle_goto_hist(int ev)
+int
+zle_goto_hist(int ev, int n)
 {
-    char *t;
-
-    remember_edits();
-    if(!(t = zle_get_event(ev)))
+    Histent he = movehistent(quietgethist(ev), n, hist_skip_flags);
+    if (!he)
 	return 0;
-    mkundoent();
-    histline = ev;
-    setline(t);
-    setlastline();
-    clearlist = 1;
+    zle_setline(he);
     return 1;
 }
 
@@ -603,6 +582,7 @@ static struct isrch_spot {
 
 static int max_spot = 0;
 
+/**/
 #ifdef MODULE
 
 /**/
@@ -612,6 +592,7 @@ free_isrch_spots(void)
     zfree(isrch_spots, max_spot * sizeof(*isrch_spots));
 }
 
+/**/
 #endif /* MODULE */
 
 /**/
@@ -665,13 +646,15 @@ doisearch(int dir)
     char *okeymap = curkeymapname;
     static char *previous_search = NULL;
     static int previous_search_len = 0;
+    Histent he;
 
     clearlist = 1;
 
     strcpy(ibuf, ISEARCH_PROMPT);
     memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
     remember_edits();
-    s = zle_get_event(hl);
+    he = quietgethist(hl);
+    s = ZLETEXT(he);
     selectkeymap("main", 1);
     pos = metalen(s, cs);
     for (;;) {
@@ -705,15 +688,14 @@ doisearch(int dir)
 		if (!skip_line && ((sbuf[0] == '^') ?
 		    (t = metadiffer(s, sbuf + 1, sbptr - 1) < sens ? s : NULL) :
 		    (t = hstrnstr(s, pos, sbuf, sbptr, dir, sens)))) {
-		    zle_goto_hist(hl);
+		    zle_setline(he);
 		    pos = t - s;
 		    cs = ztrsub(t, s) + (dir == 1? sbptr - (sbuf[0]=='^') : 0);
 	    	    nomatch = 0;
 		    statusline = ibuf + NORM_PROMPT_POS;
 		    break;
 		}
-		hl += dir;
-		if (!(s = zle_get_event(hl))) {
+		if (!(he = movehistent(he, dir, hist_skip_flags))) {
 		    if (sbptr == (int)isrch_spots[top_spot-1].len
 		     && (isrch_spots[top_spot-1].flags & ISS_FAILING))
 			top_spot--;
@@ -723,13 +705,17 @@ doisearch(int dir)
 			feep();
 			nomatch = 1;
 		    }
-		    s = last_line;
+		    he = quietgethist(hl);
+		    s = ZLETEXT(he);
 		    skip_line = 0;
 		    statusline = ibuf;
 		    break;
 		}
+		hl = he->histnum;
+		s = ZLETEXT(he);
 		pos = dir == 1? 0 : strlen(s);
-		skip_line = !strcmp(last_line, s);
+		skip_line = isset(HISTFINDNODUPS)? !!(he->flags & HIST_DUP)
+						 : !strcmp(last_line, s);
 	    }
 	} else {
 	    top_spot = 0;
@@ -743,8 +729,9 @@ doisearch(int dir)
 	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
 	    int i;
 	    get_isrch_spot(0, &hl, &pos, &i, &sbptr, &dir, &nomatch);
-	    s = zle_get_event(hl);
-	    zle_goto_hist(hl);
+	    he = quietgethist(hl);
+	    zle_setline(he);
+	    s = ZLETEXT(he);
 	    cs = i;
 	    break;
 	}
@@ -769,10 +756,11 @@ doisearch(int dir)
 		statusline = ibuf;
 		skip_pos = 1;
 	    }
-	    s = zle_get_event(hl);
+	    he = quietgethist(hl);
+	    s = ZLETEXT(he);
 	    if (nomatch || !sbptr || (sbptr == 1 && sbuf[0] == '^')) {
 		int i = cs;
-		zle_goto_hist(hl);
+		zle_setline(he);
 		cs = i;
 	    }
 	    memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
@@ -874,42 +862,35 @@ doisearch(int dir)
 void
 acceptandinfernexthistory(void)
 {
-    int t0;
-    char *s;
+    Histent he;
 
     done = 1;
-    for (t0 = histline - 2;; t0--) {
-	if (!(s = qgetevent(t0)))
+    for (he = movehistent(quietgethist(histline), -2, HIST_FOREIGN);
+	 he; he = movehistent(he, -1, HIST_FOREIGN)) {
+	if (!metadiffer(ZLETEXT(he), (char *) line, ll)) {
+	    he = movehistent(he, 1, HIST_FOREIGN);
+	    pushnode(bufstack, ztrdup(ZLETEXT(he)));
+	    stackhist = he->histnum;
 	    return;
-	if (!metadiffer(s, (char *) line, ll))
-	    break;
+	}
     }
-    if (!(s = qgetevent(t0 + 1)))
-	return;
-    pushnode(bufstack, ztrdup(s));
-    stackhist = t0 + 1;
 }
 
 /**/
 void
 infernexthistory(void)
 {
-    int t0;
-    char *s;
+    Histent he;
 
-    for (t0 = histline - 2;; t0--) {
-	if (!(s = qgetevent(t0))) {
-	    feep();
+    for (he = movehistent(quietgethist(histline), -2, HIST_FOREIGN);
+	 he; he = movehistent(he, -1, HIST_FOREIGN)) {
+	if (!metadiffer(ZLETEXT(he), (char *) line, ll)) {
+	    he = movehistent(he, 1, HIST_FOREIGN);
+	    zle_setline(he);
 	    return;
 	}
-	if (! metadiffer(s, (char *) line, ll))
-	    break;
     }
-    if (!(s = qgetevent(t0 + 1))) {
-	feep();
-	return;
-    }
-    zle_goto_hist(t0 + 1);
+    feep();
 }
 
 /**/
@@ -925,7 +906,7 @@ vifetchhistory(void)
 	    return;
 	}
     }
-    if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist) &&
+    if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist, 0) &&
 	isset(HISTBEEP))
 	feep();
 }
@@ -1041,7 +1022,8 @@ vihistorysearchbackward(void)
 void
 virepeatsearch(void)
 {
-    int hl = histline, t0;
+    Histent he;
+    int t0;
     int n = zmult;
     char *s;
 
@@ -1054,23 +1036,21 @@ virepeatsearch(void)
 	visrchsense = -visrchsense;
     }
     t0 = strlen(visrchstr);
-    for (;;) {
-	hl += visrchsense;
-	if (!(s = zle_get_event(hl))) {
-	    feep();
-	    return;
-	}
-	if (!metadiffer(s, (char *) line, ll))
-	    continue;
-	if (*visrchstr == '^') {
-	    if (strncmp(s, visrchstr + 1, t0 - 1) != 0)
-		continue;
-	} else if (!hstrnstr(s, 0, visrchstr, t0, 1, 1))
+    he = quietgethist(histline);
+    while ((he = movehistent(he, visrchsense, hist_skip_flags))) {
+	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
 	    continue;
-	if (--n <= 0)
-	    break;
+	s = ZLETEXT(he);
+	if (metadiffer(s, (char *) line, ll)
+	 && (*visrchstr == '^'? strncmp(s, visrchstr + 1, t0 - 1) == 0
+			      : hstrnstr(s, 0, visrchstr, t0, 1, 1) != 0)) {
+	    if (--n <= 0) {
+		zle_setline(he);
+		return;
+	    }
+	}
     }
-    zle_goto_hist(hl);
+    feep();
 }
 
 /**/
@@ -1090,8 +1070,8 @@ virevrepeatsearch(void)
 void
 historybeginningsearchbackward(void)
 {
+    Histent he;
     int cpos = cs;		/* save cursor position */
-    int hl = histline;
     int n = zmult;
     char *s;
 
@@ -1101,20 +1081,21 @@ historybeginningsearchbackward(void)
 	zmult = n;
 	return;
     }
-    for (;;) {
-	hl--;
-	if (!(s = zle_get_event(hl))) {
-	    feep();
-	    return;
-	}
+    he = quietgethist(histline);
+    while ((he = movehistent(he, -1, hist_skip_flags))) {
+	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
+	    continue;
+	s = ZLETEXT(he);
 	if (metadiffer(s, (char *)line, cs) < 0 &&
-	    metadiffer(s, (char *)line, ll))
-	    if (--n <= 0)
-		break;
+	    metadiffer(s, (char *)line, ll)) {
+	    if (--n <= 0) {
+		zle_setline(he);
+		cs = cpos;
+		return;
+	    }
+	}
     }
-
-    zle_goto_hist(hl);
-    cs = cpos;
+    feep();
 }
 
 /* Extra function added by A.R. Iano-Fletcher.	*/
@@ -1124,8 +1105,8 @@ historybeginningsearchbackward(void)
 void
 historybeginningsearchforward(void)
 {
+    Histent he;
     int cpos = cs;		/* save cursor position */
-    int hl = histline;
     int n = zmult;
     char *s;
 
@@ -1135,18 +1116,19 @@ historybeginningsearchforward(void)
 	zmult = n;
 	return;
     }
-    for (;;) {
-	hl++;
-	if (!(s = zle_get_event(hl))) {
-	    feep();
-	    return;
+    he = quietgethist(histline);
+    while ((he = movehistent(he, 1, hist_skip_flags))) {
+	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
+	    continue;
+	s = ZLETEXT(he);
+	if (metadiffer(s, (char *)line, cs) < (he->histnum == curhist) &&
+	    metadiffer(s, (char *)line, ll)) {
+	    if (--n <= 0) {
+		zle_setline(he);
+		cs = cpos;
+		return;
+	    }
 	}
-	if (metadiffer(s, (char *)line, cs) < (hl == curhist) &&
-	    metadiffer(s, (char *)line, ll))
-	    if (--n <= 0)
-		break;
     }
-
-    zle_goto_hist(hl);
-    cs = cpos;
+    feep();
 }
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 1baa3a845..7504ed809 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -130,6 +130,7 @@ createkeymapnamtab(void)
     keymapnamtab->hash        = hasher;
     keymapnamtab->emptytable  = emptyhashtable;
     keymapnamtab->filltable   = NULL;
+    keymapnamtab->cmpnodes    = strcmp;
     keymapnamtab->addnode     = addhashnode;
     keymapnamtab->getnode     = gethashnode2;
     keymapnamtab->getnode2    = gethashnode2;
@@ -172,6 +173,7 @@ newkeytab(char *kmname)
     ht->hash        = hasher;
     ht->emptytable  = emptyhashtable;
     ht->filltable   = NULL;
+    ht->cmpnodes    = strcmp;
     ht->addnode     = addhashnode;
     ht->getnode     = gethashnode2;
     ht->getnode2    = gethashnode2;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index a5da84b66..dfb437308 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -111,6 +111,10 @@ int prefixflag;
 /**/
 int feepflag;
 
+#ifdef FIONREAD
+static int delayzsetterm;
+#endif
+
 /* set up terminal */
 
 /**/
@@ -119,12 +123,30 @@ zsetterm(void)
 {
     struct ttyinfo ti;
 
-#if defined(CLOBBERS_TYPEAHEAD) && defined(FIONREAD)
+#if defined(FIONREAD)
     int val;
 
     ioctl(SHTTY, FIONREAD, (char *)&val);
-    if (val)
+    if (val) {
+	/*
+	 * Problems can occur on some systems when switching from
+	 * canonical to non-canonical input.  The former is usually
+	 * set while running programmes, but the latter is necessary
+	 * for zle.  If there is input in canonical mode, then we
+	 * need to read it without setting up the terminal.  Furthermore,
+	 * while that input gets processed there may be more input
+	 * being typed (i.e. further typeahead).  This means that
+	 * we can't set up the terminal for zle *at all* until
+	 * we are sure there is no more typeahead to come.  So
+	 * if there is typeahead, we set the flag delayzsetterm.
+	 * Then getkey() performs another FIONREAD call; if that is
+	 * 0, we have finally used up all the typeahead, and it is
+	 * safe to alter the terminal, which we do at that point.
+	 */
+	delayzsetterm = 1;
 	return;
+    } else
+	delayzsetterm = 0;
 #endif
 
 /* sanitize the tty */
@@ -298,7 +320,19 @@ getkey(int keytmout)
     if (kungetct)
 	ret = STOUC(kungetbuf[--kungetct]);
     else {
-	if (keytmout) {
+#ifdef FIONREAD
+	if (delayzsetterm) {
+	    int val;
+	    ioctl(SHTTY, FIONREAD, (char *)&val);
+	    if (!val)
+		zsetterm();
+	}
+#endif
+	if (keytmout
+#ifdef FIONREAD
+	    && ! delayzsetterm
+#endif
+	    ) {
 	    if (keytimeout > 500)
 		exp100ths = 500;
 	    else if (keytimeout > 0)
@@ -461,7 +495,6 @@ zleread(char *lp, char *rp, int flags)
 	undoing = 1;
 	line = (unsigned char *)zalloc((linesz = 256) + 2);
 	virangeflag = lastcmd = done = cs = ll = mark = 0;
-	curhistline = NULL;
 	vichgflag = 0;
 	viinsbegin = 0;
 	statusline = NULL;
@@ -542,7 +575,6 @@ zleread(char *lp, char *rp, int flags)
 	zleactive = no_restore_tty = 0;
 	alarm(0);
     } LASTALLOC;
-    zsfree(curhistline);
     freeundo();
     if (eofsent) {
 	free(line);
diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c
index d6e9c4b7d..7169f5700 100644
--- a/Src/Zle/zle_move.c
+++ b/Src/Zle/zle_move.c
@@ -476,17 +476,10 @@ vigotomark(void)
 	feep();
 	return;
     }
-    if (curhist != vimarkline[ch]) {
-	char *s;
-
-	remember_edits();
-	if (!(s = qgetevent(vimarkline[ch]))) {
-	    vimarkline[ch] = 0;
-	    feep();
-	    return;
-	}
-	histline = vimarkline[ch];
-	setline(s);
+    if (curhist != vimarkline[ch] && !zle_goto_hist(vimarkline[ch], 0)) {
+	vimarkline[ch] = 0;
+	feep();
+	return;
     }
     cs = vimarkcs[ch];
     if (cs > ll)
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index b879669ac..dbf982899 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -64,6 +64,7 @@ createthingytab(void)
     thingytab->hash        = hasher;
     thingytab->emptytable  = emptythingytab;
     thingytab->filltable   = NULL;
+    thingytab->cmpnodes    = strcmp;
     thingytab->addnode     = addhashnode;
     thingytab->getnode     = gethashnode;
     thingytab->getnode2    = gethashnode2;
@@ -356,7 +357,7 @@ bin_zle(char *name, char **args, char *ops, int func)
 
     /* check number of arguments */
     for(n = 0; args[n]; n++) ;
-    if(!op->o && n != 1) {
+    if(!op->o && n != 1 && n != 2) {
 	zerrnam(name, "wrong number of arguments", NULL, 0);
 	return 1;
     }
@@ -515,17 +516,31 @@ static int
 bin_zle_call(char *name, char **args, char *ops, char func)
 {
     Thingy t;
+    struct modifier modsave;
 
     if(!zleactive || incompctlfunc || incompfunc) {
 	zerrnam(name, "widgets can only be called when ZLE is active",
 	    NULL, 0);
 	return 1;
     }
+    if (args[1]) {
+	modsave = zmod;
+	if (isdigit(*args[1])) {
+	    zmod.mult = atoi(args[1]);
+	    zmod.flags |= MOD_MULT;
+	}
+	else {
+	    zmod.mult = 1;
+	    zmod.flags &= ~MOD_MULT;
+	}
+    }
     t = rthingy(args[0]);
     PERMALLOC {
       execzlefunc(t);
     } LASTALLOC;
     unrefthingy(t);
+    if (args[1])
+	zmod = modsave;
     return 0;
 }
 
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 8db571d0b..bc6cff8b6 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -578,6 +578,10 @@ static char *varname;
 
 static int insubscr;
 
+/* Parameter pointer for completing keys of an assoc array. */
+
+static Param keypm;
+
 /* 1 if we are completing in a quoted string (or inside `...`) */
 
 /**/
@@ -1364,9 +1368,15 @@ get_comp_string(void)
 	    zsfree(varname);
 	    varname = ztrdup(tt);
 	    *s = sav;
-	    if (skipparens(Inbrack, Outbrack, &s) > 0 || s > tt + cs - wb)
-		s = NULL, inwhat = IN_MATH, insubscr = 1;
-	    else if (*s == '=' && cs > wb + (s - tt)) {
+	    if (skipparens(Inbrack, Outbrack, &s) > 0 || s > tt + cs - wb) {
+		s = NULL;
+		inwhat = IN_MATH;
+		if ((keypm = (Param) paramtab->getnode(paramtab, varname)) &&
+		    (keypm->flags & PM_HASHED))
+		    insubscr = 2;
+		else
+		    insubscr = 1;
+	    } else if (*s == '=' && cs > wb + (s - tt)) {
 		s++;
 		wb += s - tt;
 		t0 = STRING;
@@ -1424,11 +1434,15 @@ get_comp_string(void)
 		    zsfree(varname);
 		    varname = ztrdup(nb);
 		    *ne = sav;
+		    if ((keypm = (Param) paramtab->getnode(paramtab,
+							   varname)) &&
+			(keypm->flags & PM_HASHED))
+			insubscr = 2;
 		}
 	    }
 	}
 	if (inwhat == IN_MATH) {
-	    if (compfunc) {
+	    if (compfunc || insubscr == 2) {
 		int lev;
 		char *p;
 
@@ -1472,7 +1486,11 @@ get_comp_string(void)
 		zsfree(varname);
 		varname = ztrdup((char *) line + i + 1);
 		line[wb - 1] = sav;
-		insubscr = 1;
+		if ((keypm = (Param) paramtab->getnode(paramtab, varname)) &&
+		    (keypm->flags & PM_HASHED))
+		    insubscr = 2;
+		else
+		    insubscr = 1;
 	    }
 	}
 	/* This variable will hold the current word in quoted form. */
@@ -3524,7 +3542,7 @@ int
 addmatches(Cadata dat, char **argv)
 {
     char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL;
-    char **aign = NULL, **dparr;
+    char **aign = NULL, **dparr = NULL;
     int lpl, lsl, pl, sl, bpl, bsl, llpl = 0, llsl = 0, nm = mnum;
     int oisalt = 0, isalt, isexact, doadd;
     Cline lc = NULL;
@@ -4360,7 +4378,7 @@ callcompfunc(char *s, char *fn)
 
 	    PERMALLOC {
 		q = compwords = (char **)
-		    zalloc((clwnum - aadd + 1) * sizeof(char *));
+		    zalloc((clwnum + 1) * sizeof(char *));
 		for (p = clwords + aadd; *p; p++, q++) {
 		    tmp = dupstring(*p);
 		    untokenize(tmp);
@@ -4829,13 +4847,22 @@ makecomplistglobal(char *os, int incmd, int lst, int flags)
     char *s;
 
     ccont = CC_CCCONT;
+    cc_dummy.suffix = NULL;
 
     if (linwhat == IN_ENV) {
         /* Default completion for parameter values. */
         cc = &cc_default;
+	keypm = NULL;
     } else if (linwhat == IN_MATH) {
-        /* Parameter names inside mathematical expression. */
-        cc_dummy.mask = CC_PARAMS;
+	if (insubscr == 2) {
+	    /* Inside subscript of assoc array, complete keys. */
+	    cc_dummy.mask = 0;
+	    cc_dummy.suffix = "]";
+	} else {
+	    /* Other math environment, complete paramete names. */
+	    keypm = NULL;
+	    cc_dummy.mask = CC_PARAMS;
+	}
 	cc = &cc_dummy;
 	cc_dummy.refc = 10000;
     } else if (linwhat == IN_COND) {
@@ -4851,13 +4878,16 @@ makecomplistglobal(char *os, int incmd, int lst, int flags)
 	    (CC_FILES | CC_PARAMS);
 	cc = &cc_dummy;
 	cc_dummy.refc = 10000;
-    } else if (linredir)
+	keypm = NULL;
+    } else if (linredir) {
 	/* In redirections use default completion. */
 	cc = &cc_default;
-    else
+	keypm = NULL;
+    } else {
 	/* Otherwise get the matches for the command. */
+	keypm = NULL;
 	return makecomplistcmd(os, incmd, flags);
-
+    }
     if (cc) {
 	/* First, use the -T compctl. */
 	if (!(flags & CFN_FIRST)) {
@@ -5917,7 +5947,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	Comp compc = NULL;
 	char *e, *h, hpatsav;
 	Histent he;
-	int i = curhist - 1, n = cc->hnum;
+	int i = addhistnum(curhist,-1,HIST_FOREIGN), n = cc->hnum;
 
 	/* Parse the pattern, if it isn't the null string. */
 	if (*(cc->hpat)) {
@@ -5971,7 +6001,12 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     if ((t = cc->mask & (CC_ALREG | CC_ALGLOB)))
 	/* Add the two types of aliases. */
 	dumphashtable(aliastab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
-
+    if (keypm && cc == &cc_dummy) {
+	/* Add the keys of the parameter in keypm. */
+	scanhashtable(keypm->gets.hfn(keypm), 0, 0, PM_UNSET, addhnmatch, 0);
+	keypm = NULL;
+	cc_dummy.suffix = NULL;
+    }
     if (!errflag && cc->ylist) {
 	/* generate the user-defined display list: if anything fails, *
 	 * we silently allow the normal completion list to be used.   */
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 4cdb3c52d..8524fd21e 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -572,10 +572,8 @@ undo(void)
 static void
 unapplychange(struct change *ch)
 {
-    if(ch->hist != histline) {
-	remember_edits();
-	setline(zle_get_event(histline = ch->hist));
-    }
+    if(ch->hist != histline)
+	zle_setline(quietgethist(ch->hist));
     cs = ch->off;
     if(ch->ins)
 	foredel(ztrlen(ch->ins));
@@ -613,10 +611,8 @@ redo(void)
 static void
 applychange(struct change *ch)
 {
-    if(ch->hist != histline) {
-	remember_edits();
-	setline(zle_get_event(histline = ch->hist));
-    }
+    if(ch->hist != histline)
+	zle_setline(quietgethist(ch->hist));
     cs = ch->off;
     if(ch->del)
 	foredel(ztrlen(ch->del));