mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-09-24 21:11:03 +02:00
2095 lines
49 KiB
C
2095 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;
|
|
}
|