xref: /freebsd/contrib/less/search.c (revision faf25f48d601ae39f5752602f3020e2e92605625)
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
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
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
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
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
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
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
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
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
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
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
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
399 clr_hilite(VOID_PARAM)
400 {
401 	clr_hlist(&hilite_anchor);
402 }
403 
404 	public void
405 clr_filter(VOID_PARAM)
406 {
407 	clr_hlist(&filter_anchor);
408 }
409 
410 	struct hilite_node*
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*
421 hlist_next(n)
422 	struct hilite_node *n;
423 {
424 	return n->next;
425 }
426 
427 	struct hilite_node*
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*
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
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
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
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
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
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*
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*
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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