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