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