1/*
2 * zle_hist.c - history editing
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1992-1997 Paul Falstad
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Paul Falstad or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Paul Falstad and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Paul Falstad and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose.  The software
24 * provided hereunder is on an "as is" basis, and Paul Falstad and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "zle.mdh"
31#include "zle_hist.pro"
32
33/* Column position of vi ideal cursor.  -1 if it is unknown -- most *
34 * movements and changes do this.                                   */
35
36/**/
37int lastcol;
38
39/* current history line number */
40
41/**/
42int histline;
43
44/* Previous search string use in an incremental search */
45
46/**/
47char *previous_search = NULL;
48
49/**/
50int previous_search_len;
51
52/* Previous aborted search string use in an incremental search */
53
54/**/
55char *previous_aborted_search = NULL;
56
57/* Local keymap in isearch mode */
58
59/**/
60Keymap isearch_keymap;
61
62/*** History text manipulation utilities ***/
63
64/*
65 * Text for the line:  anything previously modified within zle since
66 * the last time the line editor was started, else what was originally
67 * put in the history.
68 */
69#define GETZLETEXT(ent)	((ent)->zle_text ? (ent)->zle_text : (ent)->node.nam)
70
71/**/
72void
73remember_edits(void)
74{
75    Histent ent = quietgethist(histline);
76    if (ent) {
77	char *line =
78	    zlemetaline ? zlemetaline :
79	    zlelineasstring(zleline, zlell, 0, NULL, NULL, 0);
80	if (!ent->zle_text || strcmp(line, ent->zle_text) != 0) {
81	    if (ent->zle_text)
82		free(ent->zle_text);
83	    ent->zle_text = zlemetaline ? ztrdup(line) : line;
84	} else if (!zlemetaline)
85	    free(line);
86    }
87}
88
89/**/
90void
91forget_edits(void)
92{
93    Histent he;
94
95    for (he = hist_ring; he; he = up_histent(he)) {
96	if (he->zle_text) {
97	    free(he->zle_text);
98	    he->zle_text = NULL;
99	}
100    }
101}
102
103
104/*** Search utilities ***/
105
106
107/*
108 * Return zero if the ZLE string histp length histl and the ZLE string
109 * inputp length inputl are the same.  Return -1 if inputp is a prefix
110 * of histp.  Return 1 if inputp is the lowercase version of histp.
111 * Return 2 if inputp is the lowercase prefix of histp and return 3
112 * otherwise.
113 */
114
115static int
116zlinecmp(const char *histp, const char *inputp)
117{
118    const char *hptr = histp, *iptr = inputp;
119#ifdef MULTIBYTE_SUPPORT
120    mbstate_t hstate, istate;
121#endif
122
123    while (*iptr && *hptr == *iptr) {
124	hptr++;
125	iptr++;
126    }
127
128    if (!*iptr) {
129	if (!*hptr) {
130	    /* strings are the same */
131	    return 0;
132	} else {
133	    /* inputp is a prefix */
134	    return -1;
135	}
136    }
137
138#ifdef MULTIBYTE_SUPPORT
139    memset(&hstate, 0, sizeof(hstate));
140    memset(&istate, 0, sizeof(istate));
141#endif
142
143    /* look for lower case versions */
144    while (*histp && *inputp) {
145#ifdef MULTIBYTE_SUPPORT
146	wint_t hwc, iwc;
147	int hlen, ilen;
148
149	hlen = mb_metacharlenconv_r(histp, &hwc, &hstate);
150	ilen = mb_metacharlenconv_r(inputp, &iwc, &istate);
151
152	if (hwc == WEOF || iwc == WEOF) {
153	    /* can't convert, compare input characters */
154	    if (ilen != hlen || memcmp(histp, inputp, hlen) != 0)
155		return 3;
156	} else if (towlower(hwc) != iwc)
157	    return 3;
158
159	histp += hlen;
160	inputp += ilen;
161#else
162    	if (tulower(*histp++) != *inputp++)
163	    return 3;
164#endif
165    }
166    if (!*inputp) {
167	/* one string finished, if it's the input... */
168	if (!*histp)
169	    return 1;		/* ...same, else */
170	else
171	    return 2;		/* ...prefix */
172    }
173    /* Different */
174    return 3;
175}
176
177
178/*
179 * Search for needle in haystack.  Haystack and needle are metafied strings.
180 * Start the search at position pos in haystack.
181 * Search forward if dir > 0, otherwise search backward.
182 * sens is used to test against the return value of linecmp.
183 *
184 * Return the pointer to the location in haystack found, else NULL.
185 *
186 * We assume we'll only find needle at some sensible position in a multibyte
187 * string, so we don't bother calculating multibyte character lengths for
188 * incrementing and decrementing the search position.
189 */
190
191static char *
192zlinefind(char *haystack, int pos, char *needle, int dir, int sens)
193{
194    char *s = haystack + pos;
195
196    if (dir > 0) {
197	while (*s) {
198	    if (zlinecmp(s, needle) < sens)
199		return s;
200	    s++;
201	}
202    } else {
203	for (;;) {
204	    if (zlinecmp(s, needle) < sens)
205		return s;
206	    if (s == haystack)
207		break;
208	    s--;
209	}
210    }
211
212    return NULL;
213}
214
215
216/*** Widgets ***/
217
218
219/**/
220int
221uphistory(UNUSED(char **args))
222{
223    int nodups = isset(HISTIGNOREDUPS);
224    if (!zle_goto_hist(histline, -zmult, nodups) && isset(HISTBEEP))
225	return 1;
226    return 0;
227}
228
229/**/
230static int
231upline(void)
232{
233    int n = zmult;
234
235    if (n < 0) {
236	zmult = -zmult;
237	n = -downline();
238	zmult = -zmult;
239	return n;
240    }
241    if (lastcol == -1)
242	lastcol = zlecs - findbol();
243    zlecs = findbol();
244    while (n) {
245	if (!zlecs)
246	    break;
247	zlecs--;
248	zlecs = findbol();
249	n--;
250    }
251    if (!n) {
252	int x = findeol();
253
254	if ((zlecs += lastcol) >= x) {
255	    zlecs = x;
256	    if (zlecs > findbol() && invicmdmode())
257		DECCS();
258	}
259#ifdef MULTIBYTE_SUPPORT
260	else
261	    CCRIGHT();
262#endif
263
264    }
265    return n;
266}
267
268/**/
269int
270uplineorhistory(char **args)
271{
272    int ocs = zlecs;
273    int n = upline();
274    if (n) {
275	int m = zmult, ret;
276
277	zlecs = ocs;
278	if (virangeflag || !(zlereadflags & ZLRF_HISTORY))
279	    return 1;
280	zmult = n;
281	ret = uphistory(args);
282	zmult = m;
283	return ret;
284    }
285    return 0;
286}
287
288/**/
289int
290viuplineorhistory(char **args)
291{
292    int col = lastcol;
293    uplineorhistory(args);
294    lastcol = col;
295    return vifirstnonblank(args);
296}
297
298/**/
299int
300uplineorsearch(char **args)
301{
302    int ocs = zlecs;
303    int n = upline();
304    if (n) {
305	int m = zmult, ret;
306
307	zlecs = ocs;
308	if (virangeflag || !(zlereadflags & ZLRF_HISTORY))
309	    return 1;
310	zmult = n;
311	ret = historysearchbackward(args);
312	zmult = m;
313	return ret;
314    }
315    return 0;
316}
317
318/**/
319static int
320downline(void)
321{
322    int n = zmult;
323
324    if (n < 0) {
325	zmult = -zmult;
326	n = -upline();
327	zmult = -zmult;
328	return n;
329    }
330    if (lastcol == -1)
331	lastcol = zlecs - findbol();
332    while (n) {
333	int x = findeol();
334
335	if (x == zlell)
336	    break;
337	zlecs = x + 1;
338	n--;
339    }
340    if (!n) {
341	int x = findeol();
342
343	if ((zlecs += lastcol) >= x) {
344	    zlecs = x;
345	    if (zlecs > findbol() && invicmdmode())
346		DECCS();
347	}
348#ifdef MULTIBYTE_SUPPORT
349	else
350	    CCRIGHT();
351#endif
352    }
353    return n;
354}
355
356/**/
357int
358downlineorhistory(char **args)
359{
360    int ocs = zlecs;
361    int n = downline();
362    if (n) {
363	int m = zmult, ret;
364
365	zlecs = ocs;
366	if (virangeflag || !(zlereadflags & ZLRF_HISTORY))
367	    return 1;
368	zmult = n;
369	ret = downhistory(args);
370	zmult = m;
371	return ret;
372    }
373    return 0;
374}
375
376/**/
377int
378vidownlineorhistory(char **args)
379{
380    int col = lastcol;
381    downlineorhistory(args);
382    lastcol = col;
383    return vifirstnonblank(zlenoargs);
384}
385
386/**/
387int
388downlineorsearch(char **args)
389{
390    int ocs = zlecs;
391    int n = downline();
392    if (n) {
393	int m = zmult, ret;
394
395	zlecs = ocs;
396	if (virangeflag || !(zlereadflags & ZLRF_HISTORY))
397	    return 1;
398	zmult = n;
399	ret = historysearchforward(args);
400	zmult = m;
401	return ret;
402    }
403    return 0;
404}
405
406/**/
407int
408acceptlineanddownhistory(UNUSED(char **args))
409{
410    Histent he = quietgethist(histline);
411
412    if (he && (he = movehistent(he, 1, HIST_FOREIGN))) {
413	zpushnode(bufstack, ztrdup(he->node.nam));
414	stackhist = he->histnum;
415    }
416    done = 1;
417    return 0;
418}
419
420/**/
421int
422downhistory(UNUSED(char **args))
423{
424    int nodups = isset(HISTIGNOREDUPS);
425    if (!zle_goto_hist(histline, zmult, nodups) && isset(HISTBEEP))
426	return 1;
427    return 0;
428}
429
430/*
431 * Values remembered for history searches to enable repetition.
432 * srch_hl remembers the old value of histline, to see if it's changed
433 *   since the last search.
434 * srch_cs remembers the old value of zlecs for the same purpose (it is
435 *   not use for any other purpose, i.e. does not need to be a valid
436 *   index into anything).
437 * srch_str is the metafied search string, as extracted from the start
438 *   of zleline.
439 */
440static int histpos, srch_hl, srch_cs = -1;
441static char *srch_str;
442
443/**/
444int
445historysearchbackward(char **args)
446{
447    Histent he;
448    int n = zmult;
449    char *str;
450    char *zt;
451
452    if (zmult < 0) {
453	int ret;
454	zmult = -n;
455	ret = historysearchforward(args);
456	zmult = n;
457	return ret;
458    }
459    if (*args) {
460	str = *args;
461    } else {
462	char *line = zlelineasstring(zleline, zlell, 0, NULL, NULL, 0);
463	if (histline == curhist || histline != srch_hl || zlecs != srch_cs ||
464	    mark != 0 || strncmp(srch_str, line, histpos) != 0) {
465	    free(srch_str);
466	    for (histpos = 0; histpos < zlell && !ZC_iblank(zleline[histpos]);
467		 histpos++)
468		;
469	    if (histpos < zlell)
470		histpos++;
471	    /* ensure we're not on a combining character */
472	    CCRIGHTPOS(histpos);
473	    /* histpos from now on is an index into the metafied string */
474	    srch_str = zlelineasstring(zleline, histpos, 0, NULL, NULL, 0);
475	}
476	free(line);
477	str = srch_str;
478    }
479    if (!(he = quietgethist(histline)))
480	return 1;
481
482    metafy_line();
483    while ((he = movehistent(he, -1, hist_skip_flags))) {
484	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
485	    continue;
486	zt = GETZLETEXT(he);
487	if (zlinecmp(zt, str) < 0 &&
488	    (*args || strcmp(zt, zlemetaline) != 0)) {
489	    if (--n <= 0) {
490		unmetafy_line();
491		zle_setline(he);
492		srch_hl = histline;
493		srch_cs = zlecs;
494		return 0;
495	    }
496	}
497    }
498    unmetafy_line();
499    return 1;
500}
501
502/**/
503int
504historysearchforward(char **args)
505{
506    Histent he;
507    int n = zmult;
508    char *str;
509    char *zt;
510
511    if (zmult < 0) {
512	int ret;
513	zmult = -n;
514	ret = historysearchbackward(args);
515	zmult = n;
516	return ret;
517    }
518    if (*args) {
519	str = *args;
520    } else {
521	char *line = zlelineasstring(zleline, zlell, 0, NULL, NULL, 0);
522	if (histline == curhist || histline != srch_hl || zlecs != srch_cs ||
523	    mark != 0 || strncmp(srch_str, line, histpos) != 0) {
524	    free(srch_str);
525	    for (histpos = 0; histpos < zlell && !ZC_iblank(zleline[histpos]);
526		 histpos++)
527		;
528	    if (histpos < zlell)
529		histpos++;
530	    CCRIGHT();
531	    srch_str = zlelineasstring(zleline, histpos, 0, NULL, NULL, 0);
532	}
533	free(line);
534	str = srch_str;
535    }
536    if (!(he = quietgethist(histline)))
537	return 1;
538
539    metafy_line();
540    while ((he = movehistent(he, 1, hist_skip_flags))) {
541	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
542	    continue;
543	zt = GETZLETEXT(he);
544	if (zlinecmp(zt, str) < (he->histnum == curhist) &&
545	    (*args || strcmp(zt, zlemetaline) != 0)) {
546	    if (--n <= 0) {
547		unmetafy_line();
548		zle_setline(he);
549		srch_hl = histline;
550		srch_cs = zlecs;
551		return 0;
552	    }
553	}
554    }
555    unmetafy_line();
556    return 1;
557}
558
559/**/
560int
561beginningofbufferorhistory(char **args)
562{
563    if (findbol())
564	zlecs = 0;
565    else
566	return beginningofhistory(args);
567    return 0;
568}
569
570/**/
571int
572beginningofhistory(UNUSED(char **args))
573{
574    if (!zle_goto_hist(firsthist(), 0, 0) && isset(HISTBEEP))
575	return 1;
576    return 0;
577}
578
579/**/
580int
581endofbufferorhistory(char **args)
582{
583    if (findeol() != zlell)
584	zlecs = zlell;
585    else
586	return endofhistory(args);
587    return 0;
588}
589
590/**/
591int
592endofhistory(UNUSED(char **args))
593{
594    zle_goto_hist(curhist, 0, 0);
595    return 0;
596}
597
598/**/
599int
600insertlastword(char **args)
601{
602    int n, nwords, histstep = -1, wordpos = 0, deleteword = 0, len;
603    char *s, *t;
604    Histent he = NULL;
605    LinkList l = NULL;
606    LinkNode node;
607    ZLE_STRING_T zs;
608
609    static char *lastinsert;
610    static int lasthist, lastpos, lastlen;
611    int evhist;
612
613    /*
614     * If we have at least one argument, the first is the history
615     * step.  The default is -1 (go back).  Repeated calls take
616     * a step in this direction.  A value of 0 is allowed and doesn't
617     * move the line.
618     *
619     * If we have two arguments, the second is the position of
620     * the word to extract, 1..N.  The default is to use the
621     * numeric argument, or the last word if that is not set.
622     *
623     * If we have three arguments, we reset the history pointer to
624     * the current history event before applying the history step.
625     */
626    if (*args)
627    {
628	histstep = (int)zstrtol(*args, NULL, 10);
629	if (*++args)
630	{
631	    wordpos = (int)zstrtol(*args, NULL, 10);
632	    if (*++args)
633		lasthist = curhist;
634	}
635    }
636
637    fixsuffix();
638    metafy_line();
639    if (lastinsert && lastlen &&
640	lastpos <= zlemetacs &&
641	lastlen == zlemetacs - lastpos &&
642	memcmp(lastinsert, &zlemetaline[lastpos], lastlen) == 0)
643	deleteword = 1;
644    else
645	lasthist = curhist;
646    evhist = histstep ? addhistnum(lasthist, histstep, HIST_FOREIGN) :
647	lasthist;
648
649    if (evhist == curhist) {
650	/*
651	 * The line we are currently editing.  If we are going to
652	 * replace an existing word, delete the old one now to avoid
653	 * confusion.
654	 */
655	if (deleteword) {
656	    int pos = zlemetacs;
657	    zlemetacs = lastpos;
658	    foredel(pos - zlemetacs, CUT_RAW);
659	    /*
660	     * Mark that this has been deleted.
661	     * For consistency with history lines, we really ought to
662	     * insert it back if the current command later fails. But
663	     * - we can't be bothered
664	     * - the problem that this can screw up going to other
665	     *   lines in the history because we don't update
666	     *   the history line isn't really relevant
667	     * - you can see what you're copying, dammit, so you
668	     *   shouldn't make errors.
669	     * Of course, I could have implemented it in the time
670	     * it took to say why I haven't.
671	     */
672	    deleteword = 0;
673	}
674	/*
675	 * Can only happen fail if the line is empty, I hope.
676	 * In that case, we don't need to worry about restoring
677	 * a deleted word, because that can only have come
678	 * from a non-empty line.  I think.
679	 */
680	if (!(l = bufferwords(NULL, NULL, NULL, 0))) {
681	    unmetafy_line();
682	    return 1;
683	}
684	nwords = countlinknodes(l);
685    } else {
686	/* Some stored line. */
687	if (!(he = quietgethist(evhist)) || !he->nwords) {
688	    unmetafy_line();
689	    return 1;
690	}
691	nwords = he->nwords;
692    }
693    if (wordpos) {
694	n = (wordpos > 0) ? wordpos : nwords + wordpos + 1;
695    } else if (zmult > 0) {
696	n = nwords - (zmult - 1);
697    } else {
698	n = 1 - zmult;
699    }
700    if (n < 1 || n > nwords) {
701	/*
702	 * We can't put in the requested word, but we did find the
703	 * history entry, so we remember the position in the history
704	 * list.  This avoids getting stuck on a history line with
705	 * fewer words than expected.  The cursor location cs
706	 * has not changed, and lastinsert is still valid.
707	 */
708	lasthist = evhist;
709	unmetafy_line();
710	return 1;
711    }
712    /*
713     * Only remove the old word from the command line if we have
714     * successfully found a new one to insert.
715     */
716    if (deleteword > 0) {
717	int pos = zlemetacs;
718	zlemetacs = lastpos;
719	foredel(pos - zlemetacs, CUT_RAW);
720    }
721    if (lastinsert) {
722	zfree(lastinsert, lastlen);
723	lastinsert = NULL;
724    }
725    if (l) {
726	for (node = firstnode(l); --n; incnode(node))
727	    ;
728	s = (char *)getdata(node);
729	t = s + strlen(s);
730    } else {
731	s = he->node.nam + he->words[2*n-2];
732	t = he->node.nam + he->words[2*n-1];
733    }
734
735    lasthist = evhist;
736    lastpos = zlemetacs;
737    /* ignore trailing whitespace */
738    lastlen = t - s;
739    lastinsert = zalloc(t - s);
740    memcpy(lastinsert, s, lastlen);
741    n = zmult;
742    zmult = 1;
743
744    unmetafy_line();
745
746    zs = stringaszleline(dupstrpfx(s, t - s), 0, &len, NULL, NULL);
747    doinsert(zs, len);
748    free(zs);
749    zmult = n;
750    return 0;
751}
752
753/**/
754void
755zle_setline(Histent he)
756{
757    int remetafy;
758    if (zlemetaline) {
759	unmetafy_line();
760	remetafy = 1;
761    } else
762	remetafy = 0;
763    remember_edits();
764    mkundoent();
765    histline = he->histnum;
766
767    setline(GETZLETEXT(he), ZSL_COPY|ZSL_TOEND);
768    zlecallhook("zle-history-line-set", NULL);
769    setlastline();
770    clearlist = 1;
771    if (remetafy)
772	metafy_line();
773}
774
775/**/
776int
777setlocalhistory(UNUSED(char **args))
778{
779    if (zmod.flags & MOD_MULT) {
780	hist_skip_flags = zmult? HIST_FOREIGN : 0;
781    } else {
782	hist_skip_flags ^= HIST_FOREIGN;
783    }
784    return 0;
785}
786
787/**/
788int
789zle_goto_hist(int ev, int n, int skipdups)
790{
791    Histent he = quietgethist(ev);
792    char *line = zlelineasstring(zleline, zlell, 0, NULL, NULL, 1);
793
794    if (!he || !(he = movehistent(he, n, hist_skip_flags)))
795	return 1;
796    if (skipdups && n) {
797	n = n < 0? -1 : 1;
798	while (he) {
799	    int ret;
800
801	    ret = zlinecmp(GETZLETEXT(he), line);
802	    if (ret)
803		break;
804	    he = movehistent(he, n, hist_skip_flags);
805	}
806    }
807    if (!he)
808	return 0;
809    zle_setline(he);
810    return 1;
811}
812
813/**/
814int
815pushline(UNUSED(char **args))
816{
817    int n = zmult;
818
819    if (n < 0)
820	return 1;
821    zpushnode(bufstack, zlelineasstring(zleline, zlell, 0, NULL, NULL, 0));
822    while (--n)
823	zpushnode(bufstack, ztrdup(""));
824    stackcs = zlecs;
825    *zleline = ZWC('\0');
826    zlell = zlecs = 0;
827    clearlist = 1;
828    return 0;
829}
830
831/**/
832int
833pushlineoredit(char **args)
834{
835    int ics, ret;
836    ZLE_STRING_T s;
837    char *hline = hgetline();
838
839    if (zmult < 0)
840	return 1;
841    if (hline && *hline) {
842	ZLE_STRING_T zhline = stringaszleline(hline, 0, &ics, NULL, NULL);
843
844	sizeline(ics + zlell + 1);
845	/* careful of overlapping copy */
846	for (s = zleline + zlell; --s >= zleline; s[ics] = *s)
847	    ;
848	ZS_memcpy(zleline, zhline, ics);
849	zlell += ics;
850	zlecs += ics;
851	free(zhline);
852    }
853    ret = pushline(args);
854    if (!isfirstln)
855	errflag = done = 1;
856    clearlist = 1;
857    return ret;
858}
859
860/**/
861int
862pushinput(char **args)
863{
864    int i, ret;
865
866    if (zmult < 0)
867	return 1;
868    zmult += i = !isfirstln;
869    ret = pushlineoredit(args);
870    zmult -= i;
871    return ret;
872}
873
874/* Renamed to avoid clash with library function */
875/**/
876int
877zgetline(UNUSED(char **args))
878{
879    char *s = getlinknode(bufstack);
880
881    if (!s) {
882	return 1;
883    } else {
884	int cc;
885	ZLE_STRING_T lineadd = stringaszleline(s, 0, &cc, NULL, NULL);
886
887	spaceinline(cc);
888	ZS_memcpy(zleline + zlecs, lineadd, cc);
889	zlecs += cc;
890	free(s);
891	free(lineadd);
892	clearlist = 1;
893    }
894    return 0;
895}
896
897/**/
898int
899historyincrementalsearchbackward(char **args)
900{
901    return doisearch(args, -1, 0);
902}
903
904/**/
905int
906historyincrementalsearchforward(char **args)
907{
908    return doisearch(args, 1, 0);
909}
910
911/**/
912int
913historyincrementalpatternsearchbackward(char **args)
914{
915    return doisearch(args, -1, 1);
916}
917
918/**/
919int
920historyincrementalpatternsearchforward(char **args)
921{
922    return doisearch(args, 1, 1);
923}
924
925static struct isrch_spot {
926    int hl;			/* This spot's histline */
927    int pat_hl;			/* histline where pattern search started */
928    unsigned short pos;		/* The search position in our metafied str */
929    unsigned short pat_pos;     /* pos where pattern search started */
930    unsigned short end_pos;	/* The position of the end of the matched str */
931    unsigned short cs;		/* The visible search position to the user */
932    unsigned short len;		/* The search string's length */
933    unsigned short flags;	/* This spot's flags */
934#define ISS_FORWARD	1
935#define ISS_NOMATCH_SHIFT 1
936} *isrch_spots;
937
938static int max_spot = 0;
939
940/**/
941void
942free_isrch_spots(void)
943{
944    zfree(isrch_spots, max_spot * sizeof(*isrch_spots));
945    max_spot = 0;
946    isrch_spots = NULL;
947}
948
949/**/
950static void
951set_isrch_spot(int num, int hl, int pos, int pat_hl, int pat_pos,
952	       int end_pos, int cs, int len, int dir, int nomatch)
953{
954    if (num >= max_spot) {
955	if (!isrch_spots) {
956	    isrch_spots = (struct isrch_spot*)
957			    zalloc((max_spot = 64) * sizeof *isrch_spots);
958	} else {
959	    isrch_spots = (struct isrch_spot*)realloc((char*)isrch_spots,
960			    (max_spot += 64) * sizeof *isrch_spots);
961	}
962    }
963
964    isrch_spots[num].hl = hl;
965    isrch_spots[num].pos = (unsigned short)pos;
966    isrch_spots[num].pat_hl = pat_hl;
967    isrch_spots[num].pat_pos = (unsigned short)pat_pos;
968    isrch_spots[num].end_pos = (unsigned short)end_pos;
969    isrch_spots[num].cs = (unsigned short)cs;
970    isrch_spots[num].len = (unsigned short)len;
971    isrch_spots[num].flags = (dir > 0? ISS_FORWARD : 0)
972			   + (nomatch << ISS_NOMATCH_SHIFT);
973}
974
975/**/
976static void
977get_isrch_spot(int num, int *hlp, int *posp, int *pat_hlp, int *pat_posp,
978	       int *end_posp, int *csp, int *lenp, int *dirp, int *nomatch)
979{
980    *hlp = isrch_spots[num].hl;
981    *posp = (int)isrch_spots[num].pos;
982    *pat_hlp = isrch_spots[num].pat_hl;
983    *pat_posp = (int)isrch_spots[num].pat_pos;
984    *end_posp = (int)isrch_spots[num].end_pos;
985    *csp = (int)isrch_spots[num].cs;
986    *lenp = (int)isrch_spots[num].len;
987    *dirp = (isrch_spots[num].flags & ISS_FORWARD)? 1 : -1;
988    *nomatch = (int)(isrch_spots[num].flags >> ISS_NOMATCH_SHIFT);
989}
990
991/*
992 * In pattern search mode, look through the list for a match at, or
993 * before or after the given position, according to the direction.
994 * Return new position or -1.
995 *
996 * Note this handles curpos out of range correctly, i.e. curpos < 0
997 * never matches when searching backwards and curpos > length of string
998 * never matches when searching forwards.
999 */
1000static int
1001isearch_newpos(LinkList matchlist, int curpos, int dir,
1002	       int *endmatchpos)
1003{
1004    LinkNode node;
1005
1006    if (dir < 0) {
1007	for (node = lastnode(matchlist);
1008	     node != (LinkNode)matchlist; decnode(node)) {
1009	    Repldata rdata = (Repldata)getdata(node);
1010	    if (rdata->b <= curpos) {
1011		*endmatchpos = rdata->e;
1012		return rdata->b;
1013	    }
1014	}
1015    } else {
1016	for (node = firstnode(matchlist);
1017	     node; incnode(node)) {
1018	    Repldata rdata = (Repldata)getdata(node);
1019	    if (rdata->b >= curpos) {
1020		*endmatchpos = rdata->e;
1021		return rdata->b;
1022	    }
1023	}
1024    }
1025
1026    return -1;
1027}
1028
1029/*
1030 * Save an isearch buffer from sbuf to sbuf+sbptr
1031 * into the string *search with length *searchlen.
1032 * searchlen may be NULL; the string is a NULL-terminated metafied string.
1033 */
1034static void
1035save_isearch_buffer(char *sbuf, int sbptr,
1036		    char **search, int *searchlen)
1037{
1038    if (*search)
1039	free(*search);
1040    *search = zalloc(sbptr+1);
1041    memcpy(*search, sbuf, sbptr);
1042    if (searchlen)
1043	*searchlen = sbptr;
1044    (*search)[sbptr] = '\0';
1045}
1046
1047#define ISEARCH_PROMPT		"XXXXXXX XXX-i-search: "
1048#define FAILING_TEXT		"failing"
1049#define INVALID_TEXT		"invalid"
1050#define BAD_TEXT_LEN		7
1051#define NORM_PROMPT_POS		(BAD_TEXT_LEN+1)
1052#define FIRST_SEARCH_CHAR	(NORM_PROMPT_POS + 14)
1053
1054/**/
1055int isearch_active, isearch_startpos, isearch_endpos;
1056
1057/**/
1058static int
1059doisearch(char **args, int dir, int pattern)
1060{
1061    /* The full search buffer, including space for all prompts */
1062    char *ibuf = zhalloc(80);
1063    /*
1064     * The part of the search buffer with the search string.
1065     * This is a normal metafied string.
1066     */
1067    char *sbuf = ibuf + FIRST_SEARCH_CHAR;
1068    /* The previous line shown to the user */
1069    char *last_line = NULL;
1070    /* Text of the history line being examined */
1071    char *zt;
1072    /*
1073     * sbptr: index into sbuf.
1074     * top_spot: stack index into the "isrch_spot" stack.
1075     * sibuf: allocation size for ibuf
1076     */
1077    int sbptr = 0, top_spot = 0, sibuf = 80;
1078    /*
1079     * nomatch = 1: failing isearch
1080     * nomatch = 2: invalid pattern
1081     * skip_line: finished with current line, skip to next
1082     * skip_pos: keep current line but try before/after current position.
1083     */
1084    int nomatch = 0, skip_line = 0, skip_pos = 0;
1085    /*
1086     * odir: original search direction
1087     * sens: limit for zlinecmp to allow (3) or disallow (1) lower case
1088     *       matching upper case.
1089     */
1090    int odir = dir, sens = zmult == 1 ? 3 : 1;
1091    /*
1092     * hl: the number of the history line we are looking at
1093     * pos: the character position into it.  On backward matches the
1094     *      cursor will be set to this; on forward matches to the end
1095     *      of the matched string
1096     */
1097    int hl = histline, pos;
1098    /*
1099     * The value of hl and pos at which the last pattern match
1100     * search started.  We need to record these because there's
1101     * a pathology with pattern matching.  Here's an example.  Suppose
1102     * the history consists of:
1103     *  echo '*OH NO*'
1104     *  echo '\n'
1105     *  echo "*WHAT?*"
1106     *  <...backward pattern search starts here...>
1107     * The user types "\".  As there's nothing after it it's treated
1108     * literally (and I certainly don't want to change that).  This
1109     * goes to the second line.  Then the user types "*".  This
1110     * ought to match the "*" in the line immediately before where the
1111     * search started.  However, unless we return to that line for the
1112     * new search it will instead carry on to the first line.  This is
1113     * different from straight string matching where we never have
1114     * to backtrack.
1115     *
1116     * I think these need resetting to the current hl and pos when
1117     * we start a new search or repeat a search.  It seems to work,
1118     * anyway.
1119     *
1120     * We could optimize this more, but I don't think there's a lot
1121     * of point.  (Translation:  it's difficult.)
1122     */
1123    int pat_hl = hl, pat_pos;
1124    /*
1125     * This is the flag that we need to revert the positions to
1126     * the above for the next pattern search.
1127     */
1128    int revert_patpos = 0;
1129    /*
1130     * Another nasty feature related to the above.  When
1131     * we revert the position, we might advance the search to
1132     * the same line again.  When we do this the test for ignoring
1133     * duplicates may trigger.  This flag indicates that in this
1134     * case it's OK.
1135     */
1136    int dup_ok = 0;
1137    /*
1138     * End position of the match.
1139     * When forward matching, this is the position for the cursor.
1140     * When backward matching, the cursor position is pos.
1141     */
1142    int end_pos = 0;
1143    /*
1144     * savekeys records the unget buffer, so that if we have arguments
1145     * they don't pollute the input.
1146     * feep indicates we should feep.  This is a well-known word
1147     * meaning "to indicate an error in the zsh line editor".
1148     */
1149    int savekeys = -1, feep = 0;
1150    /* Flag that we are at an old position, no need to search again */
1151    int nosearch = 0;
1152    /* Command read as input:  we don't read characters directly. */
1153    Thingy cmd;
1154    /* Save the keymap if necessary */
1155    char *okeymap;
1156    /* The current history entry, corresponding to hl */
1157    Histent he;
1158    /* When pattern matching, the compiled pattern */
1159    Patprog patprog = NULL;
1160    /* When pattern matching, the list of match positions */
1161    LinkList matchlist = NULL;
1162    /*
1163     * When we exit isearching this may be a zle command to
1164     * execute.  We save it and execute it after unmetafying the
1165     * command line.
1166     */
1167    ZleIntFunc exitfn = (ZleIntFunc)0;
1168    /*
1169     * Flag that the search was aborted.
1170     */
1171    int aborted = 0;
1172
1173    if (!(he = quietgethist(hl)))
1174	return 1;
1175
1176    selectlocalmap(isearch_keymap);
1177
1178    clearlist = 1;
1179
1180    if (*args) {
1181	int len;
1182	char *arg;
1183	savekeys = kungetct;
1184	arg = getkeystring(*args, &len, GETKEYS_BINDKEY, NULL);
1185	ungetbytes(arg, len);
1186    }
1187
1188    strcpy(ibuf, ISEARCH_PROMPT);
1189    /* careful with fwd/bck: we don't want the NULL copied */
1190    memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
1191    okeymap = ztrdup(curkeymapname);
1192    selectkeymap("main", 1);
1193
1194    metafy_line();
1195    remember_edits();
1196    zt = GETZLETEXT(he);
1197    pat_pos = pos = zlemetacs;
1198    for (;;) {
1199	/* Remember the current values in case search fails (doesn't push). */
1200	set_isrch_spot(top_spot, hl, pos, pat_hl, pat_pos, end_pos,
1201		       zlemetacs, sbptr, dir, nomatch);
1202	if (sbptr == 1 && sbuf[0] == '^') {
1203	    zlemetacs = 0;
1204    	    nomatch = 0;
1205	    statusline = ibuf + NORM_PROMPT_POS;
1206	} else if (sbptr > 0) {
1207	    /* The matched text, used as flag that we matched */
1208	    char *t = NULL;
1209	    last_line = zt;
1210
1211	    sbuf[sbptr] = '\0';
1212	    if (pattern && !patprog && !nosearch) {
1213		/* avoid too much heap use, can get heavy round here... */
1214		char *patbuf = ztrdup(sbuf);
1215		char *patstring;
1216		/*
1217		 * Use static pattern buffer since we don't need
1218		 * to maintain it and won't call other pattern functions
1219		 * meanwhile.
1220		 * Use PAT_NOANCH because we don't need the match
1221		 * anchored to the end, even if it is at the start.
1222		 */
1223		int patflags = PAT_STATIC|PAT_NOANCH;
1224		if (sbuf[0] == '^') {
1225		    /*
1226		     * We'll handle the anchor later when
1227		     * we call into the globbing code.
1228		     */
1229		    patstring = patbuf + 1;
1230		} else {
1231		    /* Scanning for multiple matches per line */
1232		    patflags |= PAT_SCAN;
1233		    patstring = patbuf;
1234		}
1235		if (sens == 3)
1236		    patflags |= PAT_LCMATCHUC;
1237		tokenize(patstring);
1238		remnulargs(patstring);
1239		patprog = patcompile(patstring, patflags, NULL);
1240		free(patbuf);
1241		if (matchlist) {
1242		    freematchlist(matchlist);
1243		    matchlist = NULL;
1244		}
1245		if (patprog) {
1246		    revert_patpos = 1;
1247		    skip_pos = 0;
1248		} else {
1249		    if (nomatch != 2) {
1250			handlefeep(zlenoargs);
1251			nomatch = 2;
1252		    }
1253		    /* indicate "invalid" in status line */
1254		    memcpy(ibuf, INVALID_TEXT, BAD_TEXT_LEN);
1255		    statusline = ibuf;
1256		}
1257	    }
1258	    /*
1259	     * skip search if pattern compilation failed, or
1260	     * if we back somewhere we already searched.
1261	     */
1262	    while ((!pattern || patprog) && !nosearch) {
1263		if (patprog) {
1264		    if (revert_patpos) {
1265			/*
1266			 * Search from where the previous
1267			 * search started; see note above.
1268			 * This is down here within the loop because of
1269			 * the "nosearch" optimisation.
1270			 */
1271			revert_patpos = 0;
1272			dup_ok = 1;
1273			he = quietgethist(hl = pat_hl);
1274			zt = GETZLETEXT(he);
1275			pos = pat_pos;
1276		    }
1277		    /*
1278		     * We are pattern matching against the current
1279		     * line.  If anchored at the start, this is
1280		     * easy; a single test suffices.
1281		     *
1282		     * Otherwise, our strategy is to retrieve a linked
1283		     * list of all matches within the current line and
1284		     * scan through it as appropriate.  This isn't
1285		     * actually significantly more efficient, but
1286		     * it is algorithmically easier since we just
1287		     * need a single one-off line-matching interface
1288		     * to the pattern code.  We use a variant of
1289		     * the code used for replacing within parameters
1290		     * which for historical reasons is in glob.c rather
1291		     * than pattern.c.
1292		     *
1293		     * The code for deciding whether to skip something
1294		     * is a bit icky but that sort of code always is.
1295		     */
1296		    if (!skip_line) {
1297			if (sbuf[0] == '^') {
1298			    /*
1299			     * skip_pos applies to the whole line in
1300			     * this mode.
1301			     */
1302			    if (!skip_pos &&
1303				pattryrefs(patprog, zt, -1, -1, 0, NULL, NULL,
1304					   &end_pos))
1305				t = zt;
1306			} else {
1307			    if (!matchlist && !skip_pos) {
1308				if (!getmatchlist(zt, patprog, &matchlist) ||
1309				    !firstnode(matchlist)) {
1310				    if (matchlist) {
1311					freematchlist(matchlist);
1312					matchlist = NULL;
1313				    }
1314				}
1315			    }
1316			    if (matchlist) {
1317				int newpos;
1318				if (!skip_pos) {
1319				    /* OK to match at current pos */
1320				    newpos = pos;
1321				} else {
1322				    if (dir < 0)
1323					newpos = pos - 1;
1324				    else
1325					newpos = pos + 1;
1326				}
1327				newpos = isearch_newpos(matchlist, newpos,
1328							dir, &end_pos);
1329				/* need a new list next time if off the end */
1330				if (newpos < 0) {
1331				    freematchlist(matchlist);
1332				    matchlist = NULL;
1333				} else {
1334				    t = zt + newpos;
1335				}
1336			    }
1337			}
1338		    }
1339		    skip_pos = 0;
1340		} else {
1341		    /*
1342		     * If instructed, move past a match position:
1343		     * backwards if searching backwards (skipping
1344		     * the line if we're at the start), forwards
1345		     * if searching forwards (skipping a line if we're
1346		     * at the end).
1347		     */
1348		    if (skip_pos) {
1349			if (dir < 0) {
1350			    if (pos == 0)
1351				skip_line = 1;
1352			    else
1353				pos = backwardmetafiedchar(zlemetaline,
1354							   zlemetaline + pos,
1355							   NULL) - zlemetaline;
1356			} else if (sbuf[0] != '^') {
1357			    if (pos >= (int)strlen(zt) - 1)
1358				skip_line = 1;
1359			    else
1360				pos += 1;
1361			} else
1362			    skip_line = 1;
1363			skip_pos = 0;
1364		    }
1365		    /*
1366		     * First search for a(nother) match within the
1367		     * current line, unless we've been told to skip it.
1368		     */
1369		    if (!skip_line) {
1370			if (sbuf[0] == '^') {
1371			    if (zlinecmp(zt, sbuf + 1) < sens)
1372				t = zt;
1373			} else
1374			    t = zlinefind(zt, pos, sbuf, dir, sens);
1375			if (t)
1376			    end_pos = (t - zt) + sbptr - (sbuf[0] == '^');
1377		    }
1378		}
1379		if (t) {
1380		    pos = t - zt;
1381		    break;
1382		}
1383		/*
1384		 * If not found within that line, move through
1385		 * the history to try again.
1386		 */
1387		if (!(zlereadflags & ZLRF_HISTORY)
1388		 || !(he = movehistent(he, dir, hist_skip_flags))) {
1389		    if (sbptr == (int)isrch_spots[top_spot-1].len
1390		     && (isrch_spots[top_spot-1].flags >> ISS_NOMATCH_SHIFT))
1391			top_spot--;
1392		    get_isrch_spot(top_spot, &hl, &pos, &pat_hl, &pat_pos,
1393				   &end_pos, &zlemetacs, &sbptr, &dir,
1394				   &nomatch);
1395		    if (nomatch != 1) {
1396			feep = 1;
1397			nomatch = 1;
1398		    }
1399		    he = quietgethist(hl);
1400		    zt = GETZLETEXT(he);
1401		    skip_line = 0;
1402		    /* indicate "failing" in status line */
1403		    memcpy(ibuf, nomatch == 2 ? INVALID_TEXT :FAILING_TEXT,
1404			   BAD_TEXT_LEN);
1405		    statusline = ibuf;
1406		    break;
1407		}
1408		hl = he->histnum;
1409		zt = GETZLETEXT(he);
1410		pos = (dir == 1) ? 0 : strlen(zt);
1411		if (dup_ok)
1412		    skip_line = 0;
1413		else
1414		    skip_line = isset(HISTFINDNODUPS)
1415			? !!(he->node.flags & HIST_DUP)
1416			: !strcmp(zt, last_line);
1417	    }
1418	    dup_ok = 0;
1419	    /*
1420	     * If we matched above (t set), set the new line.
1421	     * If we didn't, but are here because we are on a previous
1422	     * match (nosearch set and nomatch not, set the line again).
1423	     */
1424	    if (t || (nosearch && !nomatch)) {
1425		zle_setline(he);
1426		if (dir == 1)
1427		    zlemetacs = end_pos;
1428		else
1429		    zlemetacs = pos;
1430		statusline = ibuf + NORM_PROMPT_POS;
1431		nomatch = 0;
1432	    }
1433	} else {
1434	    top_spot = 0;
1435    	    nomatch = 0;
1436	    statusline = ibuf + NORM_PROMPT_POS;
1437	}
1438	nosearch = 0;
1439	if (feep) {
1440	    handlefeep(zlenoargs);
1441	    feep = 0;
1442	}
1443	sbuf[sbptr] = '_';
1444	sbuf[sbptr+1] = '\0';
1445	if (!nomatch && sbptr && (sbptr > 1 || sbuf[0] != '^')) {
1446#ifdef MULTIBYTE_SUPPORT
1447	    int charpos = 0, charcount = 0, ret;
1448	    wint_t wc;
1449	    mbstate_t mbs;
1450
1451	    /*
1452	     * Count unmetafied character positions for the
1453	     * start and end of the match for the benefit of
1454	     * highlighting.
1455	     */
1456	    memset(&mbs, 0, sizeof(mbs));
1457	    while (charpos < end_pos) {
1458		ret = mb_metacharlenconv_r(zlemetaline + charpos, &wc, &mbs);
1459		if (ret <= 0) /* Unrecognised, treat as single char */
1460		    ret = 1;
1461		if (charpos <= pos && pos < charpos + ret)
1462		    isearch_startpos = charcount;
1463		charcount++;
1464		charpos += ret;
1465	    }
1466	    isearch_endpos = charcount;
1467#else
1468	    isearch_startpos = ztrsub(zlemetaline + pos, zlemetaline);
1469	    isearch_endpos = ztrsub(zlemetaline + end_pos,
1470				    zlemetaline);
1471#endif
1472	    isearch_active = 1;
1473	} else
1474	    isearch_active = 0;
1475    ref:
1476	zlecallhook("zle-isearch-update", NULL);
1477	zrefresh();
1478	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
1479	    int i;
1480	    aborted = 1;
1481	    save_isearch_buffer(sbuf, sbptr,
1482				&previous_aborted_search, NULL);
1483	    get_isrch_spot(0, &hl, &pos, &pat_hl, &pat_pos, &end_pos,
1484			   &i, &sbptr, &dir, &nomatch);
1485	    he = quietgethist(hl);
1486	    zle_setline(he);
1487	    zt = GETZLETEXT(he);
1488	    zlemetacs = i;
1489	    break;
1490	}
1491	if(cmd == Th(z_clearscreen)) {
1492	    clearscreen(zlenoargs);
1493	    goto ref;
1494	} else if(cmd == Th(z_redisplay)) {
1495	    redisplay(zlenoargs);
1496	    goto ref;
1497	} else if(cmd == Th(z_vicmdmode)) {
1498	    if(selectkeymap(invicmdmode() ? "main" : "vicmd", 0))
1499		feep = 1;
1500	    goto ref;
1501       } else if (cmd == Th(z_vibackwarddeletechar) ||
1502		  cmd == Th(z_backwarddeletechar) ||
1503		  cmd == Th(z_vibackwardkillword) ||
1504		  cmd == Th(z_backwardkillword) ||
1505		  cmd == Th(z_backwarddeleteword)) {
1506	    int only_one = (cmd == Th(z_vibackwarddeletechar) ||
1507			    cmd == Th(z_backwarddeletechar));
1508	    int old_sbptr = sbptr;
1509	    if (top_spot) {
1510		for (;;) {
1511		    get_isrch_spot(--top_spot, &hl, &pos, &pat_hl,
1512				   &pat_pos,  &end_pos, &zlemetacs,
1513				   &sbptr, &dir, &nomatch);
1514		    if (only_one || !top_spot || old_sbptr != sbptr)
1515			break;
1516		}
1517		patprog = NULL;
1518		nosearch = 1;
1519		skip_pos = 0;
1520	    } else
1521		feep = 1;
1522	    if (nomatch) {
1523		memcpy(ibuf, nomatch == 2 ? INVALID_TEXT : FAILING_TEXT,
1524		       BAD_TEXT_LEN);
1525		statusline = ibuf;
1526		skip_pos = 1;
1527	    }
1528	    he = quietgethist(hl);
1529	    zt = GETZLETEXT(he);
1530	    /*
1531	     * Set the line for the cases where we won't go past
1532	     * the usual line-setting logic:  if we're not on a match,
1533	     * or if we don't have enough to search for.
1534	     */
1535	    if (nomatch || !sbptr || (sbptr == 1 && sbuf[0] == '^')) {
1536		int i = zlemetacs;
1537		zle_setline(he);
1538		zlemetacs = i;
1539	    }
1540	    memcpy(ibuf + NORM_PROMPT_POS,
1541		   (dir == 1) ? "fwd" : "bck", 3);
1542	    continue;
1543	} else if(cmd == Th(z_acceptandhold)) {
1544	    exitfn = acceptandhold;
1545	    break;
1546	} else if(cmd == Th(z_acceptandinfernexthistory)) {
1547	    exitfn = acceptandinfernexthistory;
1548	    break;
1549	} else if(cmd == Th(z_acceptlineanddownhistory)) {
1550	    exitfn = acceptlineanddownhistory;
1551	    break;
1552	} else if(cmd == Th(z_acceptline)) {
1553	    exitfn = acceptline;
1554	    break;
1555	} else if(cmd == Th(z_historyincrementalsearchbackward) ||
1556		  cmd == Th(z_historyincrementalpatternsearchbackward)) {
1557	    pat_hl = hl;
1558	    pat_pos = pos;
1559	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
1560			   zlemetacs, sbptr, dir, nomatch);
1561	    if (dir != -1)
1562		dir = -1;
1563	    else
1564		skip_pos = 1;
1565	    goto rpt;
1566	} else if(cmd == Th(z_historyincrementalsearchforward) ||
1567		  cmd == Th(z_historyincrementalpatternsearchforward)) {
1568	    pat_hl = hl;
1569	    pat_pos = pos;
1570	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
1571			   zlemetacs, sbptr, dir, nomatch);
1572	    if (dir != 1)
1573		dir = 1;
1574	    else
1575		skip_pos = 1;
1576	    goto rpt;
1577	} else if(cmd == Th(z_virevrepeatsearch)) {
1578	    pat_hl = hl;
1579	    pat_pos = pos;
1580	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
1581			   zlemetacs, sbptr, dir, nomatch);
1582	    dir = -odir;
1583	    skip_pos = 1;
1584	    goto rpt;
1585	} else if(cmd == Th(z_virepeatsearch)) {
1586	    pat_hl = hl;
1587	    pat_pos = pos;
1588	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
1589			   zlemetacs, sbptr, dir, nomatch);
1590	    dir = odir;
1591	    skip_pos = 1;
1592	rpt:
1593	    if (!sbptr && previous_search_len) {
1594		if (previous_search_len > sibuf - FIRST_SEARCH_CHAR - 2) {
1595		    ibuf = hrealloc((char *)ibuf, sibuf,
1596				    (sibuf + previous_search_len));
1597		    sbuf = ibuf + FIRST_SEARCH_CHAR;
1598		    sibuf += previous_search_len;
1599		}
1600		memcpy(sbuf, previous_search, sbptr = previous_search_len);
1601	    }
1602	    memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
1603	    continue;
1604	} else if(cmd == Th(z_viquotedinsert) ||
1605	    	cmd == Th(z_quotedinsert)) {
1606	    if(cmd == Th(z_viquotedinsert)) {
1607		sbuf[sbptr] = '^';
1608		sbuf[sbptr+1] = '\0';
1609		zrefresh();
1610	    }
1611	    if (getfullchar(0) == ZLEEOF)
1612		feep = 1;
1613	    else
1614		goto ins;
1615	} else if (cmd == Th(z_acceptsearch)) {
1616	    break;
1617	} else {
1618	    if(cmd == Th(z_selfinsertunmeta)) {
1619		fixunmeta();
1620	    } else if (cmd == Th(z_magicspace)) {
1621		fixmagicspace();
1622	    } else if (cmd == Th(z_selfinsert)) {
1623#ifdef MULTIBYTE_SUPPORT
1624		if (!lastchar_wide_valid)
1625		    if (getrestchar(lastchar) == WEOF) {
1626			handlefeep(zlenoargs);
1627			continue;
1628		    }
1629#else
1630		;
1631#endif
1632	    } else {
1633		ungetkeycmd();
1634		if (cmd == Th(z_sendbreak)) {
1635		    aborted = 1;
1636		    save_isearch_buffer(sbuf, sbptr,
1637					&previous_aborted_search, NULL);
1638		    sbptr = 0;
1639		}
1640		break;
1641	    }
1642	ins:
1643	    if (sbptr == PATH_MAX) {
1644		feep = 1;
1645		continue;
1646	    }
1647	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
1648			   zlemetacs, sbptr, dir, nomatch);
1649	    if (sbptr >= sibuf - FIRST_SEARCH_CHAR - 2
1650#ifdef MULTIBYTE_SUPPORT
1651		- 2 * (int)MB_CUR_MAX
1652#endif
1653		) {
1654		ibuf = hrealloc(ibuf, sibuf, sibuf * 2);
1655		sbuf = ibuf + FIRST_SEARCH_CHAR;
1656		sibuf *= 2;
1657	    }
1658	    /*
1659	     * We've supposedly arranged above that lastchar_wide is
1660	     * always valid at this point.
1661	     */
1662	    sbptr += zlecharasstring(LASTFULLCHAR, sbuf + sbptr);
1663	    patprog = NULL;
1664	}
1665	if (feep)
1666	    handlefeep(zlenoargs);
1667	feep = 0;
1668    }
1669    if (sbptr) {
1670	save_isearch_buffer(sbuf, sbptr,
1671			    &previous_search, &previous_search_len);
1672    }
1673    statusline = NULL;
1674    unmetafy_line();
1675    zlecallhook("zle-isearch-exit", NULL);
1676    if (exitfn)
1677	exitfn(zlenoargs);
1678    selectkeymap(okeymap, 1);
1679    zsfree(okeymap);
1680    if (matchlist)
1681	freematchlist(matchlist);
1682    isearch_active = 0;
1683    /*
1684     * Don't allow unused characters provided as a string to the
1685     * widget to overflow and be used as separated commands.
1686     */
1687    if (savekeys >= 0 && kungetct > savekeys)
1688	kungetct = savekeys;
1689
1690    selectlocalmap(NULL);
1691
1692    return aborted ? 3 : nomatch;
1693}
1694
1695static Histent
1696infernexthist(Histent he, UNUSED(char **args))
1697{
1698    metafy_line();
1699    for (he = movehistent(he, -2, HIST_FOREIGN);
1700	 he; he = movehistent(he, -1, HIST_FOREIGN)) {
1701	if (!zlinecmp(GETZLETEXT(he), zlemetaline)) {
1702	    unmetafy_line();
1703	    return movehistent(he, 1, HIST_FOREIGN);
1704	}
1705    }
1706    unmetafy_line();
1707    return NULL;
1708}
1709
1710/**/
1711int
1712acceptandinfernexthistory(char **args)
1713{
1714    Histent he;
1715
1716    if (!(he = infernexthist(hist_ring, args)))
1717	return 1;
1718    zpushnode(bufstack, ztrdup(he->node.nam));
1719    done = 1;
1720    stackhist = he->histnum;
1721    return 0;
1722}
1723
1724/**/
1725int
1726infernexthistory(char **args)
1727{
1728    Histent he = quietgethist(histline);
1729
1730    if (!he || !(he = infernexthist(he, args)))
1731	return 1;
1732    zle_setline(he);
1733    return 0;
1734}
1735
1736/**/
1737int
1738vifetchhistory(UNUSED(char **args))
1739{
1740    if (zmult < 0)
1741	return 1;
1742    if (histline == curhist) {
1743	if (!(zmod.flags & MOD_MULT)) {
1744	    zlecs = zlell;
1745	    zlecs = findbol();
1746	    return 0;
1747	}
1748    }
1749    if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist, 0, 0) &&
1750	isset(HISTBEEP)) {
1751	return 1;
1752    }
1753    return 0;
1754}
1755
1756/* the last vi search */
1757
1758static char *visrchstr, *vipenultsrchstr;
1759static int visrchsense;
1760
1761/**/
1762static int
1763getvisrchstr(void)
1764{
1765    char *sbuf = zhalloc(80);
1766    int sptr = 1, ret = 0, ssbuf = 80, feep = 0;
1767    Thingy cmd;
1768    char *okeymap = ztrdup(curkeymapname);
1769
1770    if (vipenultsrchstr) {
1771	zsfree(vipenultsrchstr);
1772	vipenultsrchstr = NULL;
1773    }
1774
1775    if (visrchstr) {
1776	vipenultsrchstr = visrchstr;
1777	visrchstr = NULL;
1778    }
1779    clearlist = 1;
1780    statusline = sbuf;
1781    sbuf[0] = (visrchsense == -1) ? '?' : '/';
1782    selectkeymap("main", 1);
1783    while (sptr) {
1784	sbuf[sptr] = '_';
1785	sbuf[sptr+1] = '\0';
1786	zrefresh();
1787	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
1788	    ret = 0;
1789	    break;
1790	}
1791	if(cmd == Th(z_magicspace)) {
1792	    fixmagicspace();
1793	    cmd = Th(z_selfinsert);
1794	}
1795	if(cmd == Th(z_redisplay)) {
1796	    redisplay(zlenoargs);
1797	} else if(cmd == Th(z_clearscreen)) {
1798	    clearscreen(zlenoargs);
1799	} else if(cmd == Th(z_acceptline) ||
1800	    	cmd == Th(z_vicmdmode)) {
1801	    sbuf[sptr] = ZWC('\0');
1802	    visrchstr = ztrdup(sbuf+1);
1803	    if (!strlen(visrchstr)) {
1804	        zsfree(visrchstr);
1805		visrchstr = ztrdup(vipenultsrchstr);
1806	    }
1807	    ret = 1;
1808	    sptr = 0;
1809	} else if(cmd == Th(z_backwarddeletechar) ||
1810		  cmd == Th(z_vibackwarddeletechar)) {
1811	    sptr = backwardmetafiedchar(sbuf+1, sbuf+sptr, NULL) - sbuf;
1812	} else if(cmd == Th(z_backwardkillword) ||
1813		  cmd == Th(z_vibackwardkillword)) {
1814	    convchar_t cc;
1815	    char *newpos;
1816	    while (sptr != 1) {
1817		newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc);
1818		if (!ZC_iblank(cc))
1819		    break;
1820		sptr = newpos - sbuf;
1821	    }
1822	    if (sptr > 1) {
1823		newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc);
1824		if (ZC_iident(cc)) {
1825		    for (;;) {
1826			sptr = newpos - sbuf;
1827			if (sptr == 1)
1828			    break;
1829			newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc);
1830			if (!ZC_iident(cc))
1831			    break;
1832		    }
1833		} else {
1834		    for (;;) {
1835			sptr = newpos - sbuf;
1836			if (sptr == 1)
1837			    break;
1838			newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc);
1839			if (ZC_iident(cc) || ZC_iblank(cc))
1840			    break;
1841		    }
1842		}
1843	    }
1844	} else if(cmd == Th(z_viquotedinsert) || cmd == Th(z_quotedinsert)) {
1845	    if(cmd == Th(z_viquotedinsert)) {
1846		sbuf[sptr] = '^';
1847		zrefresh();
1848	    }
1849	    if (getfullchar(0) == ZLEEOF)
1850		feep = 1;
1851	    else
1852		goto ins;
1853	} else if(cmd == Th(z_selfinsertunmeta) || cmd == Th(z_selfinsert)) {
1854	    if(cmd == Th(z_selfinsertunmeta)) {
1855		fixunmeta();
1856	    } else {
1857#ifdef MULTIBYTE_SUPPORT
1858		if (!lastchar_wide_valid)
1859		    if (getrestchar(lastchar) == WEOF) {
1860			handlefeep(zlenoargs);
1861			continue;
1862		    }
1863#else
1864		;
1865#endif
1866	    }
1867	  ins:
1868	    if (sptr == ssbuf - 1) {
1869		char *newbuf = (char *)zhalloc((ssbuf *= 2));
1870		strcpy(newbuf, sbuf);
1871		statusline = sbuf = newbuf;
1872	    }
1873	    sptr += zlecharasstring(LASTFULLCHAR, sbuf + sptr);
1874	} else {
1875	    feep = 1;
1876	}
1877	if (feep)
1878	    handlefeep(zlenoargs);
1879	feep = 0;
1880    }
1881    statusline = NULL;
1882    selectkeymap(okeymap, 1);
1883    zsfree(okeymap);
1884    return ret;
1885}
1886
1887/**/
1888int
1889vihistorysearchforward(char **args)
1890{
1891    if (*args) {
1892	int ose = visrchsense, ret;
1893	char *ost = visrchstr;
1894
1895	visrchsense = 1;
1896	visrchstr = *args;
1897	ret = virepeatsearch(zlenoargs);
1898	visrchsense = ose;
1899	visrchstr = ost;
1900	return ret;
1901    }
1902    visrchsense = 1;
1903    if (getvisrchstr())
1904	return virepeatsearch(zlenoargs);
1905    return 1;
1906}
1907
1908/**/
1909int
1910vihistorysearchbackward(char **args)
1911{
1912    if (*args) {
1913	int ose = visrchsense, ret;
1914	char *ost = visrchstr;
1915
1916	visrchsense = -1;
1917	visrchstr = *args;
1918	ret = virepeatsearch(zlenoargs);
1919	visrchsense = ose;
1920	visrchstr = ost;
1921	return ret;
1922    }
1923    visrchsense = -1;
1924    if (getvisrchstr())
1925	return virepeatsearch(zlenoargs);
1926    return 1;
1927}
1928
1929/**/
1930int
1931virepeatsearch(UNUSED(char **args))
1932{
1933    Histent he;
1934    int n = zmult;
1935    char *zt;
1936
1937    if (!visrchstr)
1938	return 1;
1939    if (zmult < 0) {
1940	n = -n;
1941	visrchsense = -visrchsense;
1942    }
1943    if (!(he = quietgethist(histline)))
1944	return 1;
1945    metafy_line();
1946    while ((he = movehistent(he, visrchsense, hist_skip_flags))) {
1947	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
1948	    continue;
1949	zt = GETZLETEXT(he);
1950	if (zlinecmp(zt, zlemetaline) &&
1951	    (*visrchstr == '^' ? strpfx(visrchstr + 1, zt) :
1952	     zlinefind(zt, 0, visrchstr, 1, 1) != 0)) {
1953	    if (--n <= 0) {
1954		unmetafy_line();
1955		zle_setline(he);
1956		return 0;
1957	    }
1958	}
1959    }
1960    unmetafy_line();
1961    return 1;
1962}
1963
1964/**/
1965int
1966virevrepeatsearch(char **args)
1967{
1968    int ret;
1969    visrchsense = -visrchsense;
1970    ret = virepeatsearch(args);
1971    visrchsense = -visrchsense;
1972    return ret;
1973}
1974
1975/* Extra function added by A.R. Iano-Fletcher.	*/
1976/*The extern variable "zlecs" is the position of the cursor. */
1977/* history-beginning-search-backward */
1978
1979/**/
1980int
1981historybeginningsearchbackward(char **args)
1982{
1983    Histent he;
1984    int cpos = zlecs;		/* save cursor position */
1985    int n = zmult;
1986    char *zt;
1987
1988    if (zmult < 0) {
1989	int ret;
1990	zmult = -n;
1991	ret = historybeginningsearchforward(args);
1992	zmult = n;
1993	return ret;
1994    }
1995    if (!(he = quietgethist(histline)))
1996	return 1;
1997    metafy_line();
1998    while ((he = movehistent(he, -1, hist_skip_flags))) {
1999	int tst;
2000	char sav;
2001	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
2002	    continue;
2003	zt = GETZLETEXT(he);
2004	sav = zlemetaline[zlemetacs];
2005	zlemetaline[zlemetacs] = '\0';
2006	tst = zlinecmp(zt, zlemetaline);
2007	zlemetaline[zlemetacs] = sav;
2008	if (tst < 0 && zlinecmp(zt, zlemetaline)) {
2009	    if (--n <= 0) {
2010		unmetafy_line();
2011		zle_setline(he);
2012		zlecs = cpos;
2013		CCRIGHT();
2014		return 0;
2015	    }
2016	}
2017    }
2018    unmetafy_line();
2019    return 1;
2020}
2021
2022/* Extra function added by A.R. Iano-Fletcher.	*/
2023
2024/* history-beginning-search-forward */
2025/**/
2026int
2027historybeginningsearchforward(char **args)
2028{
2029    Histent he;
2030    int cpos = zlecs;		/* save cursor position */
2031    int n = zmult;
2032    char *zt;
2033
2034    if (zmult < 0) {
2035	int ret;
2036	zmult = -n;
2037	ret = historybeginningsearchbackward(args);
2038	zmult = n;
2039	return ret;
2040    }
2041    if (!(he = quietgethist(histline)))
2042	return 1;
2043    metafy_line();
2044    while ((he = movehistent(he, 1, hist_skip_flags))) {
2045	char sav;
2046	int tst;
2047	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
2048	    continue;
2049	zt = GETZLETEXT(he);
2050	sav = zlemetaline[zlemetacs];
2051	zlemetaline[zlemetacs] = '\0';
2052	tst = zlinecmp(zt, zlemetaline) < (he->histnum == curhist);
2053	zlemetaline[zlemetacs] = sav;
2054	if (tst && zlinecmp(zt, zlemetaline)) {
2055	    if (--n <= 0) {
2056		unmetafy_line();
2057		zle_setline(he);
2058		zlecs = cpos;
2059		CCRIGHT();
2060		return 0;
2061	    }
2062	}
2063    }
2064    unmetafy_line();
2065    return 1;
2066}
2067