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