#include "config.h"
#if !defined(lint) && !defined(SCCSID)
#if 0
static char sccsid[] = "@(#)search.c 8.1 (Berkeley) 6/4/93";
#else
__RCSID("$NetBSD: search.c,v 1.24 2010/04/15 00:57:33 christos Exp $");
#endif
#endif
#include <stdlib.h>
#include <sys/types.h>
#if defined(REGEX)
#include <regex.h>
#elif defined(REGEXP)
#include <regexp.h>
#endif
#include "el.h"
#define EL_CURSOR(el) \
((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \
((el)->el_map.current == (el)->el_map.alt)))
protected int
search_init(EditLine *el)
{
el->el_search.patbuf = el_malloc(EL_BUFSIZ *
sizeof(*el->el_search.patbuf));
if (el->el_search.patbuf == NULL)
return (-1);
el->el_search.patlen = 0;
el->el_search.patdir = -1;
el->el_search.chacha = '\0';
el->el_search.chadir = CHAR_FWD;
el->el_search.chatflg = 0;
return (0);
}
protected void
search_end(EditLine *el)
{
el_free((ptr_t) el->el_search.patbuf);
el->el_search.patbuf = NULL;
}
#ifdef REGEXP
public void
regerror(const char *msg)
{
}
#endif
protected int
el_match(const Char *str, const Char *pat)
{
#ifdef WIDECHAR
static ct_buffer_t conv;
#endif
#if defined (REGEX)
regex_t re;
int rv;
#elif defined (REGEXP)
regexp *rp;
int rv;
#else
extern char *re_comp(const char *);
extern int re_exec(const char *);
#endif
if (Strstr(str, pat) != 0)
return (1);
#if defined(REGEX)
if (regcomp(&re, ct_encode_string(pat, &conv), 0) == 0) {
rv = regexec(&re, ct_encode_string(str, &conv), 0, NULL, 0) == 0;
regfree(&re);
} else {
rv = 0;
}
return (rv);
#elif defined(REGEXP)
if ((re = regcomp(ct_encode_string(pat, &conv))) != NULL) {
rv = regexec(re, ct_encode_string(str, &conv));
free((ptr_t) re);
} else {
rv = 0;
}
return (rv);
#else
if (re_comp(ct_encode_string(pat, &conv)) != NULL)
return (0);
else
return (re_exec(ct_encode_string(str, &conv)) == 1);
#endif
}
protected int
c_hmatch(EditLine *el, const Char *str)
{
#ifdef SDEBUG
(void) fprintf(el->el_errfile, "match `%s' with `%s'\n",
el->el_search.patbuf, str);
#endif
return (el_match(str, el->el_search.patbuf));
}
protected void
c_setpat(EditLine *el)
{
if (el->el_state.lastcmd != ED_SEARCH_PREV_HISTORY &&
el->el_state.lastcmd != ED_SEARCH_NEXT_HISTORY) {
el->el_search.patlen = EL_CURSOR(el) - el->el_line.buffer;
if (el->el_search.patlen >= EL_BUFSIZ)
el->el_search.patlen = EL_BUFSIZ - 1;
if (el->el_search.patlen != 0) {
(void) Strncpy(el->el_search.patbuf, el->el_line.buffer,
el->el_search.patlen);
el->el_search.patbuf[el->el_search.patlen] = '\0';
} else
el->el_search.patlen = Strlen(el->el_search.patbuf);
}
#ifdef SDEBUG
(void) fprintf(el->el_errfile, "\neventno = %d\n",
el->el_history.eventno);
(void) fprintf(el->el_errfile, "patlen = %d\n", el->el_search.patlen);
(void) fprintf(el->el_errfile, "patbuf = \"%s\"\n",
el->el_search.patbuf);
(void) fprintf(el->el_errfile, "cursor %d lastchar %d\n",
EL_CURSOR(el) - el->el_line.buffer,
el->el_line.lastchar - el->el_line.buffer);
#endif
}
protected el_action_t
ce_inc_search(EditLine *el, int dir)
{
static const Char STRfwd[] = {'f', 'w', 'd', '\0'},
STRbck[] = {'b', 'c', 'k', '\0'};
static Char pchar = ':';
static Char endcmd[2] = {'\0', '\0'};
Char ch, *ocursor = el->el_line.cursor, oldpchar = pchar;
const Char *cp;
el_action_t ret = CC_NORM;
int ohisteventno = el->el_history.eventno;
size_t oldpatlen = el->el_search.patlen;
int newdir = dir;
int done, redo;
if (el->el_line.lastchar + sizeof(STRfwd) /
sizeof(*el->el_line.lastchar) + 2 +
el->el_search.patlen >= el->el_line.limit)
return (CC_ERROR);
for (;;) {
if (el->el_search.patlen == 0) {
pchar = ':';
#ifdef ANCHOR
#define LEN 2
el->el_search.patbuf[el->el_search.patlen++] = '.';
el->el_search.patbuf[el->el_search.patlen++] = '*';
#else
#define LEN 0
#endif
}
done = redo = 0;
*el->el_line.lastchar++ = '\n';
for (cp = (newdir == ED_SEARCH_PREV_HISTORY) ? STRbck : STRfwd;
*cp; *el->el_line.lastchar++ = *cp++)
continue;
*el->el_line.lastchar++ = pchar;
for (cp = &el->el_search.patbuf[LEN];
cp < &el->el_search.patbuf[el->el_search.patlen];
*el->el_line.lastchar++ = *cp++)
continue;
*el->el_line.lastchar = '\0';
re_refresh(el);
if (FUN(el,getc)(el, &ch) != 1)
return (ed_end_of_file(el, 0));
switch (el->el_map.current[(unsigned char) ch]) {
case ED_INSERT:
case ED_DIGIT:
if (el->el_search.patlen >= EL_BUFSIZ - LEN)
term_beep(el);
else {
el->el_search.patbuf[el->el_search.patlen++] =
ch;
*el->el_line.lastchar++ = ch;
*el->el_line.lastchar = '\0';
re_refresh(el);
}
break;
case EM_INC_SEARCH_NEXT:
newdir = ED_SEARCH_NEXT_HISTORY;
redo++;
break;
case EM_INC_SEARCH_PREV:
newdir = ED_SEARCH_PREV_HISTORY;
redo++;
break;
case EM_DELETE_PREV_CHAR:
case ED_DELETE_PREV_CHAR:
if (el->el_search.patlen > LEN)
done++;
else
term_beep(el);
break;
default:
switch (ch) {
case 0007:
ret = CC_ERROR;
done++;
break;
case 0027:
for (cp = &el->el_search.patbuf[LEN];; cp++)
if (cp >= &el->el_search.patbuf[
el->el_search.patlen]) {
el->el_line.cursor +=
el->el_search.patlen - LEN - 1;
cp = c__next_word(el->el_line.cursor,
el->el_line.lastchar, 1,
ce__isword);
while (el->el_line.cursor < cp &&
*el->el_line.cursor != '\n') {
if (el->el_search.patlen >=
EL_BUFSIZ - LEN) {
term_beep(el);
break;
}
el->el_search.patbuf[el->el_search.patlen++] =
*el->el_line.cursor;
*el->el_line.lastchar++ =
*el->el_line.cursor++;
}
el->el_line.cursor = ocursor;
*el->el_line.lastchar = '\0';
re_refresh(el);
break;
} else if (isglob(*cp)) {
term_beep(el);
break;
}
break;
default:
endcmd[0] = ch;
FUN(el,push)(el, endcmd);
case 0033:
ret = CC_REFRESH;
done++;
break;
}
break;
}
while (el->el_line.lastchar > el->el_line.buffer &&
*el->el_line.lastchar != '\n')
*el->el_line.lastchar-- = '\0';
*el->el_line.lastchar = '\0';
if (!done) {
for (cp = &el->el_search.patbuf[el->el_search.patlen-1],
ch = ']';
cp >= &el->el_search.patbuf[LEN];
cp--)
if (*cp == '[' || *cp == ']') {
ch = *cp;
break;
}
if (el->el_search.patlen > LEN && ch != '[') {
if (redo && newdir == dir) {
if (pchar == '?') {
el->el_history.eventno =
newdir == ED_SEARCH_PREV_HISTORY ? 0 : 0x7fffffff;
if (hist_get(el) == CC_ERROR)
(void) hist_get(el);
el->el_line.cursor = newdir ==
ED_SEARCH_PREV_HISTORY ?
el->el_line.lastchar :
el->el_line.buffer;
} else
el->el_line.cursor +=
newdir ==
ED_SEARCH_PREV_HISTORY ?
-1 : 1;
}
#ifdef ANCHOR
el->el_search.patbuf[el->el_search.patlen++] =
'.';
el->el_search.patbuf[el->el_search.patlen++] =
'*';
#endif
el->el_search.patbuf[el->el_search.patlen] =
'\0';
if (el->el_line.cursor < el->el_line.buffer ||
el->el_line.cursor > el->el_line.lastchar ||
(ret = ce_search_line(el, newdir))
== CC_ERROR) {
el->el_state.lastcmd =
(el_action_t) newdir;
ret = newdir == ED_SEARCH_PREV_HISTORY ?
ed_search_prev_history(el, 0) :
ed_search_next_history(el, 0);
if (ret != CC_ERROR) {
el->el_line.cursor = newdir ==
ED_SEARCH_PREV_HISTORY ?
el->el_line.lastchar :
el->el_line.buffer;
(void) ce_search_line(el,
newdir);
}
}
el->el_search.patlen -= LEN;
el->el_search.patbuf[el->el_search.patlen] =
'\0';
if (ret == CC_ERROR) {
term_beep(el);
if (el->el_history.eventno !=
ohisteventno) {
el->el_history.eventno =
ohisteventno;
if (hist_get(el) == CC_ERROR)
return (CC_ERROR);
}
el->el_line.cursor = ocursor;
pchar = '?';
} else {
pchar = ':';
}
}
ret = ce_inc_search(el, newdir);
if (ret == CC_ERROR && pchar == '?' && oldpchar == ':')
ret = CC_NORM;
}
if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) {
pchar = oldpchar;
el->el_search.patlen = oldpatlen;
if (el->el_history.eventno != ohisteventno) {
el->el_history.eventno = ohisteventno;
if (hist_get(el) == CC_ERROR)
return (CC_ERROR);
}
el->el_line.cursor = ocursor;
if (ret == CC_ERROR)
re_refresh(el);
}
if (done || ret != CC_NORM)
return (ret);
}
}
protected el_action_t
cv_search(EditLine *el, int dir)
{
Char ch;
Char tmpbuf[EL_BUFSIZ];
int tmplen;
#ifdef ANCHOR
tmpbuf[0] = '.';
tmpbuf[1] = '*';
#endif
tmplen = LEN;
el->el_search.patdir = dir;
tmplen = c_gets(el, &tmpbuf[LEN],
dir == ED_SEARCH_PREV_HISTORY ? STR("\n/") : STR("\n?") );
if (tmplen == -1)
return CC_REFRESH;
tmplen += LEN;
ch = tmpbuf[tmplen];
tmpbuf[tmplen] = '\0';
if (tmplen == LEN) {
if (el->el_search.patlen == 0) {
re_refresh(el);
return (CC_ERROR);
}
#ifdef ANCHOR
if (el->el_search.patbuf[0] != '.' &&
el->el_search.patbuf[0] != '*') {
(void) Strncpy(tmpbuf, el->el_search.patbuf,
sizeof(tmpbuf) / sizeof(*tmpbuf) - 1);
el->el_search.patbuf[0] = '.';
el->el_search.patbuf[1] = '*';
(void) Strncpy(&el->el_search.patbuf[2], tmpbuf,
EL_BUFSIZ - 3);
el->el_search.patlen++;
el->el_search.patbuf[el->el_search.patlen++] = '.';
el->el_search.patbuf[el->el_search.patlen++] = '*';
el->el_search.patbuf[el->el_search.patlen] = '\0';
}
#endif
} else {
#ifdef ANCHOR
tmpbuf[tmplen++] = '.';
tmpbuf[tmplen++] = '*';
#endif
tmpbuf[tmplen] = '\0';
(void) Strncpy(el->el_search.patbuf, tmpbuf, EL_BUFSIZ - 1);
el->el_search.patlen = tmplen;
}
el->el_state.lastcmd = (el_action_t) dir;
el->el_line.cursor = el->el_line.lastchar = el->el_line.buffer;
if ((dir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) :
ed_search_next_history(el, 0)) == CC_ERROR) {
re_refresh(el);
return (CC_ERROR);
}
if (ch == 0033) {
re_refresh(el);
return ed_newline(el, 0);
}
return (CC_REFRESH);
}
protected el_action_t
ce_search_line(EditLine *el, int dir)
{
Char *cp = el->el_line.cursor;
Char *pattern = el->el_search.patbuf;
Char oc, *ocp;
#ifdef ANCHOR
ocp = &pattern[1];
oc = *ocp;
*ocp = '^';
#else
ocp = pattern;
oc = *ocp;
#endif
if (dir == ED_SEARCH_PREV_HISTORY) {
for (; cp >= el->el_line.buffer; cp--) {
if (el_match(cp, ocp)) {
*ocp = oc;
el->el_line.cursor = cp;
return (CC_NORM);
}
}
*ocp = oc;
return (CC_ERROR);
} else {
for (; *cp != '\0' && cp < el->el_line.limit; cp++) {
if (el_match(cp, ocp)) {
*ocp = oc;
el->el_line.cursor = cp;
return (CC_NORM);
}
}
*ocp = oc;
return (CC_ERROR);
}
}
protected el_action_t
cv_repeat_srch(EditLine *el, Int c)
{
#ifdef SDEBUG
(void) fprintf(el->el_errfile, "dir %d patlen %d patbuf %s\n",
c, el->el_search.patlen, ct_encode_string(el->el_search.patbuf));
#endif
el->el_state.lastcmd = (el_action_t) c;
el->el_line.lastchar = el->el_line.buffer;
switch (c) {
case ED_SEARCH_NEXT_HISTORY:
return (ed_search_next_history(el, 0));
case ED_SEARCH_PREV_HISTORY:
return (ed_search_prev_history(el, 0));
default:
return (CC_ERROR);
}
}
protected el_action_t
cv_csearch(EditLine *el, int direction, Int ch, int count, int tflag)
{
Char *cp;
if (ch == 0)
return CC_ERROR;
if (ch == -1) {
Char c;
if (FUN(el,getc)(el, &c) != 1)
return ed_end_of_file(el, 0);
ch = c;
}
el->el_search.chacha = ch;
el->el_search.chadir = direction;
el->el_search.chatflg = tflag;
cp = el->el_line.cursor;
while (count--) {
if (*cp == ch)
cp += direction;
for (;;cp += direction) {
if (cp >= el->el_line.lastchar)
return CC_ERROR;
if (cp < el->el_line.buffer)
return CC_ERROR;
if (*cp == ch)
break;
}
}
if (tflag)
cp -= direction;
el->el_line.cursor = cp;
if (el->el_chared.c_vcmd.action != NOP) {
if (direction > 0)
el->el_line.cursor++;
cv_delfini(el);
return CC_REFRESH;
}
return CC_CURSOR;
}