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