1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-05-19 06:26:04 +02:00
zsh/Src/Zle/zle_hist.c
Peter Stephenson 48cadf48ff 40285: Be more careful with pattern allocation in history isearch.
If there are ZLE hooks to be called, they may use patterns, in
which case it's not safe to allocate the isearch pattern in the
static buffer.
2017-01-06 17:42:13 +00:00

2093 lines
48 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 instead.
* 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;
}