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