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