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