date and time created 92/06/22 18:08:34 by bostic
authorKeith Bostic <bostic@ucbvax.Berkeley.EDU>
Tue, 23 Jun 1992 09:08:34 +0000 (01:08 -0800)
committerKeith Bostic <bostic@ucbvax.Berkeley.EDU>
Tue, 23 Jun 1992 09:08:34 +0000 (01:08 -0800)
SCCS-vsn: lib/libedit/search.c 5.1

usr/src/lib/libedit/search.c [new file with mode: 0644]

diff --git a/usr/src/lib/libedit/search.c b/usr/src/lib/libedit/search.c
new file mode 100644 (file)
index 0000000..dec34ef
--- /dev/null
@@ -0,0 +1,588 @@
+/*-
+ * Copyright (c) 1992 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.
+ *
+ * %sccs.include.redist.c%
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)search.c   5.1 (Berkeley) %G%";
+#endif /* not lint */
+
+/*
+ * el.search.c: History and character search functions
+ */
+#include "sys.h"
+#include <stdlib.h>
+#ifndef sun
+#include <regexp.h>
+#endif
+#include "el.h"
+
+private int el_match   __P((const char *, const char *));
+
+/* 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;
+}
+
+#ifndef sun
+/* regerror():
+ *     Handle regular expression errors
+ */
+public void 
+/*ARGSUSED*/
+regerror(msg)
+    const char *msg;
+{
+}
+#endif
+
+/* el_match():
+ *     Return if string matches pattern
+ */
+private int
+el_match(str, pat)
+    const char *str;
+    const char *pat;
+{
+#ifdef sun
+    extern char *re_comp __P((const char *));
+    extern int re_exec __P((const char *));
+#else
+    regexp *re;
+    int rv;
+#endif
+
+    if (strstr(str, pat) != NULL)
+       return 1;
+#ifdef sun
+    if (re_comp(pat) != NULL)
+       return 0;
+    else
+    return re_exec(str) == 1;
+#else
+    if ((re = regcomp(pat)) == NULL) {
+       rv = regexec(re, str);
+       free((ptr_t) re);
+    }
+    else
+       rv = 0;
+    return rv;
+#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->el_line.cursor - 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->el_line.cursor - 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;
+}