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