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 if (sp == NULL || ep == NULL) 481 return; 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 create_hilites(linepos, (intptr_t)sp - (intptr_t)line, 495 (intptr_t)ep - (intptr_t)line, chpos); 496 /* 497 * If we matched more than zero characters, 498 * move to the first char after the string we matched. 499 * If we matched zero, just move to the next char. 500 */ 501 if (ep > searchp) 502 searchp = ep; 503 else if (searchp != line_end) 504 searchp++; 505 else /* end of line */ 506 break; 507 } while (match_pattern(info_compiled(&search_info), search_info.text, 508 searchp, (intptr_t)line_end - (intptr_t)searchp, &sp, &ep, 1, 509 search_info.search_type)); 510 } 511 512 /* 513 * Change the caseless-ness of searches. 514 * Updates the internal search state to reflect a change in the -i flag. 515 */ 516 void 517 chg_caseless(void) 518 { 519 if (!is_ucase_pattern) 520 /* 521 * Pattern did not have uppercase. 522 * Just set the search caselessness to the global caselessness. 523 */ 524 is_caseless = caseless; 525 else 526 /* 527 * Pattern did have uppercase. 528 * Discard the pattern; we can't change search caselessness now. 529 */ 530 clear_pattern(&search_info); 531 } 532 533 /* 534 * Find matching text which is currently on screen and highlight it. 535 */ 536 static void 537 hilite_screen(void) 538 { 539 struct scrpos scrpos; 540 541 get_scrpos(&scrpos); 542 if (scrpos.pos == -1) 543 return; 544 prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1); 545 repaint_hilite(1); 546 } 547 548 /* 549 * Change highlighting parameters. 550 */ 551 void 552 chg_hilite(void) 553 { 554 /* 555 * Erase any highlights currently on screen. 556 */ 557 clr_hilite(); 558 hide_hilite = 0; 559 560 if (hilite_search == OPT_ONPLUS) 561 /* 562 * Display highlights. 563 */ 564 hilite_screen(); 565 } 566 567 /* 568 * Figure out where to start a search. 569 */ 570 static off_t 571 search_pos(int search_type) 572 { 573 off_t pos; 574 int linenum; 575 576 if (empty_screen()) { 577 /* 578 * Start at the beginning (or end) of the file. 579 * The empty_screen() case is mainly for 580 * command line initiated searches; 581 * for example, "+/xyz" on the command line. 582 * Also for multi-file (SRCH_PAST_EOF) searches. 583 */ 584 if (search_type & SRCH_FORW) { 585 pos = ch_zero(); 586 } else { 587 pos = ch_length(); 588 if (pos == -1) { 589 (void) ch_end_seek(); 590 pos = ch_length(); 591 } 592 } 593 linenum = 0; 594 } else { 595 int add_one = 0; 596 597 if (how_search == OPT_ON) { 598 /* 599 * Search does not include current screen. 600 */ 601 if (search_type & SRCH_FORW) 602 linenum = BOTTOM_PLUS_ONE; 603 else 604 linenum = TOP; 605 } else if (how_search == OPT_ONPLUS && 606 !(search_type & SRCH_AFTER_TARGET)) { 607 /* 608 * Search includes all of displayed screen. 609 */ 610 if (search_type & SRCH_FORW) 611 linenum = TOP; 612 else 613 linenum = BOTTOM_PLUS_ONE; 614 } else { 615 /* 616 * Search includes the part of current screen beyond 617 * the jump target. 618 * It starts at the jump target (if searching 619 * backwards), or at the jump target plus one 620 * (if forwards). 621 */ 622 linenum = jump_sline; 623 if (search_type & SRCH_FORW) 624 add_one = 1; 625 } 626 linenum = adjsline(linenum); 627 pos = position(linenum); 628 if (add_one) 629 pos = forw_raw_line(pos, NULL, NULL); 630 } 631 632 /* 633 * If the line is empty, look around for a plausible starting place. 634 */ 635 if (search_type & SRCH_FORW) { 636 while (pos == -1) { 637 if (++linenum >= sc_height) 638 break; 639 pos = position(linenum); 640 } 641 } else { 642 while (pos == -1) { 643 if (--linenum < 0) 644 break; 645 pos = position(linenum); 646 } 647 } 648 return (pos); 649 } 650 651 /* 652 * Search a subset of the file, specified by start/end position. 653 */ 654 static int 655 search_range(off_t pos, off_t endpos, int search_type, int matches, 656 int maxlines, off_t *plinepos, off_t *pendpos) 657 { 658 char *line; 659 char *cline; 660 int line_len; 661 off_t linenum; 662 char *sp, *ep; 663 int line_match; 664 int cvt_ops; 665 int cvt_len; 666 int *chpos; 667 off_t linepos, oldpos; 668 669 linenum = find_linenum(pos); 670 oldpos = pos; 671 for (;;) { 672 /* 673 * Get lines until we find a matching one or until 674 * we hit end-of-file (or beginning-of-file if we're 675 * going backwards), or until we hit the end position. 676 */ 677 if (ABORT_SIGS()) { 678 /* 679 * A signal aborts the search. 680 */ 681 return (-1); 682 } 683 684 if ((endpos != -1 && pos >= endpos) || 685 maxlines == 0) { 686 /* 687 * Reached end position without a match. 688 */ 689 if (pendpos != NULL) 690 *pendpos = pos; 691 return (matches); 692 } 693 if (maxlines > 0) 694 maxlines--; 695 696 if (search_type & SRCH_FORW) { 697 /* 698 * Read the next line, and save the 699 * starting position of that line in linepos. 700 */ 701 linepos = pos; 702 pos = forw_raw_line(pos, &line, &line_len); 703 if (linenum != 0) 704 linenum++; 705 } else { 706 /* 707 * Read the previous line and save the 708 * starting position of that line in linepos. 709 */ 710 pos = back_raw_line(pos, &line, &line_len); 711 linepos = pos; 712 if (linenum != 0) 713 linenum--; 714 } 715 716 if (pos == -1) { 717 /* 718 * Reached EOF/BOF without a match. 719 */ 720 if (pendpos != NULL) 721 *pendpos = oldpos; 722 return (matches); 723 } 724 725 /* 726 * If we're using line numbers, we might as well 727 * remember the information we have now (the position 728 * and line number of the current line). 729 * Don't do it for every line because it slows down 730 * the search. Remember the line number only if 731 * we're "far" from the last place we remembered it. 732 */ 733 if (linenums && abs((int)(pos - oldpos)) > 2048) 734 add_lnum(linenum, pos); 735 oldpos = pos; 736 737 if (is_filtered(linepos)) 738 continue; 739 740 /* 741 * If it's a caseless search, convert the line to lowercase. 742 * If we're doing backspace processing, delete backspaces. 743 */ 744 cvt_ops = get_cvt_ops(); 745 cvt_len = cvt_length(line_len); 746 cline = ecalloc(1, cvt_len); 747 chpos = cvt_alloc_chpos(cvt_len); 748 cvt_text(cline, line, chpos, &line_len, cvt_ops); 749 750 /* 751 * Check to see if the line matches the filter pattern. 752 * If so, add an entry to the filter list. 753 */ 754 if ((search_type & SRCH_FIND_ALL) && 755 prev_pattern(&filter_info)) { 756 int line_filter = 757 match_pattern(info_compiled(&filter_info), 758 filter_info.text, cline, line_len, &sp, &ep, 0, 759 filter_info.search_type); 760 if (line_filter) { 761 struct hilite *hl = 762 ecalloc(1, sizeof (struct hilite)); 763 hl->hl_startpos = linepos; 764 hl->hl_endpos = pos; 765 add_hilite(&filter_anchor, hl); 766 } 767 } 768 769 /* 770 * Test the next line to see if we have a match. 771 * We are successful if we either want a match and got one, 772 * or if we want a non-match and got one. 773 */ 774 if (prev_pattern(&search_info)) { 775 line_match = match_pattern(info_compiled(&search_info), 776 search_info.text, cline, line_len, &sp, &ep, 0, 777 search_type); 778 if (line_match) { 779 /* 780 * Got a match. 781 */ 782 if (search_type & SRCH_FIND_ALL) { 783 /* 784 * We are supposed to find all matches 785 * in the range. 786 * Just add the matches in this line 787 * to the hilite list and keep 788 * searching. 789 */ 790 hilite_line(linepos, cline, line_len, 791 chpos, sp, ep); 792 } else if (--matches <= 0) { 793 /* 794 * Found the one match we're looking 795 * for. Return it. 796 */ 797 if (hilite_search == OPT_ON) { 798 /* 799 * Clear the hilite list and 800 * add only 801 * the matches in this one line. 802 */ 803 clr_hilite(); 804 hilite_line(linepos, cline, 805 line_len, chpos, sp, ep); 806 } 807 free(cline); 808 free(chpos); 809 if (plinepos != NULL) 810 *plinepos = linepos; 811 return (0); 812 } 813 } 814 } 815 free(cline); 816 free(chpos); 817 } 818 } 819 820 /* 821 * search for a pattern in history. If found, compile that pattern. 822 */ 823 static int 824 hist_pattern(int search_type) 825 { 826 char *pattern; 827 828 set_mlist(ml_search, 0); 829 pattern = cmd_lastpattern(); 830 if (pattern == NULL) 831 return (0); 832 833 if (set_pattern(&search_info, pattern, search_type) < 0) 834 return (0); 835 836 if (hilite_search == OPT_ONPLUS && !hide_hilite) 837 hilite_screen(); 838 839 return (1); 840 } 841 842 /* 843 * Search for the n-th occurrence of a specified pattern, 844 * either forward or backward. 845 * Return the number of matches not yet found in this file 846 * (that is, n minus the number of matches found). 847 * Return -1 if the search should be aborted. 848 * Caller may continue the search in another file 849 * if less than n matches are found in this file. 850 */ 851 int 852 search(int search_type, char *pattern, int n) 853 { 854 off_t pos; 855 856 if (pattern == NULL || *pattern == '\0') { 857 /* 858 * A null pattern means use the previously compiled pattern. 859 */ 860 search_type |= SRCH_AFTER_TARGET; 861 if (!prev_pattern(&search_info) && !hist_pattern(search_type)) { 862 error("No previous regular expression", NULL); 863 return (-1); 864 } 865 if ((search_type & SRCH_NO_REGEX) != 866 (search_info.search_type & SRCH_NO_REGEX)) { 867 error("Please re-enter search pattern", NULL); 868 return (-1); 869 } 870 if (hilite_search == OPT_ON) { 871 /* 872 * Erase the highlights currently on screen. 873 * If the search fails, we'll redisplay them later. 874 */ 875 repaint_hilite(0); 876 } 877 if (hilite_search == OPT_ONPLUS && hide_hilite) { 878 /* 879 * Highlight any matches currently on screen, 880 * before we actually start the search. 881 */ 882 hide_hilite = 0; 883 hilite_screen(); 884 } 885 hide_hilite = 0; 886 } else { 887 /* 888 * Compile the pattern. 889 */ 890 if (set_pattern(&search_info, pattern, search_type) < 0) 891 return (-1); 892 if (hilite_search) { 893 /* 894 * Erase the highlights currently on screen. 895 * Also permanently delete them from the hilite list. 896 */ 897 repaint_hilite(0); 898 hide_hilite = 0; 899 clr_hilite(); 900 } 901 if (hilite_search == OPT_ONPLUS) { 902 /* 903 * Highlight any matches currently on screen, 904 * before we actually start the search. 905 */ 906 hilite_screen(); 907 } 908 } 909 910 /* 911 * Figure out where to start the search. 912 */ 913 pos = search_pos(search_type); 914 if (pos == -1) { 915 /* 916 * Can't find anyplace to start searching from. 917 */ 918 if (search_type & SRCH_PAST_EOF) 919 return (n); 920 /* repaint(); -- why was this here? */ 921 error("Nothing to search", NULL); 922 return (-1); 923 } 924 925 n = search_range(pos, -1, search_type, n, -1, &pos, NULL); 926 if (n != 0) { 927 /* 928 * Search was unsuccessful. 929 */ 930 if (hilite_search == OPT_ON && n > 0) 931 /* 932 * Redisplay old hilites. 933 */ 934 repaint_hilite(1); 935 return (n); 936 } 937 938 if (!(search_type & SRCH_NO_MOVE)) { 939 /* 940 * Go to the matching line. 941 */ 942 jump_loc(pos, jump_sline); 943 } 944 945 if (hilite_search == OPT_ON) 946 /* 947 * Display new hilites in the matching line. 948 */ 949 repaint_hilite(1); 950 return (0); 951 } 952 953 954 /* 955 * Prepare hilites in a given range of the file. 956 * 957 * The pair (prep_startpos,prep_endpos) delimits a contiguous region 958 * of the file that has been "prepared"; that is, scanned for matches for 959 * the current search pattern, and hilites have been created for such matches. 960 * If prep_startpos == -1, the prep region is empty. 961 * If prep_endpos == -1, the prep region extends to EOF. 962 * prep_hilite asks that the range (spos,epos) be covered by the prep region. 963 */ 964 void 965 prep_hilite(off_t spos, off_t epos, int maxlines) 966 { 967 off_t nprep_startpos = prep_startpos; 968 off_t nprep_endpos = prep_endpos; 969 off_t new_epos; 970 off_t max_epos; 971 int result; 972 int i; 973 974 /* 975 * Search beyond where we're asked to search, so the prep region covers 976 * more than we need. Do one big search instead of a bunch of small ones. 977 */ 978 #define SEARCH_MORE (3*size_linebuf) 979 980 if (!prev_pattern(&search_info) && !is_filtering()) 981 return; 982 983 /* 984 * If we're limited to a max number of lines, figure out the 985 * file position we should stop at. 986 */ 987 if (maxlines < 0) { 988 max_epos = -1; 989 } else { 990 max_epos = spos; 991 for (i = 0; i < maxlines; i++) 992 max_epos = forw_raw_line(max_epos, NULL, NULL); 993 } 994 995 /* 996 * Find two ranges: 997 * The range that we need to search (spos,epos); and the range that 998 * the "prep" region will then cover (nprep_startpos,nprep_endpos). 999 */ 1000 1001 if (prep_startpos == -1 || 1002 (epos != -1 && epos < prep_startpos) || 1003 spos > prep_endpos) { 1004 /* 1005 * New range is not contiguous with old prep region. 1006 * Discard the old prep region and start a new one. 1007 */ 1008 clr_hilite(); 1009 clr_filter(); 1010 if (epos != -1) 1011 epos += SEARCH_MORE; 1012 nprep_startpos = spos; 1013 } else { 1014 /* 1015 * New range partially or completely overlaps old prep region. 1016 */ 1017 if (epos != -1) { 1018 if (epos > prep_endpos) { 1019 /* 1020 * New range ends after old prep region. 1021 * Extend prep region to end at end of new 1022 * range. 1023 */ 1024 epos += SEARCH_MORE; 1025 1026 } else { 1027 /* 1028 * New range ends within old prep region. 1029 * Truncate search to end at start of old prep 1030 * region. 1031 */ 1032 epos = prep_startpos; 1033 } 1034 } 1035 1036 if (spos < prep_startpos) { 1037 /* 1038 * New range starts before old prep region. 1039 * Extend old prep region backwards to start at 1040 * start of new range. 1041 */ 1042 if (spos < SEARCH_MORE) 1043 spos = 0; 1044 else 1045 spos -= SEARCH_MORE; 1046 nprep_startpos = spos; 1047 } else { /* (spos >= prep_startpos) */ 1048 /* 1049 * New range starts within or after old prep region. 1050 * Trim search to start at end of old prep region. 1051 */ 1052 spos = prep_endpos; 1053 } 1054 } 1055 1056 if (epos != -1 && max_epos != -1 && 1057 epos > max_epos) 1058 /* 1059 * Don't go past the max position we're allowed. 1060 */ 1061 epos = max_epos; 1062 1063 if (epos == -1 || epos > spos) { 1064 int search_type = SRCH_FORW | SRCH_FIND_ALL; 1065 search_type |= (search_info.search_type & SRCH_NO_REGEX); 1066 result = search_range(spos, epos, search_type, 0, 1067 maxlines, NULL, &new_epos); 1068 if (result < 0) 1069 return; 1070 if (prep_endpos == -1 || new_epos > prep_endpos) 1071 nprep_endpos = new_epos; 1072 } 1073 prep_startpos = nprep_startpos; 1074 prep_endpos = nprep_endpos; 1075 } 1076 1077 /* 1078 * Set the pattern to be used for line filtering. 1079 */ 1080 void 1081 set_filter_pattern(char *pattern, int search_type) 1082 { 1083 clr_filter(); 1084 if (pattern == NULL || *pattern == '\0') 1085 clear_pattern(&filter_info); 1086 else 1087 (void) set_pattern(&filter_info, pattern, search_type); 1088 screen_trashed = 1; 1089 } 1090 1091 /* 1092 * Is there a line filter in effect? 1093 */ 1094 int 1095 is_filtering(void) 1096 { 1097 if (ch_getflags() & CH_HELPFILE) 1098 return (0); 1099 return (prev_pattern(&filter_info)); 1100 } 1101