mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 06:00:54 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			2094 lines
		
	
	
	
		
			49 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2094 lines
		
	
	
	
		
			49 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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"
 | |
| 
 | |
| /* 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;
 | |
| 
 | |
| /* Previous search string use in an incremental search */
 | |
| 
 | |
| /**/
 | |
| char *previous_search = NULL;
 | |
| 
 | |
| /**/
 | |
| int previous_search_len;
 | |
| 
 | |
| /* Previous aborted search string use in an incremental search */
 | |
| 
 | |
| /**/
 | |
| char *previous_aborted_search = NULL;
 | |
| 
 | |
| /* Local keymap in isearch mode */
 | |
| 
 | |
| /**/
 | |
| Keymap isearch_keymap;
 | |
| 
 | |
| /*** History text manipulation utilities ***/
 | |
| 
 | |
| /*
 | |
|  * Text for the line:  anything previously modified within zle since
 | |
|  * the last time the line editor was started, else what was originally
 | |
|  * put in the history.
 | |
|  */
 | |
| #define GETZLETEXT(ent)	((ent)->zle_text ? (ent)->zle_text : (ent)->node.nam)
 | |
| 
 | |
| /**/
 | |
| void
 | |
| remember_edits(void)
 | |
| {
 | |
|     Histent ent = quietgethist(histline);
 | |
|     if (ent) {
 | |
| 	char *line =
 | |
| 	    zlemetaline ? zlemetaline :
 | |
| 	    zlelineasstring(zleline, zlell, 0, NULL, NULL, 0);
 | |
| 	if (!ent->zle_text || strcmp(line, ent->zle_text) != 0) {
 | |
| 	    if (ent->zle_text)
 | |
| 		free(ent->zle_text);
 | |
| 	    ent->zle_text = zlemetaline ? ztrdup(line) : line;
 | |
| 	} else if (!zlemetaline)
 | |
| 	    free(line);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| void
 | |
| forget_edits(void)
 | |
| {
 | |
|     Histent he;
 | |
| 
 | |
|     for (he = hist_ring; he; he = up_histent(he)) {
 | |
| 	if (he->zle_text) {
 | |
| 	    free(he->zle_text);
 | |
| 	    he->zle_text = NULL;
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*** Search utilities ***/
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Return zero if the ZLE string histp length histl and the ZLE string
 | |
|  * inputp length inputl are the same.  Return -1 if inputp is a prefix
 | |
|  * of histp.  Return 1 if inputp is the lowercase version of histp.
 | |
|  * Return 2 if inputp is the lowercase prefix of histp and return 3
 | |
|  * otherwise.
 | |
|  */
 | |
| 
 | |
| static int
 | |
| zlinecmp(const char *histp, const char *inputp)
 | |
| {
 | |
|     const char *hptr = histp, *iptr = inputp;
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     mbstate_t hstate, istate;
 | |
| #endif
 | |
| 
 | |
|     while (*iptr && *hptr == *iptr) {
 | |
| 	hptr++;
 | |
| 	iptr++;
 | |
|     }
 | |
| 
 | |
|     if (!*iptr) {
 | |
| 	if (!*hptr) {
 | |
| 	    /* strings are the same */
 | |
| 	    return 0;
 | |
| 	} else {
 | |
| 	    /* inputp is a prefix */
 | |
| 	    return -1;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
|     memset(&hstate, 0, sizeof(hstate));
 | |
|     memset(&istate, 0, sizeof(istate));
 | |
| #endif
 | |
| 
 | |
|     /* look for lower case versions */
 | |
|     while (*histp && *inputp) {
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
| 	wint_t hwc, iwc;
 | |
| 	int hlen, ilen;
 | |
| 
 | |
| 	hlen = mb_metacharlenconv_r(histp, &hwc, &hstate);
 | |
| 	ilen = mb_metacharlenconv_r(inputp, &iwc, &istate);
 | |
| 
 | |
| 	if (hwc == WEOF || iwc == WEOF) {
 | |
| 	    /* can't convert, compare input characters */
 | |
| 	    if (ilen != hlen || memcmp(histp, inputp, hlen) != 0)
 | |
| 		return 3;
 | |
| 	} else if (towlower(hwc) != iwc)
 | |
| 	    return 3;
 | |
| 
 | |
| 	histp += hlen;
 | |
| 	inputp += ilen;
 | |
| #else
 | |
|     	if (tulower(*histp++) != *inputp++)
 | |
| 	    return 3;
 | |
| #endif
 | |
|     }
 | |
|     if (!*inputp) {
 | |
| 	/* one string finished, if it's the input... */
 | |
| 	if (!*histp)
 | |
| 	    return 1;		/* ...same, else */
 | |
| 	else
 | |
| 	    return 2;		/* ...prefix */
 | |
|     }
 | |
|     /* Different */
 | |
|     return 3;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Search for needle in haystack.  Haystack and needle are metafied strings.
 | |
|  * Start the search at position pos in haystack.
 | |
|  * Search forward if dir > 0, otherwise search backward.
 | |
|  * sens is used to test against the return value of linecmp.
 | |
|  *
 | |
|  * Return the pointer to the location in haystack found, else NULL.
 | |
|  *
 | |
|  * We assume we'll only find needle at some sensible position in a multibyte
 | |
|  * string, so we don't bother calculating multibyte character lengths for
 | |
|  * incrementing and decrementing the search position.
 | |
|  */
 | |
| 
 | |
| static char *
 | |
| zlinefind(char *haystack, int pos, char *needle, int dir, int sens)
 | |
| {
 | |
|     char *s = haystack + pos;
 | |
| 
 | |
|     if (dir > 0) {
 | |
| 	while (*s) {
 | |
| 	    if (zlinecmp(s, needle) < sens)
 | |
| 		return s;
 | |
| 	    s++;
 | |
| 	}
 | |
|     } else {
 | |
| 	for (;;) {
 | |
| 	    if (zlinecmp(s, needle) < sens)
 | |
| 		return s;
 | |
| 	    if (s == haystack)
 | |
| 		break;
 | |
| 	    s--;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*** Widgets ***/
 | |
| 
 | |
| 
 | |
| /**/
 | |
| int
 | |
| uphistory(UNUSED(char **args))
 | |
| {
 | |
|     int nodups = isset(HISTIGNOREDUPS);
 | |
|     if (!zle_goto_hist(histline, -zmult, nodups) && isset(HISTBEEP))
 | |
| 	return 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| upline(char **args)
 | |
| {
 | |
|     int n = zmult;
 | |
| 
 | |
|     if (n < 0) {
 | |
| 	zmult = -zmult;
 | |
| 	n = -downline(args);
 | |
| 	zmult = -zmult;
 | |
| 	return n;
 | |
|     }
 | |
|     if (lastcol == -1)
 | |
| 	lastcol = zlecs - findbol();
 | |
|     zlecs = findbol();
 | |
|     while (n) {
 | |
| 	if (!zlecs)
 | |
| 	    break;
 | |
| 	zlecs--;
 | |
| 	zlecs = findbol();
 | |
| 	n--;
 | |
|     }
 | |
|     if (!n) {
 | |
| 	int x = findeol();
 | |
| 
 | |
| 	if ((zlecs += lastcol) >= x) {
 | |
| 	    zlecs = x;
 | |
| 	    if (zlecs > findbol() && invicmdmode())
 | |
| 		DECCS();
 | |
| 	}
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
| 	else
 | |
| 	    CCRIGHT();
 | |
| #endif
 | |
| 	    
 | |
|     }
 | |
|     return n;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| uplineorhistory(char **args)
 | |
| {
 | |
|     int ocs = zlecs;
 | |
|     int n = upline(args);
 | |
|     if (n) {
 | |
| 	int m = zmult, ret;
 | |
| 
 | |
| 	zlecs = ocs;
 | |
| 	if (virangeflag || !(zlereadflags & ZLRF_HISTORY))
 | |
| 	    return 1;
 | |
| 	zmult = n;
 | |
| 	ret = uphistory(args);
 | |
| 	zmult = m;
 | |
| 	return ret;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| viuplineorhistory(char **args)
 | |
| {
 | |
|     int col = lastcol;
 | |
|     uplineorhistory(args);
 | |
|     lastcol = col;
 | |
|     return vifirstnonblank(args);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| uplineorsearch(char **args)
 | |
| {
 | |
|     int ocs = zlecs;
 | |
|     int n = upline(args);
 | |
|     if (n) {
 | |
| 	int m = zmult, ret;
 | |
| 
 | |
| 	zlecs = ocs;
 | |
| 	if (virangeflag || !(zlereadflags & ZLRF_HISTORY))
 | |
| 	    return 1;
 | |
| 	zmult = n;
 | |
| 	ret = historysearchbackward(args);
 | |
| 	zmult = m;
 | |
| 	return ret;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| downline(char **args)
 | |
| {
 | |
|     int n = zmult;
 | |
| 
 | |
|     if (n < 0) {
 | |
| 	zmult = -zmult;
 | |
| 	n = -upline(args);
 | |
| 	zmult = -zmult;
 | |
| 	return n;
 | |
|     }
 | |
|     if (lastcol == -1)
 | |
| 	lastcol = zlecs - findbol();
 | |
|     while (n) {
 | |
| 	int x = findeol();
 | |
| 
 | |
| 	if (x == zlell)
 | |
| 	    break;
 | |
| 	zlecs = x + 1;
 | |
| 	n--;
 | |
|     }
 | |
|     if (!n) {
 | |
| 	int x = findeol();
 | |
| 
 | |
| 	if ((zlecs += lastcol) >= x) {
 | |
| 	    zlecs = x;
 | |
| 	    if (zlecs > findbol() && invicmdmode())
 | |
| 		DECCS();
 | |
| 	}
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
| 	else
 | |
| 	    CCRIGHT();
 | |
| #endif
 | |
|     }
 | |
|     return n;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| downlineorhistory(char **args)
 | |
| {
 | |
|     int ocs = zlecs;
 | |
|     int n = downline(args);
 | |
|     if (n) {
 | |
| 	int m = zmult, ret;
 | |
| 
 | |
| 	zlecs = ocs;
 | |
| 	if (virangeflag || !(zlereadflags & ZLRF_HISTORY))
 | |
| 	    return 1;
 | |
| 	zmult = n;
 | |
| 	ret = downhistory(args);
 | |
| 	zmult = m;
 | |
| 	return ret;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| vidownlineorhistory(char **args)
 | |
| {
 | |
|     int col = lastcol;
 | |
|     downlineorhistory(args);
 | |
|     lastcol = col;
 | |
|     return vifirstnonblank(zlenoargs);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| downlineorsearch(char **args)
 | |
| {
 | |
|     int ocs = zlecs;
 | |
|     int n = downline(args);
 | |
|     if (n) {
 | |
| 	int m = zmult, ret;
 | |
| 
 | |
| 	zlecs = ocs;
 | |
| 	if (virangeflag || !(zlereadflags & ZLRF_HISTORY))
 | |
| 	    return 1;
 | |
| 	zmult = n;
 | |
| 	ret = historysearchforward(args);
 | |
| 	zmult = m;
 | |
| 	return ret;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| acceptlineanddownhistory(UNUSED(char **args))
 | |
| {
 | |
|     Histent he = quietgethist(histline);
 | |
| 
 | |
|     if (he && (he = movehistent(he, 1, HIST_FOREIGN))) {
 | |
| 	zpushnode(bufstack, ztrdup(he->node.nam));
 | |
| 	stackhist = he->histnum;
 | |
|     }
 | |
|     done = 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| downhistory(UNUSED(char **args))
 | |
| {
 | |
|     int nodups = isset(HISTIGNOREDUPS);
 | |
|     if (!zle_goto_hist(histline, zmult, nodups) && isset(HISTBEEP))
 | |
| 	return 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Values remembered for history searches to enable repetition.
 | |
|  * srch_hl remembers the old value of histline, to see if it's changed
 | |
|  *   since the last search.
 | |
|  * srch_cs remembers the old value of zlecs for the same purpose (it is
 | |
|  *   not use for any other purpose, i.e. does not need to be a valid
 | |
|  *   index into anything).
 | |
|  * srch_str is the metafied search string, as extracted from the start
 | |
|  *   of zleline.
 | |
|  */
 | |
| static int histpos, srch_hl, srch_cs = -1;
 | |
| static char *srch_str;
 | |
| 
 | |
| /**/
 | |
| int
 | |
| historysearchbackward(char **args)
 | |
| {
 | |
|     Histent he;
 | |
|     int n = zmult;
 | |
|     char *str;
 | |
|     char *zt;
 | |
| 
 | |
|     if (zmult < 0) {
 | |
| 	int ret;
 | |
| 	zmult = -n;
 | |
| 	ret = historysearchforward(args);
 | |
| 	zmult = n;
 | |
| 	return ret;
 | |
|     }
 | |
|     if (*args) {
 | |
| 	str = *args;
 | |
|     } else {
 | |
| 	char *line = zlelineasstring(zleline, zlell, 0, NULL, NULL, 0);
 | |
| 	if (histline == curhist || histline != srch_hl || zlecs != srch_cs ||
 | |
| 	    mark != 0 || strncmp(srch_str, line, histpos) != 0) {
 | |
| 	    free(srch_str);
 | |
| 	    for (histpos = 0; histpos < zlell && !ZC_iblank(zleline[histpos]);
 | |
| 		 histpos++)
 | |
| 		;
 | |
| 	    if (histpos < zlell)
 | |
| 		histpos++;
 | |
| 	    /* ensure we're not on a combining character */
 | |
| 	    CCRIGHTPOS(histpos);
 | |
| 	    /* histpos from now on is an index into the metafied string */
 | |
| 	    srch_str = zlelineasstring(zleline, histpos, 0, NULL, NULL, 0);
 | |
| 	}
 | |
| 	free(line);
 | |
| 	str = srch_str;
 | |
|     }
 | |
|     if (!(he = quietgethist(histline)))
 | |
| 	return 1;
 | |
| 
 | |
|     metafy_line();
 | |
|     while ((he = movehistent(he, -1, hist_skip_flags))) {
 | |
| 	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
 | |
| 	    continue;
 | |
| 	zt = GETZLETEXT(he);
 | |
| 	if (zlinecmp(zt, str) < 0 &&
 | |
| 	    (*args || strcmp(zt, zlemetaline) != 0)) {
 | |
| 	    if (--n <= 0) {
 | |
| 		unmetafy_line();
 | |
| 		zle_setline(he);
 | |
| 		srch_hl = histline;
 | |
| 		srch_cs = zlecs;
 | |
| 		return 0;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     unmetafy_line();
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| historysearchforward(char **args)
 | |
| {
 | |
|     Histent he;
 | |
|     int n = zmult;
 | |
|     char *str;
 | |
|     char *zt;
 | |
| 
 | |
|     if (zmult < 0) {
 | |
| 	int ret;
 | |
| 	zmult = -n;
 | |
| 	ret = historysearchbackward(args);
 | |
| 	zmult = n;
 | |
| 	return ret;
 | |
|     }
 | |
|     if (*args) {
 | |
| 	str = *args;
 | |
|     } else {
 | |
| 	char *line = zlelineasstring(zleline, zlell, 0, NULL, NULL, 0);
 | |
| 	if (histline == curhist || histline != srch_hl || zlecs != srch_cs ||
 | |
| 	    mark != 0 || strncmp(srch_str, line, histpos) != 0) {
 | |
| 	    free(srch_str);
 | |
| 	    for (histpos = 0; histpos < zlell && !ZC_iblank(zleline[histpos]);
 | |
| 		 histpos++)
 | |
| 		;
 | |
| 	    if (histpos < zlell)
 | |
| 		histpos++;
 | |
| 	    CCRIGHT();
 | |
| 	    srch_str = zlelineasstring(zleline, histpos, 0, NULL, NULL, 0);
 | |
| 	}
 | |
| 	free(line);
 | |
| 	str = srch_str;
 | |
|     }
 | |
|     if (!(he = quietgethist(histline)))
 | |
| 	return 1;
 | |
| 
 | |
|     metafy_line();
 | |
|     while ((he = movehistent(he, 1, hist_skip_flags))) {
 | |
| 	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
 | |
| 	    continue;
 | |
| 	zt = GETZLETEXT(he);
 | |
| 	if (zlinecmp(zt, str) < (he->histnum == curhist) &&
 | |
| 	    (*args || strcmp(zt, zlemetaline) != 0)) {
 | |
| 	    if (--n <= 0) {
 | |
| 		unmetafy_line();
 | |
| 		zle_setline(he);
 | |
| 		srch_hl = histline;
 | |
| 		srch_cs = zlecs;
 | |
| 		return 0;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     unmetafy_line();
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| beginningofbufferorhistory(char **args)
 | |
| {
 | |
|     if (findbol())
 | |
| 	zlecs = 0;
 | |
|     else
 | |
| 	return beginningofhistory(args);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| beginningofhistory(UNUSED(char **args))
 | |
| {
 | |
|     if (!zle_goto_hist(firsthist(), 0, 0) && isset(HISTBEEP))
 | |
| 	return 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| endofbufferorhistory(char **args)
 | |
| {
 | |
|     if (findeol() != zlell)
 | |
| 	zlecs = zlell;
 | |
|     else
 | |
| 	return endofhistory(args);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| endofhistory(UNUSED(char **args))
 | |
| {
 | |
|     zle_goto_hist(curhist, 0, 0);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| insertlastword(char **args)
 | |
| {
 | |
|     int n, nwords, histstep = -1, wordpos = 0, deleteword = 0, len;
 | |
|     char *s, *t;
 | |
|     Histent he = NULL;
 | |
|     LinkList l = NULL;
 | |
|     LinkNode node;
 | |
|     ZLE_STRING_T zs;
 | |
| 
 | |
|     static char *lastinsert;
 | |
|     static int lasthist, lastpos, lastlen;
 | |
|     int evhist;
 | |
| 
 | |
|     /*
 | |
|      * 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;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     fixsuffix();
 | |
|     metafy_line();
 | |
|     if (lastinsert && lastlen &&
 | |
| 	lastpos <= zlemetacs &&
 | |
| 	lastlen == zlemetacs - lastpos &&
 | |
| 	memcmp(lastinsert, &zlemetaline[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 = zlemetacs;
 | |
| 	    zlemetacs = lastpos;
 | |
| 	    foredel(pos - zlemetacs, CUT_RAW);
 | |
| 	    /*
 | |
| 	     * 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;
 | |
| 	}
 | |
| 	/*
 | |
| 	 * 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, 0))) {
 | |
| 	    unmetafy_line();
 | |
| 	    return 1;
 | |
| 	}
 | |
| 	nwords = countlinknodes(l);
 | |
|     } else {
 | |
| 	/* Some stored line. */
 | |
| 	if (!(he = quietgethist(evhist)) || !he->nwords) {
 | |
| 	    unmetafy_line();
 | |
| 	    return 1;
 | |
| 	}
 | |
| 	nwords = he->nwords;
 | |
|     }
 | |
|     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 > 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;
 | |
| 	unmetafy_line();
 | |
| 	return 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 = zlemetacs;
 | |
| 	zlemetacs = lastpos;
 | |
| 	foredel(pos - zlemetacs, CUT_RAW);
 | |
|     }
 | |
|     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->node.nam + he->words[2*n-2];
 | |
| 	t = he->node.nam + he->words[2*n-1];
 | |
|     }
 | |
| 
 | |
|     lasthist = evhist;
 | |
|     lastpos = zlemetacs;
 | |
|     /* ignore trailing whitespace */
 | |
|     lastlen = t - s;
 | |
|     lastinsert = zalloc(t - s);
 | |
|     memcpy(lastinsert, s, lastlen);
 | |
|     n = zmult;
 | |
|     zmult = 1;
 | |
| 
 | |
|     unmetafy_line();
 | |
| 
 | |
|     zs = stringaszleline(dupstrpfx(s, t - s), 0, &len, NULL, NULL);
 | |
|     doinsert(zs, len);
 | |
|     free(zs);
 | |
|     zmult = n;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| void
 | |
| zle_setline(Histent he)
 | |
| {
 | |
|     int remetafy;
 | |
|     if (zlemetaline) {
 | |
| 	unmetafy_line();
 | |
| 	remetafy = 1;
 | |
|     } else
 | |
| 	remetafy = 0;
 | |
|     remember_edits();
 | |
|     mkundoent();
 | |
|     histline = he->histnum;
 | |
| 
 | |
|     setline(GETZLETEXT(he), ZSL_COPY|ZSL_TOEND);
 | |
|     zlecallhook("zle-history-line-set", NULL);
 | |
|     setlastline();
 | |
|     clearlist = 1;
 | |
|     if (remetafy)
 | |
| 	metafy_line();
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| setlocalhistory(UNUSED(char **args))
 | |
| {
 | |
|     if (zmod.flags & MOD_MULT) {
 | |
| 	hist_skip_flags = zmult? HIST_FOREIGN : 0;
 | |
|     } else {
 | |
| 	hist_skip_flags ^= HIST_FOREIGN;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| zle_goto_hist(int ev, int n, int skipdups)
 | |
| {
 | |
|     Histent he = quietgethist(ev);
 | |
|     char *line = zlelineasstring(zleline, zlell, 0, NULL, NULL, 1);
 | |
| 
 | |
|     if (!he || !(he = movehistent(he, n, hist_skip_flags)))
 | |
| 	return 1;
 | |
|     if (skipdups && n) {
 | |
| 	n = n < 0? -1 : 1;
 | |
| 	while (he) {
 | |
| 	    int ret;
 | |
| 
 | |
| 	    ret = zlinecmp(GETZLETEXT(he), line);
 | |
| 	    if (ret)
 | |
| 		break;
 | |
| 	    he = movehistent(he, n, hist_skip_flags);
 | |
| 	}
 | |
|     }
 | |
|     if (!he)
 | |
| 	return 0;
 | |
|     zle_setline(he);
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| pushline(UNUSED(char **args))
 | |
| {
 | |
|     int n = zmult;
 | |
| 
 | |
|     if (n < 0)
 | |
| 	return 1;
 | |
|     zpushnode(bufstack, zlelineasstring(zleline, zlell, 0, NULL, NULL, 0));
 | |
|     while (--n)
 | |
| 	zpushnode(bufstack, ztrdup(""));
 | |
|     if (invicmdmode())
 | |
| 	INCCS();
 | |
|     stackcs = zlecs;
 | |
|     *zleline = ZWC('\0');
 | |
|     zlell = zlecs = 0;
 | |
|     clearlist = 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| pushlineoredit(char **args)
 | |
| {
 | |
|     int ics, ret;
 | |
|     ZLE_STRING_T s;
 | |
|     char *hline = hgetline();
 | |
| 
 | |
|     if (zmult < 0)
 | |
| 	return 1;
 | |
|     if (hline && *hline) {
 | |
| 	ZLE_STRING_T zhline = stringaszleline(hline, 0, &ics, NULL, NULL);
 | |
| 
 | |
| 	sizeline(ics + zlell + 1);
 | |
| 	/* careful of overlapping copy */
 | |
| 	for (s = zleline + zlell; --s >= zleline; s[ics] = *s)
 | |
| 	    ;
 | |
| 	ZS_memcpy(zleline, zhline, ics);
 | |
| 	zlell += ics;
 | |
| 	zlecs += ics;
 | |
| 	free(zhline);
 | |
|     }
 | |
|     ret = pushline(args);
 | |
|     if (!isfirstln) {
 | |
| 	errflag |= ERRFLAG_ERROR|ERRFLAG_INT;
 | |
| 	done = 1;
 | |
|     }
 | |
|     clearlist = 1;
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| pushinput(char **args)
 | |
| {
 | |
|     int i, ret;
 | |
| 
 | |
|     if (zmult < 0)
 | |
| 	return 1;
 | |
|     zmult += i = !isfirstln;
 | |
|     ret = pushlineoredit(args);
 | |
|     zmult -= i;
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /* Renamed to avoid clash with library function */
 | |
| /**/
 | |
| int
 | |
| zgetline(UNUSED(char **args))
 | |
| {
 | |
|     char *s = getlinknode(bufstack);
 | |
| 
 | |
|     if (!s) {
 | |
| 	return 1;
 | |
|     } else {
 | |
| 	int cc;
 | |
| 	ZLE_STRING_T lineadd = stringaszleline(s, 0, &cc, NULL, NULL);
 | |
| 
 | |
| 	spaceinline(cc);
 | |
| 	ZS_memcpy(zleline + zlecs, lineadd, cc);
 | |
| 	zlecs += cc;
 | |
| 	free(s);
 | |
| 	free(lineadd);
 | |
| 	clearlist = 1;
 | |
| 	/* not restoring stackhist as we're inserting into current line */
 | |
| 	stackhist = -1;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| historyincrementalsearchbackward(char **args)
 | |
| {
 | |
|     return doisearch(args, -1, 0);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| historyincrementalsearchforward(char **args)
 | |
| {
 | |
|     return doisearch(args, 1, 0);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| historyincrementalpatternsearchbackward(char **args)
 | |
| {
 | |
|     return doisearch(args, -1, 1);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| historyincrementalpatternsearchforward(char **args)
 | |
| {
 | |
|     return doisearch(args, 1, 1);
 | |
| }
 | |
| 
 | |
| static struct isrch_spot {
 | |
|     int hl;			/* This spot's histline */
 | |
|     int pat_hl;			/* histline where pattern search started */
 | |
|     unsigned short pos;		/* The search position in our metafied str */
 | |
|     unsigned short pat_pos;     /* pos where pattern search started */
 | |
|     unsigned short end_pos;	/* The position of the end of the matched 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_FORWARD	1
 | |
| #define ISS_NOMATCH_SHIFT 1
 | |
| } *isrch_spots;
 | |
| 
 | |
| static int max_spot = 0;
 | |
| 
 | |
| /**/
 | |
| void
 | |
| free_isrch_spots(void)
 | |
| {
 | |
|     zfree(isrch_spots, max_spot * sizeof(*isrch_spots));
 | |
|     max_spot = 0;
 | |
|     isrch_spots = NULL;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| set_isrch_spot(int num, int hl, int pos, int pat_hl, int pat_pos,
 | |
| 	       int end_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].pat_hl = pat_hl;
 | |
|     isrch_spots[num].pat_pos = (unsigned short)pat_pos;
 | |
|     isrch_spots[num].end_pos = (unsigned short)end_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_NOMATCH_SHIFT);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| get_isrch_spot(int num, int *hlp, int *posp, int *pat_hlp, int *pat_posp,
 | |
| 	       int *end_posp, int *csp, int *lenp, int *dirp, int *nomatch)
 | |
| {
 | |
|     *hlp = isrch_spots[num].hl;
 | |
|     *posp = (int)isrch_spots[num].pos;
 | |
|     *pat_hlp = isrch_spots[num].pat_hl;
 | |
|     *pat_posp = (int)isrch_spots[num].pat_pos;
 | |
|     *end_posp = (int)isrch_spots[num].end_pos;
 | |
|     *csp = (int)isrch_spots[num].cs;
 | |
|     *lenp = (int)isrch_spots[num].len;
 | |
|     *dirp = (isrch_spots[num].flags & ISS_FORWARD)? 1 : -1;
 | |
|     *nomatch = (int)(isrch_spots[num].flags >> ISS_NOMATCH_SHIFT);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * In pattern search mode, look through the list for a match at, or
 | |
|  * before or after the given position, according to the direction.
 | |
|  * Return new position or -1.
 | |
|  *
 | |
|  * Note this handles curpos out of range correctly, i.e. curpos < 0
 | |
|  * never matches when searching backwards and curpos > length of string
 | |
|  * never matches when searching forwards.
 | |
|  */
 | |
| static int
 | |
| isearch_newpos(LinkList matchlist, int curpos, int dir,
 | |
| 	       int *endmatchpos)
 | |
| {
 | |
|     LinkNode node;
 | |
| 
 | |
|     if (dir < 0) {
 | |
| 	for (node = lastnode(matchlist);
 | |
| 	     node != (LinkNode)matchlist; decnode(node)) {
 | |
| 	    Repldata rdata = (Repldata)getdata(node);
 | |
| 	    if (rdata->b <= curpos) {
 | |
| 		*endmatchpos = rdata->e;
 | |
| 		return rdata->b;
 | |
| 	    }
 | |
| 	}
 | |
|     } else {
 | |
| 	for (node = firstnode(matchlist);
 | |
| 	     node; incnode(node)) {
 | |
| 	    Repldata rdata = (Repldata)getdata(node);
 | |
| 	    if (rdata->b >= curpos) {
 | |
| 		*endmatchpos = rdata->e;
 | |
| 		return rdata->b;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Save an isearch buffer from sbuf to sbuf+sbptr
 | |
|  * into the string *search with length *searchlen.
 | |
|  * searchlen may be NULL; the string is a NULL-terminated metafied string.
 | |
|  */
 | |
| static void
 | |
| save_isearch_buffer(char *sbuf, int sbptr,
 | |
| 		    char **search, int *searchlen)
 | |
| {
 | |
|     if (*search)
 | |
| 	free(*search);
 | |
|     *search = zalloc(sbptr+1);
 | |
|     memcpy(*search, sbuf, sbptr);
 | |
|     if (searchlen)
 | |
| 	*searchlen = sbptr;
 | |
|     (*search)[sbptr] = '\0';
 | |
| }
 | |
| 
 | |
| #define ISEARCH_PROMPT		"XXXXXXX XXX-i-search: "
 | |
| #define FAILING_TEXT		"failing"
 | |
| #define INVALID_TEXT		"invalid"
 | |
| #define BAD_TEXT_LEN		7
 | |
| #define NORM_PROMPT_POS		(BAD_TEXT_LEN+1)
 | |
| #define FIRST_SEARCH_CHAR	(NORM_PROMPT_POS + 14)
 | |
| 
 | |
| /**/
 | |
| int isearch_active, isearch_startpos, isearch_endpos;
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| doisearch(char **args, int dir, int pattern)
 | |
| {
 | |
|     /* The full search buffer, including space for all prompts */
 | |
|     char *ibuf = zhalloc(80);
 | |
|     /*
 | |
|      * The part of the search buffer with the search string.
 | |
|      * This is a normal metafied string.
 | |
|      */
 | |
|     char *sbuf = ibuf + FIRST_SEARCH_CHAR;
 | |
|     /* The previous line shown to the user */
 | |
|     char *last_line = NULL;
 | |
|     /* Text of the history line being examined */
 | |
|     char *zt;
 | |
|     /*
 | |
|      * sbptr: index into sbuf.
 | |
|      * top_spot: stack index into the "isrch_spot" stack.
 | |
|      * sibuf: allocation size for ibuf
 | |
|      */
 | |
|     int sbptr = 0, top_spot = 0, sibuf = 80;
 | |
|     /*
 | |
|      * nomatch = 1: failing isearch
 | |
|      * nomatch = 2: invalid pattern
 | |
|      * skip_line: finished with current line, skip to next
 | |
|      * skip_pos: keep current line but try before/after current position.
 | |
|      */
 | |
|     int nomatch = 0, skip_line = 0, skip_pos = 0;
 | |
|     /*
 | |
|      * odir: original search direction
 | |
|      * sens: limit for zlinecmp to allow (3) or disallow (1) lower case
 | |
|      *       matching upper case.
 | |
|      */
 | |
|     int odir = dir, sens = zmult == 1 ? 3 : 1;
 | |
|     /*
 | |
|      * hl: the number of the history line we are looking at
 | |
|      * pos: the character position into it.  On backward matches the
 | |
|      *      cursor will be set to this; on forward matches to the end
 | |
|      *      of the matched string
 | |
|      */
 | |
|     int hl = histline, pos;
 | |
|     /*
 | |
|      * The value of hl and pos at which the last pattern match
 | |
|      * search started.  We need to record these because there's
 | |
|      * a pathology with pattern matching.  Here's an example.  Suppose
 | |
|      * the history consists of:
 | |
|      *  echo '*OH NO*'
 | |
|      *  echo '\n'
 | |
|      *  echo "*WHAT?*"
 | |
|      *  <...backward pattern search starts here...>
 | |
|      * The user types "\".  As there's nothing after it it's treated
 | |
|      * literally (and I certainly don't want to change that).  This
 | |
|      * goes to the second line.  Then the user types "*".  This
 | |
|      * ought to match the "*" in the line immediately before where the
 | |
|      * search started.  However, unless we return to that line for the
 | |
|      * new search it will instead carry on to the first line.  This is
 | |
|      * different from straight string matching where we never have
 | |
|      * to backtrack.
 | |
|      *
 | |
|      * I think these need resetting to the current hl and pos when
 | |
|      * we start a new search or repeat a search.  It seems to work,
 | |
|      * anyway.
 | |
|      *
 | |
|      * We could optimize this more, but I don't think there's a lot
 | |
|      * of point.  (Translation:  it's difficult.)
 | |
|      */
 | |
|     int pat_hl = hl, pat_pos;
 | |
|     /*
 | |
|      * This is the flag that we need to revert the positions to
 | |
|      * the above for the next pattern search.
 | |
|      */
 | |
|     int revert_patpos = 0;
 | |
|     /*
 | |
|      * Another nasty feature related to the above.  When
 | |
|      * we revert the position, we might advance the search to
 | |
|      * the same line again.  When we do this the test for ignoring
 | |
|      * duplicates may trigger.  This flag indicates that in this
 | |
|      * case it's OK.
 | |
|      */
 | |
|     int dup_ok = 0;
 | |
|     /*
 | |
|      * End position of the match.
 | |
|      * When forward matching, this is the position for the cursor.
 | |
|      * When backward matching, the cursor position is pos.
 | |
|      */
 | |
|     int end_pos = 0;
 | |
|     /*
 | |
|      * savekeys records the unget buffer, so that if we have arguments
 | |
|      * they don't pollute the input.
 | |
|      * feep indicates we should feep.  This is a well-known word
 | |
|      * meaning "to indicate an error in the zsh line editor".
 | |
|      */
 | |
|     int savekeys = -1, feep = 0;
 | |
|     /* Flag that we are at an old position, no need to search again */
 | |
|     int nosearch = 0;
 | |
|     /* Command read as input:  we don't read characters directly. */
 | |
|     Thingy cmd;
 | |
|     /* Save the keymap if necessary */
 | |
|     char *okeymap;
 | |
|     /* The current history entry, corresponding to hl */
 | |
|     Histent he;
 | |
|     /* When pattern matching, the compiled pattern */
 | |
|     Patprog patprog = NULL;
 | |
|     /* When pattern matching, the list of match positions */
 | |
|     LinkList matchlist = NULL;
 | |
|     /*
 | |
|      * When we exit isearching this may be a zle command to
 | |
|      * execute.  We save it and execute it after unmetafying the
 | |
|      * command line.
 | |
|      */
 | |
|     ZleIntFunc exitfn = (ZleIntFunc)0;
 | |
|     /*
 | |
|      * Flag that the search was aborted.
 | |
|      */
 | |
|     int aborted = 0;
 | |
| 
 | |
|     if (!(he = quietgethist(hl)))
 | |
| 	return 1;
 | |
| 
 | |
|     selectlocalmap(isearch_keymap);
 | |
| 
 | |
|     clearlist = 1;
 | |
| 
 | |
|     if (*args) {
 | |
| 	int len;
 | |
| 	char *arg;
 | |
| 	savekeys = kungetct;
 | |
| 	arg = getkeystring(*args, &len, GETKEYS_BINDKEY, NULL);
 | |
| 	ungetbytes(arg, len);
 | |
|     }
 | |
| 
 | |
|     strcpy(ibuf, ISEARCH_PROMPT);
 | |
|     /* careful with fwd/bck: we don't want the NULL copied */
 | |
|     memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
 | |
|     okeymap = ztrdup(curkeymapname);
 | |
|     selectkeymap("main", 1);
 | |
| 
 | |
|     metafy_line();
 | |
|     remember_edits();
 | |
|     zt = GETZLETEXT(he);
 | |
|     pat_pos = pos = zlemetacs;
 | |
|     for (;;) {
 | |
| 	/* Remember the current values in case search fails (doesn't push). */
 | |
| 	set_isrch_spot(top_spot, hl, pos, pat_hl, pat_pos, end_pos,
 | |
| 		       zlemetacs, sbptr, dir, nomatch);
 | |
| 	if (sbptr == 1 && sbuf[0] == '^') {
 | |
| 	    zlemetacs = 0;
 | |
|     	    nomatch = 0;
 | |
| 	    statusline = ibuf + NORM_PROMPT_POS;
 | |
| 	} else if (sbptr > 0) {
 | |
| 	    /* The matched text, used as flag that we matched */
 | |
| 	    char *t = NULL;
 | |
| 	    last_line = zt;
 | |
| 
 | |
| 	    sbuf[sbptr] = '\0';
 | |
| 	    if (pattern && !patprog && !nosearch) {
 | |
| 		/* avoid too much heap use, can get heavy round here... */
 | |
| 		char *patbuf = ztrdup(sbuf);
 | |
| 		char *patstring;
 | |
| 		/*
 | |
| 		 * Do not use static pattern buffer (PAT_STATIC) since we
 | |
| 		 * call zle hooks, which might call other pattern
 | |
| 		 * functions.  Use PAT_ZDUP because we re-use the pattern
 | |
| 		 * in subsequent loops, so we can't pushheap/popheap.
 | |
| 		 * Use PAT_NOANCH because we don't need the match anchored
 | |
| 		 * to the end, even if it is at the start.
 | |
| 		 */
 | |
| 		int patflags = PAT_ZDUP|PAT_NOANCH;
 | |
| 		if (sbuf[0] == '^') {
 | |
| 		    /*
 | |
| 		     * We'll handle the anchor later when
 | |
| 		     * we call into the globbing code.
 | |
| 		     */
 | |
| 		    patstring = patbuf + 1;
 | |
| 		} else {
 | |
| 		    /* Scanning for multiple matches per line */
 | |
| 		    patflags |= PAT_SCAN;
 | |
| 		    patstring = patbuf;
 | |
| 		}
 | |
| 		if (sens == 3)
 | |
| 		    patflags |= PAT_LCMATCHUC;
 | |
| 		tokenize(patstring);
 | |
| 		remnulargs(patstring);
 | |
| 		patprog = patcompile(patstring, patflags, NULL);
 | |
| 		free(patbuf);
 | |
| 		if (matchlist) {
 | |
| 		    freematchlist(matchlist);
 | |
| 		    matchlist = NULL;
 | |
| 		}
 | |
| 		if (patprog) {
 | |
| 		    revert_patpos = 1;
 | |
| 		    skip_pos = 0;
 | |
| 		} else {
 | |
| 		    if (nomatch != 2) {
 | |
| 			handlefeep(zlenoargs);
 | |
| 			nomatch = 2;
 | |
| 		    }
 | |
| 		    /* indicate "invalid" in status line */
 | |
| 		    memcpy(ibuf, INVALID_TEXT, BAD_TEXT_LEN);
 | |
| 		    statusline = ibuf;
 | |
| 		}
 | |
| 	    }
 | |
| 	    /*
 | |
| 	     * skip search if pattern compilation failed, or
 | |
| 	     * if we back somewhere we already searched.
 | |
| 	     */
 | |
| 	    while ((!pattern || patprog) && !nosearch) {
 | |
| 		if (patprog) {
 | |
| 		    if (revert_patpos) {
 | |
| 			/*
 | |
| 			 * Search from where the previous
 | |
| 			 * search started; see note above.
 | |
| 			 * This is down here within the loop because of
 | |
| 			 * the "nosearch" optimisation.
 | |
| 			 */
 | |
| 			revert_patpos = 0;
 | |
| 			dup_ok = 1;
 | |
| 			he = quietgethist(hl = pat_hl);
 | |
| 			zt = GETZLETEXT(he);
 | |
| 			pos = pat_pos;
 | |
| 		    }
 | |
| 		    /*
 | |
| 		     * We are pattern matching against the current
 | |
| 		     * line.  If anchored at the start, this is
 | |
| 		     * easy; a single test suffices.
 | |
| 		     *
 | |
| 		     * Otherwise, our strategy is to retrieve a linked
 | |
| 		     * list of all matches within the current line and
 | |
| 		     * scan through it as appropriate.  This isn't
 | |
| 		     * actually significantly more efficient, but
 | |
| 		     * it is algorithmically easier since we just
 | |
| 		     * need a single one-off line-matching interface
 | |
| 		     * to the pattern code.  We use a variant of
 | |
| 		     * the code used for replacing within parameters
 | |
| 		     * which for historical reasons is in glob.c rather
 | |
| 		     * than pattern.c.
 | |
| 		     *
 | |
| 		     * The code for deciding whether to skip something
 | |
| 		     * is a bit icky but that sort of code always is.
 | |
| 		     */
 | |
| 		    if (!skip_line) {
 | |
| 			if (sbuf[0] == '^') {
 | |
| 			    /*
 | |
| 			     * skip_pos applies to the whole line in
 | |
| 			     * this mode.
 | |
| 			     */
 | |
| 			    if (!skip_pos &&
 | |
| 				pattryrefs(patprog, zt, -1, -1, NULL, 0,
 | |
| 					   NULL, NULL, &end_pos))
 | |
| 				t = zt;
 | |
| 			} else {
 | |
| 			    if (!matchlist && !skip_pos) {
 | |
| 				if (!getmatchlist(zt, patprog, &matchlist) ||
 | |
| 				    !firstnode(matchlist)) {
 | |
| 				    if (matchlist) {
 | |
| 					freematchlist(matchlist);
 | |
| 					matchlist = NULL;
 | |
| 				    }
 | |
| 				}
 | |
| 			    }
 | |
| 			    if (matchlist) {
 | |
| 				int newpos;
 | |
| 				if (!skip_pos) {
 | |
| 				    /* OK to match at current pos */
 | |
| 				    newpos = pos;
 | |
| 				} else {
 | |
| 				    if (dir < 0)
 | |
| 					newpos = pos - 1;
 | |
| 				    else
 | |
| 					newpos = pos + 1;
 | |
| 				}
 | |
| 				newpos = isearch_newpos(matchlist, newpos,
 | |
| 							dir, &end_pos);
 | |
| 				/* need a new list next time if off the end */
 | |
| 				if (newpos < 0) {
 | |
| 				    freematchlist(matchlist);
 | |
| 				    matchlist = NULL;
 | |
| 				} else {
 | |
| 				    t = zt + newpos;
 | |
| 				}
 | |
| 			    }
 | |
| 			}
 | |
| 		    }
 | |
| 		    skip_pos = 0;
 | |
| 		} else {
 | |
| 		    /*
 | |
| 		     * If instructed, move past a match position:
 | |
| 		     * backwards if searching backwards (skipping
 | |
| 		     * the line if we're at the start), forwards
 | |
| 		     * if searching forwards (skipping a line if we're
 | |
| 		     * at the end).
 | |
| 		     */
 | |
| 		    if (skip_pos) {
 | |
| 			if (dir < 0) {
 | |
| 			    if (pos == 0)
 | |
| 				skip_line = 1;
 | |
| 			    else
 | |
| 				pos = backwardmetafiedchar(zlemetaline,
 | |
| 							   zlemetaline + pos,
 | |
| 							   NULL) - zlemetaline;
 | |
| 			} else if (sbuf[0] != '^') {
 | |
| 			    if (pos >= (int)strlen(zt) - 1)
 | |
| 				skip_line = 1;
 | |
| 			    else
 | |
| 				pos += 1;
 | |
| 			} else
 | |
| 			    skip_line = 1;
 | |
| 			skip_pos = 0;
 | |
| 		    }
 | |
| 		    /*
 | |
| 		     * First search for a(nother) match within the
 | |
| 		     * current line, unless we've been told to skip it.
 | |
| 		     */
 | |
| 		    if (!skip_line) {
 | |
| 			if (sbuf[0] == '^') {
 | |
| 			    if (zlinecmp(zt, sbuf + 1) < sens)
 | |
| 				t = zt;
 | |
| 			} else
 | |
| 			    t = zlinefind(zt, pos, sbuf, dir, sens);
 | |
| 			if (t)
 | |
| 			    end_pos = (t - zt) + sbptr - (sbuf[0] == '^');
 | |
| 		    }
 | |
| 		}
 | |
| 		if (t) {
 | |
| 		    pos = t - zt;
 | |
| 		    break;
 | |
| 		}
 | |
| 		/*
 | |
| 		 * If not found within that line, move through
 | |
| 		 * the history to try again.
 | |
| 		 */
 | |
| 		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_NOMATCH_SHIFT))
 | |
| 			top_spot--;
 | |
| 		    get_isrch_spot(top_spot, &hl, &pos, &pat_hl, &pat_pos,
 | |
| 				   &end_pos, &zlemetacs, &sbptr, &dir,
 | |
| 				   &nomatch);
 | |
| 		    if (nomatch != 1) {
 | |
| 			feep = 1;
 | |
| 			nomatch = 1;
 | |
| 		    }
 | |
| 		    he = quietgethist(hl);
 | |
| 		    zt = GETZLETEXT(he);
 | |
| 		    skip_line = 0;
 | |
| 		    /* indicate "failing" in status line */
 | |
| 		    memcpy(ibuf, nomatch == 2 ? INVALID_TEXT :FAILING_TEXT,
 | |
| 			   BAD_TEXT_LEN);
 | |
| 		    statusline = ibuf;
 | |
| 		    break;
 | |
| 		}
 | |
| 		hl = he->histnum;
 | |
| 		zt = GETZLETEXT(he);
 | |
| 		pos = (dir == 1) ? 0 : strlen(zt);
 | |
| 		if (dup_ok)
 | |
| 		    skip_line = 0;
 | |
| 		else
 | |
| 		    skip_line = isset(HISTFINDNODUPS)
 | |
| 			? !!(he->node.flags & HIST_DUP)
 | |
| 			: !strcmp(zt, last_line);
 | |
| 	    }
 | |
| 	    dup_ok = 0;
 | |
| 	    /*
 | |
| 	     * If we matched above (t set), set the new line.
 | |
| 	     * If we didn't, but are here because we are on a previous
 | |
| 	     * match (nosearch set and nomatch not, set the line again).
 | |
| 	     */
 | |
| 	    if (t || (nosearch && !nomatch)) {
 | |
| 		zle_setline(he);
 | |
| 		if (dir == 1)
 | |
| 		    zlemetacs = end_pos;
 | |
| 		else
 | |
| 		    zlemetacs = pos;
 | |
| 		statusline = ibuf + NORM_PROMPT_POS;
 | |
| 		nomatch = 0;
 | |
| 	    }
 | |
| 	} else {
 | |
| 	    top_spot = 0;
 | |
|     	    nomatch = 0;
 | |
| 	    statusline = ibuf + NORM_PROMPT_POS;
 | |
| 	}
 | |
| 	nosearch = 0;
 | |
| 	if (feep) {
 | |
| 	    handlefeep(zlenoargs);
 | |
| 	    feep = 0;
 | |
| 	}
 | |
| 	sbuf[sbptr] = '_';
 | |
| 	sbuf[sbptr+1] = '\0';
 | |
| 	if (!nomatch && sbptr && (sbptr > 1 || sbuf[0] != '^')) {
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
| 	    int charpos = 0, charcount = 0, ret;
 | |
| 	    wint_t wc;
 | |
| 	    mbstate_t mbs;
 | |
| 
 | |
| 	    /*
 | |
| 	     * Count unmetafied character positions for the
 | |
| 	     * start and end of the match for the benefit of
 | |
| 	     * highlighting.
 | |
| 	     */
 | |
| 	    memset(&mbs, 0, sizeof(mbs));
 | |
| 	    while (charpos < end_pos) {
 | |
| 		ret = mb_metacharlenconv_r(zlemetaline + charpos, &wc, &mbs);
 | |
| 		if (ret <= 0) /* Unrecognised, treat as single char */
 | |
| 		    ret = 1;
 | |
| 		if (charpos <= pos && pos < charpos + ret)
 | |
| 		    isearch_startpos = charcount;
 | |
| 		charcount++;
 | |
| 		charpos += ret;
 | |
| 	    }
 | |
| 	    isearch_endpos = charcount;
 | |
| #else
 | |
| 	    isearch_startpos = ztrsub(zlemetaline + pos, zlemetaline);
 | |
| 	    isearch_endpos = ztrsub(zlemetaline + end_pos,
 | |
| 				    zlemetaline);
 | |
| #endif
 | |
| 	    isearch_active = 1;
 | |
| 	} else
 | |
| 	    isearch_active = 0;
 | |
|     ref:
 | |
| 	zlecallhook("zle-isearch-update", NULL);
 | |
| 	redrawhook();
 | |
| 	zrefresh();
 | |
| 	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
 | |
| 	    int i;
 | |
| 	    aborted = 1;
 | |
| 	    save_isearch_buffer(sbuf, sbptr,
 | |
| 				&previous_aborted_search, NULL);
 | |
| 	    get_isrch_spot(0, &hl, &pos, &pat_hl, &pat_pos, &end_pos,
 | |
| 			   &i, &sbptr, &dir, &nomatch);
 | |
| 	    he = quietgethist(hl);
 | |
| 	    zle_setline(he);
 | |
| 	    zt = GETZLETEXT(he);
 | |
| 	    zlemetacs = i;
 | |
| 	    break;
 | |
| 	}
 | |
| 	if(cmd == Th(z_clearscreen)) {
 | |
| 	    clearscreen(zlenoargs);
 | |
| 	    goto ref;
 | |
| 	} else if(cmd == Th(z_redisplay)) {
 | |
| 	    redisplay(zlenoargs);
 | |
| 	    goto ref;
 | |
| 	} else if(cmd == Th(z_vicmdmode)) {
 | |
| 	    if(selectkeymap(invicmdmode() ? "main" : "vicmd", 0))
 | |
| 		feep = 1;
 | |
| 	    goto ref;
 | |
|        } else if (cmd == Th(z_vibackwarddeletechar) ||
 | |
| 		  cmd == Th(z_backwarddeletechar) ||
 | |
| 		  cmd == Th(z_vibackwardkillword) ||
 | |
| 		  cmd == Th(z_backwardkillword) ||
 | |
| 		  cmd == Th(z_backwarddeleteword)) {
 | |
| 	    int only_one = (cmd == Th(z_vibackwarddeletechar) ||
 | |
| 			    cmd == Th(z_backwarddeletechar));
 | |
| 	    int old_sbptr = sbptr;
 | |
| 	    if (top_spot) {
 | |
| 		for (;;) {
 | |
| 		    get_isrch_spot(--top_spot, &hl, &pos, &pat_hl,
 | |
| 				   &pat_pos,  &end_pos, &zlemetacs,
 | |
| 				   &sbptr, &dir, &nomatch);
 | |
| 		    if (only_one || !top_spot || old_sbptr != sbptr)
 | |
| 			break;
 | |
| 		}
 | |
| 		freepatprog(patprog);
 | |
| 		patprog = NULL;
 | |
| 		nosearch = 1;
 | |
| 		skip_pos = 0;
 | |
| 	    } else
 | |
| 		feep = 1;
 | |
| 	    if (nomatch) {
 | |
| 		memcpy(ibuf, nomatch == 2 ? INVALID_TEXT : FAILING_TEXT,
 | |
| 		       BAD_TEXT_LEN);
 | |
| 		statusline = ibuf;
 | |
| 		skip_pos = 1;
 | |
| 	    }
 | |
| 	    he = quietgethist(hl);
 | |
| 	    zt = GETZLETEXT(he);
 | |
| 	    /*
 | |
| 	     * Set the line for the cases where we won't go past
 | |
| 	     * the usual line-setting logic:  if we're not on a match,
 | |
| 	     * or if we don't have enough to search for.
 | |
| 	     */
 | |
| 	    if (nomatch || !sbptr || (sbptr == 1 && sbuf[0] == '^')) {
 | |
| 		int i = zlemetacs;
 | |
| 		zle_setline(he);
 | |
| 		zlemetacs = i;
 | |
| 	    }
 | |
| 	    memcpy(ibuf + NORM_PROMPT_POS,
 | |
| 		   (dir == 1) ? "fwd" : "bck", 3);
 | |
| 	    continue;
 | |
| 	} else if(cmd == Th(z_acceptandhold)) {
 | |
| 	    exitfn = acceptandhold;
 | |
| 	    break;
 | |
| 	} else if(cmd == Th(z_acceptandinfernexthistory)) {
 | |
| 	    exitfn = acceptandinfernexthistory;
 | |
| 	    break;
 | |
| 	} else if(cmd == Th(z_acceptlineanddownhistory)) {
 | |
| 	    exitfn = acceptlineanddownhistory;
 | |
| 	    break;
 | |
| 	} else if(cmd == Th(z_acceptline)) {
 | |
| 	    exitfn = acceptline;
 | |
| 	    break;
 | |
| 	} else if(cmd == Th(z_historyincrementalsearchbackward) ||
 | |
| 		  cmd == Th(z_historyincrementalpatternsearchbackward)) {
 | |
| 	    pat_hl = hl;
 | |
| 	    pat_pos = pos;
 | |
| 	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
 | |
| 			   zlemetacs, sbptr, dir, nomatch);
 | |
| 	    if (dir != -1)
 | |
| 		dir = -1;
 | |
| 	    else
 | |
| 		skip_pos = 1;
 | |
| 	    goto rpt;
 | |
| 	} else if(cmd == Th(z_historyincrementalsearchforward) ||
 | |
| 		  cmd == Th(z_historyincrementalpatternsearchforward)) {
 | |
| 	    pat_hl = hl;
 | |
| 	    pat_pos = pos;
 | |
| 	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
 | |
| 			   zlemetacs, sbptr, dir, nomatch);
 | |
| 	    if (dir != 1)
 | |
| 		dir = 1;
 | |
| 	    else
 | |
| 		skip_pos = 1;
 | |
| 	    goto rpt;
 | |
| 	} else if(cmd == Th(z_virevrepeatsearch)) {
 | |
| 	    pat_hl = hl;
 | |
| 	    pat_pos = pos;
 | |
| 	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
 | |
| 			   zlemetacs, sbptr, dir, nomatch);
 | |
| 	    dir = -odir;
 | |
| 	    skip_pos = 1;
 | |
| 	    goto rpt;
 | |
| 	} else if(cmd == Th(z_virepeatsearch)) {
 | |
| 	    pat_hl = hl;
 | |
| 	    pat_pos = pos;
 | |
| 	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
 | |
| 			   zlemetacs, sbptr, dir, nomatch);
 | |
| 	    dir = odir;
 | |
| 	    skip_pos = 1;
 | |
| 	rpt:
 | |
| 	    if (!sbptr && previous_search_len && dir == odir) {
 | |
| 		if (previous_search_len > sibuf - FIRST_SEARCH_CHAR - 2) {
 | |
| 		    ibuf = hrealloc((char *)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] = '^';
 | |
| 		sbuf[sbptr+1] = '\0';
 | |
| 		zrefresh();
 | |
| 	    }
 | |
| 	    if (getfullchar(0) == ZLEEOF)
 | |
| 		feep = 1;
 | |
| 	    else
 | |
| 		goto ins;
 | |
| 	} else if (cmd == Th(z_bracketedpaste)) {
 | |
| 	    char *paste = bracketedstring();
 | |
| 	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
 | |
| 			   zlemetacs, sbptr, dir, nomatch);
 | |
| 	    size_t pastelen = strlen(paste);
 | |
| 	    if (sbptr + pastelen >= sibuf - FIRST_SEARCH_CHAR - 2) {
 | |
| 		int oldsize = sibuf;
 | |
| 		sibuf += (pastelen >= sibuf) ? pastelen + 1 : sibuf;
 | |
| 		ibuf = hrealloc(ibuf, oldsize, sibuf);
 | |
| 		sbuf = ibuf + FIRST_SEARCH_CHAR;
 | |
| 	    }
 | |
| 	    strcpy(sbuf + sbptr, paste);
 | |
| 	    sbptr += pastelen;
 | |
| 	    freepatprog(patprog);
 | |
| 	    patprog = NULL;
 | |
| 	    free(paste);
 | |
| 	} else if (cmd == Th(z_acceptsearch)) {
 | |
| 	    break;
 | |
| 	} else {
 | |
| 	    if(cmd == Th(z_selfinsertunmeta)) {
 | |
| 		fixunmeta();
 | |
| 	    } else if (cmd == Th(z_magicspace)) {
 | |
| 		fixmagicspace();
 | |
| 	    } else if (cmd == Th(z_selfinsert)) {
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
| 		if (!lastchar_wide_valid)
 | |
| 		    if (getrestchar(lastchar, NULL, NULL) == WEOF) {
 | |
| 			handlefeep(zlenoargs);
 | |
| 			continue;
 | |
| 		    }
 | |
| #else
 | |
| 		;
 | |
| #endif
 | |
| 	    } else {
 | |
| 		ungetkeycmd();
 | |
| 		if (cmd == Th(z_sendbreak)) {
 | |
| 		    aborted = 1;
 | |
| 		    save_isearch_buffer(sbuf, sbptr,
 | |
| 					&previous_aborted_search, NULL);
 | |
| 		    sbptr = 0;
 | |
| 		}
 | |
| 		break;
 | |
| 	    }
 | |
| 	ins:
 | |
| 	    if (sbptr == PATH_MAX) {
 | |
| 		feep = 1;
 | |
| 		continue;
 | |
| 	    }
 | |
| 	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
 | |
| 			   zlemetacs, sbptr, dir, nomatch);
 | |
| 	    if (sbptr >= sibuf - FIRST_SEARCH_CHAR - 2 
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
| 		- 2 * (int)MB_CUR_MAX
 | |
| #endif
 | |
| 		) {
 | |
| 		ibuf = hrealloc(ibuf, sibuf, sibuf * 2);
 | |
| 		sbuf = ibuf + FIRST_SEARCH_CHAR;
 | |
| 		sibuf *= 2;
 | |
| 	    }
 | |
| 	    /*
 | |
| 	     * We've supposedly arranged above that lastchar_wide is
 | |
| 	     * always valid at this point.
 | |
| 	     */
 | |
| 	    sbptr += zlecharasstring(LASTFULLCHAR, sbuf + sbptr);
 | |
| 	    freepatprog(patprog);
 | |
| 	    patprog = NULL;
 | |
| 	}
 | |
| 	if (feep)
 | |
| 	    handlefeep(zlenoargs);
 | |
| 	feep = 0;
 | |
|     }
 | |
|     if (sbptr) {
 | |
| 	save_isearch_buffer(sbuf, sbptr,
 | |
| 			    &previous_search, &previous_search_len);
 | |
|     }
 | |
|     statusline = NULL;
 | |
|     unmetafy_line();
 | |
|     zlecallhook("zle-isearch-exit", NULL);
 | |
|     redrawhook();
 | |
|     if (exitfn)
 | |
| 	exitfn(zlenoargs);
 | |
|     selectkeymap(okeymap, 1);
 | |
|     zsfree(okeymap);
 | |
|     if (matchlist)
 | |
| 	freematchlist(matchlist);
 | |
|     freepatprog(patprog);
 | |
|     isearch_active = 0;
 | |
|     /*
 | |
|      * Don't allow unused characters provided as a string to the
 | |
|      * widget to overflow and be used as separated commands.
 | |
|      */
 | |
|     if (savekeys >= 0 && kungetct > savekeys)
 | |
| 	kungetct = savekeys;
 | |
| 
 | |
|     selectlocalmap(NULL);
 | |
| 
 | |
|     return aborted ? 3 : nomatch;
 | |
| }
 | |
| 
 | |
| static Histent
 | |
| infernexthist(Histent he, UNUSED(char **args))
 | |
| {
 | |
|     metafy_line();
 | |
|     for (he = movehistent(he, -2, HIST_FOREIGN);
 | |
| 	 he; he = movehistent(he, -1, HIST_FOREIGN)) {
 | |
| 	if (!zlinecmp(GETZLETEXT(he), zlemetaline)) {
 | |
| 	    unmetafy_line();
 | |
| 	    return movehistent(he, 1, HIST_FOREIGN);
 | |
| 	}
 | |
|     }
 | |
|     unmetafy_line();
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| acceptandinfernexthistory(char **args)
 | |
| {
 | |
|     Histent he;
 | |
| 
 | |
|     if (!(he = infernexthist(hist_ring, args)))
 | |
| 	return 1;
 | |
|     zpushnode(bufstack, ztrdup(he->node.nam));
 | |
|     done = 1;
 | |
|     stackhist = he->histnum;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| infernexthistory(char **args)
 | |
| {
 | |
|     Histent he = quietgethist(histline);
 | |
| 
 | |
|     if (!he || !(he = infernexthist(he, args)))
 | |
| 	return 1;
 | |
|     zle_setline(he);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| vifetchhistory(UNUSED(char **args))
 | |
| {
 | |
|     if (zmult < 0)
 | |
| 	return 1;
 | |
|     if (histline == curhist) {
 | |
| 	if (!(zmod.flags & MOD_MULT)) {
 | |
| 	    zlecs = zlell;
 | |
| 	    zlecs = findbol();
 | |
| 	    return 0;
 | |
| 	}
 | |
|     }
 | |
|     if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist, 0, 0) &&
 | |
| 	isset(HISTBEEP)) {
 | |
| 	return 1;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* the last vi search */
 | |
| 
 | |
| static char *visrchstr, *vipenultsrchstr;
 | |
| static int visrchsense;
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| getvisrchstr(void)
 | |
| {
 | |
|     char *sbuf = zhalloc(80);
 | |
|     int sptr = 1, ret = 0, ssbuf = 80, feep = 0;
 | |
|     Thingy cmd;
 | |
|     char *okeymap = ztrdup(curkeymapname);
 | |
| 
 | |
|     if (vipenultsrchstr) {
 | |
| 	zsfree(vipenultsrchstr);
 | |
| 	vipenultsrchstr = NULL;
 | |
|     }
 | |
| 
 | |
|     if (visrchstr) {
 | |
| 	vipenultsrchstr = visrchstr;
 | |
| 	visrchstr = NULL;
 | |
|     }
 | |
|     clearlist = 1;
 | |
|     statusline = sbuf;
 | |
|     sbuf[0] = (visrchsense == -1) ? '?' : '/';
 | |
|     selectkeymap("main", 1);
 | |
|     while (sptr) {
 | |
| 	sbuf[sptr] = '_';
 | |
| 	sbuf[sptr+1] = '\0';
 | |
| 	zrefresh();
 | |
| 	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
 | |
| 	    ret = 0;
 | |
| 	    break;
 | |
| 	}
 | |
| 	if(cmd == Th(z_magicspace)) {
 | |
| 	    fixmagicspace();
 | |
| 	    cmd = Th(z_selfinsert);
 | |
| 	}
 | |
| 	if(cmd == Th(z_redisplay)) {
 | |
| 	    redisplay(zlenoargs);
 | |
| 	} else if(cmd == Th(z_clearscreen)) {
 | |
| 	    clearscreen(zlenoargs);
 | |
| 	} else if(cmd == Th(z_acceptline) ||
 | |
| 	    	cmd == Th(z_vicmdmode)) {
 | |
| 	    sbuf[sptr] = ZWC('\0');
 | |
| 	    visrchstr = ztrdup(sbuf+1);
 | |
| 	    if (!strlen(visrchstr)) {
 | |
| 	        zsfree(visrchstr);
 | |
| 		visrchstr = ztrdup(vipenultsrchstr);
 | |
| 	    }
 | |
| 	    ret = 1;
 | |
| 	    sptr = 0;
 | |
| 	} else if(cmd == Th(z_backwarddeletechar) ||
 | |
| 		  cmd == Th(z_vibackwarddeletechar)) {
 | |
| 	    sptr = backwardmetafiedchar(sbuf+1, sbuf+sptr, NULL) - sbuf;
 | |
| 	} else if(cmd == Th(z_backwardkillword) ||
 | |
| 		  cmd == Th(z_vibackwardkillword)) {
 | |
| 	    convchar_t cc;
 | |
| 	    char *newpos;
 | |
| 	    while (sptr != 1) {
 | |
| 		newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc);
 | |
| 		if (!ZC_iblank(cc))
 | |
| 		    break;
 | |
| 		sptr = newpos - sbuf;
 | |
| 	    }
 | |
| 	    if (sptr > 1) {
 | |
| 		newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc);
 | |
| 		if (ZC_iident(cc)) {
 | |
| 		    for (;;) {
 | |
| 			sptr = newpos - sbuf;
 | |
| 			if (sptr == 1)
 | |
| 			    break;
 | |
| 			newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc);
 | |
| 			if (!ZC_iident(cc))
 | |
| 			    break;
 | |
| 		    }
 | |
| 		} else {
 | |
| 		    for (;;) {
 | |
| 			sptr = newpos - sbuf;
 | |
| 			if (sptr == 1)
 | |
| 			    break;
 | |
| 			newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc);
 | |
| 			if (ZC_iident(cc) || ZC_iblank(cc))
 | |
| 			    break;
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	} else if(cmd == Th(z_viquotedinsert) || cmd == Th(z_quotedinsert)) {
 | |
| 	    if(cmd == Th(z_viquotedinsert)) {
 | |
| 		sbuf[sptr] = '^';
 | |
| 		zrefresh();
 | |
| 	    }
 | |
| 	    if (getfullchar(0) == ZLEEOF)
 | |
| 		feep = 1;
 | |
| 	    else
 | |
| 		goto ins;
 | |
| 	} else if(cmd == Th(z_selfinsertunmeta) || cmd == Th(z_selfinsert)) {
 | |
| 	    if(cmd == Th(z_selfinsertunmeta)) {
 | |
| 		fixunmeta();
 | |
| 	    } else {
 | |
| #ifdef MULTIBYTE_SUPPORT
 | |
| 		if (!lastchar_wide_valid)
 | |
| 		    if (getrestchar(lastchar, NULL, NULL) == WEOF) {
 | |
| 			handlefeep(zlenoargs);
 | |
| 			continue;
 | |
| 		    }
 | |
| #else
 | |
| 		;
 | |
| #endif
 | |
| 	    }
 | |
| 	  ins:
 | |
| 	    if (sptr == ssbuf - 1) {
 | |
| 		char *newbuf = (char *)zhalloc((ssbuf *= 2));
 | |
| 		strcpy(newbuf, sbuf);
 | |
| 		statusline = sbuf = newbuf;
 | |
| 	    }
 | |
| 	    sptr += zlecharasstring(LASTFULLCHAR, sbuf + sptr);
 | |
| 	} else {
 | |
| 	    feep = 1;
 | |
| 	}
 | |
| 	if (feep)
 | |
| 	    handlefeep(zlenoargs);
 | |
| 	feep = 0;
 | |
|     }
 | |
|     statusline = NULL;
 | |
|     selectkeymap(okeymap, 1);
 | |
|     zsfree(okeymap);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| vihistorysearchforward(char **args)
 | |
| {
 | |
|     if (*args) {
 | |
| 	int ose = visrchsense, ret;
 | |
| 	char *ost = visrchstr;
 | |
| 
 | |
| 	visrchsense = 1;
 | |
| 	visrchstr = *args;
 | |
| 	ret = virepeatsearch(zlenoargs);
 | |
| 	visrchsense = ose;
 | |
| 	visrchstr = ost;
 | |
| 	return ret;
 | |
|     }
 | |
|     visrchsense = 1;
 | |
|     if (getvisrchstr())
 | |
| 	return virepeatsearch(zlenoargs);
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| vihistorysearchbackward(char **args)
 | |
| {
 | |
|     if (*args) {
 | |
| 	int ose = visrchsense, ret;
 | |
| 	char *ost = visrchstr;
 | |
| 
 | |
| 	visrchsense = -1;
 | |
| 	visrchstr = *args;
 | |
| 	ret = virepeatsearch(zlenoargs);
 | |
| 	visrchsense = ose;
 | |
| 	visrchstr = ost;
 | |
| 	return ret;
 | |
|     }
 | |
|     visrchsense = -1;
 | |
|     if (getvisrchstr())
 | |
| 	return virepeatsearch(zlenoargs);
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| virepeatsearch(UNUSED(char **args))
 | |
| {
 | |
|     Histent he;
 | |
|     int n = zmult;
 | |
|     char *zt;
 | |
| 
 | |
|     if (!visrchstr)
 | |
| 	return 1;
 | |
|     if (zmult < 0) {
 | |
| 	n = -n;
 | |
| 	visrchsense = -visrchsense;
 | |
|     }
 | |
|     if (!(he = quietgethist(histline)))
 | |
| 	return 1;
 | |
|     metafy_line();
 | |
|     while ((he = movehistent(he, visrchsense, hist_skip_flags))) {
 | |
| 	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
 | |
| 	    continue;
 | |
| 	zt = GETZLETEXT(he);
 | |
| 	if (zlinecmp(zt, zlemetaline) &&
 | |
| 	    (*visrchstr == '^' ? strpfx(visrchstr + 1, zt) :
 | |
| 	     zlinefind(zt, 0, visrchstr, 1, 1) != 0)) {
 | |
| 	    if (--n <= 0) {
 | |
| 		unmetafy_line();
 | |
| 		zle_setline(he);
 | |
| 		return 0;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     unmetafy_line();
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| virevrepeatsearch(char **args)
 | |
| {
 | |
|     int ret;
 | |
|     visrchsense = -visrchsense;
 | |
|     ret = virepeatsearch(args);
 | |
|     visrchsense = -visrchsense;
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /* Extra function added by A.R. Iano-Fletcher.	*/
 | |
| /*The extern variable "zlecs" is the position of the cursor. */
 | |
| /* history-beginning-search-backward */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| historybeginningsearchbackward(char **args)
 | |
| {
 | |
|     Histent he;
 | |
|     int cpos = zlecs;		/* save cursor position */
 | |
|     int n = zmult;
 | |
|     char *zt;
 | |
| 
 | |
|     if (zmult < 0) {
 | |
| 	int ret;
 | |
| 	zmult = -n;
 | |
| 	ret = historybeginningsearchforward(args);
 | |
| 	zmult = n;
 | |
| 	return ret;
 | |
|     }
 | |
|     if (!(he = quietgethist(histline)))
 | |
| 	return 1;
 | |
|     metafy_line();
 | |
|     while ((he = movehistent(he, -1, hist_skip_flags))) {
 | |
| 	int tst;
 | |
| 	char sav;
 | |
| 	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
 | |
| 	    continue;
 | |
| 	zt = GETZLETEXT(he);
 | |
| 	sav = zlemetaline[zlemetacs];
 | |
| 	zlemetaline[zlemetacs] = '\0';
 | |
| 	tst = zlinecmp(zt, zlemetaline);
 | |
| 	zlemetaline[zlemetacs] = sav;
 | |
| 	if (tst < 0 && zlinecmp(zt, zlemetaline)) {
 | |
| 	    if (--n <= 0) {
 | |
| 		unmetafy_line();
 | |
| 		zle_setline(he);
 | |
| 		zlecs = cpos;
 | |
| 		CCRIGHT();
 | |
| 		return 0;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     unmetafy_line();
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /* Extra function added by A.R. Iano-Fletcher.	*/
 | |
| 
 | |
| /* history-beginning-search-forward */
 | |
| /**/
 | |
| int
 | |
| historybeginningsearchforward(char **args)
 | |
| {
 | |
|     Histent he;
 | |
|     int cpos = zlecs;		/* save cursor position */
 | |
|     int n = zmult;
 | |
|     char *zt;
 | |
| 
 | |
|     if (zmult < 0) {
 | |
| 	int ret;
 | |
| 	zmult = -n;
 | |
| 	ret = historybeginningsearchbackward(args);
 | |
| 	zmult = n;
 | |
| 	return ret;
 | |
|     }
 | |
|     if (!(he = quietgethist(histline)))
 | |
| 	return 1;
 | |
|     metafy_line();
 | |
|     while ((he = movehistent(he, 1, hist_skip_flags))) {
 | |
| 	char sav;
 | |
| 	int tst;
 | |
| 	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
 | |
| 	    continue;
 | |
| 	zt = GETZLETEXT(he);
 | |
| 	sav = zlemetaline[zlemetacs];
 | |
| 	zlemetaline[zlemetacs] = '\0';
 | |
| 	tst = zlinecmp(zt, zlemetaline) < (he->histnum == curhist);
 | |
| 	zlemetaline[zlemetacs] = sav;
 | |
| 	if (tst && zlinecmp(zt, zlemetaline)) {
 | |
| 	    if (--n <= 0) {
 | |
| 		unmetafy_line();
 | |
| 		zle_setline(he);
 | |
| 		zlecs = cpos;
 | |
| 		CCRIGHT();
 | |
| 		return 0;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     unmetafy_line();
 | |
|     return 1;
 | |
| }
 |