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