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