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