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