1 /* $OpenBSD: search.c,v 1.50 2023/03/08 04:43:11 guenther Exp $ */
2
3 /* This file is in the public domain. */
4
5 /*
6 * Search commands.
7 * The functions in this file implement the search commands (both plain and
8 * incremental searches are supported) and the query-replace command.
9 *
10 * The plain old search code is part of the original MicroEMACS "distribution".
11 * The incremental search code and the query-replace code is by Rich Ellison.
12 */
13
14 #include <sys/queue.h>
15 #include <ctype.h>
16 #include <signal.h>
17 #include <stdio.h>
18 #include <string.h>
19
20 #include "def.h"
21 #include "macro.h"
22
23 #define SRCH_BEGIN (0) /* Search sub-codes. */
24 #define SRCH_FORW (-1)
25 #define SRCH_BACK (-2)
26 #define SRCH_NOPR (-3)
27 #define SRCH_ACCM (-4)
28 #define SRCH_MARK (-5)
29
30 struct srchcom {
31 int s_code;
32 struct line *s_dotp;
33 int s_doto;
34 int s_dotline;
35 };
36
37 static int isearch(int);
38 static void is_cpush(int);
39 static void is_lpush(void);
40 static void is_pop(void);
41 static int is_peek(void);
42 static void is_undo(int *, int *);
43 static int is_find(int);
44 static void is_prompt(int, int, int);
45 static void is_dspl(char *, int);
46 static int eq(int, int, int);
47
48 static struct srchcom cmds[NSRCH];
49 static int cip;
50
51 int srch_lastdir = SRCH_NOPR; /* Last search flags. */
52
53 /*
54 * Search forward. Get a search string from the user, and search for it
55 * starting at ".". If found, "." gets moved to just after the matched
56 * characters, and display does all the hard stuff. If not found, it just
57 * prints a message.
58 */
59 int
forwsearch(int f,int n)60 forwsearch(int f, int n)
61 {
62 int s;
63
64 if ((s = readpattern("Search")) != TRUE)
65 return (s);
66 if (forwsrch() == FALSE) {
67 dobeep();
68 ewprintf("Search failed: \"%s\"", pat);
69 return (FALSE);
70 }
71 srch_lastdir = SRCH_FORW;
72 return (TRUE);
73 }
74
75 /*
76 * Reverse search. Get a search string from the user, and search, starting
77 * at "." and proceeding toward the front of the buffer. If found "." is
78 * left pointing at the first character of the pattern [the last character
79 * that was matched].
80 */
81 int
backsearch(int f,int n)82 backsearch(int f, int n)
83 {
84 int s;
85
86 if ((s = readpattern("Search backward")) != TRUE)
87 return (s);
88 if (backsrch() == FALSE) {
89 dobeep();
90 ewprintf("Search failed: \"%s\"", pat);
91 return (FALSE);
92 }
93 srch_lastdir = SRCH_BACK;
94 return (TRUE);
95 }
96
97 /*
98 * Search again, using the same search string and direction as the last
99 * search command. The direction has been saved in "srch_lastdir", so you
100 * know which way to go.
101 */
102 int
searchagain(int f,int n)103 searchagain(int f, int n)
104 {
105 if (srch_lastdir == SRCH_FORW) {
106 if (forwsrch() == FALSE) {
107 dobeep();
108 ewprintf("Search failed: \"%s\"", pat);
109 return (FALSE);
110 }
111 return (TRUE);
112 }
113 if (srch_lastdir == SRCH_BACK) {
114 if (backsrch() == FALSE) {
115 dobeep();
116 ewprintf("Search failed: \"%s\"", pat);
117 return (FALSE);
118 }
119 return (TRUE);
120 }
121 dobeep();
122 ewprintf("No last search");
123 return (FALSE);
124 }
125
126 /*
127 * Use incremental searching, initially in the forward direction.
128 * isearch ignores any explicit arguments.
129 */
130 int
forwisearch(int f,int n)131 forwisearch(int f, int n)
132 {
133 if (macrodef || inmacro)
134 /* We can't isearch in macro. Use search instead */
135 return (forwsearch(f,n));
136 else
137 return (isearch(SRCH_FORW));
138 }
139
140 /*
141 * Use incremental searching, initially in the reverse direction.
142 * isearch ignores any explicit arguments.
143 */
144 int
backisearch(int f,int n)145 backisearch(int f, int n)
146 {
147 if (macrodef || inmacro)
148 /* We can't isearch in macro. Use search instead */
149 return (backsearch(f,n));
150 else
151 return (isearch(SRCH_BACK));
152 }
153
154 /*
155 * Incremental Search.
156 * dir is used as the initial direction to search.
157 * ^M exit from Isearch, set mark
158 * ^S switch direction to forward
159 * ^R switch direction to reverse
160 * ^Q quote next character (allows searching for ^N etc.)
161 * <ESC> exit from Isearch, set mark
162 * <DEL> undoes last character typed. (tricky job to do this correctly).
163 * other ^ exit search, don't set mark
164 * else accumulate into search string
165 */
166 static int
isearch(int dir)167 isearch(int dir)
168 {
169 struct line *clp; /* Saved line pointer */
170 int c;
171 int cbo; /* Saved offset */
172 int success;
173 int pptr;
174 int firstc;
175 int xcase;
176 int i;
177 char opat[NPAT];
178 int cdotline; /* Saved line number */
179
180 if (macrodef) {
181 dobeep();
182 ewprintf("Can't isearch in macro");
183 return (FALSE);
184 }
185 for (cip = 0; cip < NSRCH; cip++)
186 cmds[cip].s_code = SRCH_NOPR;
187
188 (void)strlcpy(opat, pat, sizeof(opat));
189 cip = 0;
190 pptr = -1;
191 clp = curwp->w_dotp;
192 cbo = curwp->w_doto;
193 cdotline = curwp->w_dotline;
194 is_lpush();
195 is_cpush(SRCH_BEGIN);
196 success = TRUE;
197 is_prompt(dir, TRUE, success);
198
199 for (;;) {
200 update(CMODE);
201
202 switch (c = getkey(FALSE)) {
203 case CCHR('['):
204 /*
205 * If new characters come in the next 300 msec,
206 * we can assume that they belong to a longer
207 * escaped sequence so we should ungetkey the
208 * ESC to avoid writing out garbage.
209 */
210 if (ttwait(300) == FALSE)
211 ungetkey(c);
212 /* FALLTHRU */
213 case CCHR('M'):
214 srch_lastdir = dir;
215 curwp->w_markp = clp;
216 curwp->w_marko = cbo;
217 curwp->w_markline = cdotline;
218 ewprintf("Mark set");
219 return (TRUE);
220 case CCHR('G'):
221 if (success != TRUE) {
222 while (is_peek() == SRCH_ACCM)
223 is_undo(&pptr, &dir);
224 success = TRUE;
225 is_prompt(dir, pptr < 0, success);
226 break;
227 }
228 curwp->w_dotp = clp;
229 curwp->w_doto = cbo;
230 curwp->w_dotline = cdotline;
231 curwp->w_rflag |= WFMOVE;
232 srch_lastdir = dir;
233 (void)ctrlg(FFRAND, 0);
234 (void)strlcpy(pat, opat, sizeof(pat));
235 return (ABORT);
236 case CCHR('S'):
237 if (dir == SRCH_BACK) {
238 dir = SRCH_FORW;
239 is_lpush();
240 is_cpush(SRCH_FORW);
241 success = TRUE;
242 }
243 if (success == FALSE && dir == SRCH_FORW) {
244 /* wrap the search to beginning */
245 curwp->w_dotp = bfirstlp(curbp);
246 curwp->w_doto = 0;
247 curwp->w_dotline = 1;
248 if (is_find(dir) != FALSE) {
249 is_cpush(SRCH_MARK);
250 success = TRUE;
251 }
252 ewprintf("Overwrapped I-search: %s", pat);
253 break;
254 }
255 is_lpush();
256 pptr = strlen(pat);
257 if (forwchar(FFRAND, 1) == FALSE) {
258 dobeep();
259 success = FALSE;
260 ewprintf("Failed I-search: %s", pat);
261 } else {
262 if (is_find(SRCH_FORW) != FALSE)
263 is_cpush(SRCH_MARK);
264 else {
265 (void)backchar(FFRAND, 1);
266 dobeep();
267 success = FALSE;
268 ewprintf("Failed I-search: %s", pat);
269 }
270 }
271 is_prompt(dir, pptr < 0, success);
272 break;
273 case CCHR('R'):
274 if (dir == SRCH_FORW) {
275 dir = SRCH_BACK;
276 is_lpush();
277 is_cpush(SRCH_BACK);
278 success = TRUE;
279 }
280 if (success == FALSE && dir == SRCH_BACK) {
281 /* wrap the search to end */
282 curwp->w_dotp = blastlp(curbp);
283 curwp->w_doto = llength(curwp->w_dotp);
284 curwp->w_dotline = curwp->w_bufp->b_lines;
285 if (is_find(dir) != FALSE) {
286 is_cpush(SRCH_MARK);
287 success = TRUE;
288 }
289 ewprintf("Overwrapped I-search: %s", pat);
290 break;
291 }
292 is_lpush();
293 pptr = strlen(pat);
294 if (backchar(FFRAND, 1) == FALSE) {
295 dobeep();
296 success = FALSE;
297 } else {
298 if (is_find(SRCH_BACK) != FALSE)
299 is_cpush(SRCH_MARK);
300 else {
301 (void)forwchar(FFRAND, 1);
302 dobeep();
303 success = FALSE;
304 }
305 }
306 is_prompt(dir, pptr < 0, success);
307 break;
308 case CCHR('W'):
309 /* add the rest of the current word to the pattern */
310 clp = curwp->w_dotp;
311 cbo = curwp->w_doto;
312 firstc = 1;
313 if (pptr == -1)
314 pptr = 0;
315 if (dir == SRCH_BACK) {
316 /* when isearching backwards, cbo is the start of the pattern */
317 cbo += pptr;
318 }
319
320 /* if the search is case insensitive, add to pattern using lowercase */
321 xcase = 0;
322 for (i = 0; pat[i]; i++)
323 if (ISUPPER(CHARMASK(pat[i])))
324 xcase = 1;
325
326 while (cbo < llength(clp)) {
327 c = lgetc(clp, cbo++);
328 if ((!firstc && !isalnum(c)))
329 break;
330
331 if (pptr == NPAT - 1) {
332 dobeep();
333 break;
334 }
335 firstc = 0;
336 if (!xcase && ISUPPER(c))
337 c = TOLOWER(c);
338
339 pat[pptr++] = c;
340 pat[pptr] = '\0';
341 /* cursor only moves when isearching forwards */
342 if (dir == SRCH_FORW) {
343 curwp->w_doto = cbo;
344 curwp->w_rflag |= WFMOVE;
345 update(CMODE);
346 }
347 }
348 is_prompt(dir, pptr < 0, success);
349 break;
350 case CCHR('H'):
351 case CCHR('?'):
352 is_undo(&pptr, &dir);
353 if (is_peek() != SRCH_ACCM)
354 success = TRUE;
355 is_prompt(dir, pptr < 0, success);
356 break;
357 case CCHR('\\'):
358 case CCHR('Q'):
359 c = (char)getkey(FALSE);
360 goto addchar;
361 default:
362 if (ISCTRL(c)) {
363 ungetkey(c);
364 curwp->w_markp = clp;
365 curwp->w_marko = cbo;
366 curwp->w_markline = cdotline;
367 ewprintf("Mark set");
368 curwp->w_rflag |= WFMOVE;
369 return (TRUE);
370 }
371 /* FALLTHRU */
372 case CCHR('I'):
373 case CCHR('J'):
374 addchar:
375 if (pptr == -1)
376 pptr = 0;
377 if (pptr == 0)
378 success = TRUE;
379 if (pptr == NPAT - 1)
380 dobeep();
381 else {
382 pat[pptr++] = c;
383 pat[pptr] = '\0';
384 }
385 is_lpush();
386 if (success != FALSE) {
387 if (is_find(dir) != FALSE)
388 is_cpush(c);
389 else {
390 success = FALSE;
391 dobeep();
392 is_cpush(SRCH_ACCM);
393 }
394 } else
395 is_cpush(SRCH_ACCM);
396 is_prompt(dir, FALSE, success);
397 }
398 }
399 /* NOTREACHED */
400 }
401
402 static void
is_cpush(int cmd)403 is_cpush(int cmd)
404 {
405 if (++cip >= NSRCH)
406 cip = 0;
407 cmds[cip].s_code = cmd;
408 }
409
410 static void
is_lpush(void)411 is_lpush(void)
412 {
413 int ctp;
414
415 ctp = cip + 1;
416 if (ctp >= NSRCH)
417 ctp = 0;
418 cmds[ctp].s_code = SRCH_NOPR;
419 cmds[ctp].s_doto = curwp->w_doto;
420 cmds[ctp].s_dotp = curwp->w_dotp;
421 cmds[ctp].s_dotline = curwp->w_dotline;
422 }
423
424 static void
is_pop(void)425 is_pop(void)
426 {
427 if (cmds[cip].s_code != SRCH_NOPR) {
428 curwp->w_doto = cmds[cip].s_doto;
429 curwp->w_dotp = cmds[cip].s_dotp;
430 curwp->w_dotline = cmds[cip].s_dotline;
431 curwp->w_rflag |= WFMOVE;
432 cmds[cip].s_code = SRCH_NOPR;
433 }
434 if (--cip <= 0)
435 cip = NSRCH - 1;
436 }
437
438 static int
is_peek(void)439 is_peek(void)
440 {
441 return (cmds[cip].s_code);
442 }
443
444 /* this used to always return TRUE (the return value was checked) */
445 static void
is_undo(int * pptr,int * dir)446 is_undo(int *pptr, int *dir)
447 {
448 int redo = FALSE;
449
450 switch (cmds[cip].s_code) {
451 case SRCH_BEGIN:
452 case SRCH_NOPR:
453 *pptr = -1;
454 break;
455 case SRCH_MARK:
456 break;
457 case SRCH_FORW:
458 *dir = SRCH_BACK;
459 redo = TRUE;
460 break;
461 case SRCH_BACK:
462 *dir = SRCH_FORW;
463 redo = TRUE;
464 break;
465 case SRCH_ACCM:
466 default:
467 *pptr -= 1;
468 if (*pptr < 0)
469 *pptr = 0;
470 pat[*pptr] = '\0';
471 break;
472 }
473 is_pop();
474 if (redo)
475 is_undo(pptr, dir);
476 }
477
478 static int
is_find(int dir)479 is_find(int dir)
480 {
481 int plen, odoto, odotline;
482 struct line *odotp;
483
484 odoto = curwp->w_doto;
485 odotp = curwp->w_dotp;
486 odotline = curwp->w_dotline;
487 plen = strlen(pat);
488 if (plen != 0) {
489 if (dir == SRCH_FORW) {
490 (void)backchar(FFARG | FFRAND, plen);
491 if (forwsrch() == FALSE) {
492 curwp->w_doto = odoto;
493 curwp->w_dotp = odotp;
494 curwp->w_dotline = odotline;
495 return (FALSE);
496 }
497 return (TRUE);
498 }
499 if (dir == SRCH_BACK) {
500 (void)forwchar(FFARG | FFRAND, plen);
501 if (backsrch() == FALSE) {
502 curwp->w_doto = odoto;
503 curwp->w_dotp = odotp;
504 curwp->w_dotline = odotline;
505 return (FALSE);
506 }
507 return (TRUE);
508 }
509 dobeep();
510 ewprintf("bad call to is_find");
511 return (FALSE);
512 }
513 return (FALSE);
514 }
515
516 /*
517 * If called with "dir" not one of SRCH_FORW or SRCH_BACK, this routine used
518 * to print an error message. It also used to return TRUE or FALSE, depending
519 * on if it liked the "dir". However, none of the callers looked at the
520 * status, so I just made the checking vanish.
521 */
522 static void
is_prompt(int dir,int flag,int success)523 is_prompt(int dir, int flag, int success)
524 {
525 if (dir == SRCH_FORW) {
526 if (success != FALSE)
527 is_dspl("I-search", flag);
528 else
529 is_dspl("Failing I-search", flag);
530 } else if (dir == SRCH_BACK) {
531 if (success != FALSE)
532 is_dspl("I-search backward", flag);
533 else
534 is_dspl("Failing I-search backward", flag);
535 } else
536 ewprintf("Broken call to is_prompt");
537 }
538
539 /*
540 * Prompt writing routine for the incremental search. The "i_prompt" is just
541 * a string. The "flag" determines whether pat should be printed.
542 */
543 static void
is_dspl(char * i_prompt,int flag)544 is_dspl(char *i_prompt, int flag)
545 {
546 if (flag != FALSE)
547 ewprintf("%s: ", i_prompt);
548 else
549 ewprintf("%s: %s", i_prompt, pat);
550 }
551
552 /*
553 * Query Replace.
554 * Replace strings selectively. Does a search and replace operation.
555 */
556 int
queryrepl(int f,int n)557 queryrepl(int f, int n)
558 {
559 int s;
560 int rcnt = 0; /* replacements made so far */
561 int plen; /* length of found string */
562 char news[NPAT], *rep; /* replacement string */
563
564 if (macrodef) {
565 dobeep();
566 ewprintf("Can't query replace in macro");
567 return (FALSE);
568 }
569
570 if ((s = readpattern("Query replace")) != TRUE)
571 return (s);
572 if ((rep = eread("Query replace %s with: ", news, NPAT,
573 EFNUL | EFNEW | EFCR, pat)) == NULL)
574 return (ABORT);
575 else if (rep[0] == '\0')
576 news[0] = '\0';
577 ewprintf("Query replacing %s with %s:", pat, news);
578 plen = strlen(pat);
579
580 /*
581 * Search forward repeatedly, checking each time whether to insert
582 * or not. The "!" case makes the check always true, so it gets put
583 * into a tighter loop for efficiency.
584 */
585 while (forwsrch() == TRUE) {
586 retry:
587 update(CMODE);
588 switch (getkey(FALSE)) {
589 case 'y':
590 case ' ':
591 if (lreplace((RSIZE)plen, news) == FALSE)
592 return (FALSE);
593 rcnt++;
594 break;
595 case '.':
596 if (lreplace((RSIZE)plen, news) == FALSE)
597 return (FALSE);
598 rcnt++;
599 goto stopsearch;
600 /* ^G, CR or ESC */
601 case CCHR('G'):
602 (void)ctrlg(FFRAND, 0);
603 goto stopsearch;
604 case CCHR('['):
605 case CCHR('M'):
606 goto stopsearch;
607 case '!':
608 do {
609 if (lreplace((RSIZE)plen, news) == FALSE)
610 return (FALSE);
611 rcnt++;
612 } while (forwsrch() == TRUE);
613 goto stopsearch;
614 case 'n':
615 case CCHR('H'):
616 /* To not replace */
617 case CCHR('?'):
618 break;
619 default:
620 ewprintf("y/n or <SP>/<DEL>: replace/don't, [.] repl-end, [!] repl-rest, <CR>/<ESC> quit");
621 goto retry;
622 }
623 }
624 stopsearch:
625 curwp->w_rflag |= WFFULL;
626 update(CMODE);
627 if (rcnt == 1)
628 ewprintf("Replaced 1 occurrence");
629 else
630 ewprintf("Replaced %d occurrences", rcnt);
631 return (TRUE);
632 }
633
634 /*
635 * Replace string globally without individual prompting.
636 */
637 int
replstr(int f,int n)638 replstr(int f, int n)
639 {
640 char news[NPAT];
641 int s, plen, rcnt = 0;
642 char *r;
643
644 if ((s = readpattern("Replace string")) != TRUE)
645 return s;
646
647 r = eread("Replace string %s with: ", news, NPAT,
648 EFNUL | EFNEW | EFCR, pat);
649 if (r == NULL)
650 return (ABORT);
651
652 plen = strlen(pat);
653 while (forwsrch() == TRUE) {
654 update(CMODE);
655 if (lreplace((RSIZE)plen, news) == FALSE)
656 return (FALSE);
657
658 rcnt++;
659 }
660
661 curwp->w_rflag |= WFFULL;
662 update(CMODE);
663
664 if (rcnt == 1)
665 ewprintf("Replaced 1 occurrence");
666 else
667 ewprintf("Replaced %d occurrences", rcnt);
668
669 return (TRUE);
670 }
671
672 /*
673 * This routine does the real work of a forward search. The pattern is sitting
674 * in the external variable "pat". If found, dot is updated, the window system
675 * is notified of the change, and TRUE is returned. If the string isn't found,
676 * FALSE is returned.
677 */
678 int
forwsrch(void)679 forwsrch(void)
680 {
681 struct line *clp, *tlp;
682 int cbo, tbo, c, i, xcase = 0;
683 char *pp;
684 int nline;
685
686 clp = curwp->w_dotp;
687 cbo = curwp->w_doto;
688 nline = curwp->w_dotline;
689 for (i = 0; pat[i]; i++)
690 if (ISUPPER(CHARMASK(pat[i])))
691 xcase = 1;
692 for (;;) {
693 if (cbo == llength(clp)) {
694 if ((clp = lforw(clp)) == curbp->b_headp)
695 break;
696 nline++;
697 cbo = 0;
698 c = CCHR('J');
699 } else
700 c = lgetc(clp, cbo++);
701 if (eq(c, pat[0], xcase) != FALSE) {
702 tlp = clp;
703 tbo = cbo;
704 pp = &pat[1];
705 while (*pp != 0) {
706 if (tbo == llength(tlp)) {
707 tlp = lforw(tlp);
708 if (tlp == curbp->b_headp)
709 goto fail;
710 tbo = 0;
711 c = CCHR('J');
712 if (eq(c, *pp++, xcase) == FALSE)
713 goto fail;
714 nline++;
715 } else {
716 c = lgetc(tlp, tbo++);
717 if (eq(c, *pp++, xcase) == FALSE)
718 goto fail;
719 }
720 }
721 curwp->w_dotp = tlp;
722 curwp->w_doto = tbo;
723 curwp->w_dotline = nline;
724 curwp->w_rflag |= WFMOVE;
725 return (TRUE);
726 }
727 fail: ;
728 }
729 return (FALSE);
730 }
731
732 /*
733 * This routine does the real work of a backward search. The pattern is
734 * sitting in the external variable "pat". If found, dot is updated, the
735 * window system is notified of the change, and TRUE is returned. If the
736 * string isn't found, FALSE is returned.
737 */
738 int
backsrch(void)739 backsrch(void)
740 {
741 struct line *clp, *tlp;
742 int cbo, tbo, c, i, xcase = 0;
743 char *epp, *pp;
744 int nline, pline;
745
746 for (epp = &pat[0]; epp[1] != 0; ++epp);
747 clp = curwp->w_dotp;
748 cbo = curwp->w_doto;
749 nline = curwp->w_dotline;
750 for (i = 0; pat[i]; i++)
751 if (ISUPPER(CHARMASK(pat[i])))
752 xcase = 1;
753 for (;;) {
754 if (cbo == 0) {
755 clp = lback(clp);
756 if (clp == curbp->b_headp)
757 return (FALSE);
758 nline--;
759 cbo = llength(clp) + 1;
760 }
761 if (--cbo == llength(clp))
762 c = CCHR('J');
763 else
764 c = lgetc(clp, cbo);
765 if (eq(c, *epp, xcase) != FALSE) {
766 tlp = clp;
767 tbo = cbo;
768 pp = epp;
769 pline = nline;
770 while (pp != &pat[0]) {
771 if (tbo == 0) {
772 tlp = lback(tlp);
773 if (tlp == curbp->b_headp)
774 goto fail;
775 nline--;
776 tbo = llength(tlp) + 1;
777 }
778 if (--tbo == llength(tlp))
779 c = CCHR('J');
780 else
781 c = lgetc(tlp, tbo);
782 if (eq(c, *--pp, xcase) == FALSE) {
783 nline = pline;
784 goto fail;
785 }
786 }
787 curwp->w_dotp = tlp;
788 curwp->w_doto = tbo;
789 curwp->w_dotline = nline;
790 curwp->w_rflag |= WFMOVE;
791 return (TRUE);
792 }
793 fail: ;
794 }
795 /* NOTREACHED */
796 }
797
798 /*
799 * Compare two characters. The "bc" comes from the buffer. It has its case
800 * folded out. The "pc" is from the pattern.
801 */
802 static int
eq(int bc,int pc,int xcase)803 eq(int bc, int pc, int xcase)
804 {
805 bc = CHARMASK(bc);
806 pc = CHARMASK(pc);
807 if (bc == pc)
808 return (TRUE);
809 if (xcase)
810 return (FALSE);
811 if (ISUPPER(bc))
812 return (TOLOWER(bc) == pc);
813 if (ISUPPER(pc))
814 return (bc == TOLOWER(pc));
815 return (FALSE);
816 }
817
818 /*
819 * Read a pattern. Stash it in the external variable "pat". The "pat" is not
820 * updated if the user types in an empty line. If the user typed an empty
821 * line, and there is no old pattern, it is an error. Display the old pattern,
822 * in the style of Jeff Lomicka. There is some do-it-yourself control
823 * expansion.
824 */
825 int
readpattern(char * r_prompt)826 readpattern(char *r_prompt)
827 {
828 char tpat[NPAT], *rep;
829 int retval;
830
831 if (pat[0] == '\0')
832 rep = eread("%s: ", tpat, NPAT, EFNEW | EFCR, r_prompt);
833 else
834 rep = eread("%s (default %s): ", tpat, NPAT,
835 EFNUL | EFNEW | EFCR, r_prompt, pat);
836
837 /* specified */
838 if (rep == NULL) {
839 retval = ABORT;
840 } else if (rep[0] != '\0') {
841 (void)strlcpy(pat, tpat, sizeof(pat));
842 retval = TRUE;
843 } else if (pat[0] != '\0') {
844 retval = TRUE;
845 } else
846 retval = FALSE;
847 return (retval);
848 }
849
850 /*
851 * Prompt for a character and kill until its next occurrence,
852 * including it. Mark is cleared afterwards.
853 */
854 int
zaptochar(int f,int n)855 zaptochar(int f, int n)
856 {
857 return (zap(TRUE, n));
858 }
859
860 /* Like zaptochar but stops before the character. */
861 int
zapuptochar(int f,int n)862 zapuptochar(int f, int n)
863 {
864 return (zap(FALSE, n));
865 }
866
867 /*
868 * Prompt for a character and deletes from the point up to, optionally
869 * including, the first instance of that character. Marks is cleared
870 * afterwards.
871 */
872 int
zap(int including,int n)873 zap(int including, int n)
874 {
875 int s, backward;
876
877 backward = n < 0;
878 if (backward)
879 n = -n;
880
881 if (including)
882 ewprintf("Zap to char: ");
883 else
884 ewprintf("Zap up to char: ");
885
886 s = getkey(FALSE);
887 eerase();
888 if (s == ABORT || s == CCHR('G'))
889 return (FALSE);
890
891 if (n == 0)
892 return (TRUE);
893
894 pat[0] = (char)s;
895 pat[1] = '\0';
896
897 isetmark();
898 while (n--) {
899 s = backward ? backsrch() : forwsrch();
900 if (s != TRUE) {
901 dobeep();
902 ewprintf("Search failed: \"%s\"", pat);
903 swapmark(FFARG, 0);
904 clearmark(FFARG, 0);
905 return (s);
906 }
907 }
908
909 if (!including) {
910 if (backward)
911 forwchar(FFARG, 1);
912 else
913 backchar(FFARG, 1);
914 }
915
916 killregion(FFARG, 0);
917 clearmark(FFARG, 0);
918 return (TRUE);
919 }
920