Annotation of src/usr.bin/mg/search.c, Revision 1.2
1.1 deraadt 1: /*
2: * Search commands.
3: * The functions in this file implement the
4: * search commands (both plain and incremental searches
5: * are supported) and the query-replace command.
6: *
7: * The plain old search code is part of the original
8: * MicroEMACS "distribution". The incremental search code,
9: * and the query-replace code, is by Rich Ellison.
10: */
11: #include "def.h"
12: #ifndef NO_MACRO
13: #include "macro.h"
14: #endif
15:
1.2 ! millert 16: #define SRCH_BEGIN (0) /* Search sub-codes. */
1.1 deraadt 17: #define SRCH_FORW (-1)
18: #define SRCH_BACK (-2)
19: #define SRCH_NOPR (-3)
20: #define SRCH_ACCM (-4)
21: #define SRCH_MARK (-5)
22:
1.2 ! millert 23: typedef struct {
! 24: int s_code;
! 25: LINE *s_dotp;
! 26: int s_doto;
! 27: } SRCHCOM;
! 28:
! 29: static SRCHCOM cmds[NSRCH];
! 30: static int cip;
! 31:
! 32: int srch_lastdir = SRCH_NOPR; /* Last search flags. */
! 33:
! 34: static VOID is_cpush();
! 35: static VOID is_lpush();
! 36: static VOID is_pop();
! 37: static int is_peek();
! 38: static VOID is_undo();
! 39: static int is_find();
! 40: static VOID is_prompt();
! 41: static VOID is_dspl();
! 42: static int eq();
1.1 deraadt 43:
44: /*
45: * Search forward.
46: * Get a search string from the user, and search for it,
47: * starting at ".". If found, "." gets moved to just after the
48: * matched characters, and display does all the hard stuff.
49: * If not found, it just prints a message.
50: */
1.2 ! millert 51: /* ARGSUSED */
1.1 deraadt 52: forwsearch(f, n)
53: {
1.2 ! millert 54: register int s;
1.1 deraadt 55:
1.2 ! millert 56: if ((s = readpattern("Search")) != TRUE)
1.1 deraadt 57: return s;
58: if (forwsrch() == FALSE) {
59: ewprintf("Search failed: \"%s\"", pat);
60: return FALSE;
61: }
62: srch_lastdir = SRCH_FORW;
63: return TRUE;
64: }
65:
66: /*
67: * Reverse search.
68: * Get a search string from the user, and search, starting at "."
69: * and proceeding toward the front of the buffer. If found "." is left
70: * pointing at the first character of the pattern [the last character that
71: * was matched].
72: */
1.2 ! millert 73: /* ARGSUSED */
1.1 deraadt 74: backsearch(f, n)
75: {
1.2 ! millert 76: register int s;
1.1 deraadt 77:
1.2 ! millert 78: if ((s = readpattern("Search backward")) != TRUE)
1.1 deraadt 79: return (s);
80: if (backsrch() == FALSE) {
81: ewprintf("Search failed: \"%s\"", pat);
82: return FALSE;
83: }
84: srch_lastdir = SRCH_BACK;
85: return TRUE;
86: }
87:
88: /*
89: * Search again, using the same search string
90: * and direction as the last search command. The direction
91: * has been saved in "srch_lastdir", so you know which way
92: * to go.
93: */
1.2 ! millert 94: /* ARGSUSED */
1.1 deraadt 95: searchagain(f, n)
96: {
97: if (srch_lastdir == SRCH_FORW) {
98: if (forwsrch() == FALSE) {
99: ewprintf("Search failed: \"%s\"", pat);
100: return FALSE;
101: }
102: return TRUE;
103: }
104: if (srch_lastdir == SRCH_BACK) {
105: if (backsrch() == FALSE) {
106: ewprintf("Search failed: \"%s\"", pat);
107: return FALSE;
108: }
109: return TRUE;
110: }
111: ewprintf("No last search");
112: return FALSE;
113: }
114:
115: /*
116: * Use incremental searching, initially in the forward direction.
117: * isearch ignores any explicit arguments.
118: */
1.2 ! millert 119: /* ARGSUSED */
1.1 deraadt 120: forwisearch(f, n)
121: {
122: return isearch(SRCH_FORW);
123: }
124:
125: /*
126: * Use incremental searching, initially in the reverse direction.
127: * isearch ignores any explicit arguments.
128: */
1.2 ! millert 129: /* ARGSUSED */
1.1 deraadt 130: backisearch(f, n)
131: {
132: return isearch(SRCH_BACK);
133: }
134:
135: /*
136: * Incremental Search.
137: * dir is used as the initial direction to search.
138: * ^S switch direction to forward
139: * ^R switch direction to reverse
140: * ^Q quote next character (allows searching for ^N etc.)
141: * <ESC> exit from Isearch
142: * <DEL> undoes last character typed. (tricky job to do this correctly).
143: * other ^ exit search, don't set mark
144: * else accumulate into search string
145: */
1.2 ! millert 146: isearch(dir)
! 147: {
! 148: register int c;
! 149: register LINE *clp;
! 150: register int cbo;
! 151: register int success;
! 152: int pptr;
! 153: char opat[NPAT];
! 154: VOID ungetkey();
1.1 deraadt 155:
156: #ifndef NO_MACRO
1.2 ! millert 157: if (macrodef) {
! 158: ewprintf("Can't isearch in macro");
! 159: return FALSE;
1.1 deraadt 160: }
161: #endif
1.2 ! millert 162: for (cip = 0; cip < NSRCH; cip++)
1.1 deraadt 163: cmds[cip].s_code = SRCH_NOPR;
164: (VOID) strcpy(opat, pat);
165: cip = 0;
166: pptr = -1;
167: clp = curwp->w_dotp;
168: cbo = curwp->w_doto;
169: is_lpush();
170: is_cpush(SRCH_BEGIN);
171: success = TRUE;
172: is_prompt(dir, TRUE, success);
173: for (;;) {
174: update();
175: switch (c = getkey(FALSE)) {
176: case CCHR('['):
177: srch_lastdir = dir;
178: curwp->w_markp = clp;
179: curwp->w_marko = cbo;
180: ewprintf("Mark set");
181: return (TRUE);
182:
183: case CCHR('G'):
184: if (success != TRUE) {
185: while (is_peek() == SRCH_ACCM)
186: is_undo(&pptr, &dir);
187: success = TRUE;
188: is_prompt(dir, pptr < 0, success);
189: break;
190: }
191: curwp->w_dotp = clp;
192: curwp->w_doto = cbo;
193: curwp->w_flag |= WFMOVE;
194: srch_lastdir = dir;
195: (VOID) ctrlg(FFRAND, 0);
196: (VOID) strcpy(pat, opat);
197: return ABORT;
198:
199: case CCHR(']'):
200: case CCHR('S'):
201: if (dir == SRCH_BACK) {
202: dir = SRCH_FORW;
203: is_lpush();
204: is_cpush(SRCH_FORW);
205: success = TRUE;
206: }
1.2 ! millert 207: if (success == FALSE && dir == SRCH_FORW)
1.1 deraadt 208: break;
209: is_lpush();
210: pptr = strlen(pat);
211: (VOID) forwchar(FFRAND, 1);
1.2 ! millert 212: if (is_find(SRCH_FORW) != FALSE)
! 213: is_cpush(SRCH_MARK);
1.1 deraadt 214: else {
215: (VOID) backchar(FFRAND, 1);
216: ttbeep();
217: success = FALSE;
218: }
219: is_prompt(dir, pptr < 0, success);
220: break;
221:
222: case CCHR('R'):
223: if (dir == SRCH_FORW) {
224: dir = SRCH_BACK;
225: is_lpush();
226: is_cpush(SRCH_BACK);
227: success = TRUE;
228: }
1.2 ! millert 229: if (success == FALSE && dir == SRCH_BACK)
1.1 deraadt 230: break;
231: is_lpush();
232: pptr = strlen(pat);
233: (VOID) backchar(FFRAND, 1);
1.2 ! millert 234: if (is_find(SRCH_BACK) != FALSE)
! 235: is_cpush(SRCH_MARK);
1.1 deraadt 236: else {
237: (VOID) forwchar(FFRAND, 1);
238: ttbeep();
239: success = FALSE;
240: }
241: is_prompt(dir, pptr < 0, success);
242: break;
243:
244: case CCHR('H'):
245: case CCHR('?'):
246: is_undo(&pptr, &dir);
1.2 ! millert 247: if (is_peek() != SRCH_ACCM)
! 248: success = TRUE;
1.1 deraadt 249: is_prompt(dir, pptr < 0, success);
250: break;
251:
252: case CCHR('\\'):
253: case CCHR('Q'):
254: c = (char) getkey(FALSE);
1.2 ! millert 255: goto addchar;
1.1 deraadt 256: case CCHR('M'):
257: c = CCHR('J');
1.2 ! millert 258: goto addchar;
1.1 deraadt 259:
260: default:
261: if (ISCTRL(c)) {
262: ungetkey(c);
263: curwp->w_markp = clp;
264: curwp->w_marko = cbo;
265: ewprintf("Mark set");
266: curwp->w_flag |= WFMOVE;
1.2 ! millert 267: return TRUE;
1.1 deraadt 268: } /* and continue */
269: case CCHR('I'):
270: case CCHR('J'):
1.2 ! millert 271: addchar:
1.1 deraadt 272: if (pptr == -1)
273: pptr = 0;
274: if (pptr == 0)
275: success = TRUE;
276: pat[pptr++] = c;
277: if (pptr == NPAT) {
278: ewprintf("Pattern too long");
279: return FALSE;
280: }
281: pat[pptr] = '\0';
282: is_lpush();
283: if (success != FALSE) {
284: if (is_find(dir) != FALSE)
285: is_cpush(c);
286: else {
287: success = FALSE;
288: ttbeep();
289: is_cpush(SRCH_ACCM);
290: }
291: } else
292: is_cpush(SRCH_ACCM);
293: is_prompt(dir, FALSE, success);
294: }
295: }
1.2 ! millert 296: /* NOTREACHED */
1.1 deraadt 297: }
298:
1.2 ! millert 299: static VOID
! 300: is_cpush(cmd)
! 301: register int cmd;
! 302: {
1.1 deraadt 303: if (++cip >= NSRCH)
304: cip = 0;
305: cmds[cip].s_code = cmd;
306: }
307:
1.2 ! millert 308: static VOID
! 309: is_lpush()
! 310: {
! 311: register int ctp;
1.1 deraadt 312:
1.2 ! millert 313: ctp = cip + 1;
1.1 deraadt 314: if (ctp >= NSRCH)
315: ctp = 0;
316: cmds[ctp].s_code = SRCH_NOPR;
317: cmds[ctp].s_doto = curwp->w_doto;
318: cmds[ctp].s_dotp = curwp->w_dotp;
319: }
320:
1.2 ! millert 321: static VOID
! 322: is_pop()
! 323: {
1.1 deraadt 324: if (cmds[cip].s_code != SRCH_NOPR) {
1.2 ! millert 325: curwp->w_doto = cmds[cip].s_doto;
! 326: curwp->w_dotp = cmds[cip].s_dotp;
1.1 deraadt 327: curwp->w_flag |= WFMOVE;
328: cmds[cip].s_code = SRCH_NOPR;
329: }
330: if (--cip <= 0)
1.2 ! millert 331: cip = NSRCH - 1;
1.1 deraadt 332: }
333:
334: static int
1.2 ! millert 335: is_peek()
! 336: {
1.1 deraadt 337: return cmds[cip].s_code;
338: }
339:
340: /* this used to always return TRUE (the return value was checked) */
1.2 ! millert 341: static VOID
! 342: is_undo(pptr, dir)
! 343: register int *pptr;
! 344: register int *dir;
! 345: {
! 346: register int redo = FALSE;
1.1 deraadt 347: switch (cmds[cip].s_code) {
348: case SRCH_BEGIN:
349: case SRCH_NOPR:
350: *pptr = -1;
351: case SRCH_MARK:
352: break;
353:
354: case SRCH_FORW:
355: *dir = SRCH_BACK;
356: redo = TRUE;
357: break;
358:
359: case SRCH_BACK:
360: *dir = SRCH_FORW;
361: redo = TRUE;
362: break;
363:
364: case SRCH_ACCM:
365: default:
366: *pptr -= 1;
367: if (*pptr < 0)
368: *pptr = 0;
369: pat[*pptr] = '\0';
370: break;
371: }
372: is_pop();
1.2 ! millert 373: if (redo)
! 374: is_undo(pptr, dir);
1.1 deraadt 375: }
376:
377: static int
1.2 ! millert 378: is_find(dir)
! 379: register int dir;
! 380: {
! 381: register int plen, odoto;
! 382: register LINE *odotp;
1.1 deraadt 383:
384: odoto = curwp->w_doto;
385: odotp = curwp->w_dotp;
386: plen = strlen(pat);
387: if (plen != 0) {
1.2 ! millert 388: if (dir == SRCH_FORW) {
1.1 deraadt 389: (VOID) backchar(FFARG | FFRAND, plen);
390: if (forwsrch() == FALSE) {
391: curwp->w_doto = odoto;
392: curwp->w_dotp = odotp;
393: return FALSE;
394: }
395: return TRUE;
396: }
1.2 ! millert 397: if (dir == SRCH_BACK) {
1.1 deraadt 398: (VOID) forwchar(FFARG | FFRAND, plen);
399: if (backsrch() == FALSE) {
400: curwp->w_doto = odoto;
401: curwp->w_dotp = odotp;
402: return FALSE;
403: }
404: return TRUE;
405: }
406: ewprintf("bad call to is_find");
407: return FALSE;
408: }
409: return FALSE;
410: }
411:
412: /*
413: * If called with "dir" not one of SRCH_FORW
414: * or SRCH_BACK, this routine used to print an error
415: * message. It also used to return TRUE or FALSE,
416: * depending on if it liked the "dir". However, none
417: * of the callers looked at the status, so I just
418: * made the checking vanish.
419: */
1.2 ! millert 420: static VOID
! 421: is_prompt(dir, flag, success)
! 422: {
1.1 deraadt 423: if (dir == SRCH_FORW) {
424: if (success != FALSE)
425: is_dspl("I-search", flag);
426: else
427: is_dspl("Failing I-search", flag);
428: } else if (dir == SRCH_BACK) {
429: if (success != FALSE)
430: is_dspl("I-search backward", flag);
431: else
432: is_dspl("Failing I-search backward", flag);
1.2 ! millert 433: } else
! 434: ewprintf("Broken call to is_prompt");
1.1 deraadt 435: }
436:
437: /*
438: * Prompt writing routine for the incremental search.
439: * The "prompt" is just a string. The "flag" determines
440: * whether pat should be printed.
441: */
1.2 ! millert 442: static VOID
! 443: is_dspl(prompt, flag)
! 444: char *prompt;
! 445: {
1.1 deraadt 446:
447: if (flag != FALSE)
448: ewprintf("%s: ", prompt);
449: else
450: ewprintf("%s: %s", prompt, pat);
451: }
452:
453: /*
454: * Query Replace.
455: * Replace strings selectively. Does a search and replace operation.
456: */
1.2 ! millert 457: /* ARGSUSED */
1.1 deraadt 458: queryrepl(f, n)
459: {
1.2 ! millert 460: register int s;
! 461: register int rcnt = 0; /* Replacements made so far */
! 462: register int plen; /* length of found string */
! 463: char news[NPAT]; /* replacement string */
1.1 deraadt 464:
465: #ifndef NO_MACRO
1.2 ! millert 466: if (macrodef) {
! 467: ewprintf("Can't query replace in macro");
! 468: return FALSE;
1.1 deraadt 469: }
470: #endif
1.2 ! millert 471: if ((s = readpattern("Query replace")) != TRUE)
1.1 deraadt 472: return (s);
1.2 ! millert 473: if ((s = ereply("Query replace %s with: ", news, NPAT, pat)) == ABORT)
1.1 deraadt 474: return (s);
475: if (s == FALSE)
476: news[0] = '\0';
477: ewprintf("Query replacing %s with %s:", pat, news);
478: plen = strlen(pat);
479:
480: /*
481: * Search forward repeatedly, checking each time whether to insert
482: * or not. The "!" case makes the check always true, so it gets put
483: * into a tighter loop for efficiency.
484: */
485:
486: while (forwsrch() == TRUE) {
1.2 ! millert 487: retry:
1.1 deraadt 488: update();
489: switch (getkey(FALSE)) {
490: case ' ':
491: if (lreplace((RSIZE) plen, news, f) == FALSE)
492: return (FALSE);
493: rcnt++;
494: break;
495:
496: case '.':
497: if (lreplace((RSIZE) plen, news, f) == FALSE)
498: return (FALSE);
499: rcnt++;
500: goto stopsearch;
501:
1.2 ! millert 502: case CCHR('G'):/* ^G or ESC */
1.1 deraadt 503: (VOID) ctrlg(FFRAND, 0);
504: case CCHR('['):
505: goto stopsearch;
506:
507: case '!':
508: do {
509: if (lreplace((RSIZE) plen, news, f) == FALSE)
510: return (FALSE);
511: rcnt++;
512: } while (forwsrch() == TRUE);
513: goto stopsearch;
514:
515: case CCHR('H'):
1.2 ! millert 516: case CCHR('?'):/* To not replace */
1.1 deraadt 517: break;
518:
519: default:
1.2 ! millert 520: ewprintf("<SP> replace, [.] rep-end, <DEL> don't, [!] repl rest <ESC> quit");
1.1 deraadt 521: goto retry;
522: }
523: }
524: stopsearch:
525: curwp->w_flag |= WFHARD;
526: update();
527: if (rcnt == 0)
528: ewprintf("(No replacements done)");
529: else if (rcnt == 1)
530: ewprintf("(1 replacement done)");
531: else
532: ewprintf("(%d replacements done)", rcnt);
533: return TRUE;
534: }
535:
536: /*
537: * This routine does the real work of a
538: * forward search. The pattern is sitting in the external
539: * variable "pat". If found, dot is updated, the window system
540: * is notified of the change, and TRUE is returned. If the
541: * string isn't found, FALSE is returned.
542: */
1.2 ! millert 543: forwsrch()
! 544: {
! 545: register LINE *clp;
! 546: register int cbo;
! 547: register LINE *tlp;
! 548: register int tbo;
! 549: char *pp;
! 550: register int c;
1.1 deraadt 551:
552: clp = curwp->w_dotp;
553: cbo = curwp->w_doto;
1.2 ! millert 554: for (;;) {
1.1 deraadt 555: if (cbo == llength(clp)) {
1.2 ! millert 556: if ((clp = lforw(clp)) == curbp->b_linep)
! 557: break;
1.1 deraadt 558: cbo = 0;
559: c = CCHR('J');
560: } else
561: c = lgetc(clp, cbo++);
562: if (eq(c, pat[0]) != FALSE) {
563: tlp = clp;
564: tbo = cbo;
1.2 ! millert 565: pp = &pat[1];
1.1 deraadt 566: while (*pp != 0) {
567: if (tbo == llength(tlp)) {
568: tlp = lforw(tlp);
569: if (tlp == curbp->b_linep)
570: goto fail;
571: tbo = 0;
572: c = CCHR('J');
573: } else
574: c = lgetc(tlp, tbo++);
575: if (eq(c, *pp++) == FALSE)
576: goto fail;
577: }
1.2 ! millert 578: curwp->w_dotp = tlp;
! 579: curwp->w_doto = tbo;
1.1 deraadt 580: curwp->w_flag |= WFMOVE;
581: return TRUE;
582: }
1.2 ! millert 583: fail: ;
1.1 deraadt 584: }
585: return FALSE;
586: }
587:
588: /*
589: * This routine does the real work of a
590: * backward search. The pattern is sitting in the external
591: * variable "pat". If found, dot is updated, the window system
592: * is notified of the change, and TRUE is returned. If the
593: * string isn't found, FALSE is returned.
594: */
1.2 ! millert 595: backsrch()
! 596: {
! 597: register LINE *clp;
! 598: register int cbo;
! 599: register LINE *tlp;
! 600: register int tbo;
! 601: register int c;
! 602: register char *epp;
! 603: register char *pp;
1.1 deraadt 604:
1.2 ! millert 605: for (epp = &pat[0]; epp[1] != 0; ++epp);
1.1 deraadt 606: clp = curwp->w_dotp;
607: cbo = curwp->w_doto;
608: for (;;) {
609: if (cbo == 0) {
610: clp = lback(clp);
611: if (clp == curbp->b_linep)
612: return FALSE;
1.2 ! millert 613: cbo = llength(clp) + 1;
1.1 deraadt 614: }
615: if (--cbo == llength(clp))
616: c = CCHR('J');
617: else
1.2 ! millert 618: c = lgetc(clp, cbo);
1.1 deraadt 619: if (eq(c, *epp) != FALSE) {
620: tlp = clp;
621: tbo = cbo;
1.2 ! millert 622: pp = epp;
1.1 deraadt 623: while (pp != &pat[0]) {
624: if (tbo == 0) {
625: tlp = lback(tlp);
626: if (tlp == curbp->b_linep)
627: goto fail;
1.2 ! millert 628: tbo = llength(tlp) + 1;
1.1 deraadt 629: }
630: if (--tbo == llength(tlp))
631: c = CCHR('J');
632: else
1.2 ! millert 633: c = lgetc(tlp, tbo);
1.1 deraadt 634: if (eq(c, *--pp) == FALSE)
635: goto fail;
636: }
1.2 ! millert 637: curwp->w_dotp = tlp;
! 638: curwp->w_doto = tbo;
1.1 deraadt 639: curwp->w_flag |= WFMOVE;
640: return TRUE;
641: }
1.2 ! millert 642: fail: ;
1.1 deraadt 643: }
1.2 ! millert 644: /* NOTREACHED */
1.1 deraadt 645: }
646:
647: /*
648: * Compare two characters.
649: * The "bc" comes from the buffer.
650: * It has its case folded out. The
651: * "pc" is from the pattern.
652: */
653: static int
654: eq(bc, pc)
1.2 ! millert 655: register int bc, pc;
1.1 deraadt 656: {
657: bc = CHARMASK(bc);
658: pc = CHARMASK(pc);
1.2 ! millert 659: if (bc == pc)
! 660: return TRUE;
! 661: if (ISUPPER(bc))
! 662: return TOLOWER(bc) == pc;
! 663: if (ISUPPER(pc))
! 664: return bc == TOLOWER(pc);
1.1 deraadt 665: return FALSE;
666: }
667:
668: /*
669: * Read a pattern.
670: * Stash it in the external variable "pat". The "pat" is
671: * not updated if the user types in an empty line. If the user typed
672: * an empty line, and there is no old pattern, it is an error.
673: * Display the old pattern, in the style of Jeff Lomicka. There is
674: * some do-it-yourself control expansion.
675: */
1.2 ! millert 676: readpattern(prompt)
! 677: char *prompt;
! 678: {
! 679: register int s;
! 680: char tpat[NPAT];
1.1 deraadt 681:
1.2 ! millert 682: if (tpat[0] == '\0')
! 683: s = ereply("%s: ", tpat, NPAT, prompt);
! 684: else
! 685: s = ereply("%s: (default %s) ", tpat, NPAT, prompt, pat);
1.1 deraadt 686:
1.2 ! millert 687: if (s == TRUE) /* Specified */
1.1 deraadt 688: (VOID) strcpy(pat, tpat);
1.2 ! millert 689: else if (s == FALSE && pat[0] != 0) /* CR, but old one */
1.1 deraadt 690: s = TRUE;
691: return s;
692: }