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