]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libedit/search.c
MFC r352242, r352249
[FreeBSD/FreeBSD.git] / lib / libedit / search.c
1 /*      $NetBSD: search.c,v 1.39 2016/02/24 14:25:38 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.39 2016/02/24 14:25:38 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 #include <string.h>
51 #if defined(REGEX)
52 #include <regex.h>
53 #elif defined(REGEXP)
54 #include <regexp.h>
55 #endif
56
57 #include "el.h"
58 #include "common.h"
59
60 /*
61  * Adjust cursor in vi mode to include the character under it
62  */
63 #define EL_CURSOR(el) \
64     ((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \
65                             ((el)->el_map.current == (el)->el_map.alt)))
66
67 /* search_init():
68  *      Initialize the search stuff
69  */
70 protected int
71 search_init(EditLine *el)
72 {
73
74         el->el_search.patbuf = el_malloc(EL_BUFSIZ *
75             sizeof(*el->el_search.patbuf));
76         if (el->el_search.patbuf == NULL)
77                 return -1;
78         el->el_search.patlen = 0;
79         el->el_search.patdir = -1;
80         el->el_search.chacha = '\0';
81         el->el_search.chadir = CHAR_FWD;
82         el->el_search.chatflg = 0;
83         return 0;
84 }
85
86
87 /* search_end():
88  *      Initialize the search stuff
89  */
90 protected void
91 search_end(EditLine *el)
92 {
93
94         el_free(el->el_search.patbuf);
95         el->el_search.patbuf = NULL;
96 }
97
98
99 #ifdef REGEXP
100 /* regerror():
101  *      Handle regular expression errors
102  */
103 public void
104 /*ARGSUSED*/
105 regerror(const char *msg)
106 {
107 }
108 #endif
109
110
111 /* el_match():
112  *      Return if string matches pattern
113  */
114 protected int
115 el_match(const Char *str, const Char *pat)
116 {
117 #ifdef WIDECHAR
118         static ct_buffer_t conv;
119 #endif
120 #if defined (REGEX)
121         regex_t re;
122         int rv;
123 #elif defined (REGEXP)
124         regexp *rp;
125         int rv;
126 #else
127         extern char     *re_comp(const char *);
128         extern int       re_exec(const char *);
129 #endif
130
131         if (Strstr(str, pat) != 0)
132                 return 1;
133
134 #if defined(REGEX)
135         if (regcomp(&re, ct_encode_string(pat, &conv), 0) == 0) {
136                 rv = regexec(&re, ct_encode_string(str, &conv), (size_t)0, NULL,
137                     0) == 0;
138                 regfree(&re);
139         } else {
140                 rv = 0;
141         }
142         return rv;
143 #elif defined(REGEXP)
144         if ((re = regcomp(ct_encode_string(pat, &conv))) != NULL) {
145                 rv = regexec(re, ct_encode_string(str, &conv));
146                 el_free(re);
147         } else {
148                 rv = 0;
149         }
150         return rv;
151 #else
152         if (re_comp(ct_encode_string(pat, &conv)) != NULL)
153                 return 0;
154         else
155                 return re_exec(ct_encode_string(str, &conv)) == 1;
156 #endif
157 }
158
159
160 /* c_hmatch():
161  *       return True if the pattern matches the prefix
162  */
163 protected int
164 c_hmatch(EditLine *el, const Char *str)
165 {
166 #ifdef SDEBUG
167         (void) fprintf(el->el_errfile, "match `%s' with `%s'\n",
168             el->el_search.patbuf, str);
169 #endif /* SDEBUG */
170
171         return el_match(str, el->el_search.patbuf);
172 }
173
174
175 /* c_setpat():
176  *      Set the history seatch pattern
177  */
178 protected void
179 c_setpat(EditLine *el)
180 {
181         if (el->el_state.lastcmd != ED_SEARCH_PREV_HISTORY &&
182             el->el_state.lastcmd != ED_SEARCH_NEXT_HISTORY) {
183                 el->el_search.patlen =
184                     (size_t)(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 *ocursor = el->el_line.cursor, oldpchar = pchar, ch;
218         const Char *cp;
219         wchar_t wch;
220
221         el_action_t ret = CC_NORM;
222
223         int ohisteventno = el->el_history.eventno;
224         size_t oldpatlen = el->el_search.patlen;
225         int newdir = dir;
226         int done, redo;
227
228         if (el->el_line.lastchar + sizeof(STRfwd) /
229             sizeof(*el->el_line.lastchar) + 2 +
230             el->el_search.patlen >= el->el_line.limit)
231                 return CC_ERROR;
232
233         for (;;) {
234
235                 if (el->el_search.patlen == 0) {        /* first round */
236                         pchar = ':';
237 #ifdef ANCHOR
238 #define LEN     2
239                         el->el_search.patbuf[el->el_search.patlen++] = '.';
240                         el->el_search.patbuf[el->el_search.patlen++] = '*';
241 #else
242 #define LEN     0
243 #endif
244                 }
245                 done = redo = 0;
246                 *el->el_line.lastchar++ = '\n';
247                 for (cp = (newdir == ED_SEARCH_PREV_HISTORY) ? STRbck : STRfwd;
248                     *cp; *el->el_line.lastchar++ = *cp++)
249                         continue;
250                 *el->el_line.lastchar++ = pchar;
251                 for (cp = &el->el_search.patbuf[LEN];
252                     cp < &el->el_search.patbuf[el->el_search.patlen];
253                     *el->el_line.lastchar++ = *cp++)
254                         continue;
255                 *el->el_line.lastchar = '\0';
256                 re_refresh(el);
257
258                 if (el_wgetc(el, &wch) != 1)
259                         return ed_end_of_file(el, 0);
260
261                 ch = (Char)wch;
262
263                 switch (el->el_map.current[(unsigned char) ch]) {
264                 case ED_INSERT:
265                 case ED_DIGIT:
266                         if (el->el_search.patlen >= EL_BUFSIZ - LEN)
267                                 terminal_beep(el);
268                         else {
269                                 el->el_search.patbuf[el->el_search.patlen++] =
270                                     ch;
271                                 *el->el_line.lastchar++ = ch;
272                                 *el->el_line.lastchar = '\0';
273                                 re_refresh(el);
274                         }
275                         break;
276
277                 case EM_INC_SEARCH_NEXT:
278                         newdir = ED_SEARCH_NEXT_HISTORY;
279                         redo++;
280                         break;
281
282                 case EM_INC_SEARCH_PREV:
283                         newdir = ED_SEARCH_PREV_HISTORY;
284                         redo++;
285                         break;
286
287                 case EM_DELETE_PREV_CHAR:
288                 case ED_DELETE_PREV_CHAR:
289                         if (el->el_search.patlen > LEN)
290                                 done++;
291                         else
292                                 terminal_beep(el);
293                         break;
294
295                 default:
296                         switch (ch) {
297                         case 0007:      /* ^G: Abort */
298                                 ret = CC_ERROR;
299                                 done++;
300                                 break;
301
302                         case 0027:      /* ^W: Append word */
303                         /* No can do if globbing characters in pattern */
304                                 for (cp = &el->el_search.patbuf[LEN];; cp++)
305                                     if (cp >= &el->el_search.patbuf[
306                                         el->el_search.patlen]) {
307                                         el->el_line.cursor +=
308                                             el->el_search.patlen - LEN - 1;
309                                         cp = c__next_word(el->el_line.cursor,
310                                             el->el_line.lastchar, 1,
311                                             ce__isword);
312                                         while (el->el_line.cursor < cp &&
313                                             *el->el_line.cursor != '\n') {
314                                                 if (el->el_search.patlen >=
315                                                     EL_BUFSIZ - LEN) {
316                                                         terminal_beep(el);
317                                                         break;
318                                                 }
319                                                 el->el_search.patbuf[el->el_search.patlen++] =
320                                                     *el->el_line.cursor;
321                                                 *el->el_line.lastchar++ =
322                                                     *el->el_line.cursor++;
323                                         }
324                                         el->el_line.cursor = ocursor;
325                                         *el->el_line.lastchar = '\0';
326                                         re_refresh(el);
327                                         break;
328                                     } else if (isglob(*cp)) {
329                                             terminal_beep(el);
330                                             break;
331                                     }
332                                 break;
333
334                         default:        /* Terminate and execute cmd */
335                                 endcmd[0] = ch;
336                                 FUN(el,push)(el, endcmd);
337                                 /* FALLTHROUGH */
338
339                         case 0033:      /* ESC: Terminate */
340                                 ret = CC_REFRESH;
341                                 done++;
342                                 break;
343                         }
344                         break;
345                 }
346
347                 while (el->el_line.lastchar > el->el_line.buffer &&
348                     *el->el_line.lastchar != '\n')
349                         *el->el_line.lastchar-- = '\0';
350                 *el->el_line.lastchar = '\0';
351
352                 if (!done) {
353
354                         /* Can't search if unmatched '[' */
355                         for (cp = &el->el_search.patbuf[el->el_search.patlen-1],
356                             ch = L']';
357                             cp >= &el->el_search.patbuf[LEN];
358                             cp--)
359                                 if (*cp == '[' || *cp == ']') {
360                                         ch = *cp;
361                                         break;
362                                 }
363                         if (el->el_search.patlen > LEN && ch != L'[') {
364                                 if (redo && newdir == dir) {
365                                         if (pchar == '?') { /* wrap around */
366                                                 el->el_history.eventno =
367                                                     newdir == ED_SEARCH_PREV_HISTORY ? 0 : 0x7fffffff;
368                                                 if (hist_get(el) == CC_ERROR)
369                                                         /* el->el_history.event
370                                                          * no was fixed by
371                                                          * first call */
372                                                         (void) hist_get(el);
373                                                 el->el_line.cursor = newdir ==
374                                                     ED_SEARCH_PREV_HISTORY ?
375                                                     el->el_line.lastchar :
376                                                     el->el_line.buffer;
377                                         } else
378                                                 el->el_line.cursor +=
379                                                     newdir ==
380                                                     ED_SEARCH_PREV_HISTORY ?
381                                                     -1 : 1;
382                                 }
383 #ifdef ANCHOR
384                                 el->el_search.patbuf[el->el_search.patlen++] =
385                                     '.';
386                                 el->el_search.patbuf[el->el_search.patlen++] =
387                                     '*';
388 #endif
389                                 el->el_search.patbuf[el->el_search.patlen] =
390                                     '\0';
391                                 if (el->el_line.cursor < el->el_line.buffer ||
392                                     el->el_line.cursor > el->el_line.lastchar ||
393                                     (ret = ce_search_line(el, newdir))
394                                     == CC_ERROR) {
395                                         /* avoid c_setpat */
396                                         el->el_state.lastcmd =
397                                             (el_action_t) newdir;
398                                         ret = (el_action_t)
399                                             (newdir == ED_SEARCH_PREV_HISTORY ?
400                                             ed_search_prev_history(el, 0) :
401                                             ed_search_next_history(el, 0));
402                                         if (ret != CC_ERROR) {
403                                                 el->el_line.cursor = newdir ==
404                                                     ED_SEARCH_PREV_HISTORY ?
405                                                     el->el_line.lastchar :
406                                                     el->el_line.buffer;
407                                                 (void) ce_search_line(el,
408                                                     newdir);
409                                         }
410                                 }
411                                 el->el_search.patlen -= LEN;
412                                 el->el_search.patbuf[el->el_search.patlen] =
413                                     '\0';
414                                 if (ret == CC_ERROR) {
415                                         terminal_beep(el);
416                                         if (el->el_history.eventno !=
417                                             ohisteventno) {
418                                                 el->el_history.eventno =
419                                                     ohisteventno;
420                                                 if (hist_get(el) == CC_ERROR)
421                                                         return CC_ERROR;
422                                         }
423                                         el->el_line.cursor = ocursor;
424                                         pchar = '?';
425                                 } else {
426                                         pchar = ':';
427                                 }
428                         }
429                         ret = ce_inc_search(el, newdir);
430
431                         if (ret == CC_ERROR && pchar == '?' && oldpchar == ':')
432                                 /*
433                                  * break abort of failed search at last
434                                  * non-failed
435                                  */
436                                 ret = CC_NORM;
437
438                 }
439                 if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) {
440                         /* restore on normal return or error exit */
441                         pchar = oldpchar;
442                         el->el_search.patlen = oldpatlen;
443                         if (el->el_history.eventno != ohisteventno) {
444                                 el->el_history.eventno = ohisteventno;
445                                 if (hist_get(el) == CC_ERROR)
446                                         return CC_ERROR;
447                         }
448                         el->el_line.cursor = ocursor;
449                         if (ret == CC_ERROR)
450                                 re_refresh(el);
451                 }
452                 if (done || ret != CC_NORM)
453                         return ret;
454         }
455 }
456
457
458 /* cv_search():
459  *      Vi search.
460  */
461 protected el_action_t
462 cv_search(EditLine *el, int dir)
463 {
464         Char ch;
465         Char tmpbuf[EL_BUFSIZ];
466         ssize_t tmplen;
467
468 #ifdef ANCHOR
469         tmpbuf[0] = '.';
470         tmpbuf[1] = '*';
471 #endif
472         tmplen = LEN;
473
474         el->el_search.patdir = dir;
475
476         tmplen = c_gets(el, &tmpbuf[LEN],
477                 dir == ED_SEARCH_PREV_HISTORY ? STR("\n/") : STR("\n?") );
478         if (tmplen == -1)
479                 return CC_REFRESH;
480
481         tmplen += 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                         re_refresh(el);
491                         return CC_ERROR;
492                 }
493 #ifdef ANCHOR
494                 if (el->el_search.patbuf[0] != '.' &&
495                     el->el_search.patbuf[0] != '*') {
496                         (void) Strncpy(tmpbuf, el->el_search.patbuf,
497                             sizeof(tmpbuf) / sizeof(*tmpbuf) - 1);
498                         el->el_search.patbuf[0] = '.';
499                         el->el_search.patbuf[1] = '*';
500                         (void) Strncpy(&el->el_search.patbuf[2], tmpbuf,
501                             EL_BUFSIZ - 3);
502                         el->el_search.patlen++;
503                         el->el_search.patbuf[el->el_search.patlen++] = '.';
504                         el->el_search.patbuf[el->el_search.patlen++] = '*';
505                         el->el_search.patbuf[el->el_search.patlen] = '\0';
506                 }
507 #endif
508         } else {
509 #ifdef ANCHOR
510                 tmpbuf[tmplen++] = '.';
511                 tmpbuf[tmplen++] = '*';
512 #endif
513                 tmpbuf[tmplen] = '\0';
514                 (void) Strncpy(el->el_search.patbuf, tmpbuf, EL_BUFSIZ - 1);
515                 el->el_search.patlen = (size_t)tmplen;
516         }
517         el->el_state.lastcmd = (el_action_t) dir;       /* avoid c_setpat */
518         el->el_line.cursor = el->el_line.lastchar = el->el_line.buffer;
519         if ((dir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) :
520             ed_search_next_history(el, 0)) == CC_ERROR) {
521                 re_refresh(el);
522                 return CC_ERROR;
523         }
524         if (ch == 0033) {
525                 re_refresh(el);
526                 return ed_newline(el, 0);
527         }
528         return CC_REFRESH;
529 }
530
531
532 /* ce_search_line():
533  *      Look for a pattern inside a line
534  */
535 protected el_action_t
536 ce_search_line(EditLine *el, int dir)
537 {
538         Char *cp = el->el_line.cursor;
539         Char *pattern = el->el_search.patbuf;
540         Char oc, *ocp;
541 #ifdef ANCHOR
542         ocp = &pattern[1];
543         oc = *ocp;
544         *ocp = '^';
545 #else
546         ocp = pattern;
547         oc = *ocp;
548 #endif
549
550         if (dir == ED_SEARCH_PREV_HISTORY) {
551                 for (; cp >= el->el_line.buffer; cp--) {
552                         if (el_match(cp, ocp)) {
553                                 *ocp = oc;
554                                 el->el_line.cursor = cp;
555                                 return CC_NORM;
556                         }
557                 }
558                 *ocp = oc;
559                 return CC_ERROR;
560         } else {
561                 for (; *cp != '\0' && cp < el->el_line.limit; cp++) {
562                         if (el_match(cp, ocp)) {
563                                 *ocp = oc;
564                                 el->el_line.cursor = cp;
565                                 return CC_NORM;
566                         }
567                 }
568                 *ocp = oc;
569                 return CC_ERROR;
570         }
571 }
572
573
574 /* cv_repeat_srch():
575  *      Vi repeat search
576  */
577 protected el_action_t
578 cv_repeat_srch(EditLine *el, wint_t c)
579 {
580
581 #ifdef SDEBUG
582         (void) fprintf(el->el_errfile, "dir %d patlen %d patbuf %s\n",
583             c, el->el_search.patlen, ct_encode_string(el->el_search.patbuf));
584 #endif
585
586         el->el_state.lastcmd = (el_action_t) c; /* Hack to stop c_setpat */
587         el->el_line.lastchar = el->el_line.buffer;
588
589         switch (c) {
590         case ED_SEARCH_NEXT_HISTORY:
591                 return ed_search_next_history(el, 0);
592         case ED_SEARCH_PREV_HISTORY:
593                 return ed_search_prev_history(el, 0);
594         default:
595                 return CC_ERROR;
596         }
597 }
598
599
600 /* cv_csearch():
601  *      Vi character search
602  */
603 protected el_action_t
604 cv_csearch(EditLine *el, int direction, wint_t ch, int count, int tflag)
605 {
606         Char *cp;
607
608         if (ch == 0)
609                 return CC_ERROR;
610
611         if (ch == (wint_t)-1) {
612                 if (el_wgetc(el, &ch) != 1)
613                         return ed_end_of_file(el, 0);
614         }
615
616         /* Save for ';' and ',' commands */
617         el->el_search.chacha = (Char)ch;
618         el->el_search.chadir = direction;
619         el->el_search.chatflg = (char)tflag;
620
621         cp = el->el_line.cursor;
622         while (count--) {
623                 if ((wint_t)*cp == ch)
624                         cp += direction;
625                 for (;;cp += direction) {
626                         if (cp >= el->el_line.lastchar)
627                                 return CC_ERROR;
628                         if (cp < el->el_line.buffer)
629                                 return CC_ERROR;
630                         if ((wint_t)*cp == ch)
631                                 break;
632                 }
633         }
634
635         if (tflag)
636                 cp -= direction;
637
638         el->el_line.cursor = cp;
639
640         if (el->el_chared.c_vcmd.action != NOP) {
641                 if (direction > 0)
642                         el->el_line.cursor++;
643                 cv_delfini(el);
644                 return CC_REFRESH;
645         }
646         return CC_CURSOR;
647 }