Annotation of src/usr.bin/mg/search.c, Revision 1.40
1.40 ! lum 1: /* $OpenBSD: search.c,v 1.39 2012/04/12 04:47:59 lum 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: }
1.1 deraadt 248: is_lpush();
249: pptr = strlen(pat);
1.40 ! lum 250: if (forwchar(FFRAND, 1) == FALSE) {
! 251: ttbeep();
! 252: success = FALSE;
! 253: ewprintf("Failed I-search: %s", pat);
! 254: } else {
! 255: if (is_find(SRCH_FORW) != FALSE)
! 256: is_cpush(SRCH_MARK);
! 257: else {
! 258: (void)backchar(FFRAND, 1);
! 259: ttbeep();
! 260: success = FALSE;
! 261: ewprintf("Failed I-search: %s", pat);
! 262: }
1.1 deraadt 263: }
264: is_prompt(dir, pptr < 0, success);
265: break;
266: case CCHR('R'):
267: if (dir == SRCH_FORW) {
268: dir = SRCH_BACK;
269: is_lpush();
270: is_cpush(SRCH_BACK);
271: success = TRUE;
272: }
1.14 cloder 273: if (success == FALSE && dir == SRCH_BACK) {
274: /* wrap the search to end */
1.35 kjell 275: curwp->w_dotp = blastlp(curbp);
276: curwp->w_doto = llength(curwp->w_dotp);
1.30 kjell 277: curwp->w_dotline = curwp->w_bufp->b_lines;
1.14 cloder 278: if (is_find(dir) != FALSE) {
279: is_cpush(SRCH_MARK);
280: success = TRUE;
281: }
1.35 kjell 282: ewprintf("Overwrapped I-search: %s", pat);
1.1 deraadt 283: break;
1.14 cloder 284: }
1.1 deraadt 285: is_lpush();
286: pptr = strlen(pat);
1.40 ! lum 287: if (backchar(FFRAND, 1) == FALSE) {
! 288: ttbeep();
! 289: success = FALSE;
! 290: } else {
! 291: if (is_find(SRCH_BACK) != FALSE)
! 292: is_cpush(SRCH_MARK);
! 293: else {
! 294: (void)forwchar(FFRAND, 1);
! 295: ttbeep();
! 296: success = FALSE;
! 297: }
1.18 cloder 298: }
299: is_prompt(dir, pptr < 0, success);
300: break;
301: case CCHR('W'):
302: /* add the rest of the current word to the pattern */
303: clp = curwp->w_dotp;
304: cbo = curwp->w_doto;
305: firstc = 1;
1.28 otto 306: if (pptr == -1)
307: pptr = 0;
1.18 cloder 308: if (dir == SRCH_BACK) {
309: /* when isearching backwards, cbo is the start of the pattern */
310: cbo += pptr;
311: }
312:
313: /* if the search is case insensitive, add to pattern using lowercase */
314: xcase = 0;
315: for (i = 0; pat[i]; i++)
316: if (ISUPPER(CHARMASK(pat[i])))
317: xcase = 1;
318:
319: while (cbo < llength(clp)) {
320: c = lgetc(clp, cbo++);
1.33 kjell 321: if ((!firstc && !isalnum(c)))
1.18 cloder 322: break;
323:
1.33 kjell 324: if (pptr == NPAT - 1) {
325: ttbeep();
326: break;
327: }
1.18 cloder 328: firstc = 0;
329: if (!xcase && ISUPPER(c))
330: c = TOLOWER(c);
331:
332: pat[pptr++] = c;
333: pat[pptr] = '\0';
334: /* cursor only moves when isearching forwards */
335: if (dir == SRCH_FORW) {
336: curwp->w_doto = cbo;
1.37 kjell 337: curwp->w_rflag |= WFMOVE;
1.18 cloder 338: update();
339: }
1.1 deraadt 340: }
341: is_prompt(dir, pptr < 0, success);
342: break;
343: case CCHR('H'):
344: case CCHR('?'):
345: is_undo(&pptr, &dir);
1.2 millert 346: if (is_peek() != SRCH_ACCM)
347: success = TRUE;
1.1 deraadt 348: is_prompt(dir, pptr < 0, success);
349: break;
350: case CCHR('\\'):
351: case CCHR('Q'):
1.3 millert 352: c = (char)getkey(FALSE);
1.2 millert 353: goto addchar;
1.1 deraadt 354: case CCHR('M'):
355: c = CCHR('J');
1.2 millert 356: goto addchar;
1.1 deraadt 357: default:
358: if (ISCTRL(c)) {
359: ungetkey(c);
360: curwp->w_markp = clp;
361: curwp->w_marko = cbo;
1.34 kjell 362: curwp->w_markline = cdotline;
1.1 deraadt 363: ewprintf("Mark set");
1.37 kjell 364: curwp->w_rflag |= WFMOVE;
1.12 db 365: return (TRUE);
1.27 kjell 366: }
367: /* FALLTHRU */
1.1 deraadt 368: case CCHR('I'):
369: case CCHR('J'):
1.2 millert 370: addchar:
1.1 deraadt 371: if (pptr == -1)
372: pptr = 0;
373: if (pptr == 0)
374: success = TRUE;
1.33 kjell 375: if (pptr == NPAT - 1)
376: ttbeep();
377: else {
378: pat[pptr++] = c;
379: pat[pptr] = '\0';
1.1 deraadt 380: }
381: is_lpush();
382: if (success != FALSE) {
383: if (is_find(dir) != FALSE)
384: is_cpush(c);
385: else {
386: success = FALSE;
387: ttbeep();
388: is_cpush(SRCH_ACCM);
389: }
390: } else
391: is_cpush(SRCH_ACCM);
392: is_prompt(dir, FALSE, success);
393: }
394: }
1.2 millert 395: /* NOTREACHED */
1.1 deraadt 396: }
397:
1.6 art 398: static void
1.10 cloder 399: is_cpush(int cmd)
1.2 millert 400: {
1.1 deraadt 401: if (++cip >= NSRCH)
402: cip = 0;
403: cmds[cip].s_code = cmd;
404: }
405:
1.6 art 406: static void
1.10 cloder 407: is_lpush(void)
1.2 millert 408: {
1.3 millert 409: int ctp;
1.1 deraadt 410:
1.2 millert 411: ctp = cip + 1;
1.1 deraadt 412: if (ctp >= NSRCH)
413: ctp = 0;
414: cmds[ctp].s_code = SRCH_NOPR;
415: cmds[ctp].s_doto = curwp->w_doto;
416: cmds[ctp].s_dotp = curwp->w_dotp;
1.34 kjell 417: cmds[ctp].s_dotline = curwp->w_dotline;
1.1 deraadt 418: }
419:
1.6 art 420: static void
1.10 cloder 421: is_pop(void)
1.2 millert 422: {
1.1 deraadt 423: if (cmds[cip].s_code != SRCH_NOPR) {
1.2 millert 424: curwp->w_doto = cmds[cip].s_doto;
425: curwp->w_dotp = cmds[cip].s_dotp;
1.34 kjell 426: curwp->w_dotline = cmds[cip].s_dotline;
1.37 kjell 427: curwp->w_rflag |= WFMOVE;
1.1 deraadt 428: cmds[cip].s_code = SRCH_NOPR;
429: }
430: if (--cip <= 0)
1.2 millert 431: cip = NSRCH - 1;
1.1 deraadt 432: }
433:
434: static int
1.10 cloder 435: is_peek(void)
1.2 millert 436: {
1.12 db 437: return (cmds[cip].s_code);
1.1 deraadt 438: }
439:
440: /* this used to always return TRUE (the return value was checked) */
1.6 art 441: static void
1.10 cloder 442: is_undo(int *pptr, int *dir)
1.2 millert 443: {
1.3 millert 444: int redo = FALSE;
445:
1.1 deraadt 446: switch (cmds[cip].s_code) {
447: case SRCH_BEGIN:
448: case SRCH_NOPR:
449: *pptr = -1;
1.27 kjell 450: break;
1.1 deraadt 451: case SRCH_MARK:
452: break;
453: case SRCH_FORW:
454: *dir = SRCH_BACK;
455: redo = TRUE;
456: break;
457: case SRCH_BACK:
458: *dir = SRCH_FORW;
459: redo = TRUE;
460: break;
461: case SRCH_ACCM:
462: default:
463: *pptr -= 1;
464: if (*pptr < 0)
465: *pptr = 0;
466: pat[*pptr] = '\0';
467: break;
468: }
469: is_pop();
1.2 millert 470: if (redo)
471: is_undo(pptr, dir);
1.1 deraadt 472: }
473:
474: static int
1.10 cloder 475: is_find(int dir)
1.2 millert 476: {
1.34 kjell 477: int plen, odoto, odotline;
1.25 deraadt 478: struct line *odotp;
1.1 deraadt 479:
480: odoto = curwp->w_doto;
481: odotp = curwp->w_dotp;
1.34 kjell 482: odotline = curwp->w_dotline;
1.1 deraadt 483: plen = strlen(pat);
484: if (plen != 0) {
1.2 millert 485: if (dir == SRCH_FORW) {
1.6 art 486: (void)backchar(FFARG | FFRAND, plen);
1.1 deraadt 487: if (forwsrch() == FALSE) {
488: curwp->w_doto = odoto;
489: curwp->w_dotp = odotp;
1.34 kjell 490: curwp->w_dotline = odotline;
1.12 db 491: return (FALSE);
1.1 deraadt 492: }
1.12 db 493: return (TRUE);
1.1 deraadt 494: }
1.2 millert 495: if (dir == SRCH_BACK) {
1.6 art 496: (void)forwchar(FFARG | FFRAND, plen);
1.1 deraadt 497: if (backsrch() == FALSE) {
498: curwp->w_doto = odoto;
499: curwp->w_dotp = odotp;
1.34 kjell 500: curwp->w_dotline = odotline;
1.12 db 501: return (FALSE);
1.1 deraadt 502: }
1.12 db 503: return (TRUE);
1.1 deraadt 504: }
505: ewprintf("bad call to is_find");
1.12 db 506: return (FALSE);
1.1 deraadt 507: }
1.12 db 508: return (FALSE);
1.1 deraadt 509: }
510:
511: /*
1.7 mickey 512: * If called with "dir" not one of SRCH_FORW or SRCH_BACK, this routine used
513: * to print an error message. It also used to return TRUE or FALSE, depending
514: * on if it liked the "dir". However, none of the callers looked at the
1.3 millert 515: * status, so I just made the checking vanish.
1.1 deraadt 516: */
1.6 art 517: static void
1.10 cloder 518: is_prompt(int dir, int flag, int success)
1.2 millert 519: {
1.1 deraadt 520: if (dir == SRCH_FORW) {
521: if (success != FALSE)
522: is_dspl("I-search", flag);
523: else
524: is_dspl("Failing I-search", flag);
525: } else if (dir == SRCH_BACK) {
526: if (success != FALSE)
527: is_dspl("I-search backward", flag);
528: else
529: is_dspl("Failing I-search backward", flag);
1.2 millert 530: } else
531: ewprintf("Broken call to is_prompt");
1.1 deraadt 532: }
533:
534: /*
1.7 mickey 535: * Prompt writing routine for the incremental search. The "prompt" is just
1.3 millert 536: * a string. The "flag" determines whether pat should be printed.
1.1 deraadt 537: */
1.6 art 538: static void
1.10 cloder 539: is_dspl(char *prompt, int flag)
1.2 millert 540: {
1.1 deraadt 541: if (flag != FALSE)
542: ewprintf("%s: ", prompt);
543: else
544: ewprintf("%s: %s", prompt, pat);
545: }
546:
547: /*
548: * Query Replace.
549: * Replace strings selectively. Does a search and replace operation.
550: */
1.2 millert 551: /* ARGSUSED */
1.3 millert 552: int
1.10 cloder 553: queryrepl(int f, int n)
1.1 deraadt 554: {
1.3 millert 555: int s;
556: int rcnt = 0; /* replacements made so far */
557: int plen; /* length of found string */
1.11 vincent 558: char news[NPAT], *rep; /* replacement string */
1.1 deraadt 559:
1.2 millert 560: if (macrodef) {
561: ewprintf("Can't query replace in macro");
1.12 db 562: return (FALSE);
1.1 deraadt 563: }
1.3 millert 564:
1.2 millert 565: if ((s = readpattern("Query replace")) != TRUE)
1.1 deraadt 566: return (s);
1.20 kjell 567: if ((rep = eread("Query replace %s with: ", news, NPAT,
568: EFNUL | EFNEW | EFCR, pat)) == NULL)
1.12 db 569: return (ABORT);
1.11 vincent 570: else if (rep[0] == '\0')
1.1 deraadt 571: news[0] = '\0';
572: ewprintf("Query replacing %s with %s:", pat, news);
573: plen = strlen(pat);
574:
575: /*
576: * Search forward repeatedly, checking each time whether to insert
577: * or not. The "!" case makes the check always true, so it gets put
578: * into a tighter loop for efficiency.
579: */
580: while (forwsrch() == TRUE) {
1.2 millert 581: retry:
1.1 deraadt 582: update();
583: switch (getkey(FALSE)) {
1.23 kjell 584: case 'y':
1.1 deraadt 585: case ' ':
1.24 kjell 586: if (lreplace((RSIZE)plen, news) == FALSE)
1.1 deraadt 587: return (FALSE);
588: rcnt++;
589: break;
590: case '.':
1.24 kjell 591: if (lreplace((RSIZE)plen, news) == FALSE)
1.1 deraadt 592: return (FALSE);
593: rcnt++;
594: goto stopsearch;
1.23 kjell 595: /* ^G, CR or ESC */
1.3 millert 596: case CCHR('G'):
1.6 art 597: (void)ctrlg(FFRAND, 0);
1.27 kjell 598: goto stopsearch;
1.1 deraadt 599: case CCHR('['):
1.23 kjell 600: case CCHR('M'):
1.1 deraadt 601: goto stopsearch;
602: case '!':
603: do {
1.24 kjell 604: if (lreplace((RSIZE)plen, news) == FALSE)
1.1 deraadt 605: return (FALSE);
606: rcnt++;
607: } while (forwsrch() == TRUE);
608: goto stopsearch;
1.23 kjell 609: case 'n':
1.1 deraadt 610: case CCHR('H'):
1.3 millert 611: /* To not replace */
612: case CCHR('?'):
1.1 deraadt 613: break;
614: default:
1.23 kjell 615: ewprintf("y/n or <SP>/<DEL>: replace/don't, [.] repl-end, [!] repl-rest, <CR>/<ESC> quit");
1.1 deraadt 616: goto retry;
617: }
618: }
619: stopsearch:
1.37 kjell 620: curwp->w_rflag |= WFFULL;
1.1 deraadt 621: update();
1.23 kjell 622: if (rcnt == 1)
623: ewprintf("Replaced 1 occurrence");
1.1 deraadt 624: else
1.23 kjell 625: ewprintf("Replaced %d occurrences", rcnt);
1.17 cloder 626: return (TRUE);
627: }
628:
629: /*
630: * Replace string globally without individual prompting.
631: */
632: /* ARGSUSED */
633: int
634: replstr(int f, int n)
635: {
636: char news[NPAT];
637: int s, plen, rcnt = 0;
638: char *r;
639:
640: if ((s = readpattern("Replace string")) != TRUE)
641: return s;
642:
1.20 kjell 643: r = eread("Replace string %s with: ", news, NPAT,
644: EFNUL | EFNEW | EFCR, pat);
1.17 cloder 645: if (r == NULL)
646: return (ABORT);
647:
648: plen = strlen(pat);
649: while (forwsrch() == TRUE) {
650: update();
1.24 kjell 651: if (lreplace((RSIZE)plen, news) == FALSE)
1.17 cloder 652: return (FALSE);
653:
654: rcnt++;
655: }
656:
1.37 kjell 657: curwp->w_rflag |= WFFULL;
1.17 cloder 658: update();
659:
660: if (rcnt == 1)
1.23 kjell 661: ewprintf("Replaced 1 occurrence");
1.17 cloder 662: else
1.23 kjell 663: ewprintf("Replaced %d occurrences", rcnt);
1.17 cloder 664:
1.12 db 665: return (TRUE);
1.1 deraadt 666: }
667:
668: /*
1.7 mickey 669: * This routine does the real work of a forward search. The pattern is sitting
1.3 millert 670: * in the external variable "pat". If found, dot is updated, the window system
1.7 mickey 671: * is notified of the change, and TRUE is returned. If the string isn't found,
1.3 millert 672: * FALSE is returned.
1.1 deraadt 673: */
1.3 millert 674: int
1.10 cloder 675: forwsrch(void)
1.2 millert 676: {
1.25 deraadt 677: struct line *clp, *tlp;
1.13 jason 678: int cbo, tbo, c, i, xcase = 0;
1.3 millert 679: char *pp;
1.30 kjell 680: int nline;
1.1 deraadt 681:
682: clp = curwp->w_dotp;
683: cbo = curwp->w_doto;
1.30 kjell 684: nline = curwp->w_dotline;
1.13 jason 685: for (i = 0; pat[i]; i++)
686: if (ISUPPER(CHARMASK(pat[i])))
687: xcase = 1;
1.2 millert 688: for (;;) {
1.1 deraadt 689: if (cbo == llength(clp)) {
1.31 kjell 690: if ((clp = lforw(clp)) == curbp->b_headp)
1.2 millert 691: break;
1.30 kjell 692: nline++;
1.1 deraadt 693: cbo = 0;
694: c = CCHR('J');
695: } else
696: c = lgetc(clp, cbo++);
1.13 jason 697: if (eq(c, pat[0], xcase) != FALSE) {
1.1 deraadt 698: tlp = clp;
699: tbo = cbo;
1.2 millert 700: pp = &pat[1];
1.1 deraadt 701: while (*pp != 0) {
702: if (tbo == llength(tlp)) {
703: tlp = lforw(tlp);
1.31 kjell 704: if (tlp == curbp->b_headp)
1.1 deraadt 705: goto fail;
706: tbo = 0;
707: c = CCHR('J');
1.30 kjell 708: if (eq(c, *pp++, xcase) == FALSE)
709: goto fail;
710: nline++;
711: } else {
1.1 deraadt 712: c = lgetc(tlp, tbo++);
1.30 kjell 713: if (eq(c, *pp++, xcase) == FALSE)
714: goto fail;
715: }
1.1 deraadt 716: }
1.2 millert 717: curwp->w_dotp = tlp;
718: curwp->w_doto = tbo;
1.30 kjell 719: curwp->w_dotline = nline;
1.37 kjell 720: curwp->w_rflag |= WFMOVE;
1.12 db 721: return (TRUE);
1.1 deraadt 722: }
1.2 millert 723: fail: ;
1.1 deraadt 724: }
1.12 db 725: return (FALSE);
1.1 deraadt 726: }
727:
728: /*
1.7 mickey 729: * This routine does the real work of a backward search. The pattern is
730: * sitting in the external variable "pat". If found, dot is updated, the
1.3 millert 731: * window system is notified of the change, and TRUE is returned. If the
1.1 deraadt 732: * string isn't found, FALSE is returned.
733: */
1.3 millert 734: int
1.10 cloder 735: backsrch(void)
1.2 millert 736: {
1.25 deraadt 737: struct line *clp, *tlp;
1.13 jason 738: int cbo, tbo, c, i, xcase = 0;
1.3 millert 739: char *epp, *pp;
1.30 kjell 740: int nline;
1.1 deraadt 741:
1.2 millert 742: for (epp = &pat[0]; epp[1] != 0; ++epp);
1.1 deraadt 743: clp = curwp->w_dotp;
744: cbo = curwp->w_doto;
1.30 kjell 745: nline = curwp->w_dotline;
1.13 jason 746: for (i = 0; pat[i]; i++)
747: if (ISUPPER(CHARMASK(pat[i])))
748: xcase = 1;
1.1 deraadt 749: for (;;) {
750: if (cbo == 0) {
751: clp = lback(clp);
1.31 kjell 752: if (clp == curbp->b_headp)
1.12 db 753: return (FALSE);
1.30 kjell 754: nline--;
1.2 millert 755: cbo = llength(clp) + 1;
1.1 deraadt 756: }
757: if (--cbo == llength(clp))
758: c = CCHR('J');
759: else
1.2 millert 760: c = lgetc(clp, cbo);
1.13 jason 761: if (eq(c, *epp, xcase) != FALSE) {
1.1 deraadt 762: tlp = clp;
763: tbo = cbo;
1.2 millert 764: pp = epp;
1.1 deraadt 765: while (pp != &pat[0]) {
766: if (tbo == 0) {
767: tlp = lback(tlp);
1.31 kjell 768: if (tlp == curbp->b_headp)
1.1 deraadt 769: goto fail;
1.30 kjell 770: nline--;
1.2 millert 771: tbo = llength(tlp) + 1;
1.1 deraadt 772: }
773: if (--tbo == llength(tlp))
774: c = CCHR('J');
775: else
1.2 millert 776: c = lgetc(tlp, tbo);
1.13 jason 777: if (eq(c, *--pp, xcase) == FALSE)
1.1 deraadt 778: goto fail;
779: }
1.2 millert 780: curwp->w_dotp = tlp;
781: curwp->w_doto = tbo;
1.30 kjell 782: curwp->w_dotline = nline;
1.37 kjell 783: curwp->w_rflag |= WFMOVE;
1.12 db 784: return (TRUE);
1.1 deraadt 785: }
1.2 millert 786: fail: ;
1.1 deraadt 787: }
1.2 millert 788: /* NOTREACHED */
1.1 deraadt 789: }
790:
791: /*
1.7 mickey 792: * Compare two characters. The "bc" comes from the buffer. It has its case
1.3 millert 793: * folded out. The "pc" is from the pattern.
1.1 deraadt 794: */
795: static int
1.13 jason 796: eq(int bc, int pc, int xcase)
1.1 deraadt 797: {
798: bc = CHARMASK(bc);
799: pc = CHARMASK(pc);
1.2 millert 800: if (bc == pc)
1.12 db 801: return (TRUE);
1.13 jason 802: if (xcase)
803: return (FALSE);
1.2 millert 804: if (ISUPPER(bc))
1.12 db 805: return (TOLOWER(bc) == pc);
1.2 millert 806: if (ISUPPER(pc))
1.12 db 807: return (bc == TOLOWER(pc));
808: return (FALSE);
1.1 deraadt 809: }
810:
811: /*
1.7 mickey 812: * Read a pattern. Stash it in the external variable "pat". The "pat" is not
813: * updated if the user types in an empty line. If the user typed an empty
814: * line, and there is no old pattern, it is an error. Display the old pattern,
815: * in the style of Jeff Lomicka. There is some do-it-yourself control
1.3 millert 816: * expansion.
1.1 deraadt 817: */
1.3 millert 818: int
1.10 cloder 819: readpattern(char *prompt)
1.2 millert 820: {
1.11 vincent 821: char tpat[NPAT], *rep;
1.12 db 822: int retval;
1.1 deraadt 823:
1.16 cloder 824: if (pat[0] == '\0')
1.22 kjell 825: rep = eread("%s: ", tpat, NPAT, EFNEW | EFCR, prompt);
1.2 millert 826: else
1.19 kjell 827: rep = eread("%s: (default %s) ", tpat, NPAT,
828: EFNUL | EFNEW | EFCR, prompt, pat);
1.1 deraadt 829:
1.3 millert 830: /* specified */
1.15 cloder 831: if (rep == NULL) {
832: retval = ABORT;
1.22 kjell 833: } else if (rep[0] != '\0') {
834: (void)strlcpy(pat, tpat, sizeof(pat));
1.11 vincent 835: retval = TRUE;
1.15 cloder 836: } else if (pat[0] != '\0') {
1.11 vincent 837: retval = TRUE;
838: } else
839: retval = FALSE;
1.12 db 840: return (retval);
1.1 deraadt 841: }