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