xref: /original-bsd/lib/libedit/search.c (revision c3e32dec)
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  * %sccs.include.redist.c%
9  */
10 
11 #if !defined(lint) && !defined(SCCSID)
12 static char sccsid[] = "@(#)search.c	8.1 (Berkeley) 06/04/93";
13 #endif /* not lint && not SCCSID */
14 
15 /*
16  * search.c: History and character search functions
17  */
18 #include "sys.h"
19 #include <stdlib.h>
20 #ifdef REGEXP
21 #include <regexp.h>
22 #endif
23 #include "el.h"
24 
25 /*
26  * Adjust cursor in vi mode to include the character under it
27  */
28 #define EL_CURSOR(el) \
29     ((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \
30 			    ((el)->el_map.current == (el)->el_map.alt)))
31 
32 /* search_init():
33  *	Initialize the search stuff
34  */
35 protected int
36 search_init(el)
37     EditLine *el;
38 {
39     el->el_search.patbuf = (char *) el_malloc(EL_BUFSIZ);
40     el->el_search.patlen = 0;
41     el->el_search.patdir = -1;
42     el->el_search.chacha = '\0';
43     el->el_search.chadir = -1;
44     return 0;
45 }
46 
47 
48 /* search_end():
49  *	Initialize the search stuff
50  */
51 protected void
52 search_end(el)
53     EditLine *el;
54 {
55     el_free((ptr_t) el->el_search.patbuf);
56     el->el_search.patbuf = NULL;
57 }
58 
59 #ifdef REGEXP
60 /* regerror():
61  *	Handle regular expression errors
62  */
63 public void
64 /*ARGSUSED*/
65 regerror(msg)
66     const char *msg;
67 {
68 }
69 #endif
70 
71 /* el_match():
72  *	Return if string matches pattern
73  */
74 protected int
75 el_match(str, pat)
76     const char *str;
77     const char *pat;
78 {
79 #ifndef REGEXP
80     extern char *re_comp __P((const char *));
81     extern int re_exec __P((const char *));
82 #else
83     regexp *re;
84     int rv;
85 #endif
86 
87     if (strstr(str, pat) != NULL)
88 	return 1;
89 #ifndef REGEXP
90     if (re_comp(pat) != NULL)
91 	return 0;
92     else
93     return re_exec(str) == 1;
94 #else
95     if ((re = regcomp(pat)) != NULL) {
96 	rv = regexec(re, str);
97 	free((ptr_t) re);
98     }
99     else
100 	rv = 0;
101     return rv;
102 #endif
103 
104 }
105 
106 
107 /* c_hmatch():
108  *	 return True if the pattern matches the prefix
109  */
110 protected int
111 c_hmatch(el, str)
112     EditLine *el;
113     const char *str;
114 {
115 #ifdef SDEBUG
116     (void) fprintf(el->el_errfile, "match `%s' with `%s'\n",
117 		   el->el_search.patbuf, str);
118 #endif /* SDEBUG */
119 
120     return el_match(str, el->el_search.patbuf);
121 }
122 
123 
124 /* c_setpat():
125  *	Set the history seatch pattern
126  */
127 protected void
128 c_setpat(el)
129     EditLine *el;
130 {
131     if (el->el_state.lastcmd != ED_SEARCH_PREV_HISTORY &&
132 	el->el_state.lastcmd != ED_SEARCH_NEXT_HISTORY) {
133 	el->el_search.patlen = EL_CURSOR(el) - el->el_line.buffer;
134 	if (el->el_search.patlen >= EL_BUFSIZ)
135 	    el->el_search.patlen = EL_BUFSIZ -1;
136 	if (el->el_search.patlen >= 0)  {
137 	    (void) strncpy(el->el_search.patbuf, el->el_line.buffer,
138 			   el->el_search.patlen);
139 	    el->el_search.patbuf[el->el_search.patlen] = '\0';
140 	}
141 	else
142 	    el->el_search.patlen = strlen(el->el_search.patbuf);
143     }
144 #ifdef SDEBUG
145     (void) fprintf(el->el_errfile, "\neventno = %d\n", el->el_history.eventno);
146     (void) fprintf(el->el_errfile, "patlen = %d\n", el->el_search.patlen);
147     (void) fprintf(el->el_errfile, "patbuf = \"%s\"\n", el->el_search.patbuf);
148     (void) fprintf(el->el_errfile, "cursor %d lastchar %d\n",
149 		   EL_CURSOR(el) - el->el_line.buffer,
150 		   el->el_line.lastchar - el->el_line.buffer);
151 #endif
152 }
153 
154 
155 /* ce_inc_search():
156  *	Emacs incremental search
157  */
158 protected el_action_t
159 ce_inc_search(el, dir)
160     EditLine *el;
161     int dir;
162 {
163     static char STRfwd[] = { 'f', 'w', 'd', '\0' },
164 		STRbck[] = { 'b', 'c', 'k', '\0' };
165     static char pchar = ':';	/* ':' = normal, '?' = failed */
166     static char endcmd[2] = { '\0', '\0' };
167     char ch, *cp, *ocursor = el->el_line.cursor, oldpchar = pchar;
168 
169     el_action_t ret = CC_NORM;
170 
171     int ohisteventno = el->el_history.eventno,
172 	oldpatlen = el->el_search.patlen,
173 	newdir = dir,
174         done, redo;
175 
176     if (el->el_line.lastchar + sizeof(STRfwd) / sizeof(char) + 2 +
177 	el->el_search.patlen >= el->el_line.limit)
178 	return CC_ERROR;
179 
180     for (;;) {
181 
182 	if (el->el_search.patlen == 0) {	/* first round */
183 	    pchar = ':';
184 #ifdef ANCHOR
185 	    el->el_search.patbuf[el->el_search.patlen++] = '.';
186 	    el->el_search.patbuf[el->el_search.patlen++] = '*';
187 #endif
188 	}
189 	done = redo = 0;
190 	*el->el_line.lastchar++ = '\n';
191 	for (cp = newdir == ED_SEARCH_PREV_HISTORY ? STRbck : STRfwd;
192 	     *cp; *el->el_line.lastchar++ = *cp++)
193 	     continue;
194 	*el->el_line.lastchar++ = pchar;
195 	for (cp = &el->el_search.patbuf[1];
196 	      cp < &el->el_search.patbuf[el->el_search.patlen];
197 	      *el->el_line.lastchar++ = *cp++)
198 	    continue;
199 	*el->el_line.lastchar = '\0';
200 	re_refresh(el);
201 
202 	if (el_getc(el, &ch) != 1)
203 	    return ed_end_of_file(el, 0);
204 
205 	switch (el->el_map.current[(unsigned char) ch]) {
206 	case ED_INSERT:
207 	case ED_DIGIT:
208 	    if (el->el_search.patlen > EL_BUFSIZ - 3)
209 		term_beep(el);
210 	    else {
211 		el->el_search.patbuf[el->el_search.patlen++] = ch;
212 		*el->el_line.lastchar++ = ch;
213 		*el->el_line.lastchar = '\0';
214 		re_refresh(el);
215 	    }
216 	    break;
217 
218 	case EM_INC_SEARCH_NEXT:
219 	    newdir = ED_SEARCH_NEXT_HISTORY;
220 	    redo++;
221 	    break;
222 
223 	case EM_INC_SEARCH_PREV:
224 	    newdir = ED_SEARCH_PREV_HISTORY;
225 	    redo++;
226 	    break;
227 
228 	case ED_DELETE_PREV_CHAR:
229 	    if (el->el_search.patlen > 1)
230 		done++;
231 	    else
232 		term_beep(el);
233 	    break;
234 
235 	default:
236 	    switch (ch) {
237 	    case 0007:		/* ^G: Abort */
238 		ret = CC_ERROR;
239 		done++;
240 		break;
241 
242 	    case 0027:		/* ^W: Append word */
243 		/* No can do if globbing characters in pattern */
244 		for (cp = &el->el_search.patbuf[1]; ; cp++)
245 		    if (cp >= &el->el_search.patbuf[el->el_search.patlen]) {
246 			el->el_line.cursor += el->el_search.patlen - 1;
247 			cp = c__next_word(el->el_line.cursor,
248 					  el->el_line.lastchar, 1, ce__isword);
249 			while (el->el_line.cursor < cp &&
250 			       *el->el_line.cursor != '\n') {
251 			    if (el->el_search.patlen > EL_BUFSIZ - 3) {
252 				term_beep(el);
253 				break;
254 			    }
255 			    el->el_search.patbuf[el->el_search.patlen++] =
256 				*el->el_line.cursor;
257 			    *el->el_line.lastchar++ = *el->el_line.cursor++;
258 			}
259 			el->el_line.cursor = ocursor;
260 			*el->el_line.lastchar = '\0';
261 			re_refresh(el);
262 			break;
263 		    } else if (isglob(*cp)) {
264 			term_beep(el);
265 			break;
266 		    }
267 		break;
268 
269 	    default:		/* Terminate and execute cmd */
270 		endcmd[0] = ch;
271 		el_push(el, endcmd);
272 		/*FALLTHROUGH*/
273 
274 	    case 0033:		/* ESC: Terminate */
275 		ret = CC_REFRESH;
276 		done++;
277 		break;
278 	    }
279 	    break;
280 	}
281 
282 	while (el->el_line.lastchar > el->el_line.buffer &&
283 	       *el->el_line.lastchar != '\n')
284 	    *el->el_line.lastchar-- = '\0';
285 	*el->el_line.lastchar = '\0';
286 
287 	if (!done) {
288 
289 	    /* Can't search if unmatched '[' */
290 	    for (cp = &el->el_search.patbuf[el->el_search.patlen-1], ch = ']';
291 		 cp > el->el_search.patbuf; cp--)
292 		if (*cp == '[' || *cp == ']') {
293 		    ch = *cp;
294 		    break;
295 		}
296 
297 	    if (el->el_search.patlen > 1 && ch != '[') {
298 		if (redo && newdir == dir) {
299 		    if (pchar == '?') {	/* wrap around */
300 			el->el_history.eventno =
301 			    newdir == ED_SEARCH_PREV_HISTORY ? 0 : 0x7fffffff;
302 			if (hist_get(el) == CC_ERROR)
303 			    /* el->el_history.eventno was fixed by first call */
304 			    (void) hist_get(el);
305 			el->el_line.cursor = newdir == ED_SEARCH_PREV_HISTORY ?
306 			    el->el_line.lastchar : el->el_line.buffer;
307 		    } else
308 			el->el_line.cursor +=
309 				newdir == ED_SEARCH_PREV_HISTORY ? -1 : 1;
310 		}
311 #ifdef ANCHOR
312 		el->el_search.patbuf[el->el_search.patlen++] = '.';
313 		el->el_search.patbuf[el->el_search.patlen++] = '*';
314 #endif
315 		el->el_search.patbuf[el->el_search.patlen] = '\0';
316 		if (el->el_line.cursor < el->el_line.buffer ||
317 		    el->el_line.cursor > el->el_line.lastchar ||
318 		    (ret = ce_search_line(el, &el->el_search.patbuf[1],
319 					  newdir)) == CC_ERROR) {
320 		    /* avoid c_setpat */
321 		    el->el_state.lastcmd = (el_action_t) newdir;
322 		    ret = newdir == ED_SEARCH_PREV_HISTORY ?
323 			ed_search_prev_history(el, 0) :
324 			ed_search_next_history(el, 0);
325 		    if (ret != CC_ERROR) {
326 			el->el_line.cursor = newdir == ED_SEARCH_PREV_HISTORY ?
327 			    el->el_line.lastchar : el->el_line.buffer;
328 			(void) ce_search_line(el, &el->el_search.patbuf[1],
329 					      newdir);
330 		    }
331 		}
332 		el->el_search.patbuf[--el->el_search.patlen] = '\0';
333 		if (ret == CC_ERROR) {
334 		    term_beep(el);
335 		    if (el->el_history.eventno != ohisteventno) {
336 			el->el_history.eventno = ohisteventno;
337 			if (hist_get(el) == CC_ERROR)
338 			    return CC_ERROR;
339 		    }
340 		    el->el_line.cursor = ocursor;
341 		    pchar = '?';
342 		} else {
343 		    pchar = ':';
344 		}
345 	    }
346 
347 	    ret = ce_inc_search(el, newdir);
348 
349 	    if (ret == CC_ERROR && pchar == '?' && oldpchar == ':')
350 		/* break abort of failed search at last non-failed */
351 		ret = CC_NORM;
352 
353 	}
354 
355 	if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) {
356 	    /* restore on normal return or error exit */
357 	    pchar = oldpchar;
358 	    el->el_search.patlen = oldpatlen;
359 	    if (el->el_history.eventno != ohisteventno) {
360 		el->el_history.eventno = ohisteventno;
361 		if (hist_get(el) == CC_ERROR)
362 		    return CC_ERROR;
363 	    }
364 	    el->el_line.cursor = ocursor;
365 	    if (ret == CC_ERROR)
366 		re_refresh(el);
367 	}
368 	if (done || ret != CC_NORM)
369 	    return ret;
370     }
371 }
372 
373 
374 /* cv_search():
375  *	Vi search.
376  */
377 protected el_action_t
378 cv_search(el, dir)
379     EditLine *el;
380     int dir;
381 {
382     char ch;
383     char tmpbuf[EL_BUFSIZ];
384     int tmplen;
385 
386     tmplen = 0;
387 #ifdef ANCHOR
388     tmpbuf[tmplen++] = '.';
389     tmpbuf[tmplen++] = '*';
390 #endif
391 
392     el->el_line.buffer[0] = '\0';
393     el->el_line.lastchar = el->el_line.buffer;
394     el->el_line.cursor = el->el_line.buffer;
395     el->el_search.patdir = dir;
396 
397     c_insert(el, 2);	/* prompt + '\n' */
398     *el->el_line.cursor++ = '\n';
399     *el->el_line.cursor++ = dir == ED_SEARCH_PREV_HISTORY ? '/' : '?';
400     re_refresh(el);
401 
402 #ifdef ANCHOR
403 # define LEN 2
404 #else
405 # define LEN 0
406 #endif
407 
408     tmplen = c_gets(el, &tmpbuf[LEN]) + LEN;
409     ch = tmpbuf[tmplen];
410     tmpbuf[tmplen] = '\0';
411 
412     if (tmplen == LEN) {
413 	/*
414 	 * Use the old pattern, but wild-card it.
415 	 */
416 	if (el->el_search.patlen == 0) {
417 	    el->el_line.buffer[0] = '\0';
418 	    el->el_line.lastchar = el->el_line.buffer;
419 	    el->el_line.cursor = el->el_line.buffer;
420 	    re_refresh(el);
421 	    return CC_ERROR;
422 	}
423 #ifdef ANCHOR
424 	if (el->el_search.patbuf[0] != '.' && el->el_search.patbuf[0] != '*') {
425 	    (void) strcpy(tmpbuf, el->el_search.patbuf);
426 	    el->el_search.patbuf[0] = '.';
427 	    el->el_search.patbuf[1] = '*';
428 	    (void) strcpy(&el->el_search.patbuf[2], tmpbuf);
429 	    el->el_search.patlen++;
430 	    el->el_search.patbuf[el->el_search.patlen++] = '.';
431 	    el->el_search.patbuf[el->el_search.patlen++] = '*';
432 	    el->el_search.patbuf[el->el_search.patlen] = '\0';
433 	}
434 #endif
435     }
436     else {
437 #ifdef ANCHOR
438 	tmpbuf[tmplen++] = '.';
439 	tmpbuf[tmplen++] = '*';
440 #endif
441 	tmpbuf[tmplen] = '\0';
442 	(void) strcpy(el->el_search.patbuf, tmpbuf);
443 	el->el_search.patlen = tmplen;
444     }
445     el->el_state.lastcmd = (el_action_t) dir; /* avoid c_setpat */
446     el->el_line.cursor = el->el_line.lastchar = el->el_line.buffer;
447     if ((dir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) :
448 			        ed_search_next_history(el, 0)) == CC_ERROR) {
449 	re_refresh(el);
450 	return CC_ERROR;
451     }
452     else {
453 	if (ch == 0033) {
454 	    re_refresh(el);
455 	    *el->el_line.lastchar++ = '\n';
456 	    *el->el_line.lastchar = '\0';
457 	    re_goto_bottom(el);
458 	    return CC_NEWLINE;
459 	}
460 	else
461 	    return CC_REFRESH;
462     }
463 }
464 
465 
466 /* ce_search_line():
467  *	Look for a pattern inside a line
468  */
469 protected el_action_t
470 ce_search_line(el, pattern, dir)
471     EditLine *el;
472     char *pattern;
473     int dir;
474 {
475     char *cp;
476 
477     if (dir == ED_SEARCH_PREV_HISTORY) {
478 	for (cp = el->el_line.cursor; cp >= el->el_line.buffer; cp--)
479 	    if (el_match(cp, pattern)) {
480 		el->el_line.cursor = cp;
481 		return CC_NORM;
482 	    }
483 	return CC_ERROR;
484     } else {
485 	for (cp = el->el_line.cursor; *cp != '\0' &&
486 	     cp < el->el_line.limit; cp++)
487 	    if (el_match(cp, pattern)) {
488 		el->el_line.cursor = cp;
489 		return CC_NORM;
490 	    }
491 	return CC_ERROR;
492     }
493 }
494 
495 
496 /* cv_repeat_srch():
497  *	Vi repeat search
498  */
499 protected el_action_t
500 cv_repeat_srch(el, c)
501     EditLine *el;
502     int c;
503 {
504 #ifdef SDEBUG
505     (void) fprintf(el->el_errfile, "dir %d patlen %d patbuf %s\n",
506 		   c, el->el_search.patlen, el->el_search.patbuf);
507 #endif
508 
509     el->el_state.lastcmd = (el_action_t) c;  /* Hack to stop c_setpat */
510     el->el_line.lastchar = el->el_line.buffer;
511 
512     switch (c) {
513     case ED_SEARCH_NEXT_HISTORY:
514 	return ed_search_next_history(el, 0);
515     case ED_SEARCH_PREV_HISTORY:
516 	return ed_search_prev_history(el, 0);
517     default:
518 	return CC_ERROR;
519     }
520 }
521 
522 
523 /* cv_csearch_back():
524  *	Vi character search reverse
525  */
526 protected el_action_t
527 cv_csearch_back(el, ch, count, tflag)
528     EditLine *el;
529     int ch, count, tflag;
530 {
531     char *cp;
532 
533     cp = el->el_line.cursor;
534     while (count--) {
535 	if (*cp == ch)
536 	    cp--;
537 	while (cp > el->el_line.buffer && *cp != ch)
538 	    cp--;
539     }
540 
541     if (cp < el->el_line.buffer || (cp == el->el_line.buffer && *cp != ch))
542 	return CC_ERROR;
543 
544     if (*cp == ch && tflag)
545 	cp++;
546 
547     el->el_line.cursor = cp;
548 
549     if (el->el_chared.c_vcmd.action & DELETE) {
550 	el->el_line.cursor++;
551 	cv_delfini(el);
552 	return CC_REFRESH;
553     }
554 
555     re_refresh_cursor(el);
556     return CC_NORM;
557 }
558 
559 
560 /* cv_csearch_fwd():
561  *	Vi character search forward
562  */
563 protected el_action_t
564 cv_csearch_fwd(el, ch, count, tflag)
565     EditLine *el;
566     int ch, count, tflag;
567 {
568     char *cp;
569 
570     cp = el->el_line.cursor;
571     while (count--) {
572 	if(*cp == ch)
573 	    cp++;
574 	while (cp < el->el_line.lastchar && *cp != ch)
575 	    cp++;
576     }
577 
578     if (cp >= el->el_line.lastchar)
579 	return CC_ERROR;
580 
581     if (*cp == ch && tflag)
582 	cp--;
583 
584     el->el_line.cursor = cp;
585 
586     if (el->el_chared.c_vcmd.action & DELETE) {
587 	el->el_line.cursor++;
588 	cv_delfini(el);
589 	return CC_REFRESH;
590     }
591     re_refresh_cursor(el);
592     return CC_NORM;
593 }
594