1 /* 2 * Copyright (C) 1984-2012 Mark Nudelman 3 * Modified for use with illumos by Garrett D'Amore. 4 * Copyright 2014 Garrett D'Amore <garrett@damore.org> 5 * 6 * You may distribute under the terms of either the GNU General Public 7 * License or the Less License, as specified in the README file. 8 * 9 * For more information, see the README file. 10 */ 11 12 /* 13 * Routines to search a file for a pattern. 14 */ 15 16 #include "charset.h" 17 #include "less.h" 18 #include "pattern.h" 19 #include "position.h" 20 21 #define MINPOS(a, b) (((a) < (b)) ? (a) : (b)) 22 #define MAXPOS(a, b) (((a) > (b)) ? (a) : (b)) 23 24 extern volatile sig_atomic_t sigs; 25 extern int how_search; 26 extern int caseless; 27 extern int linenums; 28 extern int sc_height; 29 extern int jump_sline; 30 extern int bs_mode; 31 extern int ctldisp; 32 extern int status_col; 33 extern void *const ml_search; 34 extern off_t start_attnpos; 35 extern off_t end_attnpos; 36 extern int utf_mode; 37 extern int screen_trashed; 38 extern int hilite_search; 39 extern int size_linebuf; 40 extern int squished; 41 extern int can_goto_line; 42 static int hide_hilite; 43 static off_t prep_startpos; 44 static off_t prep_endpos; 45 static int is_caseless; 46 static int is_ucase_pattern; 47 48 struct hilite { 49 struct hilite *hl_next; 50 off_t hl_startpos; 51 off_t hl_endpos; 52 }; 53 static struct hilite hilite_anchor = { NULL, -1, -1 }; 54 static struct hilite filter_anchor = { NULL, -1, -1 }; 55 #define hl_first hl_next 56 57 /* 58 * These are the static variables that represent the "remembered" 59 * search pattern and filter pattern. 60 */ 61 struct pattern_info { 62 regex_t *compiled; 63 char *text; 64 int search_type; 65 }; 66 67 #define info_compiled(info) ((info)->compiled) 68 69 static struct pattern_info search_info; 70 static struct pattern_info filter_info; 71 72 /* 73 * Are there any uppercase letters in this string? 74 */ 75 static int 76 is_ucase(char *str) 77 { 78 wchar_t ch; 79 int len; 80 81 for (; *str != '\0'; str += len) { 82 if ((len = mbtowc(&ch, str, MB_CUR_MAX)) == -1) { 83 mbtowc(NULL, NULL, MB_CUR_MAX); 84 len = 1; 85 } else if (iswupper(ch)) 86 return (1); 87 } 88 return (0); 89 } 90 91 /* 92 * Compile and save a search pattern. 93 */ 94 static int 95 set_pattern(struct pattern_info *info, char *pattern, int search_type) 96 { 97 if (pattern == NULL) 98 info->compiled = NULL; 99 else if (compile_pattern(pattern, search_type, &info->compiled) < 0) 100 return (-1); 101 /* Pattern compiled successfully; save the text too. */ 102 free(info->text); 103 info->text = NULL; 104 if (pattern != NULL) 105 info->text = estrdup(pattern); 106 info->search_type = search_type; 107 108 /* 109 * Ignore case if -I is set OR 110 * -i is set AND the pattern is all lowercase. 111 */ 112 is_ucase_pattern = is_ucase(pattern); 113 if (is_ucase_pattern && caseless != OPT_ONPLUS) 114 is_caseless = 0; 115 else 116 is_caseless = caseless; 117 return (0); 118 } 119 120 /* 121 * Discard a saved pattern. 122 */ 123 static void 124 clear_pattern(struct pattern_info *info) 125 { 126 free(info->text); 127 info->text = NULL; 128 uncompile_pattern(&info->compiled); 129 } 130 131 /* 132 * Initialize saved pattern to nothing. 133 */ 134 static void 135 init_pattern(struct pattern_info *info) 136 { 137 info->compiled = NULL; 138 info->text = NULL; 139 info->search_type = 0; 140 } 141 142 /* 143 * Initialize search variables. 144 */ 145 void 146 init_search(void) 147 { 148 init_pattern(&search_info); 149 init_pattern(&filter_info); 150 } 151 152 /* 153 * Determine which text conversions to perform before pattern matching. 154 */ 155 static int 156 get_cvt_ops(void) 157 { 158 int ops = 0; 159 if (is_caseless || bs_mode == BS_SPECIAL) { 160 if (is_caseless) 161 ops |= CVT_TO_LC; 162 if (bs_mode == BS_SPECIAL) 163 ops |= CVT_BS; 164 if (bs_mode != BS_CONTROL) 165 ops |= CVT_CRLF; 166 } else if (bs_mode != BS_CONTROL) { 167 ops |= CVT_CRLF; 168 } 169 if (ctldisp == OPT_ONPLUS) 170 ops |= CVT_ANSI; 171 return (ops); 172 } 173 174 /* 175 * Is there a previous (remembered) search pattern? 176 */ 177 static int 178 prev_pattern(struct pattern_info *info) 179 { 180 if ((info->search_type & SRCH_NO_REGEX) == 0) 181 return (info->compiled != NULL); 182 return (info->text != NULL); 183 } 184 185 /* 186 * Repaint the hilites currently displayed on the screen. 187 * Repaint each line which contains highlighted text. 188 * If on==0, force all hilites off. 189 */ 190 void 191 repaint_hilite(int on) 192 { 193 int slinenum; 194 off_t pos; 195 int save_hide_hilite; 196 197 if (squished) 198 repaint(); 199 200 save_hide_hilite = hide_hilite; 201 if (!on) { 202 if (hide_hilite) 203 return; 204 hide_hilite = 1; 205 } 206 207 if (!can_goto_line) { 208 repaint(); 209 hide_hilite = save_hide_hilite; 210 return; 211 } 212 213 for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) { 214 pos = position(slinenum); 215 if (pos == -1) 216 continue; 217 (void) forw_line(pos); 218 goto_line(slinenum); 219 put_line(); 220 } 221 lower_left(); 222 hide_hilite = save_hide_hilite; 223 } 224 225 /* 226 * Clear the attn hilite. 227 */ 228 void 229 clear_attn(void) 230 { 231 int slinenum; 232 off_t old_start_attnpos; 233 off_t old_end_attnpos; 234 off_t pos; 235 off_t epos; 236 int moved = 0; 237 238 if (start_attnpos == -1) 239 return; 240 old_start_attnpos = start_attnpos; 241 old_end_attnpos = end_attnpos; 242 start_attnpos = end_attnpos = -1; 243 244 if (!can_goto_line) { 245 repaint(); 246 return; 247 } 248 if (squished) 249 repaint(); 250 251 for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) { 252 pos = position(slinenum); 253 if (pos == -1) 254 continue; 255 epos = position(slinenum+1); 256 if (pos < old_end_attnpos && 257 (epos == -1 || epos > old_start_attnpos)) { 258 (void) forw_line(pos); 259 goto_line(slinenum); 260 put_line(); 261 moved = 1; 262 } 263 } 264 if (moved) 265 lower_left(); 266 } 267 268 /* 269 * Hide search string highlighting. 270 */ 271 void 272 undo_search(void) 273 { 274 if (!prev_pattern(&search_info)) { 275 error("No previous regular expression", NULL); 276 return; 277 } 278 hide_hilite = !hide_hilite; 279 repaint_hilite(1); 280 } 281 282 /* 283 * Clear the hilite list. 284 */ 285 static void 286 clr_hlist(struct hilite *anchor) 287 { 288 struct hilite *hl; 289 struct hilite *nexthl; 290 291 for (hl = anchor->hl_first; hl != NULL; hl = nexthl) { 292 nexthl = hl->hl_next; 293 free(hl); 294 } 295 anchor->hl_first = NULL; 296 prep_startpos = prep_endpos = -1; 297 } 298 299 void 300 clr_hilite(void) 301 { 302 clr_hlist(&hilite_anchor); 303 } 304 305 static void 306 clr_filter(void) 307 { 308 clr_hlist(&filter_anchor); 309 } 310 311 /* 312 * Should any characters in a specified range be highlighted? 313 */ 314 static int 315 is_hilited_range(off_t pos, off_t epos) 316 { 317 struct hilite *hl; 318 319 /* 320 * Look at each highlight and see if any part of it falls in the range. 321 */ 322 for (hl = hilite_anchor.hl_first; hl != NULL; hl = hl->hl_next) { 323 if (hl->hl_endpos > pos && 324 (epos == -1 || epos > hl->hl_startpos)) 325 return (1); 326 } 327 return (0); 328 } 329 330 /* 331 * Is a line "filtered" -- that is, should it be hidden? 332 */ 333 int 334 is_filtered(off_t pos) 335 { 336 struct hilite *hl; 337 338 if (ch_getflags() & CH_HELPFILE) 339 return (0); 340 341 /* 342 * Look at each filter and see if the start position 343 * equals the start position of the line. 344 */ 345 for (hl = filter_anchor.hl_first; hl != NULL; hl = hl->hl_next) { 346 if (hl->hl_startpos == pos) 347 return (1); 348 } 349 return (0); 350 } 351 352 /* 353 * Should any characters in a specified range be highlighted? 354 * If nohide is nonzero, don't consider hide_hilite. 355 */ 356 int 357 is_hilited(off_t pos, off_t epos, int nohide, int *p_matches) 358 { 359 int match; 360 361 if (p_matches != NULL) 362 *p_matches = 0; 363 364 if (!status_col && 365 start_attnpos != -1 && 366 pos < end_attnpos && 367 (epos == -1 || epos > start_attnpos)) 368 /* 369 * The attn line overlaps this range. 370 */ 371 return (1); 372 373 match = is_hilited_range(pos, epos); 374 if (!match) 375 return (0); 376 377 if (p_matches != NULL) 378 /* 379 * Report matches, even if we're hiding highlights. 380 */ 381 *p_matches = 1; 382 383 if (hilite_search == 0) 384 /* 385 * Not doing highlighting. 386 */ 387 return (0); 388 389 if (!nohide && hide_hilite) 390 /* 391 * Highlighting is hidden. 392 */ 393 return (0); 394 395 return (1); 396 } 397 398 /* 399 * Add a new hilite to a hilite list. 400 */ 401 static void 402 add_hilite(struct hilite *anchor, struct hilite *hl) 403 { 404 struct hilite *ihl; 405 406 /* 407 * Hilites are sorted in the list; find where new one belongs. 408 * Insert new one after ihl. 409 */ 410 for (ihl = anchor; ihl->hl_next != NULL; ihl = ihl->hl_next) 411 { 412 if (ihl->hl_next->hl_startpos > hl->hl_startpos) 413 break; 414 } 415 416 /* 417 * Truncate hilite so it doesn't overlap any existing ones 418 * above and below it. 419 */ 420 if (ihl != anchor) 421 hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos); 422 if (ihl->hl_next != NULL) 423 hl->hl_endpos = MINPOS(hl->hl_endpos, 424 ihl->hl_next->hl_startpos); 425 if (hl->hl_startpos >= hl->hl_endpos) { 426 /* 427 * Hilite was truncated out of existence. 428 */ 429 free(hl); 430 return; 431 } 432 hl->hl_next = ihl->hl_next; 433 ihl->hl_next = hl; 434 } 435 436 /* 437 * Hilight every character in a range of displayed characters. 438 */ 439 static void 440 create_hilites(off_t linepos, int start_index, int end_index, int *chpos) 441 { 442 struct hilite *hl; 443 int i; 444 445 /* Start the first hilite. */ 446 hl = ecalloc(1, sizeof (struct hilite)); 447 hl->hl_startpos = linepos + chpos[start_index]; 448 449 /* 450 * Step through the displayed chars. 451 * If the source position (before cvt) of the char is one more 452 * than the source pos of the previous char (the usual case), 453 * just increase the size of the current hilite by one. 454 * Otherwise (there are backspaces or something involved), 455 * finish the current hilite and start a new one. 456 */ 457 for (i = start_index+1; i <= end_index; i++) { 458 if (chpos[i] != chpos[i-1] + 1 || i == end_index) { 459 hl->hl_endpos = linepos + chpos[i-1] + 1; 460 add_hilite(&hilite_anchor, hl); 461 /* Start new hilite unless this is the last char. */ 462 if (i < end_index) { 463 hl = ecalloc(1, sizeof (struct hilite)); 464 hl->hl_startpos = linepos + chpos[i]; 465 } 466 } 467 } 468 } 469 470 /* 471 * Make a hilite for each string in a physical line which matches 472 * the current pattern. 473 * sp,ep delimit the first match already found. 474 */ 475 static void 476 hilite_line(off_t linepos, char *line, int line_len, int *chpos, 477 char *sp, char *ep) 478 { 479 char *searchp; 480 char *line_end = line + line_len; 481 482 /* 483 * sp and ep delimit the first match in the line. 484 * Mark the corresponding file positions, then 485 * look for further matches and mark them. 486 * {{ This technique, of calling match_pattern on subsequent 487 * substrings of the line, may mark more than is correct 488 * if the pattern starts with "^". This bug is fixed 489 * for those regex functions that accept a notbol parameter 490 * (currently POSIX, PCRE and V8-with-regexec2). }} 491 */ 492 searchp = line; 493 do { 494 if (sp == NULL || ep == NULL) 495 return; 496 497 create_hilites(linepos, (intptr_t)sp - (intptr_t)line, 498 (intptr_t)ep - (intptr_t)line, chpos); 499 /* 500 * If we matched more than zero characters, 501 * move to the first char after the string we matched. 502 * If we matched zero, just move to the next char. 503 */ 504 if (ep > searchp) 505 searchp = ep; 506 else if (searchp != line_end) 507 searchp++; 508 else /* end of line */ 509 break; 510 } while (match_pattern(info_compiled(&search_info), search_info.text, 511 searchp, (intptr_t)line_end - (intptr_t)searchp, &sp, &ep, 1, 512 search_info.search_type)); 513 } 514 515 /* 516 * Change the caseless-ness of searches. 517 * Updates the internal search state to reflect a change in the -i flag. 518 */ 519 void 520 chg_caseless(void) 521 { 522 if (!is_ucase_pattern) 523 /* 524 * Pattern did not have uppercase. 525 * Just set the search caselessness to the global caselessness. 526 */ 527 is_caseless = caseless; 528 else 529 /* 530 * Pattern did have uppercase. 531 * Discard the pattern; we can't change search caselessness now. 532 */ 533 clear_pattern(&search_info); 534 } 535 536 /* 537 * Find matching text which is currently on screen and highlight it. 538 */ 539 static void 540 hilite_screen(void) 541 { 542 struct scrpos scrpos; 543 544 get_scrpos(&scrpos); 545 if (scrpos.pos == -1) 546 return; 547 prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1); 548 repaint_hilite(1); 549 } 550 551 /* 552 * Change highlighting parameters. 553 */ 554 void 555 chg_hilite(void) 556 { 557 /* 558 * Erase any highlights currently on screen. 559 */ 560 clr_hilite(); 561 hide_hilite = 0; 562 563 if (hilite_search == OPT_ONPLUS) 564 /* 565 * Display highlights. 566 */ 567 hilite_screen(); 568 } 569 570 /* 571 * Figure out where to start a search. 572 */ 573 static off_t 574 search_pos(int search_type) 575 { 576 off_t pos; 577 int linenum; 578 579 if (empty_screen()) { 580 /* 581 * Start at the beginning (or end) of the file. 582 * The empty_screen() case is mainly for 583 * command line initiated searches; 584 * for example, "+/xyz" on the command line. 585 * Also for multi-file (SRCH_PAST_EOF) searches. 586 */ 587 if (search_type & SRCH_FORW) { 588 pos = ch_zero(); 589 } else { 590 pos = ch_length(); 591 if (pos == -1) { 592 (void) ch_end_seek(); 593 pos = ch_length(); 594 } 595 } 596 linenum = 0; 597 } else { 598 int add_one = 0; 599 600 if (how_search == OPT_ON) { 601 /* 602 * Search does not include current screen. 603 */ 604 if (search_type & SRCH_FORW) 605 linenum = BOTTOM_PLUS_ONE; 606 else 607 linenum = TOP; 608 } else if (how_search == OPT_ONPLUS && 609 !(search_type & SRCH_AFTER_TARGET)) { 610 /* 611 * Search includes all of displayed screen. 612 */ 613 if (search_type & SRCH_FORW) 614 linenum = TOP; 615 else 616 linenum = BOTTOM_PLUS_ONE; 617 } else { 618 /* 619 * Search includes the part of current screen beyond 620 * the jump target. 621 * It starts at the jump target (if searching 622 * backwards), or at the jump target plus one 623 * (if forwards). 624 */ 625 linenum = jump_sline; 626 if (search_type & SRCH_FORW) 627 add_one = 1; 628 } 629 linenum = adjsline(linenum); 630 pos = position(linenum); 631 if (add_one) 632 pos = forw_raw_line(pos, NULL, NULL); 633 } 634 635 /* 636 * If the line is empty, look around for a plausible starting place. 637 */ 638 if (search_type & SRCH_FORW) { 639 while (pos == -1) { 640 if (++linenum >= sc_height) 641 break; 642 pos = position(linenum); 643 } 644 } else { 645 while (pos == -1) { 646 if (--linenum < 0) 647 break; 648 pos = position(linenum); 649 } 650 } 651 return (pos); 652 } 653 654 /* 655 * Search a subset of the file, specified by start/end position. 656 */ 657 static int 658 search_range(off_t pos, off_t endpos, int search_type, int matches, 659 int maxlines, off_t *plinepos, off_t *pendpos) 660 { 661 char *line; 662 char *cline; 663 int line_len; 664 off_t linenum; 665 char *sp, *ep; 666 int line_match; 667 int cvt_ops; 668 int cvt_len; 669 int *chpos; 670 off_t linepos, oldpos; 671 672 linenum = find_linenum(pos); 673 oldpos = pos; 674 for (;;) { 675 /* 676 * Get lines until we find a matching one or until 677 * we hit end-of-file (or beginning-of-file if we're 678 * going backwards), or until we hit the end position. 679 */ 680 if (ABORT_SIGS()) { 681 /* 682 * A signal aborts the search. 683 */ 684 return (-1); 685 } 686 687 if ((endpos != -1 && pos >= endpos) || 688 maxlines == 0) { 689 /* 690 * Reached end position without a match. 691 */ 692 if (pendpos != NULL) 693 *pendpos = pos; 694 return (matches); 695 } 696 if (maxlines > 0) 697 maxlines--; 698 699 if (search_type & SRCH_FORW) { 700 /* 701 * Read the next line, and save the 702 * starting position of that line in linepos. 703 */ 704 linepos = pos; 705 pos = forw_raw_line(pos, &line, &line_len); 706 if (linenum != 0) 707 linenum++; 708 } else { 709 /* 710 * Read the previous line and save the 711 * starting position of that line in linepos. 712 */ 713 pos = back_raw_line(pos, &line, &line_len); 714 linepos = pos; 715 if (linenum != 0) 716 linenum--; 717 } 718 719 if (pos == -1) { 720 /* 721 * Reached EOF/BOF without a match. 722 */ 723 if (pendpos != NULL) 724 *pendpos = oldpos; 725 return (matches); 726 } 727 728 /* 729 * If we're using line numbers, we might as well 730 * remember the information we have now (the position 731 * and line number of the current line). 732 * Don't do it for every line because it slows down 733 * the search. Remember the line number only if 734 * we're "far" from the last place we remembered it. 735 */ 736 if (linenums && abs((int)(pos - oldpos)) > 2048) 737 add_lnum(linenum, pos); 738 oldpos = pos; 739 740 if (is_filtered(linepos)) 741 continue; 742 743 /* 744 * If it's a caseless search, convert the line to lowercase. 745 * If we're doing backspace processing, delete backspaces. 746 */ 747 cvt_ops = get_cvt_ops(); 748 cvt_len = cvt_length(line_len); 749 cline = ecalloc(1, cvt_len); 750 chpos = cvt_alloc_chpos(cvt_len); 751 cvt_text(cline, line, chpos, &line_len, cvt_ops); 752 753 /* 754 * Check to see if the line matches the filter pattern. 755 * If so, add an entry to the filter list. 756 */ 757 if ((search_type & SRCH_FIND_ALL) && 758 prev_pattern(&filter_info)) { 759 int line_filter = 760 match_pattern(info_compiled(&filter_info), 761 filter_info.text, cline, line_len, &sp, &ep, 0, 762 filter_info.search_type); 763 if (line_filter) { 764 struct hilite *hl = 765 ecalloc(1, sizeof (struct hilite)); 766 hl->hl_startpos = linepos; 767 hl->hl_endpos = pos; 768 add_hilite(&filter_anchor, hl); 769 } 770 } 771 772 /* 773 * Test the next line to see if we have a match. 774 * We are successful if we either want a match and got one, 775 * or if we want a non-match and got one. 776 */ 777 if (prev_pattern(&search_info)) { 778 line_match = match_pattern(info_compiled(&search_info), 779 search_info.text, cline, line_len, &sp, &ep, 0, 780 search_type); 781 if (line_match) { 782 /* 783 * Got a match. 784 */ 785 if (search_type & SRCH_FIND_ALL) { 786 /* 787 * We are supposed to find all matches 788 * in the range. 789 * Just add the matches in this line 790 * to the hilite list and keep 791 * searching. 792 */ 793 hilite_line(linepos, cline, line_len, 794 chpos, sp, ep); 795 } else if (--matches <= 0) { 796 /* 797 * Found the one match we're looking 798 * for. Return it. 799 */ 800 if (hilite_search == OPT_ON) { 801 /* 802 * Clear the hilite list and 803 * add only 804 * the matches in this one line. 805 */ 806 clr_hilite(); 807 hilite_line(linepos, cline, 808 line_len, chpos, sp, ep); 809 } 810 free(cline); 811 free(chpos); 812 if (plinepos != NULL) 813 *plinepos = linepos; 814 return (0); 815 } 816 } 817 } 818 free(cline); 819 free(chpos); 820 } 821 } 822 823 /* 824 * search for a pattern in history. If found, compile that pattern. 825 */ 826 static int 827 hist_pattern(int search_type) 828 { 829 char *pattern; 830 831 set_mlist(ml_search, 0); 832 pattern = cmd_lastpattern(); 833 if (pattern == NULL) 834 return (0); 835 836 if (set_pattern(&search_info, pattern, search_type) < 0) 837 return (0); 838 839 if (hilite_search == OPT_ONPLUS && !hide_hilite) 840 hilite_screen(); 841 842 return (1); 843 } 844 845 /* 846 * Search for the n-th occurrence of a specified pattern, 847 * either forward or backward. 848 * Return the number of matches not yet found in this file 849 * (that is, n minus the number of matches found). 850 * Return -1 if the search should be aborted. 851 * Caller may continue the search in another file 852 * if less than n matches are found in this file. 853 */ 854 int 855 search(int search_type, char *pattern, int n) 856 { 857 off_t pos; 858 859 if (pattern == NULL || *pattern == '\0') { 860 /* 861 * A null pattern means use the previously compiled pattern. 862 */ 863 search_type |= SRCH_AFTER_TARGET; 864 if (!prev_pattern(&search_info) && !hist_pattern(search_type)) { 865 error("No previous regular expression", NULL); 866 return (-1); 867 } 868 if ((search_type & SRCH_NO_REGEX) != 869 (search_info.search_type & SRCH_NO_REGEX)) { 870 error("Please re-enter search pattern", NULL); 871 return (-1); 872 } 873 if (hilite_search == OPT_ON) { 874 /* 875 * Erase the highlights currently on screen. 876 * If the search fails, we'll redisplay them later. 877 */ 878 repaint_hilite(0); 879 } 880 if (hilite_search == OPT_ONPLUS && hide_hilite) { 881 /* 882 * Highlight any matches currently on screen, 883 * before we actually start the search. 884 */ 885 hide_hilite = 0; 886 hilite_screen(); 887 } 888 hide_hilite = 0; 889 } else { 890 /* 891 * Compile the pattern. 892 */ 893 if (set_pattern(&search_info, pattern, search_type) < 0) 894 return (-1); 895 if (hilite_search) { 896 /* 897 * Erase the highlights currently on screen. 898 * Also permanently delete them from the hilite list. 899 */ 900 repaint_hilite(0); 901 hide_hilite = 0; 902 clr_hilite(); 903 } 904 if (hilite_search == OPT_ONPLUS) { 905 /* 906 * Highlight any matches currently on screen, 907 * before we actually start the search. 908 */ 909 hilite_screen(); 910 } 911 } 912 913 /* 914 * Figure out where to start the search. 915 */ 916 pos = search_pos(search_type); 917 if (pos == -1) { 918 /* 919 * Can't find anyplace to start searching from. 920 */ 921 if (search_type & SRCH_PAST_EOF) 922 return (n); 923 /* repaint(); -- why was this here? */ 924 error("Nothing to search", NULL); 925 return (-1); 926 } 927 928 n = search_range(pos, -1, search_type, n, -1, &pos, NULL); 929 if (n != 0) { 930 /* 931 * Search was unsuccessful. 932 */ 933 if (hilite_search == OPT_ON && n > 0) 934 /* 935 * Redisplay old hilites. 936 */ 937 repaint_hilite(1); 938 return (n); 939 } 940 941 if (!(search_type & SRCH_NO_MOVE)) { 942 /* 943 * Go to the matching line. 944 */ 945 jump_loc(pos, jump_sline); 946 } 947 948 if (hilite_search == OPT_ON) 949 /* 950 * Display new hilites in the matching line. 951 */ 952 repaint_hilite(1); 953 return (0); 954 } 955 956 957 /* 958 * Prepare hilites in a given range of the file. 959 * 960 * The pair (prep_startpos,prep_endpos) delimits a contiguous region 961 * of the file that has been "prepared"; that is, scanned for matches for 962 * the current search pattern, and hilites have been created for such matches. 963 * If prep_startpos == -1, the prep region is empty. 964 * If prep_endpos == -1, the prep region extends to EOF. 965 * prep_hilite asks that the range (spos,epos) be covered by the prep region. 966 */ 967 void 968 prep_hilite(off_t spos, off_t epos, int maxlines) 969 { 970 off_t nprep_startpos = prep_startpos; 971 off_t nprep_endpos = prep_endpos; 972 off_t new_epos; 973 off_t max_epos; 974 int result; 975 int i; 976 977 /* 978 * Search beyond where we're asked to search, so the prep region covers 979 * more than we need. Do one big search instead of a bunch of small ones. 980 */ 981 #define SEARCH_MORE (3*size_linebuf) 982 983 if (!prev_pattern(&search_info) && !is_filtering()) 984 return; 985 986 /* 987 * If we're limited to a max number of lines, figure out the 988 * file position we should stop at. 989 */ 990 if (maxlines < 0) { 991 max_epos = -1; 992 } else { 993 max_epos = spos; 994 for (i = 0; i < maxlines; i++) 995 max_epos = forw_raw_line(max_epos, NULL, NULL); 996 } 997 998 /* 999 * Find two ranges: 1000 * The range that we need to search (spos,epos); and the range that 1001 * the "prep" region will then cover (nprep_startpos,nprep_endpos). 1002 */ 1003 1004 if (prep_startpos == -1 || 1005 (epos != -1 && epos < prep_startpos) || 1006 spos > prep_endpos) { 1007 /* 1008 * New range is not contiguous with old prep region. 1009 * Discard the old prep region and start a new one. 1010 */ 1011 clr_hilite(); 1012 clr_filter(); 1013 if (epos != -1) 1014 epos += SEARCH_MORE; 1015 nprep_startpos = spos; 1016 } else { 1017 /* 1018 * New range partially or completely overlaps old prep region. 1019 */ 1020 if (epos != -1) { 1021 if (epos > prep_endpos) { 1022 /* 1023 * New range ends after old prep region. 1024 * Extend prep region to end at end of new 1025 * range. 1026 */ 1027 epos += SEARCH_MORE; 1028 1029 } else { 1030 /* 1031 * New range ends within old prep region. 1032 * Truncate search to end at start of old prep 1033 * region. 1034 */ 1035 epos = prep_startpos; 1036 } 1037 } 1038 1039 if (spos < prep_startpos) { 1040 /* 1041 * New range starts before old prep region. 1042 * Extend old prep region backwards to start at 1043 * start of new range. 1044 */ 1045 if (spos < SEARCH_MORE) 1046 spos = 0; 1047 else 1048 spos -= SEARCH_MORE; 1049 nprep_startpos = spos; 1050 } else { /* (spos >= prep_startpos) */ 1051 /* 1052 * New range starts within or after old prep region. 1053 * Trim search to start at end of old prep region. 1054 */ 1055 spos = prep_endpos; 1056 } 1057 } 1058 1059 if (epos != -1 && max_epos != -1 && 1060 epos > max_epos) 1061 /* 1062 * Don't go past the max position we're allowed. 1063 */ 1064 epos = max_epos; 1065 1066 if (epos == -1 || epos > spos) { 1067 int search_type = SRCH_FORW | SRCH_FIND_ALL; 1068 search_type |= (search_info.search_type & SRCH_NO_REGEX); 1069 result = search_range(spos, epos, search_type, 0, 1070 maxlines, NULL, &new_epos); 1071 if (result < 0) 1072 return; 1073 if (prep_endpos == -1 || new_epos > prep_endpos) 1074 nprep_endpos = new_epos; 1075 } 1076 prep_startpos = nprep_startpos; 1077 prep_endpos = nprep_endpos; 1078 } 1079 1080 /* 1081 * Set the pattern to be used for line filtering. 1082 */ 1083 void 1084 set_filter_pattern(char *pattern, int search_type) 1085 { 1086 clr_filter(); 1087 if (pattern == NULL || *pattern == '\0') 1088 clear_pattern(&filter_info); 1089 else 1090 (void) set_pattern(&filter_info, pattern, search_type); 1091 screen_trashed = 1; 1092 } 1093 1094 /* 1095 * Is there a line filter in effect? 1096 */ 1097 int 1098 is_filtering(void) 1099 { 1100 if (ch_getflags() & CH_HELPFILE) 1101 return (0); 1102 return (prev_pattern(&filter_info)); 1103 } 1104