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