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