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