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