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