1 /* $OpenBSD: search.c,v 1.2 2001/01/29 01:58:04 niklas Exp $ */ 2 3 /* 4 * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice in the documentation and/or other materials provided with 14 * the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 22 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 30 /* 31 * Routines to search a file for a pattern. 32 */ 33 34 #include "less.h" 35 #include "position.h" 36 37 #define MINPOS(a,b) (((a) < (b)) ? (a) : (b)) 38 #define MAXPOS(a,b) (((a) > (b)) ? (a) : (b)) 39 40 #if HAVE_POSIX_REGCOMP 41 #include <regex.h> 42 #endif 43 #if HAVE_RE_COMP 44 char *re_comp(); 45 int re_exec(); 46 #endif 47 #if HAVE_REGCMP 48 char *regcmp(); 49 char *regex(); 50 extern char *__loc1; 51 #endif 52 #if HAVE_V8_REGCOMP 53 #include "regexp.h" 54 #endif 55 #if NO_REGEX 56 static int match(); 57 #endif 58 59 extern int sigs; 60 extern int how_search; 61 extern int caseless; 62 extern int linenums; 63 extern int sc_height; 64 extern int jump_sline; 65 extern int bs_mode; 66 #if HILITE_SEARCH 67 extern int hilite_search; 68 extern int screen_trashed; 69 extern int size_linebuf; 70 static int hide_hilite; 71 static POSITION prep_startpos; 72 static POSITION prep_endpos; 73 74 struct hilite 75 { 76 struct hilite *hl_next; 77 POSITION hl_startpos; 78 POSITION hl_endpos; 79 }; 80 static struct hilite hilite_anchor = { NULL }; 81 #define hl_first hl_next 82 #endif 83 84 /* 85 * These are the static variables that represent the "remembered" 86 * search pattern. 87 */ 88 #if HAVE_POSIX_REGCOMP 89 static regex_t *regpattern = NULL; 90 #endif 91 #if HAVE_RE_COMP 92 int re_pattern = 0; 93 #endif 94 #if HAVE_REGCMP 95 static char *cpattern = NULL; 96 #endif 97 #if HAVE_V8_REGCOMP 98 static struct regexp *regpattern = NULL; 99 #endif 100 #if NO_REGEX 101 static char *last_pattern = NULL; 102 #endif 103 104 static int is_caseless; 105 static int is_ucase_pattern; 106 107 /* 108 * Convert text. Perform one or more of these transformations: 109 */ 110 #define CVT_TO_LC 01 /* Convert upper-case to lower-case */ 111 #define CVT_BS 02 /* Do backspace processing */ 112 113 static void 114 cvt_text(odst, osrc, ops) 115 char *odst; 116 char *osrc; 117 int ops; 118 { 119 register char *dst; 120 register char *src; 121 122 for (src = osrc, dst = odst; *src != '\0'; src++, dst++) 123 { 124 if ((ops & CVT_TO_LC) && isupper(*src)) 125 /* Convert uppercase to lowercase. */ 126 *dst = tolower(*src); 127 else if ((ops & CVT_BS) && *src == '\b' && dst > odst) 128 /* Delete BS and preceding char. */ 129 dst -= 2; 130 else 131 /* Just copy. */ 132 *dst = *src; 133 } 134 *dst = '\0'; 135 } 136 137 /* 138 * Are there any uppercase letters in this string? 139 */ 140 static int 141 is_ucase(s) 142 char *s; 143 { 144 register char *p; 145 146 for (p = s; *p != '\0'; p++) 147 if (isupper(*p)) 148 return (1); 149 return (0); 150 } 151 152 /* 153 * Is there a previous (remembered) search pattern? 154 */ 155 static int 156 prev_pattern() 157 { 158 #if HAVE_POSIX_REGCOMP 159 return (regpattern != NULL); 160 #endif 161 #if HAVE_RE_COMP 162 return (re_pattern != 0); 163 #endif 164 #if HAVE_REGCMP 165 return (cpattern != NULL); 166 #endif 167 #if HAVE_V8_REGCOMP 168 return (regpattern != NULL); 169 #endif 170 #if NO_REGEX 171 return (last_pattern != NULL); 172 #endif 173 } 174 175 #if HILITE_SEARCH 176 /* 177 * Repaint the hilites currently displayed on the screen. 178 * Repaint each line which contains highlighted text. 179 * If on==0, force all hilites off. 180 */ 181 public void 182 repaint_hilite(on) 183 int on; 184 { 185 int slinenum; 186 POSITION pos; 187 POSITION epos; 188 int save_hide_hilite; 189 extern int can_goto_line; 190 191 save_hide_hilite = hide_hilite; 192 if (!on) 193 { 194 if (hide_hilite) 195 return; 196 hide_hilite = 1; 197 } 198 199 if (!can_goto_line) 200 { 201 repaint(); 202 hide_hilite = save_hide_hilite; 203 return; 204 } 205 206 for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) 207 { 208 pos = position(slinenum); 209 if (pos == NULL_POSITION) 210 continue; 211 epos = position(slinenum+1); 212 /* 213 * If any character in the line is highlighted, 214 * repaint the line. 215 */ 216 if (is_hilited(pos, epos, 1)) 217 { 218 (void) forw_line(pos); 219 goto_line(slinenum); 220 put_line(); 221 } 222 } 223 hide_hilite = save_hide_hilite; 224 } 225 #endif 226 227 /* 228 * Hide search string highlighting. 229 */ 230 public void 231 undo_search() 232 { 233 if (!prev_pattern()) 234 { 235 error("No previous regular expression", NULL_PARG); 236 return; 237 } 238 #if HILITE_SEARCH 239 hide_hilite = !hide_hilite; 240 repaint_hilite(1); 241 #endif 242 } 243 244 /* 245 * Compile a search pattern, for future use by match_pattern. 246 */ 247 static int 248 compile_pattern(pattern) 249 char *pattern; 250 { 251 #if HAVE_POSIX_REGCOMP 252 regex_t *s = (regex_t *) ecalloc(1, sizeof(regex_t)); 253 if (regcomp(s, pattern, 0)) 254 { 255 free(s); 256 error("Invalid pattern", NULL_PARG); 257 return (-1); 258 } 259 if (regpattern != NULL) 260 regfree(regpattern); 261 regpattern = s; 262 #endif 263 #if HAVE_RE_COMP 264 PARG parg; 265 if ((parg.p_string = re_comp(pattern)) != NULL) 266 { 267 error("%s", &parg); 268 return (-1); 269 } 270 re_pattern = 1; 271 #endif 272 #if HAVE_REGCMP 273 char *s; 274 if ((s = regcmp(pattern, 0)) == NULL) 275 { 276 error("Invalid pattern", NULL_PARG); 277 return (-1); 278 } 279 if (cpattern != NULL) 280 free(cpattern); 281 cpattern = s; 282 #endif 283 #if HAVE_V8_REGCOMP 284 struct regexp *s; 285 if ((s = regcomp(pattern)) == NULL) 286 { 287 /* 288 * regcomp has already printed error message via regerror(). 289 */ 290 return (-1); 291 } 292 if (regpattern != NULL) 293 free(regpattern); 294 regpattern = s; 295 #endif 296 #if NO_REGEX 297 static char lpbuf[100]; 298 strcpy(lpbuf, pattern); 299 last_pattern = lpbuf; 300 #endif 301 return (0); 302 } 303 304 /* 305 * Forget that we have a compiled pattern. 306 */ 307 static void 308 uncompile_pattern() 309 { 310 #if HAVE_POSIX_REGCOMP 311 if (regpattern != NULL) 312 regfree(regpattern); 313 regpattern = NULL; 314 #endif 315 #if HAVE_RE_COMP 316 re_pattern = 0; 317 #endif 318 #if HAVE_REGCMP 319 if (cpattern != NULL) 320 free(cpattern); 321 cpattern = NULL; 322 #endif 323 #if HAVE_V8_REGCOMP 324 if (regpattern != NULL) 325 free(regpattern); 326 regpattern = NULL; 327 #endif 328 #if NO_REGEX 329 last_pattern = NULL; 330 #endif 331 } 332 333 /* 334 * Perform a pattern match with the previously compiled pattern. 335 * Set sp and ep to the start and end of the matched string. 336 */ 337 static int 338 match_pattern(line, sp, ep) 339 char *line; 340 char **sp; 341 char **ep; 342 { 343 int matched; 344 #if HAVE_POSIX_REGCOMP 345 regmatch_t rm; 346 matched = !regexec(regpattern, line, 1, &rm, 0); 347 if (!matched) 348 return (0); 349 *sp = line + rm.rm_so; 350 *ep = line + rm.rm_eo; 351 #endif 352 #if HAVE_RE_COMP 353 matched = (re_exec(line) == 1); 354 /* 355 * re_exec doesn't seem to provide a way to get the matched string. 356 */ 357 *sp = *ep = NULL; 358 #endif 359 #if HAVE_REGCMP 360 *ep = regex(cpattern, line); 361 matched = (*ep != NULL); 362 if (!matched) 363 return (0); 364 *sp = __loc1; 365 #endif 366 #if HAVE_V8_REGCOMP 367 matched = regexec(regpattern, line); 368 if (!matched) 369 return (0); 370 *sp = regpattern->startp[0]; 371 *ep = regpattern->endp[0]; 372 #endif 373 #if NO_REGEX 374 matched = match(last_pattern, line, sp, ep); 375 #endif 376 return (matched); 377 } 378 379 #if HILITE_SEARCH 380 /* 381 * Clear the hilite list. 382 */ 383 public void 384 clr_hilite() 385 { 386 struct hilite *hl; 387 struct hilite *nexthl; 388 389 for (hl = hilite_anchor.hl_first; hl != NULL; hl = nexthl) 390 { 391 nexthl = hl->hl_next; 392 free((void*)hl); 393 } 394 hilite_anchor.hl_first = NULL; 395 prep_startpos = prep_endpos = NULL_POSITION; 396 } 397 398 /* 399 * Should any characters in a specified range be highlighted? 400 * If nohide is nonzero, don't consider hide_hilite. 401 */ 402 public int 403 is_hilited(pos, epos, nohide) 404 POSITION pos; 405 POSITION epos; 406 int nohide; 407 { 408 struct hilite *hl; 409 410 if (hilite_search == 0) 411 /* 412 * Not doing highlighting. 413 */ 414 return (0); 415 416 if (!nohide && hide_hilite) 417 /* 418 * Highlighting is hidden. 419 */ 420 return (0); 421 422 /* 423 * Look at each highlight and see if any part of it falls in the range. 424 */ 425 for (hl = hilite_anchor.hl_first; hl != NULL; hl = hl->hl_next) 426 { 427 if (hl->hl_endpos > pos && 428 (epos == NULL_POSITION || epos > hl->hl_startpos)) 429 return (1); 430 } 431 return (0); 432 } 433 434 /* 435 * Add a new hilite to a hilite list. 436 */ 437 static void 438 add_hilite(anchor, hl) 439 struct hilite *anchor; 440 struct hilite *hl; 441 { 442 struct hilite *ihl; 443 444 /* 445 * Hilites are sorted in the list; find where new one belongs. 446 * Insert new one after ihl. 447 */ 448 for (ihl = anchor; ihl->hl_next != NULL; ihl = ihl->hl_next) 449 { 450 if (ihl->hl_next->hl_startpos > hl->hl_startpos) 451 break; 452 } 453 454 /* 455 * Truncate hilite so it doesn't overlap any existing ones 456 * above and below it. 457 */ 458 if (ihl != anchor) 459 hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos); 460 if (ihl->hl_next != NULL) 461 hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos); 462 if (hl->hl_startpos >= hl->hl_endpos) 463 { 464 /* 465 * Hilite was truncated out of existence. 466 */ 467 free(hl); 468 return; 469 } 470 hl->hl_next = ihl->hl_next; 471 ihl->hl_next = hl; 472 } 473 474 /* 475 * Adjust hl_startpos & hl_endpos to account for backspace processing. 476 */ 477 static void 478 adj_hilite(anchor, linepos) 479 struct hilite *anchor; 480 POSITION linepos; 481 { 482 char *line; 483 struct hilite *hl; 484 int checkstart; 485 POSITION opos; 486 POSITION npos; 487 488 /* 489 * The line was already scanned and hilites were added (in hilite_line). 490 * But it was assumed that each char position in the line 491 * correponds to one char position in the file. 492 * This may not be true if there are backspaces in the line. 493 * Get the raw line again. Look at each character. 494 */ 495 (void) forw_raw_line(linepos, &line); 496 opos = npos = linepos; 497 hl = anchor->hl_first; 498 checkstart = TRUE; 499 while (hl != NULL) 500 { 501 /* 502 * See if we need to adjust the current hl_startpos or 503 * hl_endpos. After adjusting startpos[i], move to endpos[i]. 504 * After adjusting endpos[i], move to startpos[i+1]. 505 * The hilite list must be sorted thus: 506 * startpos[0] < endpos[0] <= startpos[1] < endpos[1] <= etc. 507 */ 508 if (checkstart && hl->hl_startpos == opos) 509 { 510 hl->hl_startpos = npos; 511 checkstart = FALSE; 512 continue; /* {{ not really necessary }} */ 513 } else if (!checkstart && hl->hl_endpos == opos) 514 { 515 hl->hl_endpos = npos; 516 checkstart = TRUE; 517 hl = hl->hl_next; 518 continue; /* {{ necessary }} */ 519 } 520 if (*line == '\0') 521 break; 522 opos++; 523 npos++; 524 line++; 525 while (line[0] == '\b' && line[1] != '\0') 526 { 527 /* 528 * Found a backspace. The file position moves 529 * forward by 2 relative to the processed line 530 * which was searched in hilite_line. 531 */ 532 npos += 2; 533 line += 2; 534 } 535 } 536 } 537 538 /* 539 * Make a hilite for each string in a physical line which matches 540 * the current pattern. 541 * sp,ep delimit the first match already found. 542 */ 543 static void 544 hilite_line(linepos, line, sp, ep) 545 POSITION linepos; 546 char *line; 547 char *sp; 548 char *ep; 549 { 550 char *searchp; 551 struct hilite *hl; 552 struct hilite hilites; 553 554 if (sp == NULL || ep == NULL) 555 return; 556 /* 557 * sp and ep delimit the first match in the line. 558 * Mark the corresponding file positions, then 559 * look for further matches and mark them. 560 * {{ This technique, of calling match_pattern on subsequent 561 * substrings of the line, may mark more than is correct 562 * if, for example, the pattern starts with "^". }} 563 */ 564 searchp = line; 565 /* 566 * Put the hilites into a temporary list until they're adjusted. 567 */ 568 hilites.hl_first = NULL; 569 do { 570 if (ep > sp) 571 { 572 /* 573 * Assume that each char position in the "line" 574 * buffer corresponds to one char position in the file. 575 * This is not quite true; we need to adjust later. 576 */ 577 hl = (struct hilite *) ecalloc(1, sizeof(struct hilite)); 578 hl->hl_startpos = linepos + (sp-line); 579 hl->hl_endpos = linepos + (ep-line); 580 add_hilite(&hilites, hl); 581 } 582 /* 583 * If we matched more than zero characters, 584 * move to the first char after the string we matched. 585 * If we matched zero, just move to the next char. 586 */ 587 if (ep > searchp) 588 searchp = ep; 589 else if (*searchp != '\0') 590 searchp++; 591 else /* end of line */ 592 break; 593 } while (match_pattern(searchp, &sp, &ep)); 594 595 if (bs_mode == BS_SPECIAL) 596 { 597 /* 598 * If there were backspaces in the original line, they 599 * were removed, and hl_startpos/hl_endpos are not correct. 600 * {{ This is very ugly. }} 601 */ 602 adj_hilite(&hilites, linepos); 603 } 604 /* 605 * Now put the hilites into the real list. 606 */ 607 while ((hl = hilites.hl_next) != NULL) 608 { 609 hilites.hl_next = hl->hl_next; 610 add_hilite(&hilite_anchor, hl); 611 } 612 } 613 #endif 614 615 /* 616 * Change the caseless-ness of searches. 617 * Updates the internal search state to reflect a change in the -i flag. 618 */ 619 public void 620 chg_caseless() 621 { 622 if (!is_ucase_pattern) 623 /* 624 * Pattern did not have uppercase. 625 * Just set the search caselessness to the global caselessness. 626 */ 627 is_caseless = caseless; 628 else 629 /* 630 * Pattern did have uppercase. 631 * Discard the pattern; we can't change search caselessness now. 632 */ 633 uncompile_pattern(); 634 } 635 636 #if HILITE_SEARCH 637 /* 638 * Find matching text which is currently on screen and highlight it. 639 */ 640 static void 641 hilite_screen() 642 { 643 struct scrpos scrpos; 644 645 get_scrpos(&scrpos); 646 if (scrpos.pos == NULL_POSITION) 647 return; 648 prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE)); 649 repaint_hilite(1); 650 } 651 652 /* 653 * Change highlighting parameters. 654 */ 655 public void 656 chg_hilite() 657 { 658 /* 659 * Erase any highlights currently on screen. 660 */ 661 clr_hilite(); 662 hide_hilite = 0; 663 664 if (hilite_search == OPT_ONPLUS) 665 /* 666 * Display highlights. 667 */ 668 hilite_screen(); 669 } 670 #endif 671 672 /* 673 * Figure out where to start a search. 674 */ 675 static POSITION 676 search_pos(search_type) 677 int search_type; 678 { 679 POSITION pos; 680 int linenum; 681 682 if (empty_screen()) 683 { 684 /* 685 * Start at the beginning (or end) of the file. 686 * The empty_screen() case is mainly for 687 * command line initiated searches; 688 * for example, "+/xyz" on the command line. 689 * Also for multi-file (SRCH_PAST_EOF) searches. 690 */ 691 if (search_type & SRCH_FORW) 692 { 693 return (ch_zero()); 694 } else 695 { 696 pos = ch_length(); 697 if (pos == NULL_POSITION) 698 { 699 (void) ch_end_seek(); 700 pos = ch_length(); 701 } 702 return (pos); 703 } 704 } 705 if (how_search) 706 { 707 /* 708 * Search does not include current screen. 709 */ 710 if (search_type & SRCH_FORW) 711 linenum = BOTTOM_PLUS_ONE; 712 else 713 linenum = TOP; 714 pos = position(linenum); 715 } else 716 { 717 /* 718 * Search includes current screen. 719 * It starts at the jump target (if searching backwards), 720 * or at the jump target plus one (if forwards). 721 */ 722 linenum = adjsline(jump_sline); 723 pos = position(linenum); 724 if (search_type & SRCH_FORW) 725 pos = forw_raw_line(pos, (char **)NULL); 726 } 727 return (pos); 728 } 729 730 /* 731 * Search a subset of the file, specified by start/end position. 732 */ 733 static int 734 search_range(pos, endpos, search_type, n, plinepos, pendpos) 735 POSITION pos; 736 POSITION endpos; 737 int search_type; 738 int n; 739 POSITION *plinepos; 740 POSITION *pendpos; 741 { 742 char *line; 743 int linenum; 744 char *sp, *ep; 745 int line_match; 746 POSITION linepos, oldpos; 747 748 linenum = find_linenum(pos); 749 oldpos = pos; 750 for (;;) 751 { 752 /* 753 * Get lines until we find a matching one or until 754 * we hit end-of-file (or beginning-of-file if we're 755 * going backwards), or until we hit the end position. 756 */ 757 if (ABORT_SIGS()) 758 { 759 /* 760 * A signal aborts the search. 761 */ 762 return (-1); 763 } 764 765 if (endpos != NULL_POSITION && pos >= endpos) 766 { 767 /* 768 * Reached end position without a match. 769 */ 770 if (pendpos != NULL) 771 *pendpos = pos; 772 return (n); 773 } 774 775 if (search_type & SRCH_FORW) 776 { 777 /* 778 * Read the next line, and save the 779 * starting position of that line in linepos. 780 */ 781 linepos = pos; 782 pos = forw_raw_line(pos, &line); 783 if (linenum != 0) 784 linenum++; 785 } else 786 { 787 /* 788 * Read the previous line and save the 789 * starting position of that line in linepos. 790 */ 791 pos = back_raw_line(pos, &line); 792 linepos = pos; 793 if (linenum != 0) 794 linenum--; 795 } 796 797 if (pos == NULL_POSITION) 798 { 799 /* 800 * Reached EOF/BOF without a match. 801 */ 802 if (pendpos != NULL) 803 *pendpos = NULL_POSITION; 804 return (n); 805 } 806 807 /* 808 * If we're using line numbers, we might as well 809 * remember the information we have now (the position 810 * and line number of the current line). 811 * Don't do it for every line because it slows down 812 * the search. Remember the line number only if 813 * we're "far" from the last place we remembered it. 814 */ 815 if (linenums && abs((int)(pos - oldpos)) > 1024) 816 { 817 add_lnum(linenum, pos); 818 oldpos = pos; 819 } 820 821 /* 822 * If it's a caseless search, convert the line to lowercase. 823 * If we're doing backspace processing, delete backspaces. 824 */ 825 if (is_caseless || bs_mode == BS_SPECIAL) 826 { 827 int ops = 0; 828 if (is_caseless) 829 ops |= CVT_TO_LC; 830 if (bs_mode == BS_SPECIAL) 831 ops |= CVT_BS; 832 cvt_text(line, line, ops); 833 } 834 835 /* 836 * Test the next line to see if we have a match. 837 * We are successful if we either want a match and got one, 838 * or if we want a non-match and got one. 839 */ 840 line_match = match_pattern(line, &sp, &ep); 841 line_match = (!(search_type & SRCH_NOMATCH) && line_match) || 842 ((search_type & SRCH_NOMATCH) && !line_match); 843 if (!line_match) 844 continue; 845 /* 846 * Got a match. 847 */ 848 if (search_type & SRCH_FIND_ALL) 849 { 850 #if HILITE_SEARCH 851 /* 852 * We are supposed to find all matches in the range. 853 * Just add the matches in this line to the 854 * hilite list and keep searching. 855 */ 856 if (line_match) 857 hilite_line(linepos, line, sp, ep); 858 #endif 859 } else if (--n <= 0) 860 { 861 /* 862 * Found the one match we're looking for. 863 * Return it. 864 */ 865 #if HILITE_SEARCH 866 if (hilite_search == 1) 867 { 868 /* 869 * Clear the hilite list and add only 870 * the matches in this one line. 871 */ 872 clr_hilite(); 873 if (line_match) 874 hilite_line(linepos, line, sp, ep); 875 } 876 #endif 877 if (plinepos != NULL) 878 *plinepos = linepos; 879 return (0); 880 } 881 } 882 } 883 884 /* 885 * Search for the n-th occurrence of a specified pattern, 886 * either forward or backward. 887 * Return the number of matches not yet found in this file 888 * (that is, n minus the number of matches found). 889 * Return -1 if the search should be aborted. 890 * Caller may continue the search in another file 891 * if less than n matches are found in this file. 892 */ 893 public int 894 search(search_type, pattern, n) 895 int search_type; 896 char *pattern; 897 int n; 898 { 899 POSITION pos; 900 int ucase; 901 902 if (pattern == NULL || *pattern == '\0') 903 { 904 /* 905 * A null pattern means use the previously compiled pattern. 906 */ 907 if (!prev_pattern()) 908 { 909 error("No previous regular expression", NULL_PARG); 910 return (-1); 911 } 912 #if HILITE_SEARCH 913 if (hilite_search == OPT_ON) 914 { 915 /* 916 * Erase the highlights currently on screen. 917 * If the search fails, we'll redisplay them later. 918 */ 919 repaint_hilite(0); 920 } 921 if (hilite_search == OPT_ONPLUS && hide_hilite) 922 { 923 /* 924 * Highlight any matches currently on screen, 925 * before we actually start the search. 926 */ 927 hide_hilite = 0; 928 hilite_screen(); 929 } 930 hide_hilite = 0; 931 #endif 932 } else 933 { 934 /* 935 * Compile the pattern. 936 */ 937 ucase = is_ucase(pattern); 938 if (caseless == OPT_ONPLUS) 939 cvt_text(pattern, pattern, CVT_TO_LC); 940 if (compile_pattern(pattern) < 0) 941 return (-1); 942 /* 943 * Ignore case if -I is set OR 944 * -i is set AND the pattern is all lowercase. 945 */ 946 is_ucase_pattern = ucase; 947 if (is_ucase_pattern && caseless != OPT_ONPLUS) 948 is_caseless = 0; 949 else 950 is_caseless = caseless; 951 #if HILITE_SEARCH 952 if (hilite_search) 953 { 954 /* 955 * Erase the highlights currently on screen. 956 * Also permanently delete them from the hilite list. 957 */ 958 repaint_hilite(0); 959 hide_hilite = 0; 960 clr_hilite(); 961 } 962 if (hilite_search == OPT_ONPLUS) 963 { 964 /* 965 * Highlight any matches currently on screen, 966 * before we actually start the search. 967 */ 968 hilite_screen(); 969 } 970 #endif 971 } 972 973 /* 974 * Figure out where to start the search. 975 */ 976 pos = search_pos(search_type); 977 if (pos == NULL_POSITION) 978 { 979 /* 980 * Can't find anyplace to start searching from. 981 */ 982 if (search_type & SRCH_PAST_EOF) 983 return (n); 984 error("Nothing to search", NULL_PARG); 985 return (-1); 986 } 987 988 n = search_range(pos, NULL_POSITION, search_type, n, 989 &pos, (POSITION*)NULL); 990 if (n != 0) 991 { 992 /* 993 * Search was unsuccessful. 994 */ 995 #if HILITE_SEARCH 996 if (hilite_search == OPT_ON && n > 0) 997 /* 998 * Redisplay old hilites. 999 */ 1000 repaint_hilite(1); 1001 #endif 1002 return (n); 1003 } 1004 1005 /* 1006 * Go to the matching line. 1007 */ 1008 jump_loc(pos, jump_sline); 1009 1010 #if HILITE_SEARCH 1011 if (hilite_search == OPT_ON) 1012 /* 1013 * Display new hilites in the matching line. 1014 */ 1015 repaint_hilite(1); 1016 #endif 1017 return (0); 1018 } 1019 1020 #if HILITE_SEARCH 1021 /* 1022 * Prepare hilites in a given range of the file. 1023 * 1024 * The pair (prep_startpos,prep_endpos) delimits a contiguous region 1025 * of the file that has been "prepared"; that is, scanned for matches for 1026 * the current search pattern, and hilites have been created for such matches. 1027 * If prep_startpos == NULL_POSITION, the prep region is empty. 1028 * If prep_endpos == NULL_POSITION, the prep region extends to EOF. 1029 * prep_hilite asks that the range (spos,epos) be covered by the prep region. 1030 */ 1031 public void 1032 prep_hilite(spos, epos) 1033 POSITION spos; 1034 POSITION epos; 1035 { 1036 POSITION nprep_startpos = prep_startpos; 1037 POSITION nprep_endpos = prep_endpos; 1038 /* 1039 * Search beyond where we're asked to search, so the prep region covers 1040 * more than we need. Do one big search instead of a bunch of small ones. 1041 */ 1042 #define SEARCH_MORE (3*size_linebuf) 1043 1044 if (!prev_pattern()) 1045 return; 1046 /* 1047 * Find two ranges: 1048 * The range that we need to search (spos,epos); and the range that 1049 * the "prep" region will then cover (nprep_startpos,nprep_endpos). 1050 */ 1051 1052 if (prep_startpos == NULL_POSITION || 1053 (epos != NULL_POSITION && epos < prep_startpos) || 1054 (prep_endpos != NULL_POSITION && spos > prep_endpos)) 1055 { 1056 /* 1057 * New range is not contiguous with old prep region. 1058 * Discard the old prep region and start a new one. 1059 */ 1060 clr_hilite(); 1061 if (epos != NULL_POSITION) 1062 epos += SEARCH_MORE; 1063 nprep_startpos = spos; 1064 nprep_endpos = epos; 1065 } else 1066 { 1067 /* 1068 * New range partially or completely overlaps old prep region. 1069 */ 1070 if (epos == NULL_POSITION) 1071 { 1072 /* 1073 * New range goes to end of file. 1074 */ 1075 nprep_endpos = NULL_POSITION; 1076 } else if (epos > prep_endpos) 1077 { 1078 /* 1079 * New range ends after old prep region. 1080 * Extend prep region to end at end of new range. 1081 */ 1082 epos += SEARCH_MORE; 1083 nprep_endpos = epos; 1084 } else /* (epos <= prep_endpos) */ 1085 { 1086 /* 1087 * New range ends within old prep region. 1088 * Truncate search to end at start of old prep region. 1089 */ 1090 epos = prep_startpos; 1091 } 1092 1093 if (spos < prep_startpos) 1094 { 1095 /* 1096 * New range starts before old prep region. 1097 * Extend old prep region backwards to start at 1098 * start of new range. 1099 */ 1100 if (spos < SEARCH_MORE) 1101 spos = 0; 1102 else 1103 spos -= SEARCH_MORE; 1104 nprep_startpos = spos; 1105 } else /* (spos >= prep_startpos) */ 1106 { 1107 /* 1108 * New range starts within or after old prep region. 1109 * Trim search to start near end of old prep region 1110 * (actually, one linebuf before end of old range). 1111 */ 1112 if (prep_endpos == NULL_POSITION) 1113 return; 1114 else if (prep_endpos < size_linebuf) 1115 spos = 0; 1116 else 1117 spos = prep_endpos - size_linebuf; 1118 } 1119 } 1120 1121 if (epos == NULL_POSITION || epos > spos) 1122 { 1123 if (search_range(spos, epos, SRCH_FORW|SRCH_FIND_ALL, 0, 1124 (POSITION*)NULL, &epos) >= 0) 1125 { 1126 if (epos == NULL_POSITION || epos > nprep_endpos) 1127 nprep_endpos = epos; 1128 } 1129 } 1130 prep_startpos = nprep_startpos; 1131 prep_endpos = nprep_endpos; 1132 } 1133 #endif 1134 1135 #if NO_REGEX 1136 /* 1137 * We have no pattern matching function from the library. 1138 * We use this function to do simple pattern matching. 1139 * It supports no metacharacters like *, etc. 1140 */ 1141 static int 1142 match(pattern, buf, pfound, pend) 1143 char *pattern, *buf; 1144 char **pfound, **pend; 1145 { 1146 register char *pp, *lp; 1147 1148 for ( ; *buf != '\0'; buf++) 1149 { 1150 for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) 1151 if (*pp == '\0' || *lp == '\0') 1152 break; 1153 if (*pp == '\0') 1154 { 1155 if (pfound != NULL) 1156 *pfound = buf; 1157 if (pend != NULL) 1158 *pend = lp; 1159 return (1); 1160 } 1161 } 1162 return (0); 1163 } 1164 #endif 1165 1166 #if HAVE_V8_REGCOMP 1167 /* 1168 * This function is called by the V8 regcomp to report 1169 * errors in regular expressions. 1170 */ 1171 void 1172 regerror(s) 1173 char *s; 1174 { 1175 PARG parg; 1176 1177 parg.p_string = s; 1178 error("%s", &parg); 1179 } 1180 #endif 1181 1182 #if !HAVE_STRCHR 1183 /* 1184 * strchr is used by regexp.c. 1185 */ 1186 char * 1187 strchr(s, c) 1188 char *s; 1189 int c; 1190 { 1191 for ( ; *s != '\0'; s++) 1192 if (*s == c) 1193 return (s); 1194 if (c == '\0') 1195 return (s); 1196 return (NULL); 1197 } 1198 #endif 1199 1200