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