1 /* $OpenBSD: search.c,v 1.27 2016/05/06 13:12:52 schwarze Exp $ */
2 /* $NetBSD: search.c,v 1.45 2016/04/18 17:01:19 christos Exp $ */
5 * Copyright (c) 1992, 1993
6 * The Regents of the University of California. All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Christos Zoulas of Cornell University.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * search.c: History and character search functions
54 * Adjust cursor in vi mode to include the character under it
56 #define EL_CURSOR(el) \
57 ((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \
58 ((el)->el_map.current == (el)->el_map.alt)))
61 * Initialize the search stuff
64 search_init(EditLine
*el
)
67 el
->el_search
.patbuf
= reallocarray(NULL
, EL_BUFSIZ
,
68 sizeof(*el
->el_search
.patbuf
));
69 if (el
->el_search
.patbuf
== NULL
)
71 *el
->el_search
.patbuf
= L
'\0';
72 el
->el_search
.patlen
= 0;
73 el
->el_search
.patdir
= -1;
74 el
->el_search
.chacha
= '\0';
75 el
->el_search
.chadir
= CHAR_FWD
;
76 el
->el_search
.chatflg
= 0;
82 * Initialize the search stuff
85 search_end(EditLine
*el
)
88 free(el
->el_search
.patbuf
);
89 el
->el_search
.patbuf
= NULL
;
95 * Handle regular expression errors
99 regerror(const char *msg
)
106 * Return if string matches pattern
109 el_match(const wchar_t *str
, const wchar_t *pat
)
111 static ct_buffer_t conv
;
115 #elif defined (REGEXP)
119 extern char *re_comp(const char *);
120 extern int re_exec(const char *);
123 if (wcsstr(str
, pat
) != 0)
127 if (regcomp(&re
, ct_encode_string(pat
, &conv
), 0) == 0) {
128 rv
= regexec(&re
, ct_encode_string(str
, &conv
), 0, NULL
, 0) == 0;
134 #elif defined(REGEXP)
135 if ((re
= regcomp(ct_encode_string(pat
, &conv
))) != NULL
) {
136 rv
= regexec(re
, ct_encode_string(str
, &conv
));
143 if (re_comp(ct_encode_string(pat
, &conv
)) != NULL
)
146 return re_exec(ct_encode_string(str
, &conv
)) == 1;
152 * return True if the pattern matches the prefix
155 c_hmatch(EditLine
*el
, const wchar_t *str
)
158 (void) fprintf(el
->el_errfile
, "match `%s' with `%s'\n",
159 el
->el_search
.patbuf
, str
);
162 return el_match(str
, el
->el_search
.patbuf
);
167 * Set the history seatch pattern
170 c_setpat(EditLine
*el
)
172 if (el
->el_state
.lastcmd
!= ED_SEARCH_PREV_HISTORY
&&
173 el
->el_state
.lastcmd
!= ED_SEARCH_NEXT_HISTORY
) {
174 el
->el_search
.patlen
= EL_CURSOR(el
) - el
->el_line
.buffer
;
175 if (el
->el_search
.patlen
>= EL_BUFSIZ
)
176 el
->el_search
.patlen
= EL_BUFSIZ
- 1;
177 if (el
->el_search
.patlen
!= 0) {
178 (void) wcsncpy(el
->el_search
.patbuf
, el
->el_line
.buffer
,
179 el
->el_search
.patlen
);
180 el
->el_search
.patbuf
[el
->el_search
.patlen
] = '\0';
182 el
->el_search
.patlen
= wcslen(el
->el_search
.patbuf
);
185 (void) fprintf(el
->el_errfile
, "\neventno = %d\n",
186 el
->el_history
.eventno
);
187 (void) fprintf(el
->el_errfile
, "patlen = %d\n", el
->el_search
.patlen
);
188 (void) fprintf(el
->el_errfile
, "patbuf = \"%s\"\n",
189 el
->el_search
.patbuf
);
190 (void) fprintf(el
->el_errfile
, "cursor %d lastchar %d\n",
191 EL_CURSOR(el
) - el
->el_line
.buffer
,
192 el
->el_line
.lastchar
- el
->el_line
.buffer
);
198 * Emacs incremental search
200 protected el_action_t
201 ce_inc_search(EditLine
*el
, int dir
)
203 static const wchar_t STRfwd
[] = L
"fwd", STRbck
[] = L
"bck";
204 static wchar_t pchar
= L
':'; /* ':' = normal, '?' = failed */
205 static wchar_t endcmd
[2] = {'\0', '\0'};
206 wchar_t *ocursor
= el
->el_line
.cursor
, oldpchar
= pchar
, ch
;
209 el_action_t ret
= CC_NORM
;
211 int ohisteventno
= el
->el_history
.eventno
;
212 size_t oldpatlen
= el
->el_search
.patlen
;
216 if (el
->el_line
.lastchar
+ sizeof(STRfwd
) /
217 sizeof(*el
->el_line
.lastchar
) + 2 +
218 el
->el_search
.patlen
>= el
->el_line
.limit
)
223 if (el
->el_search
.patlen
== 0) { /* first round */
227 el
->el_search
.patbuf
[el
->el_search
.patlen
++] = '.';
228 el
->el_search
.patbuf
[el
->el_search
.patlen
++] = '*';
234 *el
->el_line
.lastchar
++ = '\n';
235 for (cp
= (newdir
== ED_SEARCH_PREV_HISTORY
) ? STRbck
: STRfwd
;
236 *cp
; *el
->el_line
.lastchar
++ = *cp
++)
238 *el
->el_line
.lastchar
++ = pchar
;
239 for (cp
= &el
->el_search
.patbuf
[LEN
];
240 cp
< &el
->el_search
.patbuf
[el
->el_search
.patlen
];
241 *el
->el_line
.lastchar
++ = *cp
++)
243 *el
->el_line
.lastchar
= '\0';
246 if (el_wgetc(el
, &ch
) != 1)
247 return ed_end_of_file(el
, 0);
249 switch (el
->el_map
.current
[(unsigned char) ch
]) {
252 if (el
->el_search
.patlen
>= EL_BUFSIZ
- LEN
)
255 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
257 *el
->el_line
.lastchar
++ = ch
;
258 *el
->el_line
.lastchar
= '\0';
263 case EM_INC_SEARCH_NEXT
:
264 newdir
= ED_SEARCH_NEXT_HISTORY
;
268 case EM_INC_SEARCH_PREV
:
269 newdir
= ED_SEARCH_PREV_HISTORY
;
273 case EM_DELETE_PREV_CHAR
:
274 case ED_DELETE_PREV_CHAR
:
275 if (el
->el_search
.patlen
> LEN
)
283 case 0007: /* ^G: Abort */
288 case 0027: /* ^W: Append word */
289 /* No can do if globbing characters in pattern */
290 for (cp
= &el
->el_search
.patbuf
[LEN
];; cp
++)
291 if (cp
>= &el
->el_search
.patbuf
[
292 el
->el_search
.patlen
]) {
293 el
->el_line
.cursor
+=
294 el
->el_search
.patlen
- LEN
- 1;
295 cp
= c__next_word(el
->el_line
.cursor
,
296 el
->el_line
.lastchar
, 1,
298 while (el
->el_line
.cursor
< cp
&&
299 *el
->el_line
.cursor
!= '\n') {
300 if (el
->el_search
.patlen
>=
305 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
307 *el
->el_line
.lastchar
++ =
308 *el
->el_line
.cursor
++;
310 el
->el_line
.cursor
= ocursor
;
311 *el
->el_line
.lastchar
= '\0';
314 } else if (isglob(*cp
)) {
320 default: /* Terminate and execute cmd */
322 el_wpush(el
, endcmd
);
325 case 0033: /* ESC: Terminate */
333 while (el
->el_line
.lastchar
> el
->el_line
.buffer
&&
334 *el
->el_line
.lastchar
!= '\n')
335 *el
->el_line
.lastchar
-- = '\0';
336 *el
->el_line
.lastchar
= '\0';
340 /* Can't search if unmatched '[' */
341 for (cp
= &el
->el_search
.patbuf
[el
->el_search
.patlen
-1],
343 cp
>= &el
->el_search
.patbuf
[LEN
];
345 if (*cp
== '[' || *cp
== ']') {
349 if (el
->el_search
.patlen
> LEN
&& ch
!= L
'[') {
350 if (redo
&& newdir
== dir
) {
351 if (pchar
== '?') { /* wrap around */
352 el
->el_history
.eventno
=
353 newdir
== ED_SEARCH_PREV_HISTORY
? 0 : 0x7fffffff;
354 if (hist_get(el
) == CC_ERROR
)
355 /* el->el_history.event
359 el
->el_line
.cursor
= newdir
==
360 ED_SEARCH_PREV_HISTORY
?
361 el
->el_line
.lastchar
:
364 el
->el_line
.cursor
+=
366 ED_SEARCH_PREV_HISTORY
?
370 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
372 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
375 el
->el_search
.patbuf
[el
->el_search
.patlen
] =
377 if (el
->el_line
.cursor
< el
->el_line
.buffer
||
378 el
->el_line
.cursor
> el
->el_line
.lastchar
||
379 (ret
= ce_search_line(el
, newdir
))
382 el
->el_state
.lastcmd
=
383 (el_action_t
) newdir
;
384 ret
= newdir
== ED_SEARCH_PREV_HISTORY
?
385 ed_search_prev_history(el
, 0) :
386 ed_search_next_history(el
, 0);
387 if (ret
!= CC_ERROR
) {
388 el
->el_line
.cursor
= newdir
==
389 ED_SEARCH_PREV_HISTORY
?
390 el
->el_line
.lastchar
:
392 (void) ce_search_line(el
,
396 el
->el_search
.patlen
-= LEN
;
397 el
->el_search
.patbuf
[el
->el_search
.patlen
] =
399 if (ret
== CC_ERROR
) {
401 if (el
->el_history
.eventno
!=
403 el
->el_history
.eventno
=
405 if (hist_get(el
) == CC_ERROR
)
408 el
->el_line
.cursor
= ocursor
;
414 ret
= ce_inc_search(el
, newdir
);
416 if (ret
== CC_ERROR
&& pchar
== '?' && oldpchar
== ':')
418 * break abort of failed search at last
424 if (ret
== CC_NORM
|| (ret
== CC_ERROR
&& oldpatlen
== 0)) {
425 /* restore on normal return or error exit */
427 el
->el_search
.patlen
= oldpatlen
;
428 if (el
->el_history
.eventno
!= ohisteventno
) {
429 el
->el_history
.eventno
= ohisteventno
;
430 if (hist_get(el
) == CC_ERROR
)
433 el
->el_line
.cursor
= ocursor
;
437 if (done
|| ret
!= CC_NORM
)
446 protected el_action_t
447 cv_search(EditLine
*el
, int dir
)
450 wchar_t tmpbuf
[EL_BUFSIZ
];
459 el
->el_search
.patdir
= dir
;
461 tmplen
= c_gets(el
, &tmpbuf
[LEN
],
462 dir
== ED_SEARCH_PREV_HISTORY
? L
"\n/" : L
"\n?" );
468 tmpbuf
[tmplen
] = '\0';
472 * Use the old pattern, but wild-card it.
474 if (el
->el_search
.patlen
== 0) {
479 if (el
->el_search
.patbuf
[0] != '.' &&
480 el
->el_search
.patbuf
[0] != '*') {
481 (void) wcsncpy(tmpbuf
, el
->el_search
.patbuf
,
482 sizeof(tmpbuf
) / sizeof(*tmpbuf
) - 1);
483 el
->el_search
.patbuf
[0] = '.';
484 el
->el_search
.patbuf
[1] = '*';
485 (void) wcsncpy(&el
->el_search
.patbuf
[2], tmpbuf
,
487 el
->el_search
.patlen
++;
488 el
->el_search
.patbuf
[el
->el_search
.patlen
++] = '.';
489 el
->el_search
.patbuf
[el
->el_search
.patlen
++] = '*';
490 el
->el_search
.patbuf
[el
->el_search
.patlen
] = '\0';
495 tmpbuf
[tmplen
++] = '.';
496 tmpbuf
[tmplen
++] = '*';
498 tmpbuf
[tmplen
] = '\0';
499 (void) wcsncpy(el
->el_search
.patbuf
, tmpbuf
, EL_BUFSIZ
- 1);
500 el
->el_search
.patlen
= tmplen
;
502 el
->el_state
.lastcmd
= (el_action_t
) dir
; /* avoid c_setpat */
503 el
->el_line
.cursor
= el
->el_line
.lastchar
= el
->el_line
.buffer
;
504 if ((dir
== ED_SEARCH_PREV_HISTORY
? ed_search_prev_history(el
, 0) :
505 ed_search_next_history(el
, 0)) == CC_ERROR
) {
511 return ed_newline(el
, 0);
518 * Look for a pattern inside a line
520 protected el_action_t
521 ce_search_line(EditLine
*el
, int dir
)
523 wchar_t *cp
= el
->el_line
.cursor
;
524 wchar_t *pattern
= el
->el_search
.patbuf
;
535 if (dir
== ED_SEARCH_PREV_HISTORY
) {
536 for (; cp
>= el
->el_line
.buffer
; cp
--) {
537 if (el_match(cp
, ocp
)) {
539 el
->el_line
.cursor
= cp
;
546 for (; *cp
!= '\0' && cp
< el
->el_line
.limit
; cp
++) {
547 if (el_match(cp
, ocp
)) {
549 el
->el_line
.cursor
= cp
;
562 protected el_action_t
563 cv_repeat_srch(EditLine
*el
, wint_t c
)
567 (void) fprintf(el
->el_errfile
, "dir %d patlen %d patbuf %s\n",
568 c
, el
->el_search
.patlen
, ct_encode_string(el
->el_search
.patbuf
));
571 el
->el_state
.lastcmd
= (el_action_t
) c
; /* Hack to stop c_setpat */
572 el
->el_line
.lastchar
= el
->el_line
.buffer
;
575 case ED_SEARCH_NEXT_HISTORY
:
576 return ed_search_next_history(el
, 0);
577 case ED_SEARCH_PREV_HISTORY
:
578 return ed_search_prev_history(el
, 0);
586 * Vi character search
588 protected el_action_t
589 cv_csearch(EditLine
*el
, int direction
, wint_t ch
, int count
, int tflag
)
596 if (ch
== (wint_t)-1) {
597 if (el_wgetc(el
, &ch
) != 1)
598 return ed_end_of_file(el
, 0);
601 /* Save for ';' and ',' commands */
602 el
->el_search
.chacha
= ch
;
603 el
->el_search
.chadir
= direction
;
604 el
->el_search
.chatflg
= tflag
;
606 cp
= el
->el_line
.cursor
;
608 if ((wint_t)*cp
== ch
)
610 for (;;cp
+= direction
) {
611 if (cp
>= el
->el_line
.lastchar
)
613 if (cp
< el
->el_line
.buffer
)
615 if ((wint_t)*cp
== ch
)
623 el
->el_line
.cursor
= cp
;
625 if (el
->el_chared
.c_vcmd
.action
!= NOP
) {
627 el
->el_line
.cursor
++;