about summary refs log tree commit diff
path: root/Src/Zle/zle_hist.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Zle/zle_hist.c')
-rw-r--r--Src/Zle/zle_hist.c1139
1 files changed, 1139 insertions, 0 deletions
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
new file mode 100644
index 000000000..76e421c1c
--- /dev/null
+++ b/Src/Zle/zle_hist.c
@@ -0,0 +1,1139 @@
+/*
+ * zle_hist.c - history editing
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Paul Falstad or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+#include "zle_hist.pro"
+
+/* Are references to earlier history lines permitted?  == 0 if       *
+ * editing or reading a standalone line, such as in vared or select. */
+
+/**/
+int histallowed;
+
+/* Column position of vi ideal cursor.  -1 if it is unknown -- most *
+ * movements and changes do this.                                   */
+
+/**/
+int lastcol;
+
+/* current history line number */
+
+/**/
+int histline;
+
+/* the last line in the history (the current one), metafied */
+
+/**/
+char *curhistline;
+
+/**/
+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);
+	}
+    }
+}
+
+/**/
+void
+forget_edits(void)
+{
+    int i;
+
+    for (i = 0; i < histentct; i++) {
+	zsfree(histentarr[i].zle_text);
+	histentarr[i].zle_text = NULL;
+    }
+}
+
+/**/
+void
+uphistory(void)
+{
+    if (zmult < 0) {
+	zmult = -zmult;
+	downhistory();
+	zmult = -zmult;
+    } else if(!zle_goto_hist(histline - zmult) && isset(HISTBEEP))
+	feep();
+}
+
+/**/
+int
+upline(void)
+{
+    int n = zmult;
+
+    if (n < 0) {
+	zmult = -zmult;
+	n = downline();
+	zmult = -zmult;
+	return n;
+    }
+    if (lastcol == -1)
+	lastcol = cs - findbol();
+    cs = findbol();
+    while (n) {
+	if (!cs)
+	    break;
+	cs--;
+	cs = findbol();
+	n--;
+    }
+    if (!n) {
+	int x = findeol();
+
+	if ((cs += lastcol) >= x) {
+	    cs = x;
+	    if (cs > findbol() && invicmdmode())
+		cs--;
+	}
+    }
+    return n;
+}
+
+/**/
+void
+uplineorhistory(void)
+{
+    int ocs = cs;
+    int n = upline();
+    if (n) {
+	int m = zmult;
+
+	cs = ocs;
+	if (virangeflag || !histallowed) {
+	    feep();
+	    return;
+	}
+	zmult = n;
+	uphistory();
+	zmult = m;
+    }
+}
+
+/**/
+void
+viuplineorhistory(void)
+{
+    int col = lastcol;
+    uplineorhistory();
+    lastcol = col;
+    vifirstnonblank();
+}
+
+
+/**/
+void
+uplineorsearch(void)
+{
+    int ocs = cs;
+    int n = upline();
+    if (n) {
+	int m = zmult;
+
+	cs = ocs;
+	if (virangeflag || !histallowed) {
+	    feep();
+	    return;
+	}
+	zmult = n;
+	historysearchbackward();
+	zmult = m;
+    }
+}
+
+/**/
+int
+downline(void)
+{
+    int n = zmult;
+
+    if (n < 0) {
+	zmult = -zmult;
+	n = upline();
+	zmult = -zmult;
+	return n;
+    }
+    if (lastcol == -1)
+	lastcol = cs - findbol();
+    while (n) {
+	int x = findeol();
+
+	if (x == ll)
+	    break;
+	cs = x + 1;
+	n--;
+    }
+    if (!n) {
+	int x = findeol();
+
+	if ((cs += lastcol) >= x) {
+	    cs = x;
+	    if (cs > findbol() && invicmdmode())
+		cs--;
+	}
+    }
+    return n;
+}
+
+/**/
+void
+downlineorhistory(void)
+{
+    int ocs = cs;
+    int n = downline();
+    if (n) {
+	int m = zmult;
+
+	cs = ocs;
+	if (virangeflag || !histallowed) {
+	    feep();
+	    return;
+	}
+	zmult = n;
+	downhistory();
+	zmult = m;
+    }
+}
+
+/**/
+void
+vidownlineorhistory(void)
+{
+    int col = lastcol;
+    downlineorhistory();
+    lastcol = col;
+    vifirstnonblank();
+}
+
+/**/
+void
+downlineorsearch(void)
+{
+    int ocs = cs;
+    int n = downline();
+    if (n) {
+	int m = zmult;
+
+	cs = ocs;
+	if (virangeflag || !histallowed) {
+	    feep();
+	    return;
+	}
+	zmult = n;
+	historysearchforward();
+	zmult = m;
+    }
+}
+
+/**/
+void
+acceptlineanddownhistory(void)
+{
+    char *s;
+
+    if (!(s = zle_get_event(histline + 1))) {
+	feep();
+	return;
+    }
+    pushnode(bufstack, ztrdup(s));
+    done = 1;
+    stackhist = histline + 1;
+}
+
+/**/
+void
+downhistory(void)
+{
+    if (zmult < 0) {
+	zmult = -zmult;
+	uphistory();
+	zmult = -zmult;
+    } else if(!zle_goto_hist(histline + zmult) && isset(HISTBEEP))
+	feep();
+}
+
+/**/
+void
+historysearchbackward(void)
+{
+    int histpos, histmpos, hl = histline;
+    int n = zmult;
+    char *s;
+
+    if (!n)
+	return;
+    if (n < 0) {
+	zmult = -n;
+	historysearchforward();
+	zmult = n;
+	return;
+    }
+    for (histpos = histmpos = 0; histpos < ll && !iblank(line[histpos]);
+	histpos++, histmpos++)
+	if(imeta(line[histpos]))
+	    histmpos++;
+    for (;;) {
+	hl--;
+	if (!(s = zle_get_event(hl))) {
+	    feep();
+	    return;
+	}
+	if (metadiffer(s, (char *) line, histpos) < 0 &&
+	    iblank(s[histmpos] == Meta ? s[histmpos+1]^32 : s[histmpos]) &&
+	    metadiffer(s, (char *) line, ll) && !--n)
+	    break;
+    }
+    zle_goto_hist(hl);
+}
+
+/**/
+void
+historysearchforward(void)
+{
+    int histpos, histmpos, hl = histline;
+    int n = zmult;
+    char *s;
+
+    if (!n)
+	return;
+    if (n < 0) {
+	zmult = -n;
+	historysearchbackward();
+	zmult = n;
+	return;
+    }
+    for (histpos = histmpos = 0; histpos < ll && !iblank(line[histpos]);
+	histpos++, histmpos++)
+	if(imeta(line[histpos]))
+	    histmpos++;
+    for (;;) {
+	hl++;
+	if (!(s = zle_get_event(hl))) {
+	    feep();
+	    return;
+	}
+	if (metadiffer(s, (char *) line, histpos) < (histline == curhist) &&
+	    (!s[histmpos] ||
+	     iblank(s[histmpos] == Meta ? s[histmpos+1]^32 : s[histmpos])) &&
+	    metadiffer(s, (char *) line, ll) && !--n)
+	    break;
+    }
+    zle_goto_hist(hl);
+}
+
+/**/
+void
+beginningofbufferorhistory(void)
+{
+    if (findbol())
+	cs = 0;
+    else
+	beginningofhistory();
+}
+
+/**/
+void
+beginningofhistory(void)
+{
+    if (!zle_goto_hist(firsthist()) && isset(HISTBEEP))
+	feep();
+}
+
+/**/
+void
+endofbufferorhistory(void)
+{
+    if (findeol() != ll)
+	cs = ll;
+    else
+	endofhistory();
+}
+
+/**/
+void
+endofhistory(void)
+{
+    zle_goto_hist(curhist);
+}
+
+/**/
+void
+insertlastword(void)
+{
+    int n;
+    char *s, *t;
+    Histent he;
+
+/* multiple calls will now search back through the history, pem */
+    static char *lastinsert;
+    static int lasthist, lastpos;
+    int evhist = curhist - 1, save;
+
+    if (lastinsert) {
+	int lastlen = ztrlen(lastinsert);
+	int pos = cs;
+
+	if (lastpos <= pos &&
+	    lastlen == pos - lastpos &&
+	    memcmp(lastinsert, (char *)&line[lastpos], lastlen) == 0) {
+	    evhist = --lasthist;
+	    cs = lastpos;
+	    foredel(pos - cs);
+	}
+	zsfree(lastinsert);
+	lastinsert = NULL;
+    }
+    if (!(he = quietgethist(evhist)) || !he->nwords) {
+	feep();
+	return;
+    }
+    if (zmult > 0) {
+	n = he->nwords - (zmult - 1);
+    } else {
+	n = 1 - zmult;
+    }
+    if (n < 1 || n > he->nwords) {
+	feep();
+	return;
+    }
+    s = he->text + he->words[2*n-2];
+    t = he->text + he->words[2*n-1];
+    save = *t;
+    *t = '\0';			/* ignore trailing whitespace */
+
+    lasthist = evhist;
+    lastpos = cs;
+    lastinsert = ztrdup(s);
+    n = zmult;
+    zmult = 1;
+    doinsert(s);
+    zmult = n;
+    *t = save;
+}
+
+/**/
+char *
+qgetevent(int ev)
+{
+    return ((ev == curhist) ? curhistline : quietgetevent(ev));
+}
+
+/**/
+char *
+zle_get_event(int ev)
+{
+    Histent ent;
+
+    if (ev == curhist)
+	return curhistline;
+    if (! (ent = quietgethist(ev)))
+	return NULL;
+    if (ent->zle_text)
+	return ent->zle_text;
+    return ent->text;
+}
+
+/**/
+static int
+zle_goto_hist(int ev)
+{
+    char *t;
+
+    remember_edits();
+    if(!(t = zle_get_event(ev)))
+	return 0;
+    mkundoent();
+    histline = ev;
+    setline(t);
+    setlastline();
+    return 1;
+}
+
+/**/
+void
+pushline(void)
+{
+    int n = zmult;
+
+    if (n < 0)
+	return;
+    pushnode(bufstack, metafy((char *) line, ll, META_DUP));
+    while (--n)
+	pushnode(bufstack, ztrdup(""));
+    stackcs = cs;
+    *line = '\0';
+    ll = cs = 0;
+}
+
+/**/
+void
+pushlineoredit(void)
+{
+    int ics;
+    unsigned char *s;
+    char *hline = hgetline();
+
+    if (zmult < 0)
+	return;
+    if (hline && *hline) {
+	ics = ztrlen(hline);
+	sizeline(ics + ll + 1);
+	for (s = line + ll; --s >= line; *(s + ics) = *s);
+	for (s = line; *hline; hline++)
+	    *s++ = *hline == Meta ? *++hline ^ 32 : *hline;
+	ll += ics;
+	cs += ics;
+    }
+    pushline();
+    if (!isfirstln) {
+	errflag = done = 1;
+    }
+}
+
+/**/
+void
+pushinput(void)
+{
+    int i;
+
+    if (zmult < 0)
+	return;
+    zmult += i = !isfirstln;
+    pushlineoredit();
+    zmult -= i;
+}
+
+/**/
+void
+getline(void)
+{
+    char *s = (char *)getlinknode(bufstack);
+
+    if (!s)
+	feep();
+    else {
+	int cc;
+
+	unmetafy(s, &cc);
+	spaceinline(cc);
+	memcpy((char *)line + cs, s, cc);
+	cs += cc;
+	free(s);
+    }
+}
+
+/**/
+void
+historyincrementalsearchbackward(void)
+{
+    doisearch(-1);
+}
+
+/**/
+void
+historyincrementalsearchforward(void)
+{
+    doisearch(1);
+}
+
+static struct isrch_spot {
+    int hl;			/* This spot's histline */
+    unsigned short pos;		/* The search position in our metafied str */
+    unsigned short cs;		/* The visible search position to the user */
+    unsigned short len;		/* The search string's length */
+    unsigned short flags;	/* This spot's flags */
+#define ISS_FAILING	1
+#define ISS_FORWARD	2
+} *isrch_spots;
+
+static int max_spot = 0;
+
+#ifdef MODULE
+
+/**/
+void
+free_isrch_spots(void)
+{
+    zfree(isrch_spots, max_spot * sizeof(*isrch_spots));
+}
+
+#endif /* MODULE */
+
+/**/
+static void
+set_isrch_spot(int num, int hl, int pos, int cs, int len, int dir, int nomatch)
+{
+    if (num >= max_spot) {
+	if (!isrch_spots) {
+	    isrch_spots = (struct isrch_spot*)
+			    zalloc((max_spot = 64) * sizeof *isrch_spots);
+	} else {
+	    isrch_spots = (struct isrch_spot*)realloc((char*)isrch_spots,
+			    (max_spot += 64) * sizeof *isrch_spots);
+	}
+    }
+
+    isrch_spots[num].hl = hl;
+    isrch_spots[num].pos = (unsigned short)pos;
+    isrch_spots[num].cs = (unsigned short)cs;
+    isrch_spots[num].len = (unsigned short)len;
+    isrch_spots[num].flags = (dir > 0? ISS_FORWARD : 0)
+			   + (nomatch? ISS_FAILING : 0);
+}
+
+/**/
+static void
+get_isrch_spot(int num, int *hlp, int *posp, int *csp, int *lenp, int *dirp, int *nomatch)
+{
+    *hlp = isrch_spots[num].hl;
+    *posp = (int)isrch_spots[num].pos;
+    *csp = (int)isrch_spots[num].cs;
+    *lenp = (int)isrch_spots[num].len;
+    *dirp = (isrch_spots[num].flags & ISS_FORWARD)? 1 : -1;
+    *nomatch = (isrch_spots[num].flags & ISS_FAILING);
+}
+
+#define ISEARCH_PROMPT		"failing XXX-i-search: "
+#define NORM_PROMPT_POS		8
+#define FIRST_SEARCH_CHAR	(NORM_PROMPT_POS + 14)
+
+/**/
+static void
+doisearch(int dir)
+{
+    char *s, *ibuf = halloc(80), *sbuf = ibuf + FIRST_SEARCH_CHAR;
+    int sbptr = 0, top_spot = 0, pos, sibuf = 80;
+    int nomatch = 0, skip_line = 0, skip_pos = 0;
+    int odir = dir, sens = zmult == 1 ? 3 : 1;
+    int hl = histline;
+    Thingy cmd;
+    char *okeymap = curkeymapname;
+    static char *previous_search = NULL;
+    static int previous_search_len = 0;
+
+    strcpy(ibuf, ISEARCH_PROMPT);
+    memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
+    remember_edits();
+    s = zle_get_event(hl);
+    selectkeymap("main", 1);
+    pos = metalen(s, cs);
+    for (;;) {
+	/* Remember the current values in case search fails (doesn't push). */
+	set_isrch_spot(top_spot, hl, pos, cs, sbptr, dir, nomatch);
+	if (sbptr == 1 && sbuf[0] == '^') {
+	    cs = 0;
+    	    nomatch = 0;
+	    statusline = ibuf + NORM_PROMPT_POS;
+	} else if (sbptr > 0) {
+	    char *last_line = s;
+
+	    for (;;) {
+		char *t;
+
+		if (skip_pos) {
+		    if (dir < 0) {
+			if (pos == 0)
+			    skip_line = 1;
+			else
+			    pos -= 1 + (pos != 1 && s[pos-2] == Meta);
+		    } else if (sbuf[0] != '^') {
+			if (pos >= strlen(s+1))
+			    skip_line = 1;
+			else
+			    pos += 1 + (s[pos] == Meta);
+		    } else
+			skip_line = 1;
+		    skip_pos = 0;
+		}
+		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);
+		    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 (sbptr == (int)isrch_spots[top_spot-1].len
+		     && (isrch_spots[top_spot-1].flags & ISS_FAILING))
+			top_spot--;
+		    get_isrch_spot(top_spot, &hl, &pos, &cs, &sbptr,
+				   &dir, &nomatch);
+		    if (!nomatch) {
+			feep();
+			nomatch = 1;
+		    }
+		    s = last_line;
+		    skip_line = 0;
+		    statusline = ibuf;
+		    break;
+		}
+		pos = dir == 1? 0 : strlen(s);
+		skip_line = !strcmp(last_line, s);
+	    }
+	} else {
+	    top_spot = 0;
+    	    nomatch = 0;
+	    statusline = ibuf + NORM_PROMPT_POS;
+	}
+	sbuf[sbptr] = '_';
+	statusll = sbuf - statusline + sbptr + 1;
+    ref:
+	refresh();
+	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);
+	    cs = i;
+	    break;
+	}
+	if(cmd == Th(z_clearscreen)) {
+	    clearscreen();
+	    goto ref;
+	} else if(cmd == Th(z_redisplay)) {
+	    redisplay();
+	    goto ref;
+	} else if(cmd == Th(z_vicmdmode)) {
+	    if(selectkeymap(invicmdmode() ? "main" : "vicmd", 0))
+		feep();
+	    goto ref;
+	} else if(cmd == Th(z_vibackwarddeletechar) ||
+	    	cmd == Th(z_backwarddeletechar)) {
+	    if (top_spot)
+		get_isrch_spot(--top_spot, &hl, &pos, &cs, &sbptr,
+			       &dir, &nomatch);
+	    else
+		feep();
+	    if (nomatch) {
+		statusline = ibuf;
+		skip_pos = 1;
+	    }
+	    s = zle_get_event(hl);
+	    if (nomatch || !sbptr || (sbptr == 1 && sbuf[0] == '^')) {
+		int i = cs;
+		zle_goto_hist(hl);
+		cs = i;
+	    }
+	    memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
+	    continue;
+	} else if(cmd == Th(z_acceptandhold)) {
+	    acceptandhold();
+	    break;
+	} else if(cmd == Th(z_acceptandinfernexthistory)) {
+	    acceptandinfernexthistory();
+	    break;
+	} else if(cmd == Th(z_acceptlineanddownhistory)) {
+	    acceptlineanddownhistory();
+	    break;
+	} else if(cmd == Th(z_acceptline)) {
+	    acceptline();
+	    break;
+	} else if(cmd == Th(z_historyincrementalsearchbackward)) {
+	    set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
+	    if (dir != -1)
+		dir = -1;
+	    else
+		skip_pos = 1;
+	    goto rpt;
+	} else if(cmd == Th(z_historyincrementalsearchforward)) {
+	    set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
+	    if (dir != 1)
+		dir = 1;
+	    else
+		skip_pos = 1;
+	    goto rpt;
+	} else if(cmd == Th(z_virevrepeatsearch)) {
+	    set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
+	    dir = -odir;
+	    skip_pos = 1;
+	    goto rpt;
+	} else if(cmd == Th(z_virepeatsearch)) {
+	    set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
+	    dir = odir;
+	    skip_pos = 1;
+	rpt:
+	    if (!sbptr && previous_search_len) {
+		if (previous_search_len > sibuf - FIRST_SEARCH_CHAR - 2) {
+		    ibuf = hrealloc(ibuf, sibuf, sibuf + previous_search_len);
+		    sbuf = ibuf + FIRST_SEARCH_CHAR;
+		    sibuf += previous_search_len;
+		}
+		memcpy(sbuf, previous_search, sbptr = previous_search_len);
+	    }
+	    memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
+	    continue;
+	} else if(cmd == Th(z_viquotedinsert) ||
+	    	cmd == Th(z_quotedinsert)) {
+	    if(cmd == Th(z_viquotedinsert)) {
+		sbuf[sbptr] = '^';
+		refresh();
+	    }
+	    if ((c = getkey(0)) == EOF)
+		feep();
+	    else
+		goto ins;
+	} else {
+	    if(cmd == Th(z_selfinsertunmeta)) {
+		c &= 0x7f;
+		if(c == '\r')
+		    c = '\n';
+	    } else if (cmd == Th(z_magicspace))
+		c = ' ';
+	    else if (cmd != Th(z_selfinsert)) {
+		ungetkeycmd();
+		if (cmd == Th(z_sendbreak))
+		    sbptr = 0;
+		break;
+	    }
+	ins:
+	    if (sbptr == PATH_MAX) {
+		feep();
+		continue;
+	    }
+	    set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
+	    if (sbptr == sibuf - FIRST_SEARCH_CHAR - 2) {
+		ibuf = hrealloc(ibuf, sibuf, sibuf * 2);
+		sbuf = ibuf + FIRST_SEARCH_CHAR;
+		sibuf *= 2;
+	    }
+	    sbuf[sbptr++] = c;
+	}
+	handlefeep();
+    }
+    if (sbptr) {
+	zfree(previous_search, previous_search_len);
+	previous_search = zalloc(sbptr);
+	memcpy(previous_search, sbuf, previous_search_len = sbptr);
+    }
+    statusline = NULL;
+    selectkeymap(okeymap, 1);
+}
+
+/**/
+void
+acceptandinfernexthistory(void)
+{
+    int t0;
+    char *s;
+
+    done = 1;
+    for (t0 = histline - 2;; t0--) {
+	if (!(s = qgetevent(t0)))
+	    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;
+
+    for (t0 = histline - 2;; t0--) {
+	if (!(s = qgetevent(t0))) {
+	    feep();
+	    return;
+	}
+	if (! metadiffer(s, (char *) line, ll))
+	    break;
+    }
+    if (!(s = qgetevent(t0 + 1))) {
+	feep();
+	return;
+    }
+    zle_goto_hist(t0 + 1);
+}
+
+/**/
+void
+vifetchhistory(void)
+{
+    if (zmult < 0)
+	return;
+    if (histline == curhist) {
+	if (!(zmod.flags & MOD_MULT)) {
+	    cs = ll;
+	    cs = findbol();
+	    return;
+	}
+    }
+    if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist) &&
+	isset(HISTBEEP))
+	feep();
+}
+
+/* the last vi search */
+
+static char *visrchstr;
+static int visrchsense;
+
+/**/
+static int
+getvisrchstr(void)
+{
+    char *sbuf = halloc(80);
+    int sptr = 1, ret = 0, ssbuf = 80;
+    Thingy cmd;
+    char *okeymap = curkeymapname;
+
+    if (visrchstr) {
+	zsfree(visrchstr);
+	visrchstr = NULL;
+    }
+    statusline = sbuf;
+    sbuf[0] = (visrchsense == -1) ? '?' : '/';
+    selectkeymap("main", 1);
+    while (sptr) {
+	sbuf[sptr] = '_';
+	statusll = sptr + 1;
+	refresh();
+	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
+	    ret = 0;
+	    break;
+	}
+	if(cmd == Th(z_magicspace)) {
+	    c = ' ';
+	    cmd = Th(z_selfinsert);
+	}
+	if(cmd == Th(z_redisplay)) {
+	    redisplay();
+	} else if(cmd == Th(z_clearscreen)) {
+	    clearscreen();
+	} else if(cmd == Th(z_acceptline) ||
+	    	cmd == Th(z_vicmdmode)) {
+	    sbuf[sptr] = 0;
+	    visrchstr = metafy(sbuf + 1, sptr - 1, META_DUP);
+	    ret = 1;
+	    sptr = 0;
+	} else if(cmd == Th(z_backwarddeletechar) ||
+	    	cmd == Th(z_vibackwarddeletechar)) {
+	    sptr--;
+	} else if(cmd == Th(z_backwardkillword) ||
+	    	cmd == Th(z_vibackwardkillword)) {
+	    while(sptr != 1 && iblank(sbuf[sptr - 1]))
+		sptr--;
+	    if(iident(sbuf[sptr - 1]))
+		while(sptr != 1 && iident(sbuf[sptr - 1]))
+		    sptr--;
+	    else
+		while(sptr != 1 && !iident(sbuf[sptr - 1]) && !iblank(sbuf[sptr - 1]))
+		    sptr--;
+	} else if(cmd == Th(z_viquotedinsert) || cmd == Th(z_quotedinsert)) {
+	    if(cmd == Th(z_viquotedinsert)) {
+		sbuf[sptr] = '^';
+		refresh();
+	    }
+	    if ((c = getkey(0)) == EOF)
+		feep();
+	    else
+		goto ins;
+	} else if(cmd == Th(z_selfinsertunmeta) || cmd == Th(z_selfinsert)) {
+	    if(cmd == Th(z_selfinsertunmeta)) {
+		c &= 0x7f;
+		if(c == '\r')
+		    c = '\n';
+	    }
+	  ins:
+	    if(sptr == ssbuf - 1) {
+		char *newbuf = halloc(ssbuf *= 2);
+		strcpy(newbuf, sbuf);
+		statusline = sbuf = newbuf;
+	    }
+	    sbuf[sptr++] = c;
+	} else {
+	    feep();
+	}
+	handlefeep();
+    }
+    statusline = NULL;
+    selectkeymap(okeymap, 1);
+    return ret;
+}
+
+/**/
+void
+vihistorysearchforward(void)
+{
+    visrchsense = 1;
+    if (getvisrchstr())
+	virepeatsearch();
+}
+
+/**/
+void
+vihistorysearchbackward(void)
+{
+    visrchsense = -1;
+    if (getvisrchstr())
+	virepeatsearch();
+}
+
+/**/
+void
+virepeatsearch(void)
+{
+    int hl = histline, t0;
+    int n = zmult;
+    char *s;
+
+    if (!visrchstr) {
+	feep();
+	return;
+    }
+    if (!n)
+	return;
+    if (n < 0) {
+	n = -n;
+	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))
+	    continue;
+	if (--n <= 0)
+	    break;
+    }
+    zle_goto_hist(hl);
+}
+
+/**/
+void
+virevrepeatsearch(void)
+{
+    visrchsense = -visrchsense;
+    virepeatsearch();
+    visrchsense = -visrchsense;
+}
+
+/* Extra function added by A.R. Iano-Fletcher.	*/
+/*The extern variable "cs" is the position of the cursor. */
+/* history-beginning-search-backward */
+
+/**/
+void
+historybeginningsearchbackward(void)
+{
+    int cpos = cs;		/* save cursor position */
+    int hl = histline;
+    int n = zmult;
+    char *s;
+
+    if (!n)
+	return;
+    if (n < 0) {
+	zmult = -n;
+	historybeginningsearchforward();
+	zmult = n;
+	return;
+    }
+    for (;;) {
+	hl--;
+	if (!(s = zle_get_event(hl))) {
+	    feep();
+	    return;
+	}
+	if (metadiffer(s, (char *)line, cs) < 0 &&
+	    metadiffer(s, (char *)line, ll))
+	    if (--n <= 0)
+		break;
+    }
+
+    zle_goto_hist(hl);
+    cs = cpos;
+}
+
+/* Extra function added by A.R. Iano-Fletcher.	*/
+
+/* history-beginning-search-forward */
+/**/
+void
+historybeginningsearchforward(void)
+{
+    int cpos = cs;		/* save cursor position */
+    int hl = histline;
+    int n = zmult;
+    char *s;
+
+    if (!n)
+	return;
+    if (n < 0) {
+	zmult = -n;
+	historybeginningsearchbackward();
+	zmult = n;
+	return;
+    }
+    for (;;) {
+	hl++;
+	if (!(s = zle_get_event(hl))) {
+	    feep();
+	    return;
+	}
+	if (metadiffer(s, (char *)line, cs) < (hl == curhist) &&
+	    metadiffer(s, (char *)line, ll))
+	    if (--n <= 0)
+		break;
+    }
+
+    zle_goto_hist(hl);
+    cs = cpos;
+}