root/src/noitedit/search.c

Revision 52b8bc4e1ecc74b21f4db46822bfe9c609fb818a, 15.8 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

refs #283

  • Property mode set to 100644
Line 
1 /*      $NetBSD: search.c,v 1.11 2001/01/23 15:55:31 jdolecek Exp $     */
2
3 /*-
4  * Copyright (c) 1992, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Christos Zoulas of Cornell University.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by the University of
21  *      California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38
39 #include "noitedit/compat.h"
40 #if !defined(lint) && !defined(SCCSID)
41 #if 0
42 static char sccsid[] = "@(#)search.c    8.1 (Berkeley) 6/4/93";
43 #else
44 __RCSID("$NetBSD: search.c,v 1.11 2001/01/23 15:55:31 jdolecek Exp $");
45 #endif
46 #endif /* not lint && not SCCSID */
47
48 /*
49  * search.c: History and character search functions
50  */
51 #include "noit_defines.h"
52 #include "noitedit/sys.h"
53 #include <stdlib.h>
54 #if HAVE_SYS_TYPES_H
55 #include <sys/types.h>
56 #endif
57 #if defined(PCREPOSIXREGEX)
58 #include <pcre.h>
59 #include <pcreposix.h>
60 #elif defined(REGEX)
61 #include <regex.h>
62 #elif defined(REGEXP)
63 #include <regexp.h>
64 #endif
65 #include "noitedit/el.h"
66
67 /*
68  * Adjust cursor in vi mode to include the character under it
69  */
70 #define EL_CURSOR(el) \
71     ((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \
72                             ((el)->el_map.current == (el)->el_map.alt)))
73
74 /* search_init():
75  *      Initialize the search stuff
76  */
77 protected int
78 search_init(EditLine *el)
79 {
80
81         el->el_search.patbuf = (char *) el_malloc(EL_BUFSIZ);
82         if (el->el_search.patbuf == NULL)
83                 return (-1);
84         el->el_search.patlen = 0;
85         el->el_search.patdir = -1;
86         el->el_search.chacha = '\0';
87         el->el_search.chadir = -1;
88         return (0);
89 }
90
91
92 /* search_end():
93  *      Initialize the search stuff
94  */
95 protected void
96 search_end(EditLine *el)
97 {
98
99         el_free((ptr_t) el->el_search.patbuf);
100         el->el_search.patbuf = NULL;
101 }
102
103
104 #ifdef REGEXP
105 /* regerror():
106  *      Handle regular expression errors
107  */
108 public void
109 /*ARGSUSED*/
110 regerror(const char *msg)
111 {
112 }
113 #endif
114
115
116 /* el_match():
117  *      Return if string matches pattern
118  */
119 protected int
120 el_match(const char *str, const char *pat)
121 {
122 #if defined (REGEX) || defined(PCREPOSIXREGEX)
123         regex_t re;
124         int rv;
125 #elif defined (REGEXP)
126         regexp *rp;
127         int rv;
128 #else
129         extern char     *re_comp(const char *);
130         extern int       re_exec(const char *);
131 #endif
132
133         if (strstr(str, pat) != NULL)
134                 return (1);
135
136 #if defined(REGEX) || defined(PCREPOSIXREGEX)
137         if (regcomp(&re, pat, 0) == 0) {
138                 rv = regexec(&re, str, 0, NULL, 0) == 0;
139                 regfree(&re);
140         } else {
141                 rv = 0;
142         }
143         return (rv);
144 #elif defined(REGEXP)
145         if ((re = regcomp(pat)) != NULL) {
146                 rv = regexec(re, str);
147                 free((ptr_t) re);
148         } else {
149                 rv = 0;
150         }
151         return (rv);
152 #else
153         if (re_comp(pat) != NULL)
154                 return (0);
155         else
156                 return (re_exec(str) == 1);
157 #endif
158 }
159
160
161 /* c_hmatch():
162  *       return True if the pattern matches the prefix
163  */
164 protected int
165 c_hmatch(EditLine *el, const char *str)
166 {
167 #ifdef SDEBUG
168         (void) fprintf(el->el_errfile, "match `%s' with `%s'\n",
169             el->el_search.patbuf, str);
170 #endif /* SDEBUG */
171
172         return (el_match(str, el->el_search.patbuf));
173 }
174
175
176 /* c_setpat():
177  *      Set the history seatch pattern
178  */
179 protected void
180 c_setpat(EditLine *el)
181 {
182         if (el->el_state.lastcmd != ED_SEARCH_PREV_HISTORY &&
183             el->el_state.lastcmd != ED_SEARCH_NEXT_HISTORY) {
184                 el->el_search.patlen = EL_CURSOR(el) - el->el_line.buffer;
185                 if (el->el_search.patlen >= EL_BUFSIZ)
186                         el->el_search.patlen = EL_BUFSIZ - 1;
187                 if (el->el_search.patlen != 0) {
188                         (void) strncpy(el->el_search.patbuf, el->el_line.buffer,
189                             el->el_search.patlen);
190                         el->el_search.patbuf[el->el_search.patlen] = '\0';
191                 } else
192                         el->el_search.patlen = strlen(el->el_search.patbuf);
193         }
194 #ifdef SDEBUG
195         (void) fprintf(el->el_errfile, "\neventno = %d\n",
196             el->el_history.eventno);
197         (void) fprintf(el->el_errfile, "patlen = %d\n", el->el_search.patlen);
198         (void) fprintf(el->el_errfile, "patbuf = \"%s\"\n",
199             el->el_search.patbuf);
200         (void) fprintf(el->el_errfile, "cursor %d lastchar %d\n",
201             EL_CURSOR(el) - el->el_line.buffer,
202             el->el_line.lastchar - el->el_line.buffer);
203 #endif
204 }
205
206
207 /* ce_inc_search():
208  *      Emacs incremental search
209  */
210 protected el_action_t
211 ce_inc_search(EditLine *el, int dir)
212 {
213         static const char STRfwd[] = {'f', 'w', 'd', '\0'},
214              STRbck[] = {'b', 'c', 'k', '\0'};
215         static char pchar = ':';/* ':' = normal, '?' = failed */
216         static char endcmd[2] = {'\0', '\0'};
217         char ch, *ocursor = el->el_line.cursor, oldpchar = pchar;
218         const char *cp;
219
220         el_action_t ret = CC_NORM;
221
222         int ohisteventno = el->el_history.eventno;
223         int oldpatlen = el->el_search.patlen;
224         int newdir = dir;
225         int done, redo;
226
227         if (el->el_line.lastchar + sizeof(STRfwd) / sizeof(char) + 2 +
228             el->el_search.patlen >= el->el_line.limit)
229                 return (CC_ERROR);
230
231         for (;;) {
232
233                 if (el->el_search.patlen == 0) {        /* first round */
234                         pchar = ':';
235 #ifdef ANCHOR
236                         el->el_search.patbuf[el->el_search.patlen++] = '.';
237                         el->el_search.patbuf[el->el_search.patlen++] = '*';
238 #endif
239                 }
240                 done = redo = 0;
241                 *el->el_line.lastchar++ = '\n';
242                 for (cp = (newdir == ED_SEARCH_PREV_HISTORY) ? STRbck : STRfwd;
243                     *cp; *el->el_line.lastchar++ = *cp++)
244                         continue;
245                 *el->el_line.lastchar++ = pchar;
246                 for (cp = &el->el_search.patbuf[1];
247                     cp < &el->el_search.patbuf[el->el_search.patlen];
248                     *el->el_line.lastchar++ = *cp++)
249                         continue;
250                 *el->el_line.lastchar = '\0';
251                 re_refresh(el);
252
253                 if (el_getc(el, &ch) != 1)
254                         return (ed_end_of_file(el, 0));
255
256                 switch (el->el_map.current[(unsigned char) ch]) {
257                 case ED_INSERT:
258                 case ED_DIGIT:
259                         if (el->el_search.patlen > EL_BUFSIZ - 3)
260                                 term_beep(el);
261                         else {
262                                 el->el_search.patbuf[el->el_search.patlen++] =
263                                     ch;
264                                 *el->el_line.lastchar++ = ch;
265                                 *el->el_line.lastchar = '\0';
266                                 re_refresh(el);
267                         }
268                         break;
269
270                 case EM_INC_SEARCH_NEXT:
271                         newdir = ED_SEARCH_NEXT_HISTORY;
272                         redo++;
273                         break;
274
275                 case EM_INC_SEARCH_PREV:
276                         newdir = ED_SEARCH_PREV_HISTORY;
277                         redo++;
278                         break;
279
280                 case ED_DELETE_PREV_CHAR:
281                         if (el->el_search.patlen > 1)
282                                 done++;
283                         else
284                                 term_beep(el);
285                         break;
286
287                 default:
288                         switch (ch) {
289                         case 0007:      /* ^G: Abort */
290                                 ret = CC_ERROR;
291                                 done++;
292                                 break;
293
294                         case 0027:      /* ^W: Append word */
295                         /* No can do if globbing characters in pattern */
296                                 for (cp = &el->el_search.patbuf[1];; cp++)
297                                     if (cp >= &el->el_search.patbuf[el->el_search.patlen]) {
298                                         el->el_line.cursor +=
299                                             el->el_search.patlen - 1;
300                                         cp = c__next_word(el->el_line.cursor,
301                                             el->el_line.lastchar, 1,
302                                             ce__isword);
303                                         while (el->el_line.cursor < cp &&
304                                             *el->el_line.cursor != '\n') {
305                                                 if (el->el_search.patlen >
306                                                     EL_BUFSIZ - 3) {
307                                                         term_beep(el);
308                                                         break;
309                                                 }
310                                                 el->el_search.patbuf[el->el_search.patlen++] =
311                                                     *el->el_line.cursor;
312                                                 *el->el_line.lastchar++ =
313                                                     *el->el_line.cursor++;
314                                         }
315                                         el->el_line.cursor = ocursor;
316                                         *el->el_line.lastchar = '\0';
317                                         re_refresh(el);
318                                         break;
319                                     } else if (isglob(*cp)) {
320                                             term_beep(el);
321                                             break;
322                                     }
323                                 break;
324
325                         default:        /* Terminate and execute cmd */
326                                 endcmd[0] = ch;
327                                 el_push(el, endcmd);
328                                 /* FALLTHROUGH */
329
330                         case 0033:      /* ESC: Terminate */
331                                 ret = CC_REFRESH;
332                                 done++;
333                                 break;
334                         }
335                         break;
336                 }
337
338                 while (el->el_line.lastchar > el->el_line.buffer &&
339                     *el->el_line.lastchar != '\n')
340                         *el->el_line.lastchar-- = '\0';
341                 *el->el_line.lastchar = '\0';
342
343                 if (!done) {
344
345                         /* Can't search if unmatched '[' */
346                         for (cp = &el->el_search.patbuf[el->el_search.patlen-1],
347                             ch = ']';
348                             cp > el->el_search.patbuf;
349                             cp--)
350                                 if (*cp == '[' || *cp == ']') {
351                                         ch = *cp;
352                                         break;
353                                 }
354                         if (el->el_search.patlen > 1 && ch != '[') {
355                                 if (redo && newdir == dir) {
356                                         if (pchar == '?') { /* wrap around */
357                                                 el->el_history.eventno =
358                                                     newdir == ED_SEARCH_PREV_HISTORY ? 0 : 0x7fffffff;
359                                                 if (hist_get(el) == CC_ERROR)
360                                                         /* el->el_history.event
361                                                          * no was fixed by
362                                                          * first call */
363                                                         (void) hist_get(el);
364                                                 el->el_line.cursor = newdir ==
365                                                     ED_SEARCH_PREV_HISTORY ?
366                                                     el->el_line.lastchar :
367                                                     el->el_line.buffer;
368                                         } else
369                                                 el->el_line.cursor +=
370                                                     newdir ==
371                                                     ED_SEARCH_PREV_HISTORY ?
372                                                     -1 : 1;
373                                 }
374 #ifdef ANCHOR
375                                 el->el_search.patbuf[el->el_search.patlen++] =
376                                     '.';
377                                 el->el_search.patbuf[el->el_search.patlen++] =
378                                     '*';
379 #endif
380                                 el->el_search.patbuf[el->el_search.patlen] =
381                                     '\0';
382                                 if (el->el_line.cursor < el->el_line.buffer ||
383                                     el->el_line.cursor > el->el_line.lastchar ||
384                                     (ret = ce_search_line(el,
385                                     &el->el_search.patbuf[1],
386                                     newdir)) == CC_ERROR) {
387                                         /* avoid c_setpat */
388                                         el->el_state.lastcmd =
389                                             (el_action_t) newdir;
390                                         ret = newdir == ED_SEARCH_PREV_HISTORY ?
391                                             ed_search_prev_history(el, 0) :
392                                             ed_search_next_history(el, 0);
393                                         if (ret != CC_ERROR) {
394                                                 el->el_line.cursor = newdir ==
395                                                     ED_SEARCH_PREV_HISTORY ?
396                                                     el->el_line.lastchar :
397                                                     el->el_line.buffer;
398                                                 (void) ce_search_line(el,
399                                                     &el->el_search.patbuf[1],
400                                                     newdir);
401                                         }
402                                 }
403                                 el->el_search.patbuf[--el->el_search.patlen] =
404                                     '\0';
405                                 if (ret == CC_ERROR) {
406                                         term_beep(el);
407                                         if (el->el_history.eventno !=
408                                             ohisteventno) {
409                                                 el->el_history.eventno =
410                                                     ohisteventno;
411                                                 if (hist_get(el) == CC_ERROR)
412                                                         return (CC_ERROR);
413                                         }
414                                         el->el_line.cursor = ocursor;
415                                         pchar = '?';
416                                 } else {
417                                         pchar = ':';
418                                 }
419                         }
420                         ret = ce_inc_search(el, newdir);
421
422                         if (ret == CC_ERROR && pchar == '?' && oldpchar == ':')
423                                 /*
424                                  * break abort of failed search at last
425                                  * non-failed
426                                  */
427                                 ret = CC_NORM;
428
429                 }
430                 if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) {
431                         /* restore on normal return or error exit */
432                         pchar = oldpchar;
433                         el->el_search.patlen = oldpatlen;
434                         if (el->el_history.eventno != ohisteventno) {
435                                 el->el_history.eventno = ohisteventno;
436                                 if (hist_get(el) == CC_ERROR)
437                                         return (CC_ERROR);
438                         }
439                         el->el_line.cursor = ocursor;
440                         if (ret == CC_ERROR)
441                                 re_refresh(el);
442                 }
443                 if (done || ret != CC_NORM)
444                         return (ret);
445         }
446 }
447
448
449 /* cv_search():
450  *      Vi search.
451  */
452 protected el_action_t
453 cv_search(EditLine *el, int dir)
454 {
455         char ch;
456         char tmpbuf[EL_BUFSIZ];
457         int tmplen;
458
459         tmplen = 0;
460 #ifdef ANCHOR
461         tmpbuf[tmplen++] = '.';
462         tmpbuf[tmplen] = '*';
463 #endif
464
465         el->el_line.buffer[0] = '\0';
466         el->el_line.lastchar = el->el_line.buffer;
467         el->el_line.cursor = el->el_line.buffer;
468         el->el_search.patdir = dir;
469
470         c_insert(el, 2);        /* prompt + '\n' */
471         *el->el_line.cursor++ = '\n';
472         *el->el_line.cursor++ = dir == ED_SEARCH_PREV_HISTORY ? '/' : '?';
473         re_refresh(el);
474
475 #ifdef ANCHOR
476 #define LEN     2
477 #else
478 #define LEN     0
479 #endif
480
481         tmplen = c_gets(el, &tmpbuf[LEN]) + LEN;
482         ch = tmpbuf[tmplen];
483         tmpbuf[tmplen] = '\0';
484
485         if (tmplen == LEN) {
486                 /*
487                  * Use the old pattern, but wild-card it.
488                  */
489                 if (el->el_search.patlen == 0) {
490                         el->el_line.buffer[0] = '\0';
491                         el->el_line.lastchar = el->el_line.buffer;
492                         el->el_line.cursor = el->el_line.buffer;
493                         re_refresh(el);
494                         return (CC_ERROR);
495                 }
496 #ifdef ANCHOR
497                 if (el->el_search.patbuf[0] != '.' &&
498                     el->el_search.patbuf[0] != '*') {
499                         (void) strncpy(tmpbuf, el->el_search.patbuf,
500                             sizeof(tmpbuf) - 1);
501                         el->el_search.patbuf[0] = '.';
502                         el->el_search.patbuf[1] = '*';
503                         (void) strncpy(&el->el_search.patbuf[2], tmpbuf,
504                             EL_BUFSIZ - 3);
505                         el->el_search.patlen++;
506                         el->el_search.patbuf[el->el_search.patlen++] = '.';
507                         el->el_search.patbuf[el->el_search.patlen++] = '*';
508                         el->el_search.patbuf[el->el_search.patlen] = '\0';
509                 }
510 #endif
511         } else {
512 #ifdef ANCHOR
513                 tmpbuf[tmplen++] = '.';
514                 tmpbuf[tmplen++] = '*';
515 #endif
516                 tmpbuf[tmplen] = '\0';
517                 (void) strncpy(el->el_search.patbuf, tmpbuf, EL_BUFSIZ - 1);
518                 el->el_search.patlen = tmplen;
519         }
520         el->el_state.lastcmd = (el_action_t) dir;       /* avoid c_setpat */
521         el->el_line.cursor = el->el_line.lastchar = el->el_line.buffer;
522         if ((dir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) :
523                 ed_search_next_history(el, 0)) == CC_ERROR) {
524                 re_refresh(el);
525                 return (CC_ERROR);
526         } else {
527                 if (ch == 0033) {
528                         re_refresh(el);
529                         *el->el_line.lastchar++ = '\n';
530                         *el->el_line.lastchar = '\0';
531                         re_goto_bottom(el);
532                         return (CC_NEWLINE);
533                 } else
534                         return (CC_REFRESH);
535         }
536 }
537
538
539 /* ce_search_line():
540  *      Look for a pattern inside a line
541  */
542 protected el_action_t
543 ce_search_line(EditLine *el, char *pattern, int dir)
544 {
545         char *cp;
546
547         if (dir == ED_SEARCH_PREV_HISTORY) {
548                 for (cp = el->el_line.cursor; cp >= el->el_line.buffer; cp--)
549                         if (el_match(cp, pattern)) {
550                                 el->el_line.cursor = cp;
551                                 return (CC_NORM);
552                         }
553                 return (CC_ERROR);
554         } else {
555                 for (cp = el->el_line.cursor; *cp != '\0' &&
556                     cp < el->el_line.limit; cp++)
557                         if (el_match(cp, pattern)) {
558                                 el->el_line.cursor = cp;
559                                 return (CC_NORM);
560                         }
561                 return (CC_ERROR);
562         }
563 }
564
565
566 /* cv_repeat_srch():
567  *      Vi repeat search
568  */
569 protected el_action_t
570 cv_repeat_srch(EditLine *el, int c)
571 {
572
573 #ifdef SDEBUG
574         (void) fprintf(el->el_errfile, "dir %d patlen %d patbuf %s\n",
575             c, el->el_search.patlen, el->el_search.patbuf);
576 #endif
577
578         el->el_state.lastcmd = (el_action_t) c; /* Hack to stop c_setpat */
579         el->el_line.lastchar = el->el_line.buffer;
580
581         switch (c) {
582         case ED_SEARCH_NEXT_HISTORY:
583                 return (ed_search_next_history(el, 0));
584         case ED_SEARCH_PREV_HISTORY:
585                 return (ed_search_prev_history(el, 0));
586         default:
587                 return (CC_ERROR);
588         }
589 }
590
591
592 /* cv_csearch_back():
593  *      Vi character search reverse
594  */
595 protected el_action_t
596 cv_csearch_back(EditLine *el, int ch, int count, int tflag)
597 {
598         char *cp;
599
600         cp = el->el_line.cursor;
601         while (count--) {
602                 if (*cp == ch)
603                         cp--;
604                 while (cp > el->el_line.buffer && *cp != ch)
605                         cp--;
606         }
607
608         if (cp < el->el_line.buffer || (cp == el->el_line.buffer && *cp != ch))
609                 return (CC_ERROR);
610
611         if (*cp == ch && tflag)
612                 cp++;
613
614         el->el_line.cursor = cp;
615
616         if (el->el_chared.c_vcmd.action & DELETE) {
617                 el->el_line.cursor++;
618                 cv_delfini(el);
619                 return (CC_REFRESH);
620         }
621         re_refresh_cursor(el);
622         return (CC_NORM);
623 }
624
625
626 /* cv_csearch_fwd():
627  *      Vi character search forward
628  */
629 protected el_action_t
630 cv_csearch_fwd(EditLine *el, int ch, int count, int tflag)
631 {
632         char *cp;
633
634         cp = el->el_line.cursor;
635         while (count--) {
636                 if (*cp == ch)
637                         cp++;
638                 while (cp < el->el_line.lastchar && *cp != ch)
639                         cp++;
640         }
641
642         if (cp >= el->el_line.lastchar)
643                 return (CC_ERROR);
644
645         if (*cp == ch && tflag)
646                 cp--;
647
648         el->el_line.cursor = cp;
649
650         if (el->el_chared.c_vcmd.action & DELETE) {
651                 el->el_line.cursor++;
652                 cv_delfini(el);
653                 return (CC_REFRESH);
654         }
655         re_refresh_cursor(el);
656         return (CC_NORM);
657 }
Note: See TracBrowser for help on using the browser.