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