summaryrefslogtreecommitdiff
path: root/lib/libedit/search.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /lib/libedit/search.c
initial import of NetBSD tree
Diffstat (limited to 'lib/libedit/search.c')
-rw-r--r--lib/libedit/search.c632
1 files changed, 632 insertions, 0 deletions
diff --git a/lib/libedit/search.c b/lib/libedit/search.c
new file mode 100644
index 00000000000..29b2f6f5234
--- /dev/null
+++ b/lib/libedit/search.c
@@ -0,0 +1,632 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(lint) && !defined(SCCSID)
+static char sccsid[] = "@(#)search.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint && not SCCSID */
+
+/*
+ * search.c: History and character search functions
+ */
+#include "sys.h"
+#include <stdlib.h>
+#if defined(REGEX)
+#include <regex.h>
+#elif defined(REGEXP)
+#include <regexp.h>
+#endif
+#include "el.h"
+
+/*
+ * Adjust cursor in vi mode to include the character under it
+ */
+#define EL_CURSOR(el) \
+ ((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \
+ ((el)->el_map.current == (el)->el_map.alt)))
+
+/* search_init():
+ * Initialize the search stuff
+ */
+protected int
+search_init(el)
+ EditLine *el;
+{
+ el->el_search.patbuf = (char *) el_malloc(EL_BUFSIZ);
+ el->el_search.patlen = 0;
+ el->el_search.patdir = -1;
+ el->el_search.chacha = '\0';
+ el->el_search.chadir = -1;
+ return 0;
+}
+
+
+/* search_end():
+ * Initialize the search stuff
+ */
+protected void
+search_end(el)
+ EditLine *el;
+{
+ el_free((ptr_t) el->el_search.patbuf);
+ el->el_search.patbuf = NULL;
+}
+
+#ifdef REGEXP
+/* regerror():
+ * Handle regular expression errors
+ */
+public void
+/*ARGSUSED*/
+regerror(msg)
+ const char *msg;
+{
+}
+#endif
+
+/* el_match():
+ * Return if string matches pattern
+ */
+protected int
+el_match(str, pat)
+ const char *str;
+ const char *pat;
+{
+#if defined (REGEX)
+ regex_t re;
+ int rv;
+#elif defined (REGEXP)
+ regexp *rp;
+ int rv;
+#else
+ extern char *re_comp __P((const char *));
+ extern int re_exec __P((const char *));
+#endif
+
+ if (strstr(str, pat) != NULL)
+ return 1;
+
+#if defined(REGEX)
+ if (regcomp(&re, pat, 0) == 0) {
+ rv = regexec(&re, str, 0, NULL, 0) == 0;
+ regfree(&re);
+ } else {
+ rv = 0;
+ }
+ return rv;
+#elif defined(REGEXP)
+ if ((re = regcomp(pat)) != NULL) {
+ rv = regexec(re, str);
+ free((ptr_t) re);
+ } else {
+ rv = 0;
+ }
+ return rv;
+#else
+ if (re_comp(pat) != NULL)
+ return 0;
+ else
+ return re_exec(str) == 1;
+#endif
+}
+
+
+/* c_hmatch():
+ * return True if the pattern matches the prefix
+ */
+protected int
+c_hmatch(el, str)
+ EditLine *el;
+ const char *str;
+{
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "match `%s' with `%s'\n",
+ el->el_search.patbuf, str);
+#endif /* SDEBUG */
+
+ return el_match(str, el->el_search.patbuf);
+}
+
+
+/* c_setpat():
+ * Set the history seatch pattern
+ */
+protected void
+c_setpat(el)
+ 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
+}
+
+
+/* ce_inc_search():
+ * Emacs incremental search
+ */
+protected el_action_t
+ce_inc_search(el, dir)
+ EditLine *el;
+ int dir;
+{
+ static char STRfwd[] = { 'f', 'w', 'd', '\0' },
+ STRbck[] = { 'b', 'c', 'k', '\0' };
+ static char pchar = ':'; /* ':' = normal, '?' = failed */
+ static char endcmd[2] = { '\0', '\0' };
+ char ch, *cp, *ocursor = el->el_line.cursor, oldpchar = pchar;
+
+ el_action_t ret = CC_NORM;
+
+ int ohisteventno = el->el_history.eventno,
+ oldpatlen = el->el_search.patlen,
+ newdir = dir,
+ done, redo;
+
+ if (el->el_line.lastchar + sizeof(STRfwd) / sizeof(char) + 2 +
+ el->el_search.patlen >= el->el_line.limit)
+ return CC_ERROR;
+
+ for (;;) {
+
+ if (el->el_search.patlen == 0) { /* first round */
+ pchar = ':';
+#ifdef ANCHOR
+ el->el_search.patbuf[el->el_search.patlen++] = '.';
+ el->el_search.patbuf[el->el_search.patlen++] = '*';
+#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[1];
+ cp < &el->el_search.patbuf[el->el_search.patlen];
+ *el->el_line.lastchar++ = *cp++)
+ continue;
+ *el->el_line.lastchar = '\0';
+ re_refresh(el);
+
+ if (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 - 3)
+ 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 ED_DELETE_PREV_CHAR:
+ if (el->el_search.patlen > 1)
+ done++;
+ else
+ term_beep(el);
+ break;
+
+ default:
+ switch (ch) {
+ case 0007: /* ^G: Abort */
+ ret = CC_ERROR;
+ done++;
+ break;
+
+ case 0027: /* ^W: Append word */
+ /* No can do if globbing characters in pattern */
+ for (cp = &el->el_search.patbuf[1]; ; cp++)
+ if (cp >= &el->el_search.patbuf[el->el_search.patlen]) {
+ el->el_line.cursor += el->el_search.patlen - 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 - 3) {
+ 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: /* Terminate and execute cmd */
+ endcmd[0] = ch;
+ el_push(el, endcmd);
+ /*FALLTHROUGH*/
+
+ case 0033: /* ESC: Terminate */
+ 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) {
+
+ /* Can't search if unmatched '[' */
+ for (cp = &el->el_search.patbuf[el->el_search.patlen-1], ch = ']';
+ cp > el->el_search.patbuf; cp--)
+ if (*cp == '[' || *cp == ']') {
+ ch = *cp;
+ break;
+ }
+
+ if (el->el_search.patlen > 1 && ch != '[') {
+ if (redo && newdir == dir) {
+ if (pchar == '?') { /* wrap around */
+ el->el_history.eventno =
+ newdir == ED_SEARCH_PREV_HISTORY ? 0 : 0x7fffffff;
+ if (hist_get(el) == CC_ERROR)
+ /* el->el_history.eventno was fixed by first call */
+ (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, &el->el_search.patbuf[1],
+ newdir)) == CC_ERROR) {
+ /* avoid c_setpat */
+ 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, &el->el_search.patbuf[1],
+ newdir);
+ }
+ }
+ 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 == ':')
+ /* break abort of failed search at last non-failed */
+ ret = CC_NORM;
+
+ }
+
+ if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) {
+ /* restore on normal return or error exit */
+ 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;
+ }
+}
+
+
+/* cv_search():
+ * Vi search.
+ */
+protected el_action_t
+cv_search(el, dir)
+ EditLine *el;
+ int dir;
+{
+ char ch;
+ char tmpbuf[EL_BUFSIZ];
+ int tmplen;
+
+ tmplen = 0;
+#ifdef ANCHOR
+ tmpbuf[tmplen++] = '.';
+ tmpbuf[tmplen++] = '*';
+#endif
+
+ el->el_line.buffer[0] = '\0';
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+ el->el_search.patdir = dir;
+
+ c_insert(el, 2); /* prompt + '\n' */
+ *el->el_line.cursor++ = '\n';
+ *el->el_line.cursor++ = dir == ED_SEARCH_PREV_HISTORY ? '/' : '?';
+ re_refresh(el);
+
+#ifdef ANCHOR
+# define LEN 2
+#else
+# define LEN 0
+#endif
+
+ tmplen = c_gets(el, &tmpbuf[LEN]) + LEN;
+ ch = tmpbuf[tmplen];
+ tmpbuf[tmplen] = '\0';
+
+ if (tmplen == LEN) {
+ /*
+ * Use the old pattern, but wild-card it.
+ */
+ if (el->el_search.patlen == 0) {
+ el->el_line.buffer[0] = '\0';
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+ re_refresh(el);
+ return CC_ERROR;
+ }
+#ifdef ANCHOR
+ if (el->el_search.patbuf[0] != '.' && el->el_search.patbuf[0] != '*') {
+ (void) strcpy(tmpbuf, el->el_search.patbuf);
+ el->el_search.patbuf[0] = '.';
+ el->el_search.patbuf[1] = '*';
+ (void) strcpy(&el->el_search.patbuf[2], tmpbuf);
+ 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) strcpy(el->el_search.patbuf, tmpbuf);
+ el->el_search.patlen = tmplen;
+ }
+ el->el_state.lastcmd = (el_action_t) dir; /* avoid c_setpat */
+ 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;
+ }
+ else {
+ if (ch == 0033) {
+ re_refresh(el);
+ *el->el_line.lastchar++ = '\n';
+ *el->el_line.lastchar = '\0';
+ re_goto_bottom(el);
+ return CC_NEWLINE;
+ }
+ else
+ return CC_REFRESH;
+ }
+}
+
+
+/* ce_search_line():
+ * Look for a pattern inside a line
+ */
+protected el_action_t
+ce_search_line(el, pattern, dir)
+ EditLine *el;
+ char *pattern;
+ int dir;
+{
+ char *cp;
+
+ if (dir == ED_SEARCH_PREV_HISTORY) {
+ for (cp = el->el_line.cursor; cp >= el->el_line.buffer; cp--)
+ if (el_match(cp, pattern)) {
+ el->el_line.cursor = cp;
+ return CC_NORM;
+ }
+ return CC_ERROR;
+ } else {
+ for (cp = el->el_line.cursor; *cp != '\0' &&
+ cp < el->el_line.limit; cp++)
+ if (el_match(cp, pattern)) {
+ el->el_line.cursor = cp;
+ return CC_NORM;
+ }
+ return CC_ERROR;
+ }
+}
+
+
+/* cv_repeat_srch():
+ * Vi repeat search
+ */
+protected el_action_t
+cv_repeat_srch(el, c)
+ EditLine *el;
+ int c;
+{
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "dir %d patlen %d patbuf %s\n",
+ c, el->el_search.patlen, el->el_search.patbuf);
+#endif
+
+ el->el_state.lastcmd = (el_action_t) c; /* Hack to stop c_setpat */
+ 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;
+ }
+}
+
+
+/* cv_csearch_back():
+ * Vi character search reverse
+ */
+protected el_action_t
+cv_csearch_back(el, ch, count, tflag)
+ EditLine *el;
+ int ch, count, tflag;
+{
+ char *cp;
+
+ cp = el->el_line.cursor;
+ while (count--) {
+ if (*cp == ch)
+ cp--;
+ while (cp > el->el_line.buffer && *cp != ch)
+ cp--;
+ }
+
+ if (cp < el->el_line.buffer || (cp == el->el_line.buffer && *cp != ch))
+ return CC_ERROR;
+
+ if (*cp == ch && tflag)
+ cp++;
+
+ el->el_line.cursor = cp;
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ el->el_line.cursor++;
+ cv_delfini(el);
+ return CC_REFRESH;
+ }
+
+ re_refresh_cursor(el);
+ return CC_NORM;
+}
+
+
+/* cv_csearch_fwd():
+ * Vi character search forward
+ */
+protected el_action_t
+cv_csearch_fwd(el, ch, count, tflag)
+ EditLine *el;
+ int ch, count, tflag;
+{
+ char *cp;
+
+ cp = el->el_line.cursor;
+ while (count--) {
+ if(*cp == ch)
+ cp++;
+ while (cp < el->el_line.lastchar && *cp != ch)
+ cp++;
+ }
+
+ if (cp >= el->el_line.lastchar)
+ return CC_ERROR;
+
+ if (*cp == ch && tflag)
+ cp--;
+
+ el->el_line.cursor = cp;
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ el->el_line.cursor++;
+ cv_delfini(el);
+ return CC_REFRESH;
+ }
+ re_refresh_cursor(el);
+ return CC_NORM;
+}