1 /* $NetBSD: search.c,v 1.30 2011/10/04 15:27:04 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.30 2011/10/04 15:27:04 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(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
), (size_t)0, NULL
,
138 #elif defined(REGEXP)
139 if ((re
= regcomp(ct_encode_string(pat
, &conv
))) != NULL
) {
140 rv
= regexec(re
, ct_encode_string(str
, &conv
));
147 if (re_comp(ct_encode_string(pat
, &conv
)) != NULL
)
150 return re_exec(ct_encode_string(str
, &conv
) == 1);
156 * return True if the pattern matches the prefix
159 c_hmatch(EditLine
*el
, const Char
*str
)
162 (void) fprintf(el
->el_errfile
, "match `%s' with `%s'\n",
163 el
->el_search
.patbuf
, str
);
166 return el_match(str
, el
->el_search
.patbuf
);
171 * Set the history seatch pattern
174 c_setpat(EditLine
*el
)
176 if (el
->el_state
.lastcmd
!= ED_SEARCH_PREV_HISTORY
&&
177 el
->el_state
.lastcmd
!= ED_SEARCH_NEXT_HISTORY
) {
178 el
->el_search
.patlen
=
179 (size_t)(EL_CURSOR(el
) - el
->el_line
.buffer
);
180 if (el
->el_search
.patlen
>= EL_BUFSIZ
)
181 el
->el_search
.patlen
= EL_BUFSIZ
- 1;
182 if (el
->el_search
.patlen
!= 0) {
183 (void) Strncpy(el
->el_search
.patbuf
, el
->el_line
.buffer
,
184 el
->el_search
.patlen
);
185 el
->el_search
.patbuf
[el
->el_search
.patlen
] = '\0';
187 el
->el_search
.patlen
= Strlen(el
->el_search
.patbuf
);
190 (void) fprintf(el
->el_errfile
, "\neventno = %d\n",
191 el
->el_history
.eventno
);
192 (void) fprintf(el
->el_errfile
, "patlen = %d\n", el
->el_search
.patlen
);
193 (void) fprintf(el
->el_errfile
, "patbuf = \"%s\"\n",
194 el
->el_search
.patbuf
);
195 (void) fprintf(el
->el_errfile
, "cursor %d lastchar %d\n",
196 EL_CURSOR(el
) - el
->el_line
.buffer
,
197 el
->el_line
.lastchar
- el
->el_line
.buffer
);
203 * Emacs incremental search
205 protected el_action_t
206 ce_inc_search(EditLine
*el
, int dir
)
208 static const Char STRfwd
[] = {'f', 'w', 'd', '\0'},
209 STRbck
[] = {'b', 'c', 'k', '\0'};
210 static Char pchar
= ':';/* ':' = normal, '?' = failed */
211 static Char endcmd
[2] = {'\0', '\0'};
212 Char ch
, *ocursor
= el
->el_line
.cursor
, oldpchar
= pchar
;
215 el_action_t ret
= CC_NORM
;
217 int ohisteventno
= el
->el_history
.eventno
;
218 size_t oldpatlen
= el
->el_search
.patlen
;
222 if (el
->el_line
.lastchar
+ sizeof(STRfwd
) /
223 sizeof(*el
->el_line
.lastchar
) + 2 +
224 el
->el_search
.patlen
>= el
->el_line
.limit
)
229 if (el
->el_search
.patlen
== 0) { /* first round */
233 el
->el_search
.patbuf
[el
->el_search
.patlen
++] = '.';
234 el
->el_search
.patbuf
[el
->el_search
.patlen
++] = '*';
240 *el
->el_line
.lastchar
++ = '\n';
241 for (cp
= (newdir
== ED_SEARCH_PREV_HISTORY
) ? STRbck
: STRfwd
;
242 *cp
; *el
->el_line
.lastchar
++ = *cp
++)
244 *el
->el_line
.lastchar
++ = pchar
;
245 for (cp
= &el
->el_search
.patbuf
[LEN
];
246 cp
< &el
->el_search
.patbuf
[el
->el_search
.patlen
];
247 *el
->el_line
.lastchar
++ = *cp
++)
249 *el
->el_line
.lastchar
= '\0';
252 if (FUN(el
,getc
)(el
, &ch
) != 1)
253 return ed_end_of_file(el
, 0);
255 switch (el
->el_map
.current
[(unsigned char) ch
]) {
258 if (el
->el_search
.patlen
>= EL_BUFSIZ
- LEN
)
261 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
263 *el
->el_line
.lastchar
++ = ch
;
264 *el
->el_line
.lastchar
= '\0';
269 case EM_INC_SEARCH_NEXT
:
270 newdir
= ED_SEARCH_NEXT_HISTORY
;
274 case EM_INC_SEARCH_PREV
:
275 newdir
= ED_SEARCH_PREV_HISTORY
;
279 case EM_DELETE_PREV_CHAR
:
280 case ED_DELETE_PREV_CHAR
:
281 if (el
->el_search
.patlen
> LEN
)
289 case 0007: /* ^G: Abort */
294 case 0027: /* ^W: Append word */
295 /* No can do if globbing characters in pattern */
296 for (cp
= &el
->el_search
.patbuf
[LEN
];; cp
++)
297 if (cp
>= &el
->el_search
.patbuf
[
298 el
->el_search
.patlen
]) {
299 el
->el_line
.cursor
+=
300 el
->el_search
.patlen
- LEN
- 1;
301 cp
= c__next_word(el
->el_line
.cursor
,
302 el
->el_line
.lastchar
, 1,
304 while (el
->el_line
.cursor
< cp
&&
305 *el
->el_line
.cursor
!= '\n') {
306 if (el
->el_search
.patlen
>=
311 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
313 *el
->el_line
.lastchar
++ =
314 *el
->el_line
.cursor
++;
316 el
->el_line
.cursor
= ocursor
;
317 *el
->el_line
.lastchar
= '\0';
320 } else if (isglob(*cp
)) {
326 default: /* Terminate and execute cmd */
328 FUN(el
,push
)(el
, endcmd
);
331 case 0033: /* ESC: Terminate */
339 while (el
->el_line
.lastchar
> el
->el_line
.buffer
&&
340 *el
->el_line
.lastchar
!= '\n')
341 *el
->el_line
.lastchar
-- = '\0';
342 *el
->el_line
.lastchar
= '\0';
346 /* Can't search if unmatched '[' */
347 for (cp
= &el
->el_search
.patbuf
[el
->el_search
.patlen
-1],
349 cp
>= &el
->el_search
.patbuf
[LEN
];
351 if (*cp
== '[' || *cp
== ']') {
355 if (el
->el_search
.patlen
> LEN
&& ch
!= '[') {
356 if (redo
&& newdir
== dir
) {
357 if (pchar
== '?') { /* wrap around */
358 el
->el_history
.eventno
=
359 newdir
== ED_SEARCH_PREV_HISTORY
? 0 : 0x7fffffff;
360 if (hist_get(el
) == CC_ERROR
)
361 /* el->el_history.event
365 el
->el_line
.cursor
= newdir
==
366 ED_SEARCH_PREV_HISTORY
?
367 el
->el_line
.lastchar
:
370 el
->el_line
.cursor
+=
372 ED_SEARCH_PREV_HISTORY
?
376 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
378 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
381 el
->el_search
.patbuf
[el
->el_search
.patlen
] =
383 if (el
->el_line
.cursor
< el
->el_line
.buffer
||
384 el
->el_line
.cursor
> el
->el_line
.lastchar
||
385 (ret
= ce_search_line(el
, newdir
))
388 el
->el_state
.lastcmd
=
389 (el_action_t
) newdir
;
391 (newdir
== ED_SEARCH_PREV_HISTORY
?
392 ed_search_prev_history(el
, 0) :
393 ed_search_next_history(el
, 0));
394 if (ret
!= CC_ERROR
) {
395 el
->el_line
.cursor
= newdir
==
396 ED_SEARCH_PREV_HISTORY
?
397 el
->el_line
.lastchar
:
399 (void) ce_search_line(el
,
403 el
->el_search
.patlen
-= LEN
;
404 el
->el_search
.patbuf
[el
->el_search
.patlen
] =
406 if (ret
== CC_ERROR
) {
408 if (el
->el_history
.eventno
!=
410 el
->el_history
.eventno
=
412 if (hist_get(el
) == CC_ERROR
)
415 el
->el_line
.cursor
= ocursor
;
421 ret
= ce_inc_search(el
, newdir
);
423 if (ret
== CC_ERROR
&& pchar
== '?' && oldpchar
== ':')
425 * break abort of failed search at last
431 if (ret
== CC_NORM
|| (ret
== CC_ERROR
&& oldpatlen
== 0)) {
432 /* restore on normal return or error exit */
434 el
->el_search
.patlen
= oldpatlen
;
435 if (el
->el_history
.eventno
!= ohisteventno
) {
436 el
->el_history
.eventno
= ohisteventno
;
437 if (hist_get(el
) == CC_ERROR
)
440 el
->el_line
.cursor
= ocursor
;
444 if (done
|| ret
!= CC_NORM
)
453 protected el_action_t
454 cv_search(EditLine
*el
, int dir
)
457 Char tmpbuf
[EL_BUFSIZ
];
466 el
->el_search
.patdir
= dir
;
468 tmplen
= c_gets(el
, &tmpbuf
[LEN
],
469 dir
== ED_SEARCH_PREV_HISTORY
? STR("\n/") : STR("\n?") );
475 tmpbuf
[tmplen
] = '\0';
479 * Use the old pattern, but wild-card it.
481 if (el
->el_search
.patlen
== 0) {
486 if (el
->el_search
.patbuf
[0] != '.' &&
487 el
->el_search
.patbuf
[0] != '*') {
488 (void) Strncpy(tmpbuf
, el
->el_search
.patbuf
,
489 sizeof(tmpbuf
) / sizeof(*tmpbuf
) - 1);
490 el
->el_search
.patbuf
[0] = '.';
491 el
->el_search
.patbuf
[1] = '*';
492 (void) Strncpy(&el
->el_search
.patbuf
[2], tmpbuf
,
494 el
->el_search
.patlen
++;
495 el
->el_search
.patbuf
[el
->el_search
.patlen
++] = '.';
496 el
->el_search
.patbuf
[el
->el_search
.patlen
++] = '*';
497 el
->el_search
.patbuf
[el
->el_search
.patlen
] = '\0';
502 tmpbuf
[tmplen
++] = '.';
503 tmpbuf
[tmplen
++] = '*';
505 tmpbuf
[tmplen
] = '\0';
506 (void) Strncpy(el
->el_search
.patbuf
, tmpbuf
, EL_BUFSIZ
- 1);
507 el
->el_search
.patlen
= (size_t)tmplen
;
509 el
->el_state
.lastcmd
= (el_action_t
) dir
; /* avoid c_setpat */
510 el
->el_line
.cursor
= el
->el_line
.lastchar
= el
->el_line
.buffer
;
511 if ((dir
== ED_SEARCH_PREV_HISTORY
? ed_search_prev_history(el
, 0) :
512 ed_search_next_history(el
, 0)) == CC_ERROR
) {
518 return ed_newline(el
, 0);
525 * Look for a pattern inside a line
527 protected el_action_t
528 ce_search_line(EditLine
*el
, int dir
)
530 Char
*cp
= el
->el_line
.cursor
;
531 Char
*pattern
= el
->el_search
.patbuf
;
542 if (dir
== ED_SEARCH_PREV_HISTORY
) {
543 for (; cp
>= el
->el_line
.buffer
; cp
--) {
544 if (el_match(cp
, ocp
)) {
546 el
->el_line
.cursor
= cp
;
553 for (; *cp
!= '\0' && cp
< el
->el_line
.limit
; cp
++) {
554 if (el_match(cp
, ocp
)) {
556 el
->el_line
.cursor
= cp
;
569 protected el_action_t
570 cv_repeat_srch(EditLine
*el
, Int c
)
574 (void) fprintf(el
->el_errfile
, "dir %d patlen %d patbuf %s\n",
575 c
, el
->el_search
.patlen
, ct_encode_string(el
->el_search
.patbuf
));
578 el
->el_state
.lastcmd
= (el_action_t
) c
; /* Hack to stop c_setpat */
579 el
->el_line
.lastchar
= el
->el_line
.buffer
;
582 case ED_SEARCH_NEXT_HISTORY
:
583 return ed_search_next_history(el
, 0);
584 case ED_SEARCH_PREV_HISTORY
:
585 return ed_search_prev_history(el
, 0);
593 * Vi character search
595 protected el_action_t
596 cv_csearch(EditLine
*el
, int direction
, Int ch
, int count
, int tflag
)
605 if (FUN(el
,getc
)(el
, &c
) != 1)
606 return ed_end_of_file(el
, 0);
610 /* Save for ';' and ',' commands */
611 el
->el_search
.chacha
= ch
;
612 el
->el_search
.chadir
= direction
;
613 el
->el_search
.chatflg
= (char)tflag
;
615 cp
= el
->el_line
.cursor
;
619 for (;;cp
+= direction
) {
620 if (cp
>= el
->el_line
.lastchar
)
622 if (cp
< el
->el_line
.buffer
)
632 el
->el_line
.cursor
= cp
;
634 if (el
->el_chared
.c_vcmd
.action
!= NOP
) {
636 el
->el_line
.cursor
++;