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