diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /lib/libedit/search.c |
initial import of NetBSD tree
Diffstat (limited to 'lib/libedit/search.c')
-rw-r--r-- | lib/libedit/search.c | 632 |
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; +} |