about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Src/Zle/zle_hist.c300
1 files changed, 212 insertions, 88 deletions
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index 7fad3845b..dfcb82499 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -41,6 +41,14 @@ int lastcol;
 /**/
 int histline;
 
+/* Previous search string use in an incremental search */
+
+/**/
+char *previous_search = NULL;
+
+/**/
+int previous_search_len = 0;
+
 #define ZLETEXT(X) ((X)->zle_text ? (X)->zle_text : (X)->text)
 
 /**/
@@ -48,7 +56,7 @@ void
 remember_edits(void)
 {
     Histent ent = quietgethist(histline);
-    if (metadiffer(ZLETEXT(ent), (char *) line, ll)) {
+    if (ent && metadiffer(ZLETEXT(ent), (char *) line, ll)) {
 	zsfree(ent->zle_text);
 	ent->zle_text = metafy((char *) line, ll, META_DUP);
     }
@@ -68,7 +76,7 @@ forget_edits(void)
 
 /**/
 int
-uphistory(char **args)
+uphistory(UNUSED(char **args))
 {
     int nodups = isset(HISTIGNOREDUPS);
     if (!zle_goto_hist(histline, -zmult, nodups) && isset(HISTBEEP))
@@ -84,7 +92,7 @@ upline(void)
 
     if (n < 0) {
 	zmult = -zmult;
-	n = downline();
+	n = -downline();
 	zmult = -zmult;
 	return n;
     }
@@ -168,7 +176,7 @@ downline(void)
 
     if (n < 0) {
 	zmult = -zmult;
-	n = upline();
+	n = -upline();
 	zmult = -zmult;
 	return n;
     }
@@ -246,21 +254,21 @@ downlineorsearch(char **args)
 
 /**/
 int
-acceptlineanddownhistory(char **args)
+acceptlineanddownhistory(UNUSED(char **args))
 {
-    Histent he;
+    Histent he = quietgethist(histline);
 
-    if (!(he = movehistent(quietgethist(histline), 1, HIST_FOREIGN)))
-	return 1;
-    zpushnode(bufstack, ztrdup(ZLETEXT(he)));
+    if (he && (he = movehistent(he, 1, HIST_FOREIGN))) {
+	zpushnode(bufstack, ztrdup(he->text));
+	stackhist = he->histnum;
+    }
     done = 1;
-    stackhist = he->histnum;
     return 0;
 }
 
 /**/
 int
-downhistory(char **args)
+downhistory(UNUSED(char **args))
 {
     int nodups = isset(HISTIGNOREDUPS);
     if (!zle_goto_hist(histline, zmult, nodups) && isset(HISTBEEP))
@@ -301,7 +309,8 @@ historysearchbackward(char **args)
 	str = srch_str;
 	hp = histpos;
     }
-    he = quietgethist(histline);
+    if (!(he = quietgethist(histline)))
+	return 1;
     while ((he = movehistent(he, -1, hist_skip_flags))) {
 	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
 	    continue;
@@ -349,7 +358,8 @@ historysearchforward(char **args)
 	str = srch_str;
 	hp = histpos;
     }
-    he = quietgethist(histline);
+    if (!(he = quietgethist(histline)))
+	return 1;
     while ((he = movehistent(he, 1, hist_skip_flags))) {
 	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
 	    continue;
@@ -380,7 +390,7 @@ beginningofbufferorhistory(char **args)
 
 /**/
 int
-beginningofhistory(char **args)
+beginningofhistory(UNUSED(char **args))
 {
     if (!zle_goto_hist(firsthist(), 0, 0) && isset(HISTBEEP))
 	return 1;
@@ -400,7 +410,7 @@ endofbufferorhistory(char **args)
 
 /**/
 int
-endofhistory(char **args)
+endofhistory(UNUSED(char **args))
 {
     zle_goto_hist(curhist, 0, 0);
     return 0;
@@ -410,46 +420,138 @@ endofhistory(char **args)
 int
 insertlastword(char **args)
 {
-    int n;
+    int n, nwords, histstep = -1, wordpos = 0, deleteword = 0;
     char *s, *t;
-    Histent he;
+    Histent he = NULL;
+    LinkList l = NULL;
+    LinkNode node;
 
-/* multiple calls will now search back through the history, pem */
     static char *lastinsert;
-    static int lasthist, lastpos;
-    int evhist = addhistnum(curhist, -1, HIST_FOREIGN), save;
+    static int lasthist, lastpos, lastlen;
+    int evhist, save;
 
-    if (lastinsert) {
-	int lastlen = ztrlen(lastinsert);
-	int pos = cs;
+    /*
+     * If we have at least one argument, the first is the history
+     * step.  The default is -1 (go back).  Repeated calls take
+     * a step in this direction.  A value of 0 is allowed and doesn't
+     * move the line.
+     *
+     * If we have two arguments, the second is the position of
+     * the word to extract, 1..N.  The default is to use the
+     * numeric argument, or the last word if that is not set.
+     *
+     * If we have three arguments, we reset the history pointer to
+     * the current history event before applying the history step.
+     */
+    if (*args)
+    {
+	histstep = (int)zstrtol(*args, NULL, 10);
+	if (*++args)
+	{
+	    wordpos = (int)zstrtol(*args, NULL, 10);
+	    if (*++args)
+		lasthist = curhist;
+	}
+    }
 
-	if (lastpos <= pos &&
-	    lastlen == pos - lastpos &&
-	    memcmp(lastinsert, (char *)&line[lastpos], lastlen) == 0) {
-	    evhist = addhistnum(lasthist, -1, HIST_FOREIGN);
+    if (lastinsert && lastlen &&
+	lastpos <= cs &&
+	lastlen == cs - lastpos &&
+	memcmp(lastinsert, (char *)&line[lastpos], lastlen) == 0)
+	deleteword = 1;
+    else
+	lasthist = curhist;
+    evhist = histstep ? addhistnum(lasthist, histstep, HIST_FOREIGN) :
+	lasthist;
+
+    if (evhist == curhist) {
+	/*
+	 * The line we are currently editing.  If we are going to
+	 * replace an existing word, delete the old one now to avoid
+	 * confusion.
+	 */
+	if (deleteword) {
+	    int pos = cs;
 	    cs = lastpos;
 	    foredel(pos - cs);
+	    /*
+	     * Mark that this has been deleted.
+	     * For consistency with history lines, we really ought to
+	     * insert it back if the current command later fails. But
+	     * - we can't be bothered
+	     * - the problem that this can screw up going to other
+	     *   lines in the history because we don't update
+	     *   the history line isn't really relevant
+	     * - you can see what you're copying, dammit, so you
+	     *   shouldn't make errors.
+	     * Of course, I could have implemented it in the time
+	     * it took to say why I haven't.
+	     */
+	    deleteword = 0;
 	}
-	zsfree(lastinsert);
-	lastinsert = NULL;
+	/*
+	 * Can only happen fail if the line is empty, I hope.
+	 * In that case, we don't need to worry about restoring
+	 * a deleted word, because that can only have come
+	 * from a non-empty line.  I think.
+	 */
+	if (!(l = bufferwords(NULL, NULL, NULL)))
+	    return 1;
+	nwords = countlinknodes(l);
+    } else {
+	/* Some stored line. */
+	if (!(he = quietgethist(evhist)) || !he->nwords)
+	    return 1;
+	nwords = he->nwords;
     }
-    if (!(he = quietgethist(evhist)) || !he->nwords)
-	return 1;
-    if (zmult > 0) {
-	n = he->nwords - (zmult - 1);
+    if (wordpos) {
+	n = (wordpos > 0) ? wordpos : nwords + wordpos + 1;
+    } else if (zmult > 0) {
+	n = nwords - (zmult - 1);
     } else {
 	n = 1 - zmult;
     }
-    if (n < 1 || n > he->nwords)
+    if (n < 1 || n > nwords) {
+	/*
+	 * We can't put in the requested word, but we did find the
+	 * history entry, so we remember the position in the history
+	 * list.  This avoids getting stuck on a history line with
+	 * fewer words than expected.  The cursor location cs
+	 * has not changed, and lastinsert is still valid.
+	 */
+	lasthist = evhist;
 	return 1;
-    s = he->text + he->words[2*n-2];
-    t = he->text + he->words[2*n-1];
+    }
+    /*
+     * Only remove the old word from the command line if we have
+     * successfully found a new one to insert.
+     */
+    if (deleteword > 0) {
+	int pos = cs;
+	cs = lastpos;
+	foredel(pos - cs);
+    }
+    if (lastinsert) {
+	zfree(lastinsert, lastlen);
+	lastinsert = NULL;
+    }
+    if (l) {
+	for (node = firstnode(l); --n; incnode(node))
+	    ;
+	s = (char *)getdata(node);
+	t = s + strlen(s);
+    } else {
+	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);
+    lastlen = t - s;
+    lastinsert = zalloc(t - s);
+    memcpy(lastinsert, s, lastlen);
     n = zmult;
     zmult = 1;
     doinsert(s);
@@ -472,7 +574,7 @@ zle_setline(Histent he)
 
 /**/
 int
-setlocalhistory(char **args)
+setlocalhistory(UNUSED(char **args))
 {
     if (zmod.flags & MOD_MULT) {
 	hist_skip_flags = zmult? HIST_FOREIGN : 0;
@@ -486,7 +588,9 @@ setlocalhistory(char **args)
 int
 zle_goto_hist(int ev, int n, int skipdups)
 {
-    Histent he = movehistent(quietgethist(ev), n, hist_skip_flags);
+    Histent he = quietgethist(ev);
+    if (!he || !(he = movehistent(he, n, hist_skip_flags)))
+	return 1;
     if (skipdups && n) {
 	n = n < 0? -1 : 1;
 	while (he && !metadiffer(ZLETEXT(he), (char *) line, ll))
@@ -500,7 +604,7 @@ zle_goto_hist(int ev, int n, int skipdups)
 
 /**/
 int
-pushline(char **args)
+pushline(UNUSED(char **args))
 {
     int n = zmult;
 
@@ -556,9 +660,10 @@ pushinput(char **args)
     return ret;
 }
 
+/* Renamed to avoid clash with library function */
 /**/
 int
-getline(char **args)
+zgetline(UNUSED(char **args))
 {
     char *s = (char *)getlinknode(bufstack);
 
@@ -610,6 +715,8 @@ void
 free_isrch_spots(void)
 {
     zfree(isrch_spots, max_spot * sizeof(*isrch_spots));
+    max_spot = 0;
+    isrch_spots = NULL;
 }
 
 /**/
@@ -660,11 +767,12 @@ doisearch(char **args, int dir)
     int odir = dir, sens = zmult == 1 ? 3 : 1;
     int hl = histline, savekeys = -1, feep = 0;
     Thingy cmd;
-    char *okeymap = curkeymapname;
-    static char *previous_search = NULL;
-    static int previous_search_len = 0;
+    char *okeymap;
     Histent he;
 
+    if (!(he = quietgethist(hl)))
+	return;
+
     clearlist = 1;
 
     if (*args) {
@@ -678,7 +786,7 @@ doisearch(char **args, int dir)
     strcpy(ibuf, ISEARCH_PROMPT);
     memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
     remember_edits();
-    he = quietgethist(hl);
+    okeymap = ztrdup(curkeymapname);
     s = ZLETEXT(he);
     selectkeymap("main", 1);
     pos = metalen(s, cs);
@@ -702,7 +810,7 @@ doisearch(char **args, int dir)
 			else
 			    pos -= 1 + (pos != 1 && s[pos-2] == Meta);
 		    } else if (sbuf[0] != '^') {
-			if (pos >= strlen(s+1))
+			if (pos >= (int)strlen(s+1))
 			    skip_line = 1;
 			else
 			    pos += 1 + (s[pos] == Meta);
@@ -720,7 +828,8 @@ doisearch(char **args, int dir)
 		    statusline = ibuf + NORM_PROMPT_POS;
 		    break;
 		}
-		if (!(he = movehistent(he, dir, hist_skip_flags))) {
+		if (!(zlereadflags & ZLRF_HISTORY)
+		 || !(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--;
@@ -842,17 +951,17 @@ doisearch(char **args, int dir)
 		sbuf[sbptr] = '^';
 		zrefresh();
 	    }
-	    if ((c = getkey(0)) == EOF)
+	    if ((lastchar = getkey(0)) == EOF)
 		feep = 1;
 	    else
 		goto ins;
 	} else {
 	    if(cmd == Th(z_selfinsertunmeta)) {
-		c &= 0x7f;
-		if(c == '\r')
-		    c = '\n';
+		lastchar &= 0x7f;
+		if(lastchar == '\r')
+		    lastchar = '\n';
 	    } else if (cmd == Th(z_magicspace))
-		c = ' ';
+		lastchar = ' ';
 	    else if (cmd != Th(z_selfinsert)) {
 		ungetkeycmd();
 		if (cmd == Th(z_sendbreak))
@@ -870,7 +979,7 @@ doisearch(char **args, int dir)
 		sbuf = ibuf + FIRST_SEARCH_CHAR;
 		sibuf *= 2;
 	    }
-	    sbuf[sbptr++] = c;
+	    sbuf[sbptr++] = lastchar;
 	}
 	if (feep)
 	    handlefeep(zlenoargs);
@@ -883,6 +992,7 @@ doisearch(char **args, int dir)
     }
     statusline = NULL;
     selectkeymap(okeymap, 1);
+    zsfree(okeymap);
     /*
      * Don't allow unused characters provided as a string to the
      * widget to overflow and be used as separated commands.
@@ -891,45 +1001,46 @@ doisearch(char **args, int dir)
 	kungetct = savekeys;
 }
 
+static Histent
+infernexthist(Histent he, UNUSED(char **args))
+{
+    for (he = movehistent(he, -2, HIST_FOREIGN);
+	 he; he = movehistent(he, -1, HIST_FOREIGN)) {
+	if (!metadiffer(he->text, (char *) line, ll))
+	    return movehistent(he, 1, HIST_FOREIGN);
+    }
+    return NULL;
+}
+
 /**/
 int
 acceptandinfernexthistory(char **args)
 {
     Histent he;
 
+    if (!(he = infernexthist(hist_ring, args)))
+	return 1;
+    zpushnode(bufstack, ztrdup(he->text));
     done = 1;
-    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);
-	    zpushnode(bufstack, ztrdup(ZLETEXT(he)));
-	    stackhist = he->histnum;
-	    return 0;
-	}
-    }
-    return 1;
+    stackhist = he->histnum;
+    return 0;
 }
 
 /**/
 int
 infernexthistory(char **args)
 {
-    Histent he;
+    Histent he = quietgethist(histline);
 
-    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 0;
-	}
-    }
-    return 1;
+    if (!he || !(he = infernexthist(he, args)))
+	return 1;
+    zle_setline(he);
+    return 0;
 }
 
 /**/
 int
-vifetchhistory(char **args)
+vifetchhistory(UNUSED(char **args))
 {
     if (zmult < 0)
 	return 1;
@@ -949,7 +1060,7 @@ vifetchhistory(char **args)
 
 /* the last vi search */
 
-static char *visrchstr;
+static char *visrchstr, *vipenultsrchstr;
 static int visrchsense;
 
 /**/
@@ -959,10 +1070,15 @@ getvisrchstr(void)
     char *sbuf = zhalloc(80);
     int sptr = 1, ret = 0, ssbuf = 80, feep = 0;
     Thingy cmd;
-    char *okeymap = curkeymapname;
+    char *okeymap = ztrdup(curkeymapname);
+
+    if (vipenultsrchstr) {
+	zsfree(vipenultsrchstr);
+	vipenultsrchstr = NULL;
+    }
 
     if (visrchstr) {
-	zsfree(visrchstr);
+	vipenultsrchstr = visrchstr;
 	visrchstr = NULL;
     }
     clearlist = 1;
@@ -978,7 +1094,7 @@ getvisrchstr(void)
 	    break;
 	}
 	if(cmd == Th(z_magicspace)) {
-	    c = ' ';
+	    lastchar = ' ';
 	    cmd = Th(z_selfinsert);
 	}
 	if(cmd == Th(z_redisplay)) {
@@ -989,6 +1105,10 @@ getvisrchstr(void)
 	    	cmd == Th(z_vicmdmode)) {
 	    sbuf[sptr] = 0;
 	    visrchstr = metafy(sbuf + 1, sptr - 1, META_DUP);
+	    if (!strlen(visrchstr)) {
+	        zsfree(visrchstr);
+		visrchstr = ztrdup(vipenultsrchstr);
+	    }
 	    ret = 1;
 	    sptr = 0;
 	} else if(cmd == Th(z_backwarddeletechar) ||
@@ -1009,15 +1129,15 @@ getvisrchstr(void)
 		sbuf[sptr] = '^';
 		zrefresh();
 	    }
-	    if ((c = getkey(0)) == EOF)
+	    if ((lastchar = getkey(0)) == EOF)
 		feep = 1;
 	    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';
+		lastchar &= 0x7f;
+		if(lastchar == '\r')
+		    lastchar = '\n';
 	    }
 	  ins:
 	    if(sptr == ssbuf - 1) {
@@ -1025,7 +1145,7 @@ getvisrchstr(void)
 		strcpy(newbuf, sbuf);
 		statusline = sbuf = newbuf;
 	    }
-	    sbuf[sptr++] = c;
+	    sbuf[sptr++] = lastchar;
 	} else {
 	    feep = 1;
 	}
@@ -1035,6 +1155,7 @@ getvisrchstr(void)
     }
     statusline = NULL;
     selectkeymap(okeymap, 1);
+    zsfree(okeymap);
     return ret;
 }
 
@@ -1082,7 +1203,7 @@ vihistorysearchbackward(char **args)
 
 /**/
 int
-virepeatsearch(char **args)
+virepeatsearch(UNUSED(char **args))
 {
     Histent he;
     int t0;
@@ -1096,7 +1217,8 @@ virepeatsearch(char **args)
 	visrchsense = -visrchsense;
     }
     t0 = strlen(visrchstr);
-    he = quietgethist(histline);
+    if (!(he = quietgethist(histline)))
+	return 1;
     while ((he = movehistent(he, visrchsense, hist_skip_flags))) {
 	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
 	    continue;
@@ -1144,7 +1266,8 @@ historybeginningsearchbackward(char **args)
 	zmult = n;
 	return ret;
     }
-    he = quietgethist(histline);
+    if (!(he = quietgethist(histline)))
+	return 1;
     while ((he = movehistent(he, -1, hist_skip_flags))) {
 	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
 	    continue;
@@ -1180,7 +1303,8 @@ historybeginningsearchforward(char **args)
 	zmult = n;
 	return ret;
     }
-    he = quietgethist(histline);
+    if (!(he = quietgethist(histline)))
+	return 1;
     while ((he = movehistent(he, 1, hist_skip_flags))) {
 	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
 	    continue;