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