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