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