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