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
search_init(el)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
search_end(el)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*/
regerror(msg)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
el_match(str,pat)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
c_hmatch(el,str)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
c_setpat(el)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
ce_inc_search(el,dir)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
cv_search(el,dir)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
ce_search_line(el,pattern,dir)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
cv_repeat_srch(el,c)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
cv_csearch_back(el,ch,count,tflag)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
cv_csearch_fwd(el,ch,count,tflag)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