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