2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Christos Zoulas of Cornell University.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
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 $
40 * search.c: History and character search functions
51 * Adjust cursor in vi mode to include the character under it
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)))
58 * Initialize the search stuff
61 search_init(EditLine
*el
)
64 el
->el_search
.patbuf
= (char *) el_malloc(EL_BUFSIZ
);
65 if (el
->el_search
.patbuf
== NULL
)
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;
77 * Initialize the search stuff
80 search_end(EditLine
*el
)
83 el_free((ptr_t
) el
->el_search
.patbuf
);
84 el
->el_search
.patbuf
= NULL
;
90 * Handle regular expression errors
94 regerror(const char *msg
)
101 * Return if string matches pattern
104 el_match(const char *str
, const char *pat
)
109 #elif defined (REGEXP)
113 extern char *re_comp(const char *);
114 extern int re_exec(const char *);
117 if (strstr(str
, pat
) != NULL
)
121 if (regcomp(&re
, pat
, 0) == 0) {
122 rv
= regexec(&re
, str
, 0, NULL
, 0) == 0;
128 #elif defined(REGEXP)
129 if ((re
= regcomp(pat
)) != NULL
) {
130 rv
= regexec(re
, str
);
137 if (re_comp(pat
) != NULL
)
140 return (re_exec(str
) == 1);
146 * return True if the pattern matches the prefix
149 c_hmatch(EditLine
*el
, const char *str
)
152 (void) fprintf(el
->el_errfile
, "match `%s' with `%s'\n",
153 el
->el_search
.patbuf
, str
);
156 return (el_match(str
, el
->el_search
.patbuf
));
161 * Set the history seatch pattern
164 c_setpat(EditLine
*el
)
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';
176 el
->el_search
.patlen
= strlen(el
->el_search
.patbuf
);
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
);
192 * Emacs incremental search
194 protected el_action_t
195 ce_inc_search(EditLine
*el
, int dir
)
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
;
204 el_action_t ret
= CC_NORM
;
206 int ohisteventno
= el
->el_history
.eventno
;
207 int oldpatlen
= el
->el_search
.patlen
;
211 if (el
->el_line
.lastchar
+ sizeof(STRfwd
) / sizeof(char) + 2 +
212 el
->el_search
.patlen
>= el
->el_line
.limit
)
217 if (el
->el_search
.patlen
== 0) { /* first round */
221 el
->el_search
.patbuf
[el
->el_search
.patlen
++] = '.';
222 el
->el_search
.patbuf
[el
->el_search
.patlen
++] = '*';
228 *el
->el_line
.lastchar
++ = '\n';
229 for (cp
= (newdir
== ED_SEARCH_PREV_HISTORY
) ? STRbck
: STRfwd
;
230 *cp
; *el
->el_line
.lastchar
++ = *cp
++)
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
++)
237 *el
->el_line
.lastchar
= '\0';
240 if (el_getc(el
, &ch
) != 1)
241 return (ed_end_of_file(el
, 0));
243 switch (el
->el_map
.current
[(unsigned char) ch
]) {
246 if (el
->el_search
.patlen
>= EL_BUFSIZ
- LEN
)
249 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
251 *el
->el_line
.lastchar
++ = ch
;
252 *el
->el_line
.lastchar
= '\0';
257 case EM_INC_SEARCH_NEXT
:
258 newdir
= ED_SEARCH_NEXT_HISTORY
;
262 case EM_INC_SEARCH_PREV
:
263 newdir
= ED_SEARCH_PREV_HISTORY
;
267 case EM_DELETE_PREV_CHAR
:
268 case ED_DELETE_PREV_CHAR
:
269 if (el
->el_search
.patlen
> LEN
)
277 case 0007: /* ^G: Abort */
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,
292 while (el
->el_line
.cursor
< cp
&&
293 *el
->el_line
.cursor
!= '\n') {
294 if (el
->el_search
.patlen
>=
299 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
301 *el
->el_line
.lastchar
++ =
302 *el
->el_line
.cursor
++;
304 el
->el_line
.cursor
= ocursor
;
305 *el
->el_line
.lastchar
= '\0';
308 } else if (isglob(*cp
)) {
314 default: /* Terminate and execute cmd */
319 case 0033: /* ESC: Terminate */
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';
334 /* Can't search if unmatched '[' */
335 for (cp
= &el
->el_search
.patbuf
[el
->el_search
.patlen
-1],
337 cp
>= &el
->el_search
.patbuf
[LEN
];
339 if (*cp
== '[' || *cp
== ']') {
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
353 el
->el_line
.cursor
= newdir
==
354 ED_SEARCH_PREV_HISTORY
?
355 el
->el_line
.lastchar
:
358 el
->el_line
.cursor
+=
360 ED_SEARCH_PREV_HISTORY
?
364 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
366 el
->el_search
.patbuf
[el
->el_search
.patlen
++] =
369 el
->el_search
.patbuf
[el
->el_search
.patlen
] =
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
))
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
:
386 (void) ce_search_line(el
,
390 el
->el_search
.patlen
-= LEN
;
391 el
->el_search
.patbuf
[el
->el_search
.patlen
] =
393 if (ret
== CC_ERROR
) {
395 if (el
->el_history
.eventno
!=
397 el
->el_history
.eventno
=
399 if (hist_get(el
) == CC_ERROR
)
402 el
->el_line
.cursor
= ocursor
;
408 ret
= ce_inc_search(el
, newdir
);
410 if (ret
== CC_ERROR
&& pchar
== '?' && oldpchar
== ':')
412 * break abort of failed search at last
418 if (ret
== CC_NORM
|| (ret
== CC_ERROR
&& oldpatlen
== 0)) {
419 /* restore on normal return or error exit */
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
)
427 el
->el_line
.cursor
= ocursor
;
431 if (done
|| ret
!= CC_NORM
)
440 protected el_action_t
441 cv_search(EditLine
*el
, int dir
)
444 char tmpbuf
[EL_BUFSIZ
];
453 el
->el_search
.patdir
= dir
;
455 tmplen
= c_gets(el
, &tmpbuf
[LEN
],
456 dir
== ED_SEARCH_PREV_HISTORY
? "\n/" : "\n?" );
462 tmpbuf
[tmplen
] = '\0';
466 * Use the old pattern, but wild-card it.
468 if (el
->el_search
.patlen
== 0) {
473 if (el
->el_search
.patbuf
[0] != '.' &&
474 el
->el_search
.patbuf
[0] != '*') {
475 (void) strncpy(tmpbuf
, el
->el_search
.patbuf
,
477 el
->el_search
.patbuf
[0] = '.';
478 el
->el_search
.patbuf
[1] = '*';
479 (void) strncpy(&el
->el_search
.patbuf
[2], tmpbuf
,
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';
489 tmpbuf
[tmplen
++] = '.';
490 tmpbuf
[tmplen
++] = '*';
492 tmpbuf
[tmplen
] = '\0';
493 (void) strncpy(el
->el_search
.patbuf
, tmpbuf
, EL_BUFSIZ
- 1);
494 el
->el_search
.patlen
= tmplen
;
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
) {
505 return ed_newline(el
, 0);
512 * Look for a pattern inside a line
514 protected el_action_t
515 ce_search_line(EditLine
*el
, int dir
)
517 char *cp
= el
->el_line
.cursor
;
518 char *pattern
= el
->el_search
.patbuf
;
529 if (dir
== ED_SEARCH_PREV_HISTORY
) {
530 for (; cp
>= el
->el_line
.buffer
; cp
--) {
531 if (el_match(cp
, ocp
)) {
533 el
->el_line
.cursor
= cp
;
540 for (; *cp
!= '\0' && cp
< el
->el_line
.limit
; cp
++) {
541 if (el_match(cp
, ocp
)) {
543 el
->el_line
.cursor
= cp
;
556 protected el_action_t
557 cv_repeat_srch(EditLine
*el
, int c
)
561 (void) fprintf(el
->el_errfile
, "dir %d patlen %d patbuf %s\n",
562 c
, el
->el_search
.patlen
, el
->el_search
.patbuf
);
565 el
->el_state
.lastcmd
= (el_action_t
) c
; /* Hack to stop c_setpat */
566 el
->el_line
.lastchar
= el
->el_line
.buffer
;
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));
580 * Vi character search
582 protected el_action_t
583 cv_csearch(EditLine
*el
, int direction
, int ch
, int count
, int tflag
)
592 if (el_getc(el
, &c
) != 1)
593 return ed_end_of_file(el
, 0);
597 /* Save for ';' and ',' commands */
598 el
->el_search
.chacha
= ch
;
599 el
->el_search
.chadir
= direction
;
600 el
->el_search
.chatflg
= tflag
;
602 cp
= el
->el_line
.cursor
;
606 for (;;cp
+= direction
) {
607 if (cp
>= el
->el_line
.lastchar
)
609 if (cp
< el
->el_line
.buffer
)
619 el
->el_line
.cursor
= cp
;
621 if (el
->el_chared
.c_vcmd
.action
!= NOP
) {
623 el
->el_line
.cursor
++;