1 /* $NetBSD: search.c,v 1.22 2009/12/30 22:37:40 christos Exp $ */
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Christos Zoulas of Cornell University.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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
36 #if !defined(lint) && !defined(SCCSID)
38 static char sccsid
[] = "@(#)search.c 8.1 (Berkeley) 6/4/93";
40 __RCSID("$NetBSD: search.c,v 1.22 2009/12/30 22:37:40 christos Exp $");
42 #endif /* not lint && not SCCSID */
45 * search.c: History and character search functions
56 * Adjust cursor in vi mode to include the character under it
58 #define EL_CURSOR(el) \
59 ((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \
60 ((el)->el_map.current == (el)->el_map.alt)))
63 * Initialize the search stuff
66 search_init(EditLine
*el
)
69 el
->el_search
.patbuf
= el_malloc(EL_BUFSIZ
*
70 sizeof(*el
->el_search
.patbuf
));
71 if (el
->el_search
.patbuf
== NULL
)
73 el
->el_search
.patlen
= 0;
74 el
->el_search
.patdir
= -1;
75 el
->el_search
.chacha
= '\0';
76 el
->el_search
.chadir
= CHAR_FWD
;
77 el
->el_search
.chatflg
= 0;
83 * Initialize the search stuff
86 search_end(EditLine
*el
)
89 el_free((ptr_t
) el
->el_search
.patbuf
);
90 el
->el_search
.patbuf
= NULL
;
96 * Handle regular expression errors
100 regerror(const char *msg
)
107 * Return if string matches pattern
110 el_match(const Char
*str
, const Char
*pat
)
113 static ct_buffer_t conv
;
118 #elif defined (REGEXP)
122 extern char *re_comp(const char *);
123 extern int re_exec(const char *);
126 if (Strstr(str
, pat
) != 0)
130 if (regcomp(&re
, ct_encode_string(pat
, &conv
), 0) == 0) {
131 rv
= regexec(&re
, ct_encode_string(str
, &conv
), 0, NULL
, 0) == 0;
137 #elif defined(REGEXP)
138 if ((re
= regcomp(ct_encode_string(pat
, &conv
))) != NULL
) {
139 rv
= regexec(re
, ct_encode_string(str
, &conv
));
146 if (re_comp(ct_encode_string(pat
, &conv
)) != NULL
)
149 return (re_exec(ct_encode_string(str
, &conv
)) == 1);
155 * return True if the pattern matches the prefix
158 c_hmatch(EditLine
*el
, const Char
*str
)
161 (void) fprintf(el
->el_errfile
, "match `%s' with `%s'\n",
162 el
->el_search
.patbuf
, str
);
165 return (el_match(str
, el
->el_search
.patbuf
));
170 * Set the history seatch pattern
173 c_setpat(EditLine
*el
)
175 if (el
->el_state
.lastcmd
!= ED_SEARCH_PREV_HISTORY
&&
176 el
->el_state
.lastcmd
!= ED_SEARCH_NEXT_HISTORY
) {
177 el
->el_search
.patlen
= EL_CURSOR(el
) - el
->el_line
.buffer
;
178 if (el
->el_search
.patlen
>= EL_BUFSIZ
)
179 el
->el_search
.patlen
= EL_BUFSIZ
- 1;
180 if (el
->el_search
.patlen
!= 0) {
181 (void) Strncpy(el
->el_search
.patbuf
, el
->el_line
.buffer
,
182 el
->el_search
.patlen
);
183 el
->el_search
.patbuf
[el
->el_search
.patlen
] = '\0';
185 el
->el_search
.patlen
= Strlen(el
->el_search
.patbuf
);
188 (void) fprintf(el
->el_errfile
, "\neventno = %d\n",
189 el
->el_history
.eventno
);
190 (void) fprintf(el
->el_errfile
, "patlen = %d\n", el
->el_search
.patlen
);
191 (void) fprintf(el
->el_errfile
, "patbuf = \"%s\"\n",
192 el
->el_search
.patbuf
);
193 (void) fprintf(el
->el_errfile
, "cursor %d lastchar %d\n",
194 EL_CURSOR(el
) - el
->el_line
.buffer
,
195 el
->el_line
.lastchar
- el
->el_line
.buffer
);
201 * Emacs incremental search
203 protected el_action_t
204 ce_inc_search(EditLine
*el
, int dir
)
206 static const Char STRfwd
[] = {'f', 'w', 'd', '\0'},
207 STRbck
[] = {'b', 'c', 'k', '\0'};
208 static Char pchar
= ':';/* ':' = normal, '?' = failed */
209 static Char endcmd
[2] = {'\0', '\0'};
210 Char ch
, *ocursor
= el
->el_line
.cursor
, oldpchar
= pchar
;
213 el_action_t ret
= CC_NORM
;
215 int ohisteventno
= el
->el_history
.eventno
;
216 size_t oldpatlen
= el
->el_search
.patlen
;
220 if (el
->el_line
.lastchar
+ sizeof(STRfwd
) /
221 sizeof(*el
->el_line
.lastchar
) + 2 +
222 el
->el_search
.patlen
>= el
->el_line
.limit
)
227 if (el
->el_search
.patlen
== 0) { /* first round */
231 el
->el_search
.patbuf
[el
->el_search
.patlen
++] = '.';
232 el
->el_search
.patbuf
[el
->el_search
.patlen
++] = '*';
238 *el
->el_line
.lastchar
++ = '\n';
239 for (cp
= (newdir
== ED_SEARCH_PREV_HISTORY
) ? STRbck
: STRfwd
;
240 *cp
; *el
->el_line
.lastchar
++ = *cp
++)
242 *el
->el_line
.lastchar
++ = pchar
;
243 for (cp
= &el
->el_search
.patbuf
[LEN
];
244 cp
< &el
->el_search
.patbuf
[el
->el_search
.patlen
];
245 *el
->el_line
.lastchar
++ = *cp
++)
247 *el
->el_line
.lastchar
= '\0';
250 if (FUN(el
,getc
)(el
, &ch
) != 1)
251 return (ed_end_of_file(el
, 0));
253 switch (el
->el_map
.current
[(unsigned char) ch
]) {
256 if (el
->el_search
.patlen
>= EL_BUFSIZ
- LEN
)
259 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
261 *el
->el_line
.lastchar
++ = ch
;
262 *el
->el_line
.lastchar
= '\0';
267 case EM_INC_SEARCH_NEXT
:
268 newdir
= ED_SEARCH_NEXT_HISTORY
;
272 case EM_INC_SEARCH_PREV
:
273 newdir
= ED_SEARCH_PREV_HISTORY
;
277 case EM_DELETE_PREV_CHAR
:
278 case ED_DELETE_PREV_CHAR
:
279 if (el
->el_search
.patlen
> LEN
)
287 case 0007: /* ^G: Abort */
292 case 0027: /* ^W: Append word */
293 /* No can do if globbing characters in pattern */
294 for (cp
= &el
->el_search
.patbuf
[LEN
];; cp
++)
295 if (cp
>= &el
->el_search
.patbuf
[
296 el
->el_search
.patlen
]) {
297 el
->el_line
.cursor
+=
298 el
->el_search
.patlen
- LEN
- 1;
299 cp
= c__next_word(el
->el_line
.cursor
,
300 el
->el_line
.lastchar
, 1,
302 while (el
->el_line
.cursor
< cp
&&
303 *el
->el_line
.cursor
!= '\n') {
304 if (el
->el_search
.patlen
>=
309 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
311 *el
->el_line
.lastchar
++ =
312 *el
->el_line
.cursor
++;
314 el
->el_line
.cursor
= ocursor
;
315 *el
->el_line
.lastchar
= '\0';
318 } else if (isglob(*cp
)) {
324 default: /* Terminate and execute cmd */
326 FUN(el
,push
)(el
, endcmd
);
329 case 0033: /* ESC: Terminate */
337 while (el
->el_line
.lastchar
> el
->el_line
.buffer
&&
338 *el
->el_line
.lastchar
!= '\n')
339 *el
->el_line
.lastchar
-- = '\0';
340 *el
->el_line
.lastchar
= '\0';
344 /* Can't search if unmatched '[' */
345 for (cp
= &el
->el_search
.patbuf
[el
->el_search
.patlen
-1],
347 cp
>= &el
->el_search
.patbuf
[LEN
];
349 if (*cp
== '[' || *cp
== ']') {
353 if (el
->el_search
.patlen
> LEN
&& ch
!= '[') {
354 if (redo
&& newdir
== dir
) {
355 if (pchar
== '?') { /* wrap around */
356 el
->el_history
.eventno
=
357 newdir
== ED_SEARCH_PREV_HISTORY
? 0 : 0x7fffffff;
358 if (hist_get(el
) == CC_ERROR
)
359 /* el->el_history.event
363 el
->el_line
.cursor
= newdir
==
364 ED_SEARCH_PREV_HISTORY
?
365 el
->el_line
.lastchar
:
368 el
->el_line
.cursor
+=
370 ED_SEARCH_PREV_HISTORY
?
374 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
376 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
379 el
->el_search
.patbuf
[el
->el_search
.patlen
] =
381 if (el
->el_line
.cursor
< el
->el_line
.buffer
||
382 el
->el_line
.cursor
> el
->el_line
.lastchar
||
383 (ret
= ce_search_line(el
, newdir
))
386 el
->el_state
.lastcmd
=
387 (el_action_t
) newdir
;
388 ret
= newdir
== ED_SEARCH_PREV_HISTORY
?
389 ed_search_prev_history(el
, 0) :
390 ed_search_next_history(el
, 0);
391 if (ret
!= CC_ERROR
) {
392 el
->el_line
.cursor
= newdir
==
393 ED_SEARCH_PREV_HISTORY
?
394 el
->el_line
.lastchar
:
396 (void) ce_search_line(el
,
400 el
->el_search
.patlen
-= LEN
;
401 el
->el_search
.patbuf
[el
->el_search
.patlen
] =
403 if (ret
== CC_ERROR
) {
405 if (el
->el_history
.eventno
!=
407 el
->el_history
.eventno
=
409 if (hist_get(el
) == CC_ERROR
)
412 el
->el_line
.cursor
= ocursor
;
418 ret
= ce_inc_search(el
, newdir
);
420 if (ret
== CC_ERROR
&& pchar
== '?' && oldpchar
== ':')
422 * break abort of failed search at last
428 if (ret
== CC_NORM
|| (ret
== CC_ERROR
&& oldpatlen
== 0)) {
429 /* restore on normal return or error exit */
431 el
->el_search
.patlen
= oldpatlen
;
432 if (el
->el_history
.eventno
!= ohisteventno
) {
433 el
->el_history
.eventno
= ohisteventno
;
434 if (hist_get(el
) == CC_ERROR
)
437 el
->el_line
.cursor
= ocursor
;
441 if (done
|| ret
!= CC_NORM
)
450 protected el_action_t
451 cv_search(EditLine
*el
, int dir
)
454 Char tmpbuf
[EL_BUFSIZ
];
463 el
->el_search
.patdir
= dir
;
465 tmplen
= c_gets(el
, &tmpbuf
[LEN
],
466 dir
== ED_SEARCH_PREV_HISTORY
? STR("\n/") : STR("\n?") );
472 tmpbuf
[tmplen
] = '\0';
476 * Use the old pattern, but wild-card it.
478 if (el
->el_search
.patlen
== 0) {
483 if (el
->el_search
.patbuf
[0] != '.' &&
484 el
->el_search
.patbuf
[0] != '*') {
485 (void) Strncpy(tmpbuf
, el
->el_search
.patbuf
,
486 sizeof(tmpbuf
) / sizeof(*tmpbuf
) - 1);
487 el
->el_search
.patbuf
[0] = '.';
488 el
->el_search
.patbuf
[1] = '*';
489 (void) Strncpy(&el
->el_search
.patbuf
[2], tmpbuf
,
491 el
->el_search
.patlen
++;
492 el
->el_search
.patbuf
[el
->el_search
.patlen
++] = '.';
493 el
->el_search
.patbuf
[el
->el_search
.patlen
++] = '*';
494 el
->el_search
.patbuf
[el
->el_search
.patlen
] = '\0';
499 tmpbuf
[tmplen
++] = '.';
500 tmpbuf
[tmplen
++] = '*';
502 tmpbuf
[tmplen
] = '\0';
503 (void) Strncpy(el
->el_search
.patbuf
, tmpbuf
, EL_BUFSIZ
- 1);
504 el
->el_search
.patlen
= tmplen
;
506 el
->el_state
.lastcmd
= (el_action_t
) dir
; /* avoid c_setpat */
507 el
->el_line
.cursor
= el
->el_line
.lastchar
= el
->el_line
.buffer
;
508 if ((dir
== ED_SEARCH_PREV_HISTORY
? ed_search_prev_history(el
, 0) :
509 ed_search_next_history(el
, 0)) == CC_ERROR
) {
515 return ed_newline(el
, 0);
522 * Look for a pattern inside a line
524 protected el_action_t
525 ce_search_line(EditLine
*el
, int dir
)
527 Char
*cp
= el
->el_line
.cursor
;
528 Char
*pattern
= el
->el_search
.patbuf
;
539 if (dir
== ED_SEARCH_PREV_HISTORY
) {
540 for (; cp
>= el
->el_line
.buffer
; cp
--) {
541 if (el_match(cp
, ocp
)) {
543 el
->el_line
.cursor
= cp
;
550 for (; *cp
!= '\0' && cp
< el
->el_line
.limit
; cp
++) {
551 if (el_match(cp
, ocp
)) {
553 el
->el_line
.cursor
= cp
;
566 protected el_action_t
567 cv_repeat_srch(EditLine
*el
, wint_t c
)
571 (void) fprintf(el
->el_errfile
, "dir %d patlen %d patbuf %s\n",
572 c
, el
->el_search
.patlen
, ct_encode_string(el
->el_search
.patbuf
));
575 el
->el_state
.lastcmd
= (el_action_t
) c
; /* Hack to stop c_setpat */
576 el
->el_line
.lastchar
= el
->el_line
.buffer
;
579 case ED_SEARCH_NEXT_HISTORY
:
580 return (ed_search_next_history(el
, 0));
581 case ED_SEARCH_PREV_HISTORY
:
582 return (ed_search_prev_history(el
, 0));
590 * Vi character search
592 protected el_action_t
593 cv_csearch(EditLine
*el
, int direction
, wint_t ch
, int count
, int tflag
)
602 if (FUN(el
,getc
)(el
, &c
) != 1)
603 return ed_end_of_file(el
, 0);
607 /* Save for ';' and ',' commands */
608 el
->el_search
.chacha
= ch
;
609 el
->el_search
.chadir
= direction
;
610 el
->el_search
.chatflg
= tflag
;
612 cp
= el
->el_line
.cursor
;
616 for (;;cp
+= direction
) {
617 if (cp
>= el
->el_line
.lastchar
)
619 if (cp
< el
->el_line
.buffer
)
629 el
->el_line
.cursor
= cp
;
631 if (el
->el_chared
.c_vcmd
.action
!= NOP
) {
633 el
->el_line
.cursor
++;