1 /* 2 * Copyright (C) 1984-2025 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 jump_sline; 27 extern int bs_mode; 28 extern int proc_backspace; 29 extern int proc_return; 30 extern int ctldisp; 31 extern int status_col; 32 extern void *ml_search; 33 extern POSITION start_attnpos; 34 extern POSITION end_attnpos; 35 extern int utf_mode; 36 extern int sc_width; 37 extern int sc_height; 38 extern int hshift; 39 extern int match_shift; 40 extern int nosearch_header_lines; 41 extern int nosearch_header_cols; 42 extern int header_lines; 43 extern int header_cols; 44 extern LWCHAR rscroll_char; 45 #if HILITE_SEARCH 46 extern int hilite_search; 47 extern lbool squished; 48 extern int can_goto_line; 49 static lbool hide_hilite; 50 static POSITION prep_startpos; 51 static POSITION prep_endpos; 52 public POSITION header_start_pos = NULL_POSITION; 53 static POSITION header_end_pos; 54 public lbool search_wrapped = FALSE; 55 public POSITION search_incr_start = NULL_POSITION; 56 #if OSC8_LINK 57 public POSITION osc8_linepos = NULL_POSITION; 58 public POSITION osc8_match_start = NULL_POSITION; 59 public POSITION osc8_match_end = NULL_POSITION; 60 public POSITION osc8_params_start = NULL_POSITION; 61 public POSITION osc8_params_end = NULL_POSITION; 62 public POSITION osc8_uri_start = NULL_POSITION; 63 public POSITION osc8_uri_end = NULL_POSITION; 64 public POSITION osc8_text_start = NULL_POSITION; 65 public POSITION osc8_text_end = NULL_POSITION; 66 char *osc8_path = NULL; 67 char *osc8_uri = NULL; 68 constant char *osc8_search_param = NULL; 69 #endif 70 71 /* 72 * Structures for maintaining a set of ranges for hilites and filtered-out 73 * lines. Each range is stored as a node within a red-black tree, and we 74 * try to extend existing ranges (without creating overlaps) rather than 75 * create new nodes if possible. We remember the last node found by a 76 * search for constant-time lookup if the next search is near enough to 77 * the previous. To aid that, we overlay a secondary doubly-linked list 78 * on top of the red-black tree so we can find the preceding/succeeding 79 * nodes also in constant time. 80 * 81 * Each node is allocated from a series of pools, each pool double the size 82 * of the previous (for amortised constant time allocation). Since our only 83 * tree operations are clear and node insertion, not node removal, we don't 84 * need to maintain a usage bitmap or freelist and can just return nodes 85 * from the pool in-order until capacity is reached. 86 */ 87 struct hilite 88 { 89 POSITION hl_startpos; 90 POSITION hl_endpos; 91 int hl_attr; 92 }; 93 struct hilite_node 94 { 95 struct hilite_node *parent; 96 struct hilite_node *left; 97 struct hilite_node *right; 98 struct hilite_node *prev; 99 struct hilite_node *next; 100 int red; 101 struct hilite r; 102 }; 103 struct hilite_storage 104 { 105 size_t capacity; 106 size_t used; 107 struct hilite_storage *next; 108 struct hilite_node *nodes; 109 }; 110 struct hilite_tree 111 { 112 struct hilite_storage *first; 113 struct hilite_storage *current; 114 struct hilite_node *root; 115 struct hilite_node *lookaside; 116 }; 117 #define HILITE_INITIALIZER() { NULL, NULL, NULL, NULL } 118 #define HILITE_LOOKASIDE_STEPS 2 119 120 static struct hilite_tree hilite_anchor = HILITE_INITIALIZER(); 121 static struct hilite_tree filter_anchor = HILITE_INITIALIZER(); 122 static struct pattern_info *filter_infos = NULL; 123 124 #endif 125 126 /* 127 * These are the static variables that represent the "remembered" 128 * search pattern and filter pattern. 129 */ 130 struct pattern_info { 131 PATTERN_TYPE compiled; 132 char* text; 133 int search_type; 134 lbool is_ucase_pattern; 135 struct pattern_info *next; 136 }; 137 138 #if NO_REGEX 139 #define info_compiled(info) ((void*)0) 140 #else 141 #define info_compiled(info) ((info)->compiled) 142 #endif 143 144 static struct pattern_info search_info; 145 public int is_caseless; 146 147 /* 148 * Are there any uppercase letters in this string? 149 */ 150 static lbool is_ucase(constant char *str) 151 { 152 constant char *str_end = str + strlen(str); 153 LWCHAR ch; 154 155 while (str < str_end) 156 { 157 ch = step_charc(&str, +1, str_end); 158 if (IS_UPPER(ch)) 159 return (TRUE); 160 } 161 return (FALSE); 162 } 163 164 /* 165 * Discard a saved pattern. 166 */ 167 static void clear_pattern(struct pattern_info *info) 168 { 169 if (info->text != NULL) 170 free(info->text); 171 info->text = NULL; 172 #if !NO_REGEX 173 uncompile_pattern(&info->compiled); 174 #endif 175 } 176 177 /* 178 * Compile and save a search pattern. 179 */ 180 static int set_pattern(struct pattern_info *info, constant char *pattern, int search_type, int show_error) 181 { 182 /* 183 * Ignore case if -I is set OR 184 * -i is set AND the pattern is all lowercase. 185 */ 186 info->is_ucase_pattern = (pattern == NULL) ? FALSE : is_ucase(pattern); 187 is_caseless = (info->is_ucase_pattern && caseless != OPT_ONPLUS) ? 0 : caseless; 188 #if !NO_REGEX 189 if (pattern == NULL) 190 SET_NULL_PATTERN(info->compiled); 191 else if (compile_pattern(pattern, search_type, show_error, &info->compiled) < 0) 192 return -1; 193 #endif 194 /* Pattern compiled successfully; save the text too. */ 195 if (info->text != NULL) 196 free(info->text); 197 info->text = NULL; 198 if (pattern != NULL) 199 { 200 info->text = (char *) ecalloc(1, strlen(pattern)+1); 201 strcpy(info->text, pattern); 202 } 203 info->search_type = search_type; 204 return 0; 205 } 206 207 /* 208 * Initialize saved pattern to nothing. 209 */ 210 static void init_pattern(struct pattern_info *info) 211 { 212 SET_NULL_PATTERN(info->compiled); 213 info->text = NULL; 214 info->search_type = 0; 215 info->next = NULL; 216 } 217 218 /* 219 * Initialize search variables. 220 */ 221 public void init_search(void) 222 { 223 init_pattern(&search_info); 224 } 225 226 /* 227 * Determine which text conversions to perform before pattern matching. 228 */ 229 public int get_cvt_ops(int search_type) 230 { 231 int ops = 0; 232 233 if (is_caseless && (!re_handles_caseless || (search_type & SRCH_NO_REGEX))) 234 ops |= CVT_TO_LC; 235 if (proc_backspace == OPT_ON || (bs_mode == BS_SPECIAL && proc_backspace == OPT_OFF)) 236 ops |= CVT_BS; 237 if (proc_return == OPT_ON || (bs_mode != BS_CONTROL && proc_backspace == OPT_OFF)) 238 ops |= CVT_CRLF; 239 if (ctldisp == OPT_ONPLUS) 240 ops |= CVT_ANSI; 241 return (ops); 242 } 243 244 /* 245 * Is there a previous (remembered) search pattern? 246 */ 247 static lbool prev_pattern(struct pattern_info *info) 248 { 249 #if !NO_REGEX 250 if ((info->search_type & SRCH_NO_REGEX) == 0) 251 return (!is_null_pattern(info->compiled)); 252 #endif 253 return (info->text != NULL); 254 } 255 256 #if HILITE_SEARCH 257 /* 258 * Repaint the hilites currently displayed on the screen. 259 * Repaint each line which contains highlighted text. 260 * If on==0, force all hilites off. 261 */ 262 public void repaint_hilite(lbool on) 263 { 264 int sindex; 265 POSITION pos; 266 lbool save_hide_hilite; 267 268 if (squished) 269 repaint(); 270 271 save_hide_hilite = hide_hilite; 272 if (!on) 273 { 274 if (hide_hilite) 275 return; 276 hide_hilite = TRUE; 277 } 278 279 if (!can_goto_line) 280 { 281 repaint(); 282 hide_hilite = save_hide_hilite; 283 return; 284 } 285 286 for (sindex = TOP; sindex < TOP + sc_height-1; sindex++) 287 { 288 pos = position(sindex); 289 if (pos == NULL_POSITION) 290 continue; 291 (void) forw_line(pos, NULL, NULL); 292 goto_line(sindex); 293 clear_eol(); 294 put_line(FALSE); 295 } 296 overlay_header(); 297 lower_left(); 298 hide_hilite = save_hide_hilite; 299 } 300 #endif 301 302 /* 303 * Clear the attn hilite. 304 */ 305 public void clear_attn(void) 306 { 307 #if HILITE_SEARCH 308 int sindex; 309 POSITION old_start_attnpos; 310 POSITION old_end_attnpos; 311 POSITION pos; 312 POSITION epos; 313 int moved = 0; 314 315 if (start_attnpos == NULL_POSITION) 316 return; 317 old_start_attnpos = start_attnpos; 318 old_end_attnpos = end_attnpos; 319 start_attnpos = end_attnpos = NULL_POSITION; 320 321 if (!can_goto_line) 322 { 323 repaint(); 324 return; 325 } 326 if (squished) 327 repaint(); 328 329 for (sindex = TOP; sindex < TOP + sc_height-1; sindex++) 330 { 331 pos = position(sindex); 332 if (pos == NULL_POSITION) 333 continue; 334 epos = position(sindex+1); 335 if (pos <= old_end_attnpos && 336 (epos == NULL_POSITION || epos > old_start_attnpos)) 337 { 338 (void) forw_line(pos, NULL, NULL); 339 goto_line(sindex); 340 clear_eol(); 341 put_line(FALSE); 342 moved = 1; 343 } 344 } 345 if (overlay_header()) 346 moved = 1; 347 if (moved) 348 lower_left(); 349 #endif 350 } 351 352 /* 353 * Toggle or clear search string highlighting. 354 */ 355 public void undo_search(lbool clear) 356 { 357 clear_pattern(&search_info); 358 undo_osc8(); 359 #if HILITE_SEARCH 360 if (clear) 361 { 362 clr_hilite(); 363 } else 364 { 365 if (hilite_anchor.first == NULL) 366 { 367 error("No previous regular expression", NULL_PARG); 368 return; 369 } 370 hide_hilite = !hide_hilite; 371 } 372 repaint_hilite(TRUE); 373 #endif 374 } 375 376 /* 377 */ 378 public void undo_osc8(void) 379 { 380 #if OSC8_LINK 381 osc8_linepos = NULL_POSITION; 382 #endif 383 } 384 385 #if HILITE_SEARCH 386 /* 387 * Clear the hilite list. 388 */ 389 public void clr_hlist(struct hilite_tree *anchor) 390 { 391 struct hilite_storage *hls; 392 struct hilite_storage *nexthls; 393 394 for (hls = anchor->first; hls != NULL; hls = nexthls) 395 { 396 nexthls = hls->next; 397 free((void*)hls->nodes); 398 free((void*)hls); 399 } 400 anchor->first = NULL; 401 anchor->current = NULL; 402 anchor->root = NULL; 403 404 anchor->lookaside = NULL; 405 406 prep_startpos = prep_endpos = NULL_POSITION; 407 } 408 409 public void clr_hilite(void) 410 { 411 clr_hlist(&hilite_anchor); 412 } 413 414 public void clr_filter(void) 415 { 416 clr_hlist(&filter_anchor); 417 } 418 419 /* 420 * Find the node covering pos, or the node after it if no node covers it, 421 * or return NULL if pos is after the last range. Remember the found node, 422 * to speed up subsequent searches for the same or similar positions (if 423 * we return NULL, remember the last node.) 424 */ 425 static struct hilite_node* hlist_find(struct hilite_tree *anchor, POSITION pos) 426 { 427 struct hilite_node *n, *m; 428 429 if (anchor->lookaside) 430 { 431 int steps = 0; 432 int hit = 0; 433 434 n = anchor->lookaside; 435 436 for (;;) 437 { 438 if (pos < n->r.hl_endpos) 439 { 440 if (n->prev == NULL || pos >= n->prev->r.hl_endpos) 441 { 442 hit = 1; 443 break; 444 } 445 } else if (n->next == NULL) 446 { 447 n = NULL; 448 hit = 1; 449 break; 450 } 451 452 /* 453 * If we don't find the right node within a small 454 * distance, don't keep doing a linear search! 455 */ 456 if (steps >= HILITE_LOOKASIDE_STEPS) 457 break; 458 steps++; 459 460 if (pos < n->r.hl_endpos) 461 anchor->lookaside = n = n->prev; 462 else 463 anchor->lookaside = n = n->next; 464 } 465 466 if (hit) 467 return n; 468 } 469 470 n = anchor->root; 471 m = NULL; 472 473 while (n != NULL) 474 { 475 if (pos < n->r.hl_startpos) 476 { 477 if (n->left != NULL) 478 { 479 m = n; 480 n = n->left; 481 continue; 482 } 483 break; 484 } 485 if (pos >= n->r.hl_endpos) 486 { 487 if (n->right != NULL) 488 { 489 n = n->right; 490 continue; 491 } 492 if (m != NULL) 493 { 494 n = m; 495 } else 496 { 497 m = n; 498 n = NULL; 499 } 500 } 501 break; 502 } 503 504 if (n != NULL) 505 anchor->lookaside = n; 506 else if (m != NULL) 507 anchor->lookaside = m; 508 509 return n; 510 } 511 512 /* 513 * Should any characters in a specified range be highlighted? 514 */ 515 static int hilited_range_attr(POSITION pos, POSITION epos) 516 { 517 struct hilite_node *n = hlist_find(&hilite_anchor, pos); 518 if (n == NULL) 519 return 0; 520 if (epos != NULL_POSITION && epos <= n->r.hl_startpos) 521 return 0; 522 return n->r.hl_attr; 523 } 524 525 /* 526 * Set header parameters. 527 */ 528 public void set_header(POSITION pos) 529 { 530 header_start_pos = (header_lines == 0) ? NULL_POSITION : pos; 531 if (header_start_pos != NULL_POSITION) 532 { 533 int ln; 534 for (ln = 0; ln < header_lines; ++ln) 535 { 536 pos = forw_raw_line(pos, NULL, NULL); 537 if (pos == NULL_POSITION) break; 538 } 539 header_end_pos = pos; 540 } 541 } 542 543 /* 544 * Is a position within the header lines? 545 */ 546 static lbool pos_in_header(POSITION pos) 547 { 548 return (header_start_pos != NULL_POSITION && 549 pos >= header_start_pos && pos < header_end_pos); 550 } 551 552 /* 553 * Is a line "filtered" -- that is, should it be hidden? 554 */ 555 public lbool is_filtered(POSITION pos) 556 { 557 struct hilite_node *n; 558 559 if (!is_filtering()) 560 return (FALSE); 561 if (pos_in_header(pos)) 562 return (FALSE); 563 n = hlist_find(&filter_anchor, pos); 564 return (n != NULL && pos >= n->r.hl_startpos); 565 } 566 567 /* 568 * If pos is hidden, return the next position which isn't, otherwise 569 * just return pos. 570 */ 571 public POSITION next_unfiltered(POSITION pos) 572 { 573 if (!is_filtering()) 574 return (pos); 575 if (pos_in_header(pos)) 576 return (pos); 577 flush(); 578 while (pos != NULL_POSITION) 579 { 580 prep_hilite(pos, NULL_POSITION, 1); 581 if (!is_filtered(pos)) 582 break; 583 pos = forw_raw_line(pos, NULL, NULL); 584 } 585 return pos; 586 } 587 588 /* 589 * Set the hshift for the line starting at line_pos so that the string 590 * between start_off and end_off is visible on the screen. 591 */ 592 static void shift_visible(POSITION line_pos, size_t start_off, size_t end_off) 593 { 594 POSITION start_pos = line_pos + start_off; 595 POSITION end_pos = line_pos + end_off; 596 int start_col = col_from_pos(line_pos, start_pos, NULL_POSITION, -1); 597 int end_col = col_from_pos(line_pos, end_pos, start_pos, start_col); 598 int swidth = sc_width - line_pfx_width() - (rscroll_char ? 1 : 0); 599 int new_hshift; 600 if (start_col < 0 || end_col < 0) 601 return; 602 if (end_col < swidth) /* whole string is in first screen */ 603 new_hshift = 0; 604 else if (start_col > hshift && end_col < hshift + swidth) 605 new_hshift = hshift; /* already visible; leave hshift unchanged */ 606 else 607 { 608 int eol_col = col_from_pos(line_pos, NULL_POSITION, end_pos, end_col) - swidth; 609 if (start_col >= eol_col) /* whole string is in last screen */ 610 new_hshift = eol_col; 611 else /* shift it to column match_shift */ 612 new_hshift = (start_col < match_shift) ? 0 : start_col - match_shift; 613 } 614 if (new_hshift != hshift) 615 { 616 hshift = new_hshift; 617 screen_trashed(); 618 } 619 } 620 621 /* 622 * Should any characters in a specified range be highlighted? 623 * If nohide is nonzero, don't consider hide_hilite. 624 */ 625 public int is_hilited_attr(POSITION pos, POSITION epos, int nohide, int *p_matches) 626 { 627 int attr; 628 629 if (p_matches != NULL) 630 *p_matches = 0; 631 632 if (!status_col && 633 start_attnpos != NULL_POSITION && 634 pos <= end_attnpos && 635 (epos == NULL_POSITION || epos > start_attnpos)) 636 /* 637 * The attn line overlaps this range. 638 */ 639 return (AT_HILITE|AT_COLOR_ATTN); 640 641 #if OSC8_LINK 642 if (osc8_linepos != NULL_POSITION && 643 pos < osc8_text_end && (epos == NULL_POSITION || epos > osc8_text_start)) 644 return (AT_HILITE|AT_COLOR_SEARCH); 645 #endif 646 647 attr = hilited_range_attr(pos, epos); 648 if (attr == 0) 649 return (0); 650 651 if (p_matches == NULL) 652 /* 653 * Kinda kludgy way to recognize that caller is checking for 654 * hilite in status column. In this case we want to return 655 * hilite status even if hiliting is disabled or hidden. 656 */ 657 return (attr); 658 659 /* 660 * Report matches, even if we're hiding highlights. 661 */ 662 *p_matches = 1; 663 664 if (hilite_search == 0) 665 /* 666 * Not doing highlighting. 667 */ 668 return (0); 669 670 if (!nohide && hide_hilite) 671 /* 672 * Highlighting is hidden. 673 */ 674 return (0); 675 676 return (attr); 677 } 678 679 /* 680 * Tree node storage: get the current block of nodes if it has spare 681 * capacity, or create a new one if not. 682 */ 683 static struct hilite_storage * hlist_getstorage(struct hilite_tree *anchor) 684 { 685 size_t capacity = 1; 686 struct hilite_storage *s; 687 688 if (anchor->current) 689 { 690 if (anchor->current->used < anchor->current->capacity) 691 return anchor->current; 692 capacity = anchor->current->capacity * 2; 693 } 694 695 s = (struct hilite_storage *) ecalloc(1, sizeof(struct hilite_storage)); 696 s->nodes = (struct hilite_node *) ecalloc(capacity, sizeof(struct hilite_node)); 697 s->capacity = capacity; 698 s->used = 0; 699 s->next = NULL; 700 if (anchor->current) 701 anchor->current->next = s; 702 else 703 anchor->first = s; 704 anchor->current = s; 705 return s; 706 } 707 708 /* 709 * Tree node storage: retrieve a new empty node to be inserted into the 710 * tree. 711 */ 712 static struct hilite_node * hlist_getnode(struct hilite_tree *anchor) 713 { 714 struct hilite_storage *s = hlist_getstorage(anchor); 715 return &s->nodes[s->used++]; 716 } 717 718 /* 719 * Rotate the tree left around a pivot node. 720 */ 721 static void hlist_rotate_left(struct hilite_tree *anchor, struct hilite_node *n) 722 { 723 struct hilite_node *np = n->parent; 724 struct hilite_node *nr = n->right; 725 struct hilite_node *nrl = n->right->left; 726 727 if (np != NULL) 728 { 729 if (n == np->left) 730 np->left = nr; 731 else 732 np->right = nr; 733 } else 734 { 735 anchor->root = nr; 736 } 737 nr->left = n; 738 n->right = nrl; 739 740 nr->parent = np; 741 n->parent = nr; 742 if (nrl != NULL) 743 nrl->parent = n; 744 } 745 746 /* 747 * Rotate the tree right around a pivot node. 748 */ 749 static void hlist_rotate_right(struct hilite_tree *anchor, struct hilite_node *n) 750 { 751 struct hilite_node *np = n->parent; 752 struct hilite_node *nl = n->left; 753 struct hilite_node *nlr = n->left->right; 754 755 if (np != NULL) 756 { 757 if (n == np->right) 758 np->right = nl; 759 else 760 np->left = nl; 761 } else 762 { 763 anchor->root = nl; 764 } 765 nl->right = n; 766 n->left = nlr; 767 768 nl->parent = np; 769 n->parent = nl; 770 if (nlr != NULL) 771 nlr->parent = n; 772 } 773 774 775 /* 776 * Add a new hilite to a hilite list. 777 */ 778 static void add_hilite(struct hilite_tree *anchor, struct hilite *hl) 779 { 780 struct hilite_node *p, *n, *u; 781 782 /* Ignore empty ranges. */ 783 if (hl->hl_startpos >= hl->hl_endpos) 784 return; 785 786 p = anchor->root; 787 788 /* Inserting the very first node is trivial. */ 789 if (p == NULL) 790 { 791 n = hlist_getnode(anchor); 792 n->r = *hl; 793 anchor->root = n; 794 anchor->lookaside = n; 795 return; 796 } 797 798 /* 799 * Find our insertion point. If we come across any overlapping 800 * or adjoining existing ranges, shrink our range and discard 801 * if it become empty. 802 */ 803 for (;;) 804 { 805 if (hl->hl_startpos < p->r.hl_startpos) 806 { 807 if (hl->hl_endpos > p->r.hl_startpos && hl->hl_attr == p->r.hl_attr) 808 hl->hl_endpos = p->r.hl_startpos; 809 if (p->left != NULL) 810 { 811 p = p->left; 812 continue; 813 } 814 break; 815 } 816 if (hl->hl_startpos < p->r.hl_endpos && hl->hl_attr == p->r.hl_attr) { 817 hl->hl_startpos = p->r.hl_endpos; 818 if (hl->hl_startpos >= hl->hl_endpos) 819 return; 820 } 821 if (p->right != NULL) 822 { 823 p = p->right; 824 continue; 825 } 826 break; 827 } 828 829 /* 830 * Now we're at the right leaf, again check for contiguous ranges 831 * and extend the existing node if possible to avoid the 832 * insertion. Otherwise insert a new node at the leaf. 833 */ 834 if (hl->hl_startpos < p->r.hl_startpos) { 835 if (hl->hl_attr == p->r.hl_attr) 836 { 837 if (hl->hl_endpos == p->r.hl_startpos) 838 { 839 p->r.hl_startpos = hl->hl_startpos; 840 return; 841 } 842 if (p->prev != NULL && p->prev->r.hl_endpos == hl->hl_startpos) 843 { 844 p->prev->r.hl_endpos = hl->hl_endpos; 845 return; 846 } 847 } 848 p->left = n = hlist_getnode(anchor); 849 n->next = p; 850 if (p->prev != NULL) 851 { 852 n->prev = p->prev; 853 p->prev->next = n; 854 } 855 p->prev = n; 856 } else { 857 if (hl->hl_attr == p->r.hl_attr) 858 { 859 if (p->r.hl_endpos == hl->hl_startpos) 860 { 861 p->r.hl_endpos = hl->hl_endpos; 862 return; 863 } 864 if (p->next != NULL && hl->hl_endpos == p->next->r.hl_startpos) { 865 p->next->r.hl_startpos = hl->hl_startpos; 866 return; 867 } 868 } 869 p->right = n = hlist_getnode(anchor); 870 n->prev = p; 871 if (p->next != NULL) 872 { 873 n->next = p->next; 874 p->next->prev = n; 875 } 876 p->next = n; 877 } 878 n->parent = p; 879 n->red = 1; 880 n->r = *hl; 881 882 /* 883 * The tree is in the correct order and covers the right ranges 884 * now, but may have become unbalanced. Rebalance it using the 885 * standard red-black tree constraints and operations. 886 */ 887 for (;;) 888 { 889 /* case 1 - current is root, root is always black */ 890 if (n->parent == NULL) 891 { 892 n->red = 0; 893 break; 894 } 895 896 /* case 2 - parent is black, we can always be red */ 897 if (!n->parent->red) 898 break; 899 900 /* 901 * constraint: because the root must be black, if our 902 * parent is red it cannot be the root therefore we must 903 * have a grandparent 904 */ 905 906 /* 907 * case 3 - parent and uncle are red, repaint them black, 908 * the grandparent red, and start again at the grandparent. 909 */ 910 u = n->parent->parent->left; 911 if (n->parent == u) 912 u = n->parent->parent->right; 913 if (u != NULL && u->red) 914 { 915 n->parent->red = 0; 916 u->red = 0; 917 n = n->parent->parent; 918 n->red = 1; 919 continue; 920 } 921 922 /* 923 * case 4 - parent is red but uncle is black, parent and 924 * grandparent on opposite sides. We need to start 925 * changing the structure now. This and case 5 will shorten 926 * our branch and lengthen the sibling, between them 927 * restoring balance. 928 */ 929 if (n == n->parent->right && 930 n->parent == n->parent->parent->left) 931 { 932 hlist_rotate_left(anchor, n->parent); 933 n = n->left; 934 } else if (n == n->parent->left && 935 n->parent == n->parent->parent->right) 936 { 937 hlist_rotate_right(anchor, n->parent); 938 n = n->right; 939 } 940 941 /* 942 * case 5 - parent is red but uncle is black, parent and 943 * grandparent on same side 944 */ 945 n->parent->red = 0; 946 n->parent->parent->red = 1; 947 if (n == n->parent->left) 948 hlist_rotate_right(anchor, n->parent->parent); 949 else 950 hlist_rotate_left(anchor, n->parent->parent); 951 break; 952 } 953 } 954 955 /* 956 * Highlight every character in a range of displayed characters. 957 */ 958 static void create_hilites(POSITION linepos, constant char *line, constant char *sp, constant char *ep, int attr, int *chpos) 959 { 960 size_t start_index = ptr_diff(sp, line); /*{{type-issue}}*/ 961 size_t end_index = ptr_diff(ep, line); 962 struct hilite hl; 963 size_t i; 964 965 /* Start the first hilite. */ 966 hl.hl_startpos = linepos + chpos[start_index]; 967 hl.hl_attr = attr; 968 969 /* 970 * Step through the displayed chars. 971 * If the source position (before cvt) of the char is one more 972 * than the source pos of the previous char (the usual case), 973 * just increase the size of the current hilite by one. 974 * Otherwise (there are backspaces or something involved), 975 * finish the current hilite and start a new one. 976 */ 977 for (i = start_index+1; i <= end_index; i++) 978 { 979 if (chpos[i] != chpos[i-1] + 1 || i == end_index) 980 { 981 hl.hl_endpos = linepos + chpos[i-1] + 1; 982 add_hilite(&hilite_anchor, &hl); 983 /* Start new hilite unless this is the last char. */ 984 if (i < end_index) 985 { 986 hl.hl_startpos = linepos + chpos[i]; 987 } 988 } 989 } 990 } 991 992 /* 993 * Make a hilite for each string in a physical line which matches 994 * the current pattern. 995 * sp,ep delimit the first match already found. 996 */ 997 static void hilite_line(POSITION linepos, constant char *line, size_t line_len, int *chpos, constant char **sp, constant char **ep, int nsp) 998 { 999 size_t line_off = 0; 1000 1001 /* 1002 * sp[0] and ep[0] delimit the first match in the line. 1003 * Mark the corresponding file positions, then 1004 * look for further matches and mark them. 1005 * {{ This technique, of calling match_pattern on subsequent 1006 * substrings of the line, may mark more than is correct 1007 * if the pattern starts with "^". This bug is fixed 1008 * for those regex functions that accept a notbol parameter 1009 * (currently POSIX, PCRE and V8-with-regexec2). }} 1010 * sp[i] and ep[i] for i>0 delimit subpattern matches. 1011 * Color each of them with its unique color. 1012 */ 1013 do { 1014 constant char *lep = sp[0]; 1015 int i; 1016 if (sp[0] == NULL || ep[0] == NULL) 1017 break; 1018 for (i = 1; i < nsp; i++) 1019 { 1020 if (sp[i] == NULL || ep[i] == NULL) 1021 break; 1022 if (ep[i] > sp[i]) 1023 { 1024 create_hilites(linepos, line, lep, sp[i], 1025 AT_HILITE | AT_COLOR_SEARCH, chpos); 1026 create_hilites(linepos, line, sp[i], ep[i], 1027 AT_HILITE | AT_COLOR_SUBSEARCH(i), chpos); 1028 lep = ep[i]; 1029 } 1030 } 1031 create_hilites(linepos, line, lep, ep[0], 1032 AT_HILITE | AT_COLOR_SEARCH, chpos); 1033 1034 /* 1035 * If we matched more than zero characters, 1036 * move to the first char after the string we matched. 1037 * If we matched zero, just move to the next char. 1038 */ 1039 if (ep[0] > &line[line_off]) 1040 line_off = ptr_diff(ep[0], line); 1041 else if (line_off != line_len) 1042 line_off++; 1043 else /* end of line */ 1044 break; 1045 } while (match_pattern(info_compiled(&search_info), search_info.text, 1046 line, line_len, line_off, sp, ep, nsp, 1, search_info.search_type)); 1047 } 1048 #endif 1049 1050 #if HILITE_SEARCH 1051 /* 1052 * Find matching text which is currently on screen and highlight it. 1053 */ 1054 static void hilite_screen(void) 1055 { 1056 struct scrpos scrpos; 1057 1058 get_scrpos(&scrpos, TOP); 1059 if (scrpos.pos == NULL_POSITION) 1060 return; 1061 prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1); 1062 repaint_hilite(TRUE); 1063 } 1064 1065 /* 1066 * Change highlighting parameters. 1067 */ 1068 public void chg_hilite(void) 1069 { 1070 /* 1071 * Erase any highlights currently on screen. 1072 */ 1073 clr_hilite(); 1074 hide_hilite = FALSE; 1075 1076 if (hilite_search == OPT_ONPLUS) 1077 /* 1078 * Display highlights. 1079 */ 1080 hilite_screen(); 1081 } 1082 #endif 1083 1084 /* 1085 * Figure out where to start a search. 1086 */ 1087 public POSITION search_pos(int search_type) 1088 { 1089 POSITION pos; 1090 int sindex; 1091 1092 if (empty_screen()) 1093 { 1094 /* 1095 * Start at the beginning (or end) of the file. 1096 * The empty_screen() case is mainly for 1097 * command line initiated searches; 1098 * for example, "+/xyz" on the command line. 1099 * Also for multi-file (SRCH_PAST_EOF) searches. 1100 */ 1101 if (search_type & SRCH_FORW) 1102 { 1103 pos = ch_zero(); 1104 } else 1105 { 1106 pos = ch_length(); 1107 if (pos == NULL_POSITION) 1108 { 1109 (void) ch_end_seek(); 1110 pos = ch_length(); 1111 } 1112 } 1113 sindex = 0; 1114 } else 1115 { 1116 lbool add_one = FALSE; 1117 1118 if (how_search == OPT_ON) 1119 { 1120 /* 1121 * Search does not include current screen. 1122 */ 1123 if (search_type & SRCH_FORW) 1124 sindex = sc_height-1; /* BOTTOM_PLUS_ONE */ 1125 else 1126 sindex = 0; /* TOP */ 1127 } else if (how_search == OPT_ONPLUS && !(search_type & SRCH_AFTER_TARGET)) 1128 { 1129 /* 1130 * Search includes all of displayed screen. 1131 */ 1132 if (search_type & SRCH_FORW) 1133 sindex = 0; /* TOP */ 1134 else 1135 sindex = sc_height-1; /* BOTTOM_PLUS_ONE */ 1136 } else 1137 { 1138 /* 1139 * Search includes the part of current screen beyond the jump target. 1140 * It starts at the jump target (if searching backwards), 1141 * or at the jump target plus one (if forwards). 1142 */ 1143 sindex = sindex_from_sline(jump_sline); 1144 if (search_type & SRCH_FORW) 1145 add_one = TRUE; 1146 } 1147 pos = position(sindex); 1148 if (add_one) 1149 pos = forw_raw_line(pos, NULL, NULL); 1150 } 1151 1152 /* 1153 * If the line is empty, look around for a plausible starting place. 1154 */ 1155 if (search_type & SRCH_FORW) 1156 { 1157 while (pos == NULL_POSITION) 1158 { 1159 if (++sindex >= sc_height) 1160 break; 1161 pos = position(sindex); 1162 } 1163 } else 1164 { 1165 while (pos == NULL_POSITION) 1166 { 1167 if (--sindex < 0) 1168 break; 1169 pos = position(sindex); 1170 } 1171 } 1172 return (pos); 1173 } 1174 1175 /* 1176 * Check to see if the line matches the filter pattern. 1177 * If so, add an entry to the filter list. 1178 */ 1179 #if HILITE_SEARCH 1180 static lbool matches_filters(POSITION pos, char *cline, size_t line_len, int *chpos, POSITION linepos, constant char **sp, constant char **ep, int nsp) 1181 { 1182 struct pattern_info *filter; 1183 1184 for (filter = filter_infos; filter != NULL; filter = filter->next) 1185 { 1186 lbool line_filter = match_pattern(info_compiled(filter), filter->text, 1187 cline, line_len, 0, sp, ep, nsp, 0, filter->search_type); 1188 if (line_filter) 1189 { 1190 struct hilite hl; 1191 hl.hl_startpos = linepos; 1192 hl.hl_endpos = pos; 1193 hl.hl_attr = 0; 1194 add_hilite(&filter_anchor, &hl); 1195 free(cline); 1196 free(chpos); 1197 return (TRUE); 1198 } 1199 } 1200 return (FALSE); 1201 } 1202 #endif 1203 1204 /* 1205 * Get the position of the first char in the screen line which 1206 * puts tpos on screen. 1207 */ 1208 static POSITION get_lastlinepos(POSITION pos, POSITION tpos, int sheight) 1209 { 1210 int nlines; 1211 1212 flush(); 1213 for (nlines = 0;; nlines++) 1214 { 1215 POSITION npos = forw_line(pos, NULL, NULL); 1216 if (npos > tpos) 1217 { 1218 if (nlines < sheight) 1219 return NULL_POSITION; 1220 return pos; 1221 } 1222 pos = npos; 1223 } 1224 } 1225 1226 #if OSC8_LINK 1227 1228 /* 1229 * osc8_parse_info points to the component fields in a parsed OSC8 sequence. 1230 */ 1231 struct osc8_parse_info { 1232 constant char *osc8_start; 1233 constant char *osc8_end; 1234 constant char *params_start; 1235 constant char *params_end; 1236 constant char *uri_start; 1237 constant char *uri_end; 1238 }; 1239 1240 /* 1241 * Parse an OSC8 sequence in a string. 1242 */ 1243 static lbool osc8_parse(constant char *line, constant char *line_end, struct osc8_parse_info *pop) 1244 { 1245 constant char *oline; 1246 LWCHAR ch; 1247 struct ansi_state *pansi; 1248 1249 pop->osc8_start = pop->osc8_end = pop->uri_start = pop->uri_end = pop->params_start = pop->params_end = NULL; 1250 oline = line; 1251 ch = step_charc(&line, +1, line_end); 1252 /* oline points to character ch, line points to the one after it. */ 1253 pansi = ansi_start(ch); 1254 if (pansi == NULL) 1255 return FALSE; 1256 pop->osc8_start = oline; /* start at the ESC */ 1257 for (;;) 1258 { 1259 ansi_state astate = ansi_step(pansi, ch); 1260 osc8_state ostate = ansi_osc8_state(pansi); 1261 if (ostate == OSC8_NOT) 1262 break; 1263 switch (ostate) 1264 { 1265 case OSC8_PARAMS: 1266 if (pop->params_start == NULL) 1267 pop->params_start = line; 1268 break; 1269 case OSC8_URI: 1270 if (pop->uri_start == NULL) 1271 { 1272 pop->params_end = oline; 1273 pop->uri_start = line; 1274 } 1275 break; 1276 case OSC_END_CSI: 1277 if (pop->uri_end == NULL) 1278 pop->uri_end = oline; 1279 break; 1280 case OSC_END: 1281 ansi_done(pansi); 1282 if (pop->params_start == NULL || pop->uri_start == NULL) 1283 return FALSE; 1284 pop->osc8_end = line; 1285 if (pop->uri_end == NULL) /* happens when ST is "\7" */ 1286 pop->uri_end = oline; 1287 if (pop->params_end == NULL) /* should not happen */ 1288 pop->params_end = oline; 1289 return TRUE; 1290 default: 1291 break; 1292 } 1293 if (astate != ANSI_MID || line >= line_end) 1294 break; 1295 oline = line; 1296 ch = step_charc(&line, +1, line_end); 1297 } 1298 ansi_done(pansi); 1299 return FALSE; 1300 } 1301 1302 /* 1303 * Does an OSC8 sequence contain a specified parameter? 1304 */ 1305 static lbool osc8_param_match(POSITION linepos, constant char *line, constant struct osc8_parse_info *op1, constant struct osc8_parse_info *op2, constant char *param, POSITION clickpos) 1306 { 1307 size_t param_len; 1308 constant char *p; 1309 1310 if (clickpos != NULL_POSITION) 1311 { 1312 return clickpos >= linepos + ptr_diff(op1->osc8_start, line) && 1313 clickpos < linepos + ptr_diff(op2->osc8_end, line); 1314 } 1315 if (param == NULL) 1316 return TRUE; 1317 param_len = strlen(param); 1318 /* Parameters are separated by colons. */ 1319 for (p = op1->params_start; p + param_len <= op1->params_end; ) 1320 { 1321 if (strncmp(p, param, param_len) == 0) 1322 { 1323 p += param_len; 1324 if (p == op1->params_end || *p == ':') 1325 return TRUE; 1326 } 1327 while (p < op1->params_end && *p != ':') 1328 ++p; 1329 while (p < op1->params_end && *p == ':') 1330 ++p; 1331 } 1332 return FALSE; 1333 } 1334 1335 /* 1336 * Is the URI in an OSC8 sequence empty? 1337 * "Empty" means zero length, or equal to "#". 1338 */ 1339 static lbool osc8_empty_uri(constant struct osc8_parse_info *op) 1340 { 1341 return op->uri_end == op->uri_start || 1342 (op->uri_end == op->uri_start+1 && op->uri_start[0] == '#'); 1343 } 1344 1345 /* 1346 * Find the next OSC8 hyperlink in a line. 1347 * A hyperlink is two OSC8 sequences (the first with a nonempty URI) 1348 * plus the non-empty text between them. 1349 * But if searching for a parameter, allow URI and/or text to be empty. 1350 */ 1351 typedef enum { OSC8_NO_MATCH, OSC8_MATCH, OSC8_ALREADY } osc8_match; 1352 1353 static osc8_match osc8_search_line1(int search_type, POSITION linepos, POSITION spos, constant char *line, size_t line_len, constant char *param, POSITION clickpos) 1354 { 1355 constant char *line_end = &line[line_len]; 1356 struct osc8_parse_info op1; 1357 struct osc8_parse_info op2; 1358 constant char *linep; 1359 constant size_t min_osc8_size = 6; /* "\e]8;;\7" */ 1360 1361 if (search_type & SRCH_FORW) 1362 { 1363 for (linep = line; ; linep++) 1364 { 1365 if (linep + min_osc8_size > line_end) 1366 return OSC8_NO_MATCH; 1367 /* Find the first OSC8 sequence in the line with a nonempty URI, 1368 * which begins the hypertext. */ 1369 if (osc8_parse(linep, line_end, &op1) && 1370 (!osc8_empty_uri(&op1) || param != NULL)) 1371 { 1372 /* Now find the next OSC8 sequence, which ends the hypertext. */ 1373 constant char *linep2; 1374 for (linep2 = op1.osc8_end; linep2 < line_end; linep2++) 1375 { 1376 if (osc8_parse(linep2, line_end, &op2)) 1377 break; 1378 } 1379 if (linep2 == line_end) 1380 op2.osc8_end = op2.osc8_start = line_end; 1381 if ((op2.osc8_start > op1.osc8_end || param != NULL) && 1382 osc8_param_match(linepos, line, &op1, &op2, param, clickpos)) 1383 break; 1384 } 1385 } 1386 } else 1387 { 1388 op2.osc8_end = op2.osc8_start = line_end; 1389 for (linep = line_end - min_osc8_size; ; linep--) 1390 { 1391 if (linep < line) 1392 return OSC8_NO_MATCH; 1393 if (osc8_parse(linep, line_end, &op1)) 1394 { 1395 if (((!osc8_empty_uri(&op1) && op2.osc8_start > op1.osc8_end) || param != NULL) && 1396 osc8_param_match(linepos, line, &op1, &op2, param, clickpos)) 1397 break; 1398 op2 = op1; 1399 } 1400 } 1401 } 1402 if (param != NULL) 1403 /* Don't set osc8 globals if we're just searching for a parameter. */ 1404 return OSC8_MATCH; 1405 1406 if (osc8_linepos == linepos && osc8_match_start == spos + ptr_diff(op1.osc8_start, line)) 1407 return OSC8_ALREADY; /* already selected */ 1408 1409 osc8_linepos = linepos; 1410 osc8_match_start = spos + ptr_diff(op1.osc8_start, line); 1411 osc8_match_end = spos + ptr_diff(op2.osc8_start, line); 1412 osc8_params_start = spos + ptr_diff(op1.params_start, line); 1413 osc8_params_end = spos + ptr_diff(op1.params_end, line); 1414 osc8_uri_start = spos + ptr_diff(op1.uri_start, line); 1415 osc8_uri_end = spos + ptr_diff(op1.uri_end, line); 1416 osc8_text_start = spos + ptr_diff(op1.osc8_end, line); 1417 osc8_text_end = spos + ptr_diff(op2.osc8_start, line); 1418 1419 /* Save URI for message in prompt(). */ 1420 osc8_uri = saven(op1.uri_start, ptr_diff(op1.uri_end, op1.uri_start)); 1421 return OSC8_MATCH; 1422 } 1423 1424 /* 1425 * Find the N-th OSC8 hyperlink in a line. 1426 */ 1427 static osc8_match osc8_search_line(int search_type, POSITION linepos, constant char *line, size_t line_len, constant char *param, POSITION clickpos, int *matches) 1428 { 1429 while (*matches > 0) 1430 { 1431 POSITION spos = linepos; 1432 constant char *sline = line; 1433 size_t sline_len = line_len; 1434 osc8_match r; 1435 if (linepos == osc8_linepos && clickpos == NULL_POSITION) 1436 { 1437 /* 1438 * Already have a hyperlink selected. 1439 * Search for the next/previous one in the same line. 1440 */ 1441 if (search_type & SRCH_FORW) 1442 { 1443 size_t off = (size_t) (osc8_match_end - linepos); 1444 spos += off; 1445 sline += off; 1446 sline_len -= off; 1447 } else 1448 { 1449 sline_len = (size_t) (osc8_match_start - linepos); 1450 } 1451 } 1452 r = osc8_search_line1(search_type, linepos, spos, sline, sline_len, param, clickpos); 1453 if (r == OSC8_NO_MATCH) 1454 break; 1455 if (--*matches <= 0) 1456 return r; 1457 } 1458 return OSC8_NO_MATCH; 1459 } 1460 1461 /* 1462 * Shift display to make the currently selected OSC8 hyperlink visible. 1463 */ 1464 static void osc8_shift_visible(void) 1465 { 1466 if (chop_line()) 1467 { 1468 size_t start_off = (size_t)(osc8_match_start - osc8_linepos); 1469 size_t end_off = (size_t)(osc8_match_end - osc8_linepos); 1470 shift_visible(osc8_linepos, start_off, end_off); 1471 } 1472 /* {{ What about the (plastlinepos != NULL) case in search_range? }} */ 1473 } 1474 1475 #endif /* OSC8_LINK */ 1476 1477 /* 1478 * Search a subset of the file, specified by start/end position. 1479 */ 1480 static int search_range(POSITION pos, POSITION endpos, int search_type, int matches, int maxlines, POSITION *plinepos, POSITION *pendpos, POSITION *plastlinepos) 1481 { 1482 constant char *line; 1483 char *cline; 1484 size_t line_len; 1485 LINENUM linenum; 1486 #define NSP (NUM_SEARCH_COLORS+2) 1487 constant char *sp[NSP]; 1488 constant char *ep[NSP]; 1489 lbool line_match; 1490 int cvt_ops; 1491 size_t cvt_len; 1492 int *chpos; 1493 POSITION linepos, oldpos; 1494 int skip_bytes = 0; 1495 size_t swidth = (size_t) (sc_width - line_pfx_width()); /*{{type-issue}}*/ 1496 size_t sheight = (size_t) (sc_height - sindex_from_sline(jump_sline)); 1497 1498 linenum = find_linenum(pos); 1499 if (nosearch_header_lines && linenum <= header_lines) 1500 { 1501 linenum = header_lines + 1; 1502 pos = find_pos(linenum); 1503 } 1504 if (pos == NULL_POSITION) 1505 return (-1); 1506 oldpos = pos; 1507 /* When the search wraps around, end at starting position. */ 1508 if ((search_type & SRCH_WRAP) && endpos == NULL_POSITION) 1509 endpos = pos; 1510 flush(); 1511 for (;;) 1512 { 1513 /* 1514 * Get lines until we find a matching one or until 1515 * we hit end-of-file (or beginning-of-file if we're 1516 * going backwards), or until we hit the end position. 1517 */ 1518 if (ABORT_SIGS()) 1519 { 1520 /* 1521 * A signal aborts the search. 1522 */ 1523 return (-1); 1524 } 1525 1526 if ((endpos != NULL_POSITION && !(search_type & SRCH_WRAP) && 1527 (((search_type & SRCH_FORW) && pos >= endpos) || 1528 ((search_type & SRCH_BACK) && pos <= endpos))) || maxlines == 0) 1529 { 1530 /* 1531 * Reached end position without a match. 1532 */ 1533 if (pendpos != NULL) 1534 *pendpos = pos; 1535 return (matches); 1536 } 1537 if (maxlines > 0) 1538 maxlines--; 1539 1540 if (search_type & SRCH_FORW) 1541 { 1542 /* 1543 * Read the next line, and save the 1544 * starting position of that line in linepos. 1545 */ 1546 linepos = pos; 1547 pos = forw_raw_line(pos, &line, &line_len); 1548 if (linenum != 0) 1549 linenum++; 1550 } else 1551 { 1552 /* 1553 * Read the previous line and save the 1554 * starting position of that line in linepos. 1555 */ 1556 pos = back_raw_line(pos, &line, &line_len); 1557 linepos = pos; 1558 if (linenum != 0) 1559 linenum--; 1560 } 1561 1562 if (pos == NULL_POSITION) 1563 { 1564 /* 1565 * Reached EOF/BOF without a match. 1566 */ 1567 if (search_type & SRCH_WRAP) 1568 { 1569 /* 1570 * The search wraps around the current file, so 1571 * try to continue at BOF/EOF. 1572 */ 1573 if (search_type & SRCH_FORW) 1574 { 1575 pos = ch_zero(); 1576 } else 1577 { 1578 pos = ch_length(); 1579 if (pos == NULL_POSITION) 1580 { 1581 (void) ch_end_seek(); 1582 pos = ch_length(); 1583 } 1584 } 1585 if (pos != NULL_POSITION) { 1586 /* 1587 * Wrap-around was successful. Clear 1588 * the flag so we don't wrap again, and 1589 * continue the search at new pos. 1590 */ 1591 search_wrapped = TRUE; 1592 search_type &= ~SRCH_WRAP; 1593 linenum = find_linenum(pos); 1594 continue; 1595 } 1596 } 1597 if (pendpos != NULL) 1598 *pendpos = oldpos; 1599 return (matches); 1600 } 1601 1602 /* 1603 * If we're using line numbers, we might as well 1604 * remember the information we have now (the position 1605 * and line number of the current line). 1606 * Don't do it for every line because it slows down 1607 * the search. Remember the line number only if 1608 * we're "far" from the last place we remembered it. 1609 */ 1610 if (linenums && abs((int)(pos - oldpos)) > 2048) 1611 add_lnum(linenum, pos); 1612 oldpos = pos; 1613 1614 #if HILITE_SEARCH 1615 if (is_filtered(linepos)) 1616 continue; 1617 #endif 1618 if (nosearch_header_cols) 1619 skip_bytes = skip_columns(header_cols, &line, &line_len); 1620 #if OSC8_LINK 1621 if (search_type & SRCH_OSC8) 1622 { 1623 if (osc8_search_line(search_type, linepos, line, line_len, osc8_search_param, NULL_POSITION, &matches) != OSC8_NO_MATCH) 1624 { 1625 if (plinepos != NULL) 1626 *plinepos = linepos; 1627 osc8_shift_visible(); 1628 return (0); 1629 } 1630 continue; 1631 } 1632 #endif 1633 /* 1634 * If it's a caseless search, convert the line to lowercase. 1635 * If we're doing backspace processing, delete backspaces. 1636 */ 1637 cvt_ops = get_cvt_ops(search_type); 1638 cvt_len = cvt_length(line_len, cvt_ops); 1639 cline = (char *) ecalloc(1, cvt_len); 1640 chpos = cvt_alloc_chpos(cvt_len); 1641 cvt_text(cline, line, chpos, &line_len, cvt_ops); 1642 1643 #if HILITE_SEARCH 1644 /* 1645 * If any filters are in effect, ignore non-matching lines. 1646 */ 1647 if (filter_infos != NULL && 1648 ((search_type & SRCH_FIND_ALL) || 1649 prep_startpos == NULL_POSITION || 1650 linepos < prep_startpos || linepos >= prep_endpos)) { 1651 if (matches_filters(pos, cline, line_len, chpos, linepos, sp, ep, NSP)) 1652 continue; 1653 } 1654 #endif 1655 1656 /* 1657 * Test the next line to see if we have a match. 1658 * We are successful if we either want a match and got one, 1659 * or if we want a non-match and got one. 1660 */ 1661 if (prev_pattern(&search_info)) 1662 { 1663 line_match = match_pattern(info_compiled(&search_info), search_info.text, 1664 cline, line_len, 0, sp, ep, NSP, 0, search_type); 1665 if (line_match) 1666 { 1667 /* 1668 * Got a match. 1669 */ 1670 if (search_type & SRCH_FIND_ALL) 1671 { 1672 #if HILITE_SEARCH 1673 /* 1674 * We are supposed to find all matches in the range. 1675 * Just add the matches in this line to the 1676 * hilite list and keep searching. 1677 */ 1678 hilite_line(linepos + skip_bytes, cline, line_len, chpos, sp, ep, NSP); 1679 #endif 1680 } else if (--matches <= 0) 1681 { 1682 /* 1683 * Found the one match we're looking for. 1684 * Return it. 1685 */ 1686 #if HILITE_SEARCH 1687 if (hilite_search == OPT_ON) 1688 { 1689 /* 1690 * Clear the hilite list and add only 1691 * the matches in this one line. 1692 */ 1693 clr_hilite(); 1694 hilite_line(linepos + skip_bytes, cline, line_len, chpos, sp, ep, NSP); 1695 } 1696 #endif 1697 if (chop_line()) 1698 { 1699 /* 1700 * If necessary, shift horizontally to make sure 1701 * search match is fully visible. 1702 */ 1703 if (sp[0] != NULL && ep[0] != NULL) 1704 { 1705 size_t start_off = ptr_diff(sp[0], cline); 1706 size_t end_off = ptr_diff(ep[0], cline); 1707 shift_visible(linepos, chpos[start_off], chpos[end_off]); 1708 } 1709 } else if (plastlinepos != NULL) 1710 { 1711 /* 1712 * If the line is so long that the highlighted match 1713 * won't be seen when the line is displayed normally 1714 * (starting at the first char) because it fills the whole 1715 * screen and more, scroll forward until the last char 1716 * of the match appears in the last line on the screen. 1717 * lastlinepos is the position of the first char of that last line. 1718 */ 1719 if (ep[0] != NULL) 1720 { 1721 size_t end_off = ptr_diff(ep[0], cline); 1722 if (end_off >= swidth * sheight / 4) /* heuristic */ 1723 *plastlinepos = get_lastlinepos(linepos, linepos + chpos[end_off], (int) sheight); 1724 } 1725 } 1726 free(cline); 1727 free(chpos); 1728 if (plinepos != NULL) 1729 *plinepos = linepos; 1730 return (0); 1731 } 1732 } 1733 } 1734 free(cline); 1735 free(chpos); 1736 } 1737 } 1738 1739 #if OSC8_LINK 1740 1741 /* 1742 * Search for and select the next OSC8 sequence, forward or backward. 1743 */ 1744 public void osc8_search(int search_type, constant char *param, int matches) 1745 { 1746 POSITION pos; 1747 int match; 1748 int curr_sindex = -1; 1749 1750 if (osc8_linepos != NULL_POSITION && (curr_sindex = onscreen(osc8_linepos)) >= 0) 1751 { 1752 /* Continue search in same line as current match. */ 1753 constant char *line; 1754 size_t line_len; 1755 pos = forw_raw_line(osc8_linepos, &line, &line_len); 1756 if (pos != NULL_POSITION) 1757 { 1758 if (osc8_search_line(search_type, osc8_linepos, line, line_len, param, NULL_POSITION, &matches) != OSC8_NO_MATCH) 1759 { 1760 osc8_shift_visible(); 1761 #if HILITE_SEARCH 1762 repaint_hilite(TRUE); 1763 #endif 1764 return; 1765 } 1766 } 1767 search_type |= SRCH_AFTER_TARGET; 1768 } 1769 /* 1770 * If the current OSC 8 link is on screen, start searching after it. 1771 * Otherwise, start searching at the -j line like a normal search. 1772 */ 1773 if (curr_sindex >= 0) 1774 pos = osc8_linepos; 1775 else 1776 pos = search_pos(search_type); 1777 if (pos == NULL_POSITION) 1778 { 1779 error("Nothing to search", NULL_PARG); 1780 return; 1781 } 1782 osc8_search_param = param; 1783 match = search_range(pos, NULL_POSITION, search_type | SRCH_OSC8, matches, -1, &pos, NULL, NULL); 1784 osc8_search_param = NULL; 1785 if (match != 0) 1786 { 1787 error("OSC 8 link not found", NULL_PARG); 1788 return; 1789 } 1790 /* If new link is on screen, just highlight it without scrolling. */ 1791 if (onscreen(pos) < 0) 1792 jump_loc(pos, jump_sline); 1793 #if HILITE_SEARCH 1794 repaint_hilite(TRUE); 1795 #endif 1796 } 1797 1798 /* 1799 * If a mouse click is on an OSC 8 link, select the link. 1800 */ 1801 public lbool osc8_click(int sindex, int col) 1802 { 1803 #if OSC8_LINK 1804 POSITION linepos = position(sindex); 1805 POSITION clickpos; 1806 constant char *line; 1807 size_t line_len; 1808 int matches = 1; 1809 int r; 1810 1811 if (linepos == NULL_POSITION) 1812 return FALSE; 1813 clickpos = pos_from_col(linepos, col, NULL_POSITION, -1); 1814 if (clickpos == NULL_POSITION) 1815 return FALSE; 1816 if (forw_raw_line(linepos, &line, &line_len) == NULL_POSITION) 1817 return FALSE; 1818 r = osc8_search_line(SRCH_FORW|SRCH_OSC8, linepos, line, line_len, NULL, clickpos, &matches); 1819 if (r != OSC8_NO_MATCH) 1820 { 1821 #if HILITE_SEARCH 1822 repaint_hilite(TRUE); 1823 #endif 1824 if (r == OSC8_ALREADY) 1825 osc8_open(); 1826 return TRUE; 1827 } 1828 #else 1829 (void) sindex; (void) col; 1830 #endif /* OSC8_LINK */ 1831 return FALSE; 1832 } 1833 1834 /* 1835 * Return the length of the scheme prefix in a URI. 1836 */ 1837 static size_t scheme_length(constant char *uri, size_t uri_len) 1838 { 1839 size_t plen; 1840 for (plen = 0; plen < uri_len; plen++) 1841 if (uri[plen] == ':') 1842 return plen; 1843 return 0; 1844 } 1845 1846 /* 1847 * Does a URI contain any dangerous characters? 1848 */ 1849 static lbool bad_uri(constant char *uri, size_t uri_len) 1850 { 1851 size_t i; 1852 for (i = 0; i < uri_len; i++) 1853 if (strchr("'\"", uri[i]) != NULL) 1854 return TRUE; 1855 return FALSE; 1856 } 1857 1858 /* 1859 * Re-read the line containing the selected OSC8 link. 1860 */ 1861 static lbool osc8_read_selected(struct osc8_parse_info *op) 1862 { 1863 constant char *line; 1864 size_t line_len; 1865 POSITION pos; 1866 1867 pos = forw_raw_line(osc8_linepos, &line, &line_len); 1868 if (pos == NULL_POSITION) 1869 return FALSE; 1870 op->osc8_start = &line[osc8_match_start - osc8_linepos]; 1871 op->osc8_end = &line[osc8_match_end - osc8_linepos]; 1872 op->params_start = &line[osc8_params_start - osc8_linepos]; 1873 op->params_end = &line[osc8_params_end - osc8_linepos]; 1874 op->uri_start = &line[osc8_uri_start - osc8_linepos]; 1875 op->uri_end = &line[osc8_uri_end - osc8_linepos]; 1876 return TRUE; 1877 } 1878 1879 /* 1880 * Open the currently selected OSC8 link. 1881 */ 1882 public void osc8_open(void) 1883 { 1884 struct osc8_parse_info op; 1885 char env_name[64]; 1886 size_t scheme_len; 1887 constant char *handler; 1888 char *open_cmd; 1889 size_t uri_len; 1890 FILE *hf; 1891 static constant char *env_name_pfx = "LESS_OSC8_"; 1892 1893 if (osc8_linepos == NULL_POSITION) 1894 { 1895 error("No OSC8 link selected", NULL_PARG); 1896 return; 1897 } 1898 if (!osc8_read_selected(&op)) 1899 { 1900 error("Cannot find OSC8 link", NULL_PARG); 1901 return; 1902 } 1903 /* 1904 * Read a "handler" shell cmd from environment variable "LESS_OSC8_scheme". 1905 * pr_expand the handler cmd (to expand %o -> osc8_path) and execute it. 1906 * Handler's stdout is an "opener" shell cmd; execute opener to open the link. 1907 */ 1908 uri_len = ptr_diff(op.uri_end, op.uri_start); 1909 scheme_len = scheme_length(op.uri_start, uri_len); 1910 if (scheme_len == 0 && op.uri_start[0] == '#') 1911 { 1912 /* Link to "id=" in same file. */ 1913 char *param = ecalloc(uri_len+3, sizeof(char)); 1914 strcpy(param, "id="); 1915 strncpy(param+3, op.uri_start+1, uri_len-1); 1916 param[uri_len+2] = '\0'; 1917 osc8_search(SRCH_FORW|SRCH_WRAP, param, 1); 1918 free(param); 1919 return; 1920 } 1921 #if HAVE_POPEN 1922 if (bad_uri(op.uri_start, uri_len)) 1923 { 1924 error("Cannot open link containing quote characters", NULL_PARG); 1925 return; 1926 } 1927 SNPRINTF3(env_name, sizeof(env_name), "%s%.*s", env_name_pfx, (int) scheme_len, op.uri_start); 1928 handler = lgetenv(env_name); 1929 if (isnullenv(handler) || strcmp(handler, "-") == 0) 1930 handler = lgetenv("LESS_OSC8_ANY"); 1931 if (isnullenv(handler)) 1932 { 1933 PARG parg; 1934 parg.p_string = env_name + strlen(env_name_pfx); /* {{ tricky }} */ 1935 error("No handler for \"%s\" link type", &parg); 1936 return; 1937 } 1938 /* {{ ugly global osc8_path }} */ 1939 osc8_path = saven(op.uri_start, uri_len); 1940 hf = popen(pr_expand(handler), "r"); 1941 free(osc8_path); 1942 osc8_path = NULL; 1943 if (hf == NULL) 1944 { 1945 PARG parg; 1946 parg.p_string = env_name; 1947 error("Cannot execute protocol handler in %s", &parg); 1948 return; 1949 } 1950 open_cmd = readfd(hf); 1951 pclose(hf); 1952 if (strncmp(open_cmd, ":e", 2) == 0) 1953 { 1954 edit(skipsp(&open_cmd[2])); 1955 } else 1956 { 1957 lsystem(open_cmd, "link done"); 1958 } 1959 free(open_cmd); 1960 #else 1961 error("Cannot open link because your system does not support popen", NULL_PARG); 1962 #endif /* HAVE_POPEN */ 1963 } 1964 1965 /* 1966 * Jump to the currently selected OSC8 link. 1967 */ 1968 public void osc8_jump(void) 1969 { 1970 if (osc8_linepos == NULL_POSITION) 1971 { 1972 error("No OSC8 link selected", NULL_PARG); 1973 return; 1974 } 1975 jump_loc(osc8_linepos, jump_sline); 1976 } 1977 1978 #endif /* OSC8_LINK */ 1979 1980 /* 1981 * search for a pattern in history. If found, compile that pattern. 1982 */ 1983 static int hist_pattern(int search_type) 1984 { 1985 #if CMD_HISTORY 1986 constant char *pattern; 1987 1988 set_mlist(ml_search, 0); 1989 pattern = cmd_lastpattern(); 1990 if (pattern == NULL) 1991 return (0); 1992 1993 if (set_pattern(&search_info, pattern, search_type, 1) < 0) 1994 return (-1); 1995 1996 #if HILITE_SEARCH 1997 if (hilite_search == OPT_ONPLUS && !hide_hilite) 1998 hilite_screen(); 1999 #endif 2000 2001 return (1); 2002 #else /* CMD_HISTORY */ 2003 return (0); 2004 #endif /* CMD_HISTORY */ 2005 } 2006 2007 /* 2008 * Change the caseless-ness of searches. 2009 * Updates the internal search state to reflect a change in the -i flag. 2010 */ 2011 public void chg_caseless(void) 2012 { 2013 if (!search_info.is_ucase_pattern) 2014 { 2015 /* 2016 * Pattern did not have uppercase. 2017 * Set the search caselessness to the global caselessness. 2018 */ 2019 is_caseless = caseless; 2020 /* 2021 * If regex handles caseless, we need to discard 2022 * the pattern which was compiled with the old caseless. 2023 */ 2024 if (!re_handles_caseless) 2025 /* We handle caseless, so the pattern doesn't change. */ 2026 return; 2027 } 2028 /* 2029 * Regenerate the pattern using the new state. 2030 */ 2031 clear_pattern(&search_info); 2032 (void) hist_pattern(search_info.search_type); 2033 } 2034 2035 /* 2036 * Search for the n-th occurrence of a specified pattern, 2037 * either forward or backward. 2038 * Return the number of matches not yet found in this file 2039 * (that is, n minus the number of matches found). 2040 * Return -1 if the search should be aborted. 2041 * Caller may continue the search in another file 2042 * if less than n matches are found in this file. 2043 */ 2044 public int search(int search_type, constant char *pattern, int n) 2045 { 2046 POSITION pos; 2047 POSITION opos; 2048 POSITION lastlinepos = NULL_POSITION; 2049 2050 if (pattern == NULL || *pattern == '\0') 2051 { 2052 /* 2053 * A null pattern means use the previously compiled pattern. 2054 */ 2055 search_type |= SRCH_AFTER_TARGET; 2056 if (!prev_pattern(&search_info)) 2057 { 2058 int r = hist_pattern(search_type); 2059 if (r == 0) 2060 error("No previous regular expression", NULL_PARG); 2061 if (r <= 0) 2062 return (-1); 2063 } 2064 if ((search_type & SRCH_NO_REGEX) != 2065 (search_info.search_type & SRCH_NO_REGEX)) 2066 { 2067 error("Please re-enter search pattern", NULL_PARG); 2068 return -1; 2069 } 2070 #if HILITE_SEARCH 2071 if (hilite_search == OPT_ON || status_col) 2072 { 2073 /* 2074 * Erase the highlights currently on screen. 2075 * If the search fails, we'll redisplay them later. 2076 */ 2077 repaint_hilite(FALSE); 2078 } 2079 if (hilite_search == OPT_ONPLUS && hide_hilite) 2080 { 2081 /* 2082 * Highlight any matches currently on screen, 2083 * before we actually start the search. 2084 */ 2085 hide_hilite = FALSE; 2086 hilite_screen(); 2087 } 2088 hide_hilite = FALSE; 2089 #endif 2090 } else 2091 { 2092 /* 2093 * Compile the pattern. 2094 */ 2095 int show_error = !(search_type & SRCH_INCR); 2096 if (set_pattern(&search_info, pattern, search_type, show_error) < 0) 2097 return (-1); 2098 #if HILITE_SEARCH 2099 if (hilite_search || status_col) 2100 { 2101 /* 2102 * Erase the highlights currently on screen. 2103 * Also permanently delete them from the hilite list. 2104 */ 2105 repaint_hilite(FALSE); 2106 hide_hilite = FALSE; 2107 clr_hilite(); 2108 } 2109 if (hilite_search == OPT_ONPLUS || status_col) 2110 { 2111 /* 2112 * Highlight any matches currently on screen, 2113 * before we actually start the search. 2114 */ 2115 hilite_screen(); 2116 } 2117 #endif 2118 } 2119 2120 /* 2121 * Figure out where to start the search. 2122 */ 2123 pos = ((search_type & SRCH_INCR) && search_incr_start != NULL_POSITION) ? 2124 search_incr_start : search_pos(search_type); 2125 opos = position(sindex_from_sline(jump_sline)); 2126 if (pos == NULL_POSITION) 2127 { 2128 /* 2129 * Can't find anyplace to start searching from. 2130 */ 2131 if (search_type & SRCH_PAST_EOF) 2132 return (n); 2133 #if HILITE_SEARCH 2134 if (hilite_search == OPT_ON || status_col) 2135 repaint_hilite(TRUE); 2136 #endif 2137 error("Nothing to search", NULL_PARG); 2138 return (-1); 2139 } 2140 2141 n = search_range(pos, NULL_POSITION, search_type, n, -1, 2142 &pos, (POSITION*)NULL, &lastlinepos); 2143 /* 2144 * This ABORT_SIGS check ensures that if the user presses interrupt, 2145 * we don't continue and complete the search. 2146 * That is, we leave the display unchanged. 2147 * {{ Is this true? Do we always want to abort the search on interrupt? }} 2148 */ 2149 if (ABORT_SIGS()) 2150 return (-1); 2151 if (n != 0) 2152 { 2153 /* 2154 * Search was unsuccessful. 2155 */ 2156 #if HILITE_SEARCH 2157 if ((hilite_search == OPT_ON || status_col) && n > 0) 2158 /* 2159 * Redisplay old hilites. 2160 */ 2161 repaint_hilite(TRUE); 2162 #endif 2163 return (n); 2164 } 2165 2166 if (!(search_type & SRCH_NO_MOVE)) 2167 { 2168 /* 2169 * Go to the matching line. 2170 */ 2171 if (lastlinepos != NULL_POSITION) 2172 jump_loc(lastlinepos, BOTTOM); 2173 else if (pos != opos) 2174 jump_loc(pos, jump_sline); 2175 } 2176 2177 #if HILITE_SEARCH 2178 if (hilite_search == OPT_ON || status_col) 2179 /* 2180 * Display new hilites in the matching line. 2181 */ 2182 repaint_hilite(TRUE); 2183 #endif 2184 return (0); 2185 } 2186 2187 #if HILITE_SEARCH 2188 /* 2189 * Prepare hilites in a given range of the file. 2190 * 2191 * The pair (prep_startpos,prep_endpos) delimits a contiguous region 2192 * of the file that has been "prepared"; that is, scanned for matches for 2193 * the current search pattern, and hilites have been created for such matches. 2194 * If prep_startpos == NULL_POSITION, the prep region is empty. 2195 * If prep_endpos == NULL_POSITION, the prep region extends to EOF. 2196 * prep_hilite asks that the range (spos,epos) be covered by the prep region. 2197 */ 2198 public void prep_hilite(POSITION spos, POSITION epos, int maxlines) 2199 { 2200 POSITION nprep_startpos = prep_startpos; 2201 POSITION nprep_endpos = prep_endpos; 2202 POSITION new_epos; 2203 POSITION max_epos; 2204 int result; 2205 int i; 2206 2207 if (!prev_pattern(&search_info) && !is_filtering()) 2208 return; 2209 2210 /* 2211 * Make sure our prep region always starts at the beginning of 2212 * a line. (search_range takes care of the end boundary below.) 2213 */ 2214 spos = back_raw_line(spos+1, NULL, NULL); 2215 2216 /* 2217 * If we're limited to a max number of lines, figure out the 2218 * file position we should stop at. 2219 */ 2220 if (maxlines < 0) 2221 max_epos = NULL_POSITION; 2222 else 2223 { 2224 max_epos = spos; 2225 for (i = 0; i < maxlines; i++) 2226 max_epos = forw_raw_line(max_epos, NULL, NULL); 2227 } 2228 if (epos == NULL_POSITION || (max_epos != NULL_POSITION && epos > max_epos)) 2229 epos = max_epos; 2230 2231 /* 2232 * Find two ranges: 2233 * The range that we need to search (spos,epos); and the range that 2234 * the "prep" region will then cover (nprep_startpos,nprep_endpos). 2235 */ 2236 2237 if (prep_startpos == NULL_POSITION || 2238 (epos != NULL_POSITION && epos < prep_startpos) || 2239 spos > prep_endpos) 2240 { 2241 /* 2242 * New range is not contiguous with old prep region. 2243 * Discard the old prep region and start a new one. 2244 */ 2245 clr_hilite(); 2246 clr_filter(); 2247 nprep_startpos = nprep_endpos = spos; 2248 } else 2249 { 2250 /* 2251 * New range partially or completely overlaps old prep region. 2252 */ 2253 if (epos != NULL_POSITION && epos <= prep_endpos) 2254 { 2255 /* 2256 * New range ends within old prep region. 2257 * Truncate search to end at start of old prep region. 2258 */ 2259 epos = prep_startpos; 2260 } 2261 if (spos < prep_startpos) 2262 { 2263 /* 2264 * New range starts before old prep region. 2265 * Extend old prep region backwards to start at 2266 * start of new range. 2267 */ 2268 nprep_startpos = spos; 2269 } else /* (spos >= prep_startpos) */ 2270 { 2271 /* 2272 * New range starts within or after old prep region. 2273 * Trim search to start at end of old prep region. 2274 */ 2275 spos = prep_endpos; 2276 } 2277 } 2278 2279 if (epos == NULL_POSITION || epos > spos) 2280 { 2281 int search_type = SRCH_FORW | SRCH_FIND_ALL; 2282 search_type |= (search_info.search_type & SRCH_NO_REGEX); 2283 for (;;) 2284 { 2285 result = search_range(spos, epos, search_type, 0, maxlines, (POSITION*)NULL, &new_epos, (POSITION*)NULL); 2286 if (result < 0) 2287 return; 2288 if (nprep_endpos == NULL_POSITION || new_epos > nprep_endpos) 2289 nprep_endpos = new_epos; 2290 2291 /* 2292 * Check both ends of the resulting prep region to 2293 * make sure they're not filtered. If they are, 2294 * keep going at least one more line until we find 2295 * something that isn't filtered, or hit the end. 2296 */ 2297 if (prep_endpos == NULL_POSITION || nprep_endpos > prep_endpos) 2298 { 2299 if (new_epos >= nprep_endpos && is_filtered(new_epos-1)) 2300 { 2301 spos = nprep_endpos; 2302 epos = forw_raw_line(nprep_endpos, NULL, NULL); 2303 if (epos == NULL_POSITION) 2304 break; 2305 maxlines = 1; 2306 nprep_endpos = epos; 2307 continue; 2308 } 2309 } 2310 2311 if (prep_startpos == NULL_POSITION || nprep_startpos < prep_startpos) 2312 { 2313 if (nprep_startpos > 0 && is_filtered(nprep_startpos)) 2314 { 2315 epos = nprep_startpos; 2316 spos = back_raw_line(nprep_startpos, NULL, NULL); 2317 if (spos == NULL_POSITION) 2318 break; 2319 nprep_startpos = spos; 2320 maxlines = 1; 2321 continue; 2322 } 2323 } 2324 break; 2325 } 2326 } 2327 prep_startpos = nprep_startpos; 2328 prep_endpos = nprep_endpos; 2329 } 2330 2331 /* 2332 * Set the pattern to be used for line filtering. 2333 */ 2334 public void set_filter_pattern(constant char *pattern, int search_type) 2335 { 2336 struct pattern_info *filter; 2337 2338 clr_filter(); 2339 if (pattern == NULL || *pattern == '\0') 2340 { 2341 /* Clear and free all filters. */ 2342 for (filter = filter_infos; filter != NULL; ) 2343 { 2344 struct pattern_info *next_filter = filter->next; 2345 clear_pattern(filter); 2346 free(filter); 2347 filter = next_filter; 2348 } 2349 filter_infos = NULL; 2350 } else 2351 { 2352 /* Create a new filter and add it to the filter_infos list. */ 2353 filter = ecalloc(1, sizeof(struct pattern_info)); 2354 init_pattern(filter); 2355 if (set_pattern(filter, pattern, search_type, 1) < 0) 2356 { 2357 free(filter); 2358 return; 2359 } 2360 filter->next = filter_infos; 2361 filter_infos = filter; 2362 } 2363 screen_trashed(); 2364 } 2365 2366 /* 2367 * Is there a line filter in effect? 2368 */ 2369 public lbool is_filtering(void) 2370 { 2371 if (ch_getflags() & CH_HELPFILE) 2372 return (FALSE); 2373 return (filter_infos != NULL); 2374 } 2375 #endif 2376 2377 #if HAVE_V8_REGCOMP 2378 /* 2379 * This function is called by the V8 regcomp to report 2380 * errors in regular expressions. 2381 */ 2382 public int reg_show_error = 1; 2383 2384 void regerror(constant char *s) 2385 { 2386 PARG parg; 2387 2388 if (!reg_show_error) 2389 return; 2390 parg.p_string = s; 2391 error("%s", &parg); 2392 } 2393 #endif 2394 2395