1 /* 2 * Copyright (C) 1984-2022 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, see the README file. 8 */ 9 10 11 /* 12 * Routines to search a file for a pattern. 13 */ 14 15 #include "less.h" 16 #include "position.h" 17 #include "charset.h" 18 19 #define MINPOS(a,b) (((a) < (b)) ? (a) : (b)) 20 #define MAXPOS(a,b) (((a) > (b)) ? (a) : (b)) 21 22 extern int sigs; 23 extern int how_search; 24 extern int caseless; 25 extern int linenums; 26 extern int sc_height; 27 extern int jump_sline; 28 extern int bs_mode; 29 extern int ctldisp; 30 extern int status_col; 31 extern void *ml_search; 32 extern POSITION start_attnpos; 33 extern POSITION end_attnpos; 34 extern int utf_mode; 35 extern int screen_trashed; 36 extern int sc_width; 37 extern int sc_height; 38 extern int hshift; 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 extern POSITION xxpos; 48 49 /* 50 * Structures for maintaining a set of ranges for hilites and filtered-out 51 * lines. Each range is stored as a node within a red-black tree, and we 52 * try to extend existing ranges (without creating overlaps) rather than 53 * create new nodes if possible. We remember the last node found by a 54 * search for constant-time lookup if the next search is near enough to 55 * the previous. To aid that, we overlay a secondary doubly-linked list 56 * on top of the red-black tree so we can find the preceding/succeeding 57 * nodes also in constant time. 58 * 59 * Each node is allocated from a series of pools, each pool double the size 60 * of the previous (for amortised constant time allocation). Since our only 61 * tree operations are clear and node insertion, not node removal, we don't 62 * need to maintain a usage bitmap or freelist and can just return nodes 63 * from the pool in-order until capacity is reached. 64 */ 65 struct hilite 66 { 67 POSITION hl_startpos; 68 POSITION hl_endpos; 69 }; 70 struct hilite_node 71 { 72 struct hilite_node *parent; 73 struct hilite_node *left; 74 struct hilite_node *right; 75 struct hilite_node *prev; 76 struct hilite_node *next; 77 int red; 78 struct hilite r; 79 }; 80 struct hilite_storage 81 { 82 int capacity; 83 int used; 84 struct hilite_storage *next; 85 struct hilite_node *nodes; 86 }; 87 struct hilite_tree 88 { 89 struct hilite_storage *first; 90 struct hilite_storage *current; 91 struct hilite_node *root; 92 struct hilite_node *lookaside; 93 }; 94 #define HILITE_INITIALIZER() { NULL, NULL, NULL, NULL } 95 #define HILITE_LOOKASIDE_STEPS 2 96 97 static struct hilite_tree hilite_anchor = HILITE_INITIALIZER(); 98 static struct hilite_tree filter_anchor = HILITE_INITIALIZER(); 99 static struct pattern_info *filter_infos = NULL; 100 101 #endif 102 103 /* 104 * These are the static variables that represent the "remembered" 105 * search pattern and filter pattern. 106 */ 107 struct pattern_info { 108 PATTERN_TYPE compiled; 109 char* text; 110 int search_type; 111 struct pattern_info *next; 112 }; 113 114 #if NO_REGEX 115 #define info_compiled(info) ((void*)0) 116 #else 117 #define info_compiled(info) ((info)->compiled) 118 #endif 119 120 static struct pattern_info search_info; 121 static int is_ucase_pattern; 122 public int is_caseless; 123 124 /* 125 * Are there any uppercase letters in this string? 126 */ 127 static int 128 is_ucase(str) 129 char *str; 130 { 131 char *str_end = str + strlen(str); 132 LWCHAR ch; 133 134 while (str < str_end) 135 { 136 ch = step_char(&str, +1, str_end); 137 if (IS_UPPER(ch)) 138 return (1); 139 } 140 return (0); 141 } 142 143 /* 144 * Discard a saved pattern. 145 */ 146 static void 147 clear_pattern(info) 148 struct pattern_info *info; 149 { 150 if (info->text != NULL) 151 free(info->text); 152 info->text = NULL; 153 #if !NO_REGEX 154 uncompile_pattern(&info->compiled); 155 #endif 156 } 157 158 /* 159 * Compile and save a search pattern. 160 */ 161 static int 162 set_pattern(info, pattern, search_type, show_error) 163 struct pattern_info *info; 164 char *pattern; 165 int search_type; 166 int show_error; 167 { 168 /* 169 * Ignore case if -I is set OR 170 * -i is set AND the pattern is all lowercase. 171 */ 172 is_ucase_pattern = (pattern == NULL) ? FALSE : is_ucase(pattern); 173 is_caseless = (is_ucase_pattern && caseless != OPT_ONPLUS) ? 0 : caseless; 174 #if !NO_REGEX 175 if (pattern == NULL) 176 SET_NULL_PATTERN(info->compiled); 177 else if (compile_pattern(pattern, search_type, show_error, &info->compiled) < 0) 178 return -1; 179 #endif 180 /* Pattern compiled successfully; save the text too. */ 181 if (info->text != NULL) 182 free(info->text); 183 info->text = NULL; 184 if (pattern != NULL) 185 { 186 info->text = (char *) ecalloc(1, strlen(pattern)+1); 187 strcpy(info->text, pattern); 188 } 189 info->search_type = search_type; 190 return 0; 191 } 192 193 /* 194 * Initialize saved pattern to nothing. 195 */ 196 static void 197 init_pattern(info) 198 struct pattern_info *info; 199 { 200 SET_NULL_PATTERN(info->compiled); 201 info->text = NULL; 202 info->search_type = 0; 203 info->next = NULL; 204 } 205 206 /* 207 * Initialize search variables. 208 */ 209 public void 210 init_search(VOID_PARAM) 211 { 212 init_pattern(&search_info); 213 } 214 215 /* 216 * Determine which text conversions to perform before pattern matching. 217 */ 218 static int 219 get_cvt_ops(VOID_PARAM) 220 { 221 int ops = 0; 222 223 if (is_caseless && !re_handles_caseless) 224 ops |= CVT_TO_LC; 225 if (bs_mode == BS_SPECIAL) 226 ops |= CVT_BS; 227 if (bs_mode != BS_CONTROL) 228 ops |= CVT_CRLF; 229 if (ctldisp == OPT_ONPLUS) 230 ops |= CVT_ANSI; 231 return (ops); 232 } 233 234 /* 235 * Is there a previous (remembered) search pattern? 236 */ 237 static int 238 prev_pattern(info) 239 struct pattern_info *info; 240 { 241 #if !NO_REGEX 242 if ((info->search_type & SRCH_NO_REGEX) == 0) 243 return (!is_null_pattern(info->compiled)); 244 #endif 245 return (info->text != NULL); 246 } 247 248 #if HILITE_SEARCH 249 /* 250 * Repaint the hilites currently displayed on the screen. 251 * Repaint each line which contains highlighted text. 252 * If on==0, force all hilites off. 253 */ 254 public void 255 repaint_hilite(on) 256 int on; 257 { 258 int sindex; 259 POSITION pos; 260 int save_hide_hilite; 261 262 if (squished) 263 repaint(); 264 265 save_hide_hilite = hide_hilite; 266 if (!on) 267 { 268 if (hide_hilite) 269 return; 270 hide_hilite = 1; 271 } 272 273 if (!can_goto_line) 274 { 275 repaint(); 276 hide_hilite = save_hide_hilite; 277 return; 278 } 279 280 for (sindex = TOP; sindex < TOP + sc_height-1; sindex++) 281 { 282 pos = position(sindex); 283 if (pos == NULL_POSITION) 284 continue; 285 (void) forw_line(pos); 286 goto_line(sindex); 287 put_line(); 288 } 289 overlay_header(); 290 lower_left(); 291 hide_hilite = save_hide_hilite; 292 } 293 #endif 294 295 /* 296 * Clear the attn hilite. 297 */ 298 public void 299 clear_attn(VOID_PARAM) 300 { 301 #if HILITE_SEARCH 302 int sindex; 303 POSITION old_start_attnpos; 304 POSITION old_end_attnpos; 305 POSITION pos; 306 POSITION epos; 307 int moved = 0; 308 309 if (start_attnpos == NULL_POSITION) 310 return; 311 old_start_attnpos = start_attnpos; 312 old_end_attnpos = end_attnpos; 313 start_attnpos = end_attnpos = NULL_POSITION; 314 315 if (!can_goto_line) 316 { 317 repaint(); 318 return; 319 } 320 if (squished) 321 repaint(); 322 323 for (sindex = TOP; sindex < TOP + sc_height-1; sindex++) 324 { 325 pos = position(sindex); 326 if (pos == NULL_POSITION) 327 continue; 328 epos = position(sindex+1); 329 if (pos <= old_end_attnpos && 330 (epos == NULL_POSITION || epos > old_start_attnpos)) 331 { 332 (void) forw_line(pos); 333 goto_line(sindex); 334 put_line(); 335 moved = 1; 336 } 337 } 338 if (overlay_header()) 339 moved = 1; 340 if (moved) 341 lower_left(); 342 #endif 343 } 344 345 /* 346 * Toggle or clear search string highlighting. 347 */ 348 public void 349 undo_search(clear) 350 int clear; 351 { 352 clear_pattern(&search_info); 353 #if HILITE_SEARCH 354 if (clear) 355 { 356 clr_hilite(); 357 } else 358 { 359 if (hilite_anchor.first == NULL) 360 { 361 error("No previous regular expression", NULL_PARG); 362 return; 363 } 364 hide_hilite = !hide_hilite; 365 } 366 repaint_hilite(1); 367 #endif 368 } 369 370 #if HILITE_SEARCH 371 /* 372 * Clear the hilite list. 373 */ 374 public void 375 clr_hlist(anchor) 376 struct hilite_tree *anchor; 377 { 378 struct hilite_storage *hls; 379 struct hilite_storage *nexthls; 380 381 for (hls = anchor->first; hls != NULL; hls = nexthls) 382 { 383 nexthls = hls->next; 384 free((void*)hls->nodes); 385 free((void*)hls); 386 } 387 anchor->first = NULL; 388 anchor->current = NULL; 389 anchor->root = NULL; 390 391 anchor->lookaside = NULL; 392 393 prep_startpos = prep_endpos = NULL_POSITION; 394 } 395 396 public void 397 clr_hilite(VOID_PARAM) 398 { 399 clr_hlist(&hilite_anchor); 400 } 401 402 public void 403 clr_filter(VOID_PARAM) 404 { 405 clr_hlist(&filter_anchor); 406 } 407 408 struct hilite_node* 409 hlist_last(anchor) 410 struct hilite_tree *anchor; 411 { 412 struct hilite_node *n = anchor->root; 413 while (n != NULL && n->right != NULL) 414 n = n->right; 415 return n; 416 } 417 418 struct hilite_node* 419 hlist_next(n) 420 struct hilite_node *n; 421 { 422 return n->next; 423 } 424 425 struct hilite_node* 426 hlist_prev(n) 427 struct hilite_node *n; 428 { 429 return n->prev; 430 } 431 432 /* 433 * Find the node covering pos, or the node after it if no node covers it, 434 * or return NULL if pos is after the last range. Remember the found node, 435 * to speed up subsequent searches for the same or similar positions (if 436 * we return NULL, remember the last node.) 437 */ 438 struct hilite_node* 439 hlist_find(anchor, pos) 440 struct hilite_tree *anchor; 441 POSITION pos; 442 { 443 struct hilite_node *n, *m; 444 445 if (anchor->lookaside) 446 { 447 int steps = 0; 448 int hit = 0; 449 450 n = anchor->lookaside; 451 452 for (;;) 453 { 454 if (pos < n->r.hl_endpos) 455 { 456 if (n->prev == NULL || pos >= n->prev->r.hl_endpos) 457 { 458 hit = 1; 459 break; 460 } 461 } else if (n->next == NULL) 462 { 463 n = NULL; 464 hit = 1; 465 break; 466 } 467 468 /* 469 * If we don't find the right node within a small 470 * distance, don't keep doing a linear search! 471 */ 472 if (steps >= HILITE_LOOKASIDE_STEPS) 473 break; 474 steps++; 475 476 if (pos < n->r.hl_endpos) 477 anchor->lookaside = n = n->prev; 478 else 479 anchor->lookaside = n = n->next; 480 } 481 482 if (hit) 483 return n; 484 } 485 486 n = anchor->root; 487 m = NULL; 488 489 while (n != NULL) 490 { 491 if (pos < n->r.hl_startpos) 492 { 493 if (n->left != NULL) 494 { 495 m = n; 496 n = n->left; 497 continue; 498 } 499 break; 500 } 501 if (pos >= n->r.hl_endpos) 502 { 503 if (n->right != NULL) 504 { 505 n = n->right; 506 continue; 507 } 508 if (m != NULL) 509 { 510 n = m; 511 } else 512 { 513 m = n; 514 n = NULL; 515 } 516 } 517 break; 518 } 519 520 if (n != NULL) 521 anchor->lookaside = n; 522 else if (m != NULL) 523 anchor->lookaside = m; 524 525 return n; 526 } 527 528 /* 529 * Should any characters in a specified range be highlighted? 530 */ 531 static int 532 is_hilited_range(pos, epos) 533 POSITION pos; 534 POSITION epos; 535 { 536 struct hilite_node *n = hlist_find(&hilite_anchor, pos); 537 return (n != NULL && (epos == NULL_POSITION || epos > n->r.hl_startpos)); 538 } 539 540 /* 541 * Is a line "filtered" -- that is, should it be hidden? 542 */ 543 public int 544 is_filtered(pos) 545 POSITION pos; 546 { 547 struct hilite_node *n; 548 549 if (ch_getflags() & CH_HELPFILE) 550 return (0); 551 552 n = hlist_find(&filter_anchor, pos); 553 return (n != NULL && pos >= n->r.hl_startpos); 554 } 555 556 /* 557 * If pos is hidden, return the next position which isn't, otherwise 558 * just return pos. 559 */ 560 public POSITION 561 next_unfiltered(pos) 562 POSITION pos; 563 { 564 struct hilite_node *n; 565 566 if (ch_getflags() & CH_HELPFILE) 567 return (pos); 568 569 n = hlist_find(&filter_anchor, pos); 570 while (n != NULL && pos >= n->r.hl_startpos) 571 { 572 pos = n->r.hl_endpos; 573 n = n->next; 574 } 575 return (pos); 576 } 577 578 /* 579 * If pos is hidden, return the previous position which isn't or 0 if 580 * we're filtered right to the beginning, otherwise just return pos. 581 */ 582 public POSITION 583 prev_unfiltered(pos) 584 POSITION pos; 585 { 586 struct hilite_node *n; 587 588 if (ch_getflags() & CH_HELPFILE) 589 return (pos); 590 591 n = hlist_find(&filter_anchor, pos); 592 while (n != NULL && pos >= n->r.hl_startpos) 593 { 594 pos = n->r.hl_startpos; 595 if (pos == 0) 596 break; 597 pos--; 598 n = n->prev; 599 } 600 return (pos); 601 } 602 603 604 /* 605 * Should any characters in a specified range be highlighted? 606 * If nohide is nonzero, don't consider hide_hilite. 607 */ 608 public int 609 is_hilited_attr(pos, epos, nohide, p_matches) 610 POSITION pos; 611 POSITION epos; 612 int nohide; 613 int *p_matches; 614 { 615 int match; 616 617 if (p_matches != NULL) 618 *p_matches = 0; 619 620 if (!status_col && 621 start_attnpos != NULL_POSITION && 622 pos <= end_attnpos && 623 (epos == NULL_POSITION || epos >= start_attnpos)) 624 /* 625 * The attn line overlaps this range. 626 */ 627 return (AT_HILITE|AT_COLOR_ATTN); 628 629 match = is_hilited_range(pos, epos); 630 if (!match) 631 return (0); 632 633 if (p_matches == NULL) 634 /* 635 * Kinda kludgy way to recognize that caller is checking for 636 * hilite in status column. In this case we want to return 637 * hilite status even if hiliting is disabled or hidden. 638 */ 639 return (AT_HILITE|AT_COLOR_SEARCH); 640 641 /* 642 * Report matches, even if we're hiding highlights. 643 */ 644 *p_matches = 1; 645 646 if (hilite_search == 0) 647 /* 648 * Not doing highlighting. 649 */ 650 return (0); 651 652 if (!nohide && hide_hilite) 653 /* 654 * Highlighting is hidden. 655 */ 656 return (0); 657 658 return (AT_HILITE|AT_COLOR_SEARCH); 659 } 660 661 /* 662 * Tree node storage: get the current block of nodes if it has spare 663 * capacity, or create a new one if not. 664 */ 665 static struct hilite_storage* 666 hlist_getstorage(anchor) 667 struct hilite_tree *anchor; 668 { 669 int capacity = 1; 670 struct hilite_storage *s; 671 672 if (anchor->current) 673 { 674 if (anchor->current->used < anchor->current->capacity) 675 return anchor->current; 676 capacity = anchor->current->capacity * 2; 677 } 678 679 s = (struct hilite_storage *) ecalloc(1, sizeof(struct hilite_storage)); 680 s->nodes = (struct hilite_node *) ecalloc(capacity, sizeof(struct hilite_node)); 681 s->capacity = capacity; 682 s->used = 0; 683 s->next = NULL; 684 if (anchor->current) 685 anchor->current->next = s; 686 else 687 anchor->first = s; 688 anchor->current = s; 689 return s; 690 } 691 692 /* 693 * Tree node storage: retrieve a new empty node to be inserted into the 694 * tree. 695 */ 696 static struct hilite_node* 697 hlist_getnode(anchor) 698 struct hilite_tree *anchor; 699 { 700 struct hilite_storage *s = hlist_getstorage(anchor); 701 return &s->nodes[s->used++]; 702 } 703 704 /* 705 * Rotate the tree left around a pivot node. 706 */ 707 static void 708 hlist_rotate_left(anchor, n) 709 struct hilite_tree *anchor; 710 struct hilite_node *n; 711 { 712 struct hilite_node *np = n->parent; 713 struct hilite_node *nr = n->right; 714 struct hilite_node *nrl = n->right->left; 715 716 if (np != NULL) 717 { 718 if (n == np->left) 719 np->left = nr; 720 else 721 np->right = nr; 722 } else 723 { 724 anchor->root = nr; 725 } 726 nr->left = n; 727 n->right = nrl; 728 729 nr->parent = np; 730 n->parent = nr; 731 if (nrl != NULL) 732 nrl->parent = n; 733 } 734 735 /* 736 * Rotate the tree right around a pivot node. 737 */ 738 static void 739 hlist_rotate_right(anchor, n) 740 struct hilite_tree *anchor; 741 struct hilite_node *n; 742 { 743 struct hilite_node *np = n->parent; 744 struct hilite_node *nl = n->left; 745 struct hilite_node *nlr = n->left->right; 746 747 if (np != NULL) 748 { 749 if (n == np->right) 750 np->right = nl; 751 else 752 np->left = nl; 753 } else 754 { 755 anchor->root = nl; 756 } 757 nl->right = n; 758 n->left = nlr; 759 760 nl->parent = np; 761 n->parent = nl; 762 if (nlr != NULL) 763 nlr->parent = n; 764 } 765 766 767 /* 768 * Add a new hilite to a hilite list. 769 */ 770 static void 771 add_hilite(anchor, hl) 772 struct hilite_tree *anchor; 773 struct hilite *hl; 774 { 775 struct hilite_node *p, *n, *u; 776 777 /* Ignore empty ranges. */ 778 if (hl->hl_startpos >= hl->hl_endpos) 779 return; 780 781 p = anchor->root; 782 783 /* Inserting the very first node is trivial. */ 784 if (p == NULL) 785 { 786 n = hlist_getnode(anchor); 787 n->r = *hl; 788 anchor->root = n; 789 anchor->lookaside = n; 790 return; 791 } 792 793 /* 794 * Find our insertion point. If we come across any overlapping 795 * or adjoining existing ranges, shrink our range and discard 796 * if it become empty. 797 */ 798 for (;;) 799 { 800 if (hl->hl_startpos < p->r.hl_startpos) 801 { 802 if (hl->hl_endpos > p->r.hl_startpos) 803 hl->hl_endpos = p->r.hl_startpos; 804 if (p->left != NULL) 805 { 806 p = p->left; 807 continue; 808 } 809 break; 810 } 811 if (hl->hl_startpos < p->r.hl_endpos) { 812 hl->hl_startpos = p->r.hl_endpos; 813 if (hl->hl_startpos >= hl->hl_endpos) 814 return; 815 } 816 if (p->right != NULL) 817 { 818 p = p->right; 819 continue; 820 } 821 break; 822 } 823 824 /* 825 * Now we're at the right leaf, again check for contiguous ranges 826 * and extend the existing node if possible to avoid the 827 * insertion. Otherwise insert a new node at the leaf. 828 */ 829 if (hl->hl_startpos < p->r.hl_startpos) { 830 if (hl->hl_endpos == p->r.hl_startpos) 831 { 832 p->r.hl_startpos = hl->hl_startpos; 833 return; 834 } 835 if (p->prev != NULL && p->prev->r.hl_endpos == hl->hl_startpos) 836 { 837 p->prev->r.hl_endpos = hl->hl_endpos; 838 return; 839 } 840 841 p->left = n = hlist_getnode(anchor); 842 n->next = p; 843 if (p->prev != NULL) 844 { 845 n->prev = p->prev; 846 p->prev->next = n; 847 } 848 p->prev = n; 849 } else { 850 if (p->r.hl_endpos == hl->hl_startpos) 851 { 852 p->r.hl_endpos = hl->hl_endpos; 853 return; 854 } 855 if (p->next != NULL && hl->hl_endpos == p->next->r.hl_startpos) { 856 p->next->r.hl_startpos = hl->hl_startpos; 857 return; 858 } 859 860 p->right = n = hlist_getnode(anchor); 861 n->prev = p; 862 if (p->next != NULL) 863 { 864 n->next = p->next; 865 p->next->prev = n; 866 } 867 p->next = n; 868 } 869 n->parent = p; 870 n->red = 1; 871 n->r = *hl; 872 873 /* 874 * The tree is in the correct order and covers the right ranges 875 * now, but may have become unbalanced. Rebalance it using the 876 * standard red-black tree constraints and operations. 877 */ 878 for (;;) 879 { 880 /* case 1 - current is root, root is always black */ 881 if (n->parent == NULL) 882 { 883 n->red = 0; 884 break; 885 } 886 887 /* case 2 - parent is black, we can always be red */ 888 if (!n->parent->red) 889 break; 890 891 /* 892 * constraint: because the root must be black, if our 893 * parent is red it cannot be the root therefore we must 894 * have a grandparent 895 */ 896 897 /* 898 * case 3 - parent and uncle are red, repaint them black, 899 * the grandparent red, and start again at the grandparent. 900 */ 901 u = n->parent->parent->left; 902 if (n->parent == u) 903 u = n->parent->parent->right; 904 if (u != NULL && u->red) 905 { 906 n->parent->red = 0; 907 u->red = 0; 908 n = n->parent->parent; 909 n->red = 1; 910 continue; 911 } 912 913 /* 914 * case 4 - parent is red but uncle is black, parent and 915 * grandparent on opposite sides. We need to start 916 * changing the structure now. This and case 5 will shorten 917 * our branch and lengthen the sibling, between them 918 * restoring balance. 919 */ 920 if (n == n->parent->right && 921 n->parent == n->parent->parent->left) 922 { 923 hlist_rotate_left(anchor, n->parent); 924 n = n->left; 925 } else if (n == n->parent->left && 926 n->parent == n->parent->parent->right) 927 { 928 hlist_rotate_right(anchor, n->parent); 929 n = n->right; 930 } 931 932 /* 933 * case 5 - parent is red but uncle is black, parent and 934 * grandparent on same side 935 */ 936 n->parent->red = 0; 937 n->parent->parent->red = 1; 938 if (n == n->parent->left) 939 hlist_rotate_right(anchor, n->parent->parent); 940 else 941 hlist_rotate_left(anchor, n->parent->parent); 942 break; 943 } 944 } 945 946 /* 947 * Highlight every character in a range of displayed characters. 948 */ 949 static void 950 create_hilites(linepos, start_index, end_index, chpos) 951 POSITION linepos; 952 int start_index; 953 int end_index; 954 int *chpos; 955 { 956 struct hilite hl; 957 int i; 958 959 /* Start the first hilite. */ 960 hl.hl_startpos = linepos + chpos[start_index]; 961 962 /* 963 * Step through the displayed chars. 964 * If the source position (before cvt) of the char is one more 965 * than the source pos of the previous char (the usual case), 966 * just increase the size of the current hilite by one. 967 * Otherwise (there are backspaces or something involved), 968 * finish the current hilite and start a new one. 969 */ 970 for (i = start_index+1; i <= end_index; i++) 971 { 972 if (chpos[i] != chpos[i-1] + 1 || i == end_index) 973 { 974 hl.hl_endpos = linepos + chpos[i-1] + 1; 975 add_hilite(&hilite_anchor, &hl); 976 /* Start new hilite unless this is the last char. */ 977 if (i < end_index) 978 { 979 hl.hl_startpos = linepos + chpos[i]; 980 } 981 } 982 } 983 } 984 985 /* 986 * Make a hilite for each string in a physical line which matches 987 * the current pattern. 988 * sp,ep delimit the first match already found. 989 */ 990 static void 991 hilite_line(linepos, line, line_len, chpos, sp, ep, cvt_ops) 992 POSITION linepos; 993 char *line; 994 int line_len; 995 int *chpos; 996 char *sp; 997 char *ep; 998 int cvt_ops; 999 { 1000 char *searchp; 1001 char *line_end = line + line_len; 1002 1003 /* 1004 * sp and ep delimit the first match in the line. 1005 * Mark the corresponding file positions, then 1006 * look for further matches and mark them. 1007 * {{ This technique, of calling match_pattern on subsequent 1008 * substrings of the line, may mark more than is correct 1009 * if the pattern starts with "^". This bug is fixed 1010 * for those regex functions that accept a notbol parameter 1011 * (currently POSIX, PCRE and V8-with-regexec2). }} 1012 */ 1013 searchp = line; 1014 do { 1015 if (sp == NULL || ep == NULL) 1016 return; 1017 create_hilites(linepos, sp-line, ep-line, chpos); 1018 /* 1019 * If we matched more than zero characters, 1020 * move to the first char after the string we matched. 1021 * If we matched zero, just move to the next char. 1022 */ 1023 if (ep > searchp) 1024 searchp = ep; 1025 else if (searchp != line_end) 1026 searchp++; 1027 else /* end of line */ 1028 break; 1029 } while (match_pattern(info_compiled(&search_info), search_info.text, 1030 searchp, line_end - searchp, &sp, &ep, 1, search_info.search_type)); 1031 } 1032 #endif 1033 1034 #if HILITE_SEARCH 1035 /* 1036 * Find matching text which is currently on screen and highlight it. 1037 */ 1038 static void 1039 hilite_screen(VOID_PARAM) 1040 { 1041 struct scrpos scrpos; 1042 1043 get_scrpos(&scrpos, TOP); 1044 if (scrpos.pos == NULL_POSITION) 1045 return; 1046 prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1); 1047 repaint_hilite(1); 1048 } 1049 1050 /* 1051 * Change highlighting parameters. 1052 */ 1053 public void 1054 chg_hilite(VOID_PARAM) 1055 { 1056 /* 1057 * Erase any highlights currently on screen. 1058 */ 1059 clr_hilite(); 1060 hide_hilite = 0; 1061 1062 if (hilite_search == OPT_ONPLUS) 1063 /* 1064 * Display highlights. 1065 */ 1066 hilite_screen(); 1067 } 1068 #endif 1069 1070 /* 1071 * Figure out where to start a search. 1072 */ 1073 static POSITION 1074 search_pos(search_type) 1075 int search_type; 1076 { 1077 POSITION pos; 1078 int sindex; 1079 1080 if (empty_screen()) 1081 { 1082 /* 1083 * Start at the beginning (or end) of the file. 1084 * The empty_screen() case is mainly for 1085 * command line initiated searches; 1086 * for example, "+/xyz" on the command line. 1087 * Also for multi-file (SRCH_PAST_EOF) searches. 1088 */ 1089 if (search_type & SRCH_FORW) 1090 { 1091 pos = ch_zero(); 1092 } else 1093 { 1094 pos = ch_length(); 1095 if (pos == NULL_POSITION) 1096 { 1097 (void) ch_end_seek(); 1098 pos = ch_length(); 1099 } 1100 } 1101 sindex = 0; 1102 } else 1103 { 1104 int add_one = 0; 1105 1106 if (how_search == OPT_ON) 1107 { 1108 /* 1109 * Search does not include current screen. 1110 */ 1111 if (search_type & SRCH_FORW) 1112 sindex = sc_height-1; /* BOTTOM_PLUS_ONE */ 1113 else 1114 sindex = 0; /* TOP */ 1115 } else if (how_search == OPT_ONPLUS && !(search_type & SRCH_AFTER_TARGET)) 1116 { 1117 /* 1118 * Search includes all of displayed screen. 1119 */ 1120 if (search_type & SRCH_FORW) 1121 sindex = 0; /* TOP */ 1122 else 1123 sindex = sc_height-1; /* BOTTOM_PLUS_ONE */ 1124 } else 1125 { 1126 /* 1127 * Search includes the part of current screen beyond the jump target. 1128 * It starts at the jump target (if searching backwards), 1129 * or at the jump target plus one (if forwards). 1130 */ 1131 sindex = sindex_from_sline(jump_sline); 1132 if (search_type & SRCH_FORW) 1133 add_one = 1; 1134 } 1135 pos = position(sindex); 1136 if (add_one) 1137 pos = forw_raw_line(pos, (char **)NULL, (int *)NULL); 1138 } 1139 1140 /* 1141 * If the line is empty, look around for a plausible starting place. 1142 */ 1143 if (search_type & SRCH_FORW) 1144 { 1145 while (pos == NULL_POSITION) 1146 { 1147 if (++sindex >= sc_height) 1148 break; 1149 pos = position(sindex); 1150 } 1151 } else 1152 { 1153 while (pos == NULL_POSITION) 1154 { 1155 if (--sindex < 0) 1156 break; 1157 pos = position(sindex); 1158 } 1159 } 1160 return (pos); 1161 } 1162 1163 /* 1164 * Check to see if the line matches the filter pattern. 1165 * If so, add an entry to the filter list. 1166 */ 1167 #if HILITE_SEARCH 1168 static int 1169 matches_filters(pos, cline, line_len, chpos, linepos, sp, ep) 1170 POSITION pos; 1171 char *cline; 1172 int line_len; 1173 int *chpos; 1174 POSITION linepos; 1175 char **sp; 1176 char **ep; 1177 { 1178 struct pattern_info *filter; 1179 1180 for (filter = filter_infos; filter != NULL; filter = filter->next) 1181 { 1182 int line_filter = match_pattern(info_compiled(filter), filter->text, 1183 cline, line_len, sp, ep, 0, filter->search_type); 1184 if (line_filter) 1185 { 1186 struct hilite hl; 1187 hl.hl_startpos = linepos; 1188 hl.hl_endpos = pos; 1189 add_hilite(&filter_anchor, &hl); 1190 free(cline); 1191 free(chpos); 1192 return (1); 1193 } 1194 } 1195 return (0); 1196 } 1197 #endif 1198 1199 /* 1200 * Get the position of the first char in the screen line which 1201 * puts tpos on screen. 1202 */ 1203 static POSITION 1204 get_lastlinepos(pos, tpos, sheight) 1205 POSITION pos; 1206 POSITION tpos; 1207 int sheight; 1208 { 1209 int nlines; 1210 1211 for (nlines = 0;; nlines++) 1212 { 1213 POSITION npos = forw_line(pos); 1214 if (npos > tpos) 1215 { 1216 if (nlines < sheight) 1217 return NULL_POSITION; 1218 return pos; 1219 } 1220 pos = npos; 1221 } 1222 } 1223 1224 /* 1225 * Get the segment index of tpos in the line starting at pos. 1226 * A segment is a string of printable chars that fills the screen width. 1227 */ 1228 static int 1229 get_seg(pos, tpos) 1230 POSITION pos; 1231 POSITION tpos; 1232 { 1233 int seg; 1234 1235 for (seg = 0;; seg++) 1236 { 1237 POSITION npos = forw_line_seg(pos, FALSE, FALSE, TRUE); 1238 if (npos > tpos) 1239 return seg; 1240 pos = npos; 1241 } 1242 } 1243 1244 /* 1245 * Search a subset of the file, specified by start/end position. 1246 */ 1247 static int 1248 search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos, plastlinepos) 1249 POSITION pos; 1250 POSITION endpos; 1251 int search_type; 1252 int matches; 1253 int maxlines; 1254 POSITION *plinepos; 1255 POSITION *pendpos; 1256 POSITION *plastlinepos; 1257 { 1258 char *line; 1259 char *cline; 1260 int line_len; 1261 LINENUM linenum; 1262 char *sp, *ep; 1263 int line_match; 1264 int cvt_ops; 1265 int cvt_len; 1266 int *chpos; 1267 POSITION linepos, oldpos; 1268 int swidth = sc_width - line_pfx_width(); 1269 int sheight = sc_height - sindex_from_sline(jump_sline); 1270 1271 linenum = find_linenum(pos); 1272 oldpos = pos; 1273 /* When the search wraps around, end at starting position. */ 1274 if ((search_type & SRCH_WRAP) && endpos == NULL_POSITION) 1275 endpos = pos; 1276 for (;;) 1277 { 1278 /* 1279 * Get lines until we find a matching one or until 1280 * we hit end-of-file (or beginning-of-file if we're 1281 * going backwards), or until we hit the end position. 1282 */ 1283 if (ABORT_SIGS()) 1284 { 1285 /* 1286 * A signal aborts the search. 1287 */ 1288 return (-1); 1289 } 1290 1291 if ((endpos != NULL_POSITION && !(search_type & SRCH_WRAP) && 1292 (((search_type & SRCH_FORW) && pos >= endpos) || 1293 ((search_type & SRCH_BACK) && pos <= endpos))) || maxlines == 0) 1294 { 1295 /* 1296 * Reached end position without a match. 1297 */ 1298 if (pendpos != NULL) 1299 *pendpos = pos; 1300 return (matches); 1301 } 1302 if (maxlines > 0) 1303 maxlines--; 1304 1305 if (search_type & SRCH_FORW) 1306 { 1307 /* 1308 * Read the next line, and save the 1309 * starting position of that line in linepos. 1310 */ 1311 linepos = pos; 1312 pos = forw_raw_line(pos, &line, &line_len); 1313 if (linenum != 0) 1314 linenum++; 1315 } else 1316 { 1317 /* 1318 * Read the previous line and save the 1319 * starting position of that line in linepos. 1320 */ 1321 pos = back_raw_line(pos, &line, &line_len); 1322 linepos = pos; 1323 if (linenum != 0) 1324 linenum--; 1325 } 1326 1327 if (pos == NULL_POSITION) 1328 { 1329 /* 1330 * Reached EOF/BOF without a match. 1331 */ 1332 if (search_type & SRCH_WRAP) 1333 { 1334 /* 1335 * The search wraps around the current file, so 1336 * try to continue at BOF/EOF. 1337 */ 1338 if (search_type & SRCH_FORW) 1339 { 1340 pos = ch_zero(); 1341 } else 1342 { 1343 pos = ch_length(); 1344 if (pos == NULL_POSITION) 1345 { 1346 (void) ch_end_seek(); 1347 pos = ch_length(); 1348 } 1349 } 1350 if (pos != NULL_POSITION) { 1351 /* 1352 * Wrap-around was successful. Clear 1353 * the flag so we don't wrap again, and 1354 * continue the search at new pos. 1355 */ 1356 search_type &= ~SRCH_WRAP; 1357 linenum = find_linenum(pos); 1358 continue; 1359 } 1360 } 1361 if (pendpos != NULL) 1362 *pendpos = oldpos; 1363 return (matches); 1364 } 1365 1366 /* 1367 * If we're using line numbers, we might as well 1368 * remember the information we have now (the position 1369 * and line number of the current line). 1370 * Don't do it for every line because it slows down 1371 * the search. Remember the line number only if 1372 * we're "far" from the last place we remembered it. 1373 */ 1374 if (linenums && abs((int)(pos - oldpos)) > 2048) 1375 add_lnum(linenum, pos); 1376 oldpos = pos; 1377 1378 #if HILITE_SEARCH 1379 if (is_filtered(linepos)) 1380 continue; 1381 #endif 1382 1383 /* 1384 * If it's a caseless search, convert the line to lowercase. 1385 * If we're doing backspace processing, delete backspaces. 1386 */ 1387 cvt_ops = get_cvt_ops(); 1388 cvt_len = cvt_length(line_len, cvt_ops); 1389 cline = (char *) ecalloc(1, cvt_len); 1390 chpos = cvt_alloc_chpos(cvt_len); 1391 cvt_text(cline, line, chpos, &line_len, cvt_ops); 1392 1393 #if HILITE_SEARCH 1394 /* 1395 * If any filters are in effect, ignore non-matching lines. 1396 */ 1397 if (filter_infos != NULL && 1398 ((search_type & SRCH_FIND_ALL) || 1399 prep_startpos == NULL_POSITION || 1400 linepos < prep_startpos || linepos >= prep_endpos)) { 1401 if (matches_filters(pos, cline, line_len, chpos, linepos, &sp, &ep)) 1402 continue; 1403 } 1404 #endif 1405 1406 /* 1407 * Test the next line to see if we have a match. 1408 * We are successful if we either want a match and got one, 1409 * or if we want a non-match and got one. 1410 */ 1411 if (prev_pattern(&search_info)) 1412 { 1413 line_match = match_pattern(info_compiled(&search_info), search_info.text, 1414 cline, line_len, &sp, &ep, 0, search_type); 1415 if (line_match) 1416 { 1417 /* 1418 * Got a match. 1419 */ 1420 if (search_type & SRCH_FIND_ALL) 1421 { 1422 #if HILITE_SEARCH 1423 /* 1424 * We are supposed to find all matches in the range. 1425 * Just add the matches in this line to the 1426 * hilite list and keep searching. 1427 */ 1428 hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops); 1429 #endif 1430 } else if (--matches <= 0) 1431 { 1432 /* 1433 * Found the one match we're looking for. 1434 * Return it. 1435 */ 1436 #if HILITE_SEARCH 1437 if (hilite_search == OPT_ON) 1438 { 1439 /* 1440 * Clear the hilite list and add only 1441 * the matches in this one line. 1442 */ 1443 clr_hilite(); 1444 hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops); 1445 } 1446 #endif 1447 if (chop_line()) 1448 { 1449 /* 1450 * If necessary, shift horizontally to make sure 1451 * search match is fully visible. 1452 */ 1453 if (sp != NULL && ep != NULL) 1454 { 1455 int start_off = sp - cline; 1456 int end_off = ep - cline; 1457 int save_hshift = hshift; 1458 int sshift; 1459 int eshift; 1460 hshift = 0; /* make get_seg count screen lines */ 1461 sshift = swidth * get_seg(linepos, linepos + chpos[start_off]); 1462 eshift = swidth * get_seg(linepos, linepos + chpos[end_off]); 1463 if (sshift >= save_hshift && eshift <= save_hshift) 1464 { 1465 hshift = save_hshift; 1466 } else 1467 { 1468 hshift = sshift; 1469 screen_trashed = 1; 1470 } 1471 } 1472 } else if (plastlinepos != NULL) 1473 { 1474 /* 1475 * If the line is so long that the highlighted match 1476 * won't be seen when the line is displayed normally 1477 * (starting at the first char) because it fills the whole 1478 * screen and more, scroll forward until the last char 1479 * of the match appears in the last line on the screen. 1480 * lastlinepos is the position of the first char of that last line. 1481 */ 1482 if (ep != NULL) 1483 { 1484 int end_off = ep - cline; 1485 if (end_off >= swidth * sheight / 4) /* heuristic */ 1486 *plastlinepos = get_lastlinepos(linepos, linepos + chpos[end_off], sheight); 1487 } 1488 } 1489 free(cline); 1490 free(chpos); 1491 if (plinepos != NULL) 1492 *plinepos = linepos; 1493 return (0); 1494 } 1495 } 1496 } 1497 free(cline); 1498 free(chpos); 1499 } 1500 } 1501 1502 /* 1503 * search for a pattern in history. If found, compile that pattern. 1504 */ 1505 static int 1506 hist_pattern(search_type) 1507 int search_type; 1508 { 1509 #if CMD_HISTORY 1510 char *pattern; 1511 1512 set_mlist(ml_search, 0); 1513 pattern = cmd_lastpattern(); 1514 if (pattern == NULL) 1515 return (0); 1516 1517 if (set_pattern(&search_info, pattern, search_type, 1) < 0) 1518 return (-1); 1519 1520 #if HILITE_SEARCH 1521 if (hilite_search == OPT_ONPLUS && !hide_hilite) 1522 hilite_screen(); 1523 #endif 1524 1525 return (1); 1526 #else /* CMD_HISTORY */ 1527 return (0); 1528 #endif /* CMD_HISTORY */ 1529 } 1530 1531 /* 1532 * Change the caseless-ness of searches. 1533 * Updates the internal search state to reflect a change in the -i flag. 1534 */ 1535 public void 1536 chg_caseless(VOID_PARAM) 1537 { 1538 if (!is_ucase_pattern) 1539 { 1540 /* 1541 * Pattern did not have uppercase. 1542 * Set the search caselessness to the global caselessness. 1543 */ 1544 is_caseless = caseless; 1545 /* 1546 * If regex handles caseless, we need to discard 1547 * the pattern which was compiled with the old caseless. 1548 */ 1549 if (!re_handles_caseless) 1550 /* We handle caseless, so the pattern doesn't change. */ 1551 return; 1552 } 1553 /* 1554 * Regenerate the pattern using the new state. 1555 */ 1556 clear_pattern(&search_info); 1557 (void) hist_pattern(search_info.search_type); 1558 } 1559 1560 /* 1561 * Search for the n-th occurrence of a specified pattern, 1562 * either forward or backward. 1563 * Return the number of matches not yet found in this file 1564 * (that is, n minus the number of matches found). 1565 * Return -1 if the search should be aborted. 1566 * Caller may continue the search in another file 1567 * if less than n matches are found in this file. 1568 */ 1569 public int 1570 search(search_type, pattern, n) 1571 int search_type; 1572 char *pattern; 1573 int n; 1574 { 1575 POSITION pos; 1576 POSITION opos; 1577 POSITION lastlinepos = NULL_POSITION; 1578 1579 if (pattern == NULL || *pattern == '\0') 1580 { 1581 /* 1582 * A null pattern means use the previously compiled pattern. 1583 */ 1584 search_type |= SRCH_AFTER_TARGET; 1585 if (!prev_pattern(&search_info)) 1586 { 1587 int r = hist_pattern(search_type); 1588 if (r == 0) 1589 error("No previous regular expression", NULL_PARG); 1590 if (r <= 0) 1591 return (-1); 1592 } 1593 if ((search_type & SRCH_NO_REGEX) != 1594 (search_info.search_type & SRCH_NO_REGEX)) 1595 { 1596 error("Please re-enter search pattern", NULL_PARG); 1597 return -1; 1598 } 1599 #if HILITE_SEARCH 1600 if (hilite_search == OPT_ON || status_col) 1601 { 1602 /* 1603 * Erase the highlights currently on screen. 1604 * If the search fails, we'll redisplay them later. 1605 */ 1606 repaint_hilite(0); 1607 } 1608 if (hilite_search == OPT_ONPLUS && hide_hilite) 1609 { 1610 /* 1611 * Highlight any matches currently on screen, 1612 * before we actually start the search. 1613 */ 1614 hide_hilite = 0; 1615 hilite_screen(); 1616 } 1617 hide_hilite = 0; 1618 #endif 1619 } else 1620 { 1621 /* 1622 * Compile the pattern. 1623 */ 1624 int show_error = !(search_type & SRCH_INCR); 1625 if (set_pattern(&search_info, pattern, search_type, show_error) < 0) 1626 return (-1); 1627 #if HILITE_SEARCH 1628 if (hilite_search || status_col) 1629 { 1630 /* 1631 * Erase the highlights currently on screen. 1632 * Also permanently delete them from the hilite list. 1633 */ 1634 repaint_hilite(0); 1635 hide_hilite = 0; 1636 clr_hilite(); 1637 } 1638 if (hilite_search == OPT_ONPLUS || status_col) 1639 { 1640 /* 1641 * Highlight any matches currently on screen, 1642 * before we actually start the search. 1643 */ 1644 hilite_screen(); 1645 } 1646 #endif 1647 } 1648 1649 /* 1650 * Figure out where to start the search. 1651 */ 1652 pos = search_pos(search_type); 1653 opos = position(sindex_from_sline(jump_sline)); 1654 if (pos == NULL_POSITION) 1655 { 1656 /* 1657 * Can't find anyplace to start searching from. 1658 */ 1659 if (search_type & SRCH_PAST_EOF) 1660 return (n); 1661 #if HILITE_SEARCH 1662 if (hilite_search == OPT_ON || status_col) 1663 repaint_hilite(1); 1664 #endif 1665 error("Nothing to search", NULL_PARG); 1666 return (-1); 1667 } 1668 1669 n = search_range(pos, NULL_POSITION, search_type, n, -1, 1670 &pos, (POSITION*)NULL, &lastlinepos); 1671 if (n != 0) 1672 { 1673 /* 1674 * Search was unsuccessful. 1675 */ 1676 #if HILITE_SEARCH 1677 if ((hilite_search == OPT_ON || status_col) && n > 0) 1678 /* 1679 * Redisplay old hilites. 1680 */ 1681 repaint_hilite(1); 1682 #endif 1683 return (n); 1684 } 1685 1686 if (!(search_type & SRCH_NO_MOVE)) 1687 { 1688 /* 1689 * Go to the matching line. 1690 */ 1691 if (lastlinepos != NULL_POSITION) 1692 jump_loc(lastlinepos, BOTTOM); 1693 else if (pos != opos) 1694 jump_loc(pos, jump_sline); 1695 } 1696 1697 #if HILITE_SEARCH 1698 if (hilite_search == OPT_ON || status_col) 1699 /* 1700 * Display new hilites in the matching line. 1701 */ 1702 repaint_hilite(1); 1703 #endif 1704 return (0); 1705 } 1706 1707 #if HILITE_SEARCH 1708 /* 1709 * Prepare hilites in a given range of the file. 1710 * 1711 * The pair (prep_startpos,prep_endpos) delimits a contiguous region 1712 * of the file that has been "prepared"; that is, scanned for matches for 1713 * the current search pattern, and hilites have been created for such matches. 1714 * If prep_startpos == NULL_POSITION, the prep region is empty. 1715 * If prep_endpos == NULL_POSITION, the prep region extends to EOF. 1716 * prep_hilite asks that the range (spos,epos) be covered by the prep region. 1717 */ 1718 public void 1719 prep_hilite(spos, epos, maxlines) 1720 POSITION spos; 1721 POSITION epos; 1722 int maxlines; 1723 { 1724 POSITION nprep_startpos = prep_startpos; 1725 POSITION nprep_endpos = prep_endpos; 1726 POSITION new_epos; 1727 POSITION max_epos; 1728 int result; 1729 int i; 1730 1731 /* 1732 * Search beyond where we're asked to search, so the prep region covers 1733 * more than we need. Do one big search instead of a bunch of small ones. 1734 */ 1735 #define SEARCH_MORE (3*size_linebuf) 1736 1737 if (!prev_pattern(&search_info) && !is_filtering()) 1738 return; 1739 1740 /* 1741 * Make sure our prep region always starts at the beginning of 1742 * a line. (search_range takes care of the end boundary below.) 1743 */ 1744 spos = back_raw_line(spos+1, (char **)NULL, (int *)NULL); 1745 1746 /* 1747 * If we're limited to a max number of lines, figure out the 1748 * file position we should stop at. 1749 */ 1750 if (maxlines < 0) 1751 max_epos = NULL_POSITION; 1752 else 1753 { 1754 max_epos = spos; 1755 for (i = 0; i < maxlines; i++) 1756 max_epos = forw_raw_line(max_epos, (char **)NULL, (int *)NULL); 1757 } 1758 1759 /* 1760 * Find two ranges: 1761 * The range that we need to search (spos,epos); and the range that 1762 * the "prep" region will then cover (nprep_startpos,nprep_endpos). 1763 */ 1764 1765 if (prep_startpos == NULL_POSITION || 1766 (epos != NULL_POSITION && epos < prep_startpos) || 1767 spos > prep_endpos) 1768 { 1769 /* 1770 * New range is not contiguous with old prep region. 1771 * Discard the old prep region and start a new one. 1772 */ 1773 clr_hilite(); 1774 clr_filter(); 1775 if (epos != NULL_POSITION) 1776 epos += SEARCH_MORE; 1777 nprep_startpos = spos; 1778 } else 1779 { 1780 /* 1781 * New range partially or completely overlaps old prep region. 1782 */ 1783 if (epos == NULL_POSITION) 1784 { 1785 /* 1786 * New range goes to end of file. 1787 */ 1788 ; 1789 } else if (epos > prep_endpos) 1790 { 1791 /* 1792 * New range ends after old prep region. 1793 * Extend prep region to end at end of new range. 1794 */ 1795 epos += SEARCH_MORE; 1796 } else /* (epos <= prep_endpos) */ 1797 { 1798 /* 1799 * New range ends within old prep region. 1800 * Truncate search to end at start of old prep region. 1801 */ 1802 epos = prep_startpos; 1803 } 1804 1805 if (spos < prep_startpos) 1806 { 1807 /* 1808 * New range starts before old prep region. 1809 * Extend old prep region backwards to start at 1810 * start of new range. 1811 */ 1812 if (spos < SEARCH_MORE) 1813 spos = 0; 1814 else 1815 spos -= SEARCH_MORE; 1816 nprep_startpos = spos; 1817 } else /* (spos >= prep_startpos) */ 1818 { 1819 /* 1820 * New range starts within or after old prep region. 1821 * Trim search to start at end of old prep region. 1822 */ 1823 spos = prep_endpos; 1824 } 1825 } 1826 1827 if (epos != NULL_POSITION && max_epos != NULL_POSITION && 1828 epos > max_epos) 1829 /* 1830 * Don't go past the max position we're allowed. 1831 */ 1832 epos = max_epos; 1833 1834 if (epos == NULL_POSITION || epos > spos) 1835 { 1836 int search_type = SRCH_FORW | SRCH_FIND_ALL; 1837 search_type |= (search_info.search_type & SRCH_NO_REGEX); 1838 for (;;) 1839 { 1840 result = search_range(spos, epos, search_type, 0, maxlines, (POSITION*)NULL, &new_epos, (POSITION*)NULL); 1841 if (result < 0) 1842 return; 1843 if (prep_endpos == NULL_POSITION || new_epos > prep_endpos) 1844 nprep_endpos = new_epos; 1845 1846 /* 1847 * Check both ends of the resulting prep region to 1848 * make sure they're not filtered. If they are, 1849 * keep going at least one more line until we find 1850 * something that isn't filtered, or hit the end. 1851 */ 1852 if (prep_endpos == NULL_POSITION || nprep_endpos > prep_endpos) 1853 { 1854 if (new_epos >= nprep_endpos && is_filtered(new_epos-1)) 1855 { 1856 spos = nprep_endpos; 1857 epos = forw_raw_line(nprep_endpos, (char **)NULL, (int *)NULL); 1858 if (epos == NULL_POSITION) 1859 break; 1860 maxlines = 1; 1861 continue; 1862 } 1863 } 1864 1865 if (prep_startpos == NULL_POSITION || nprep_startpos < prep_startpos) 1866 { 1867 if (nprep_startpos > 0 && is_filtered(nprep_startpos)) 1868 { 1869 epos = nprep_startpos; 1870 spos = back_raw_line(nprep_startpos, (char **)NULL, (int *)NULL); 1871 if (spos == NULL_POSITION) 1872 break; 1873 nprep_startpos = spos; 1874 maxlines = 1; 1875 continue; 1876 } 1877 } 1878 break; 1879 } 1880 } 1881 prep_startpos = nprep_startpos; 1882 prep_endpos = nprep_endpos; 1883 } 1884 1885 /* 1886 * Set the pattern to be used for line filtering. 1887 */ 1888 public void 1889 set_filter_pattern(pattern, search_type) 1890 char *pattern; 1891 int search_type; 1892 { 1893 struct pattern_info *filter; 1894 1895 clr_filter(); 1896 if (pattern == NULL || *pattern == '\0') 1897 { 1898 /* Clear and free all filters. */ 1899 for (filter = filter_infos; filter != NULL; ) 1900 { 1901 struct pattern_info *next_filter = filter->next; 1902 clear_pattern(filter); 1903 free(filter); 1904 filter = next_filter; 1905 } 1906 filter_infos = NULL; 1907 } else 1908 { 1909 /* Create a new filter and add it to the filter_infos list. */ 1910 filter = ecalloc(1, sizeof(struct pattern_info)); 1911 init_pattern(filter); 1912 if (set_pattern(filter, pattern, search_type, 1) < 0) 1913 { 1914 free(filter); 1915 return; 1916 } 1917 filter->next = filter_infos; 1918 filter_infos = filter; 1919 } 1920 screen_trashed = 1; 1921 } 1922 1923 /* 1924 * Is there a line filter in effect? 1925 */ 1926 public int 1927 is_filtering(VOID_PARAM) 1928 { 1929 if (ch_getflags() & CH_HELPFILE) 1930 return (0); 1931 return (filter_infos != NULL); 1932 } 1933 #endif 1934 1935 #if HAVE_V8_REGCOMP 1936 /* 1937 * This function is called by the V8 regcomp to report 1938 * errors in regular expressions. 1939 */ 1940 public int reg_show_error = 1; 1941 1942 void 1943 regerror(s) 1944 char *s; 1945 { 1946 PARG parg; 1947 1948 if (!reg_show_error) 1949 return; 1950 parg.p_string = s; 1951 error("%s", &parg); 1952 } 1953 #endif 1954 1955