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