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