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