xref: /dragonfly/contrib/less/search.c (revision dca3c15d)
1 /*
2  * Copyright (C) 1984-2008  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 about less, or for information on how to
8  * contact the author, see the README file.
9  */
10 
11 
12 /*
13  * Routines to search a file for a pattern.
14  */
15 
16 #include "less.h"
17 #include "position.h"
18 #include "charset.h"
19 
20 #define	MINPOS(a,b)	(((a) < (b)) ? (a) : (b))
21 #define	MAXPOS(a,b)	(((a) > (b)) ? (a) : (b))
22 
23 #if HAVE_POSIX_REGCOMP
24 #include <regex.h>
25 #ifdef REG_EXTENDED
26 #define	REGCOMP_FLAG	REG_EXTENDED
27 #else
28 #define	REGCOMP_FLAG	0
29 #endif
30 #endif
31 #if HAVE_PCRE
32 #include <pcre.h>
33 #endif
34 #if HAVE_RE_COMP
35 char *re_comp();
36 int re_exec();
37 #endif
38 #if HAVE_REGCMP
39 char *regcmp();
40 char *regex();
41 extern char *__loc1;
42 #endif
43 #if HAVE_V8_REGCOMP
44 #include "regexp.h"
45 #endif
46 
47 static int match();
48 
49 extern int sigs;
50 extern int how_search;
51 extern int caseless;
52 extern int linenums;
53 extern int sc_height;
54 extern int jump_sline;
55 extern int bs_mode;
56 extern int ctldisp;
57 extern int status_col;
58 extern void * constant ml_search;
59 extern POSITION start_attnpos;
60 extern POSITION end_attnpos;
61 extern int utf_mode;
62 extern int screen_trashed;
63 #if HILITE_SEARCH
64 extern int hilite_search;
65 extern int size_linebuf;
66 extern int squished;
67 extern int can_goto_line;
68 static int hide_hilite;
69 static int oldbot;
70 static POSITION prep_startpos;
71 static POSITION prep_endpos;
72 
73 struct hilite
74 {
75 	struct hilite *hl_next;
76 	POSITION hl_startpos;
77 	POSITION hl_endpos;
78 };
79 static struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION };
80 static struct hilite filter_anchor = { NULL, NULL_POSITION, NULL_POSITION };
81 #define	hl_first	hl_next
82 #endif
83 
84 /*
85  * These are the static variables that represent the "remembered"
86  * search pattern.
87  */
88 #if HAVE_POSIX_REGCOMP
89 #define DEFINE_PATTERN(name)  static regex_t *name = NULL
90 #endif
91 #if HAVE_PCRE
92 #define DEFINE_PATTERN(name)  pcre *name = NULL;
93 #endif
94 #if HAVE_RE_COMP
95 #define DEFINE_PATTERN(name)  int name = 0;
96 #endif
97 #if HAVE_REGCMP
98 #define DEFINE_PATTERN(name)  static char *name = NULL;
99 #endif
100 #if HAVE_V8_REGCOMP
101 #define DEFINE_PATTERN(name)  static struct regexp *name = NULL;
102 #endif
103 
104 DEFINE_PATTERN(search_pattern);
105 DEFINE_PATTERN(filter_pattern);
106 
107 static int is_caseless;
108 static int is_ucase_pattern;
109 static int last_search_type;
110 static int last_filter_type;
111 static char *last_pattern = NULL;
112 
113 #define	CVT_TO_LC	01	/* Convert upper-case to lower-case */
114 #define	CVT_BS		02	/* Do backspace processing */
115 #define	CVT_CRLF	04	/* Remove CR after LF */
116 #define	CVT_ANSI	010	/* Remove ANSI escape sequences */
117 
118 /*
119  * Get the length of a buffer needed to convert a string.
120  */
121 	static int
122 cvt_length(len, ops)
123 	int len;
124 	int ops;
125 {
126 	if (utf_mode)
127 		/*
128 		 * Just copying a string in UTF-8 mode can cause it to grow
129 		 * in length.
130 		 * Six output bytes for one input byte is the worst case
131 		 * (and unfortunately is far more than is needed in any
132 		 * non-pathological situation, so this is very wasteful).
133 		 */
134 		len *= 6;
135 	return len + 1;
136 }
137 
138 /*
139  * Convert text.  Perform the transformations specified by ops.
140  */
141 	static void
142 cvt_text(odst, osrc, lenp, ops)
143 	char *odst;
144 	char *osrc;
145 	int *lenp;
146 	int ops;
147 {
148 	char *dst;
149 	char *src;
150 	register char *src_end;
151 	LWCHAR ch;
152 
153 	if (lenp != NULL)
154 		src_end = osrc + *lenp;
155 	else
156 		src_end = osrc + strlen(osrc);
157 
158 	for (src = osrc, dst = odst;  src < src_end;  )
159 	{
160 		ch = step_char(&src, +1, src_end);
161 		if ((ops & CVT_TO_LC) && IS_UPPER(ch))
162 		{
163 			/* Convert uppercase to lowercase. */
164 			put_wchar(&dst, TO_LOWER(ch));
165 		} else if ((ops & CVT_BS) && ch == '\b' && dst > odst)
166 		{
167 			/* Delete backspace and preceding char. */
168 			do {
169 				dst--;
170 			} while (dst > odst &&
171 				!IS_ASCII_OCTET(*dst) && !IS_UTF8_LEAD(*dst));
172 		} else if ((ops & CVT_ANSI) && IS_CSI_START(ch))
173 		{
174 			/* Skip to end of ANSI escape sequence. */
175 			src++;  /* skip the CSI start char */
176 			while (src < src_end)
177 				if (!is_ansi_middle(*src++))
178 					break;
179 		} else
180 			/* Just copy. */
181 			put_wchar(&dst, ch);
182 	}
183 	if ((ops & CVT_CRLF) && dst > odst && dst[-1] == '\r')
184 		dst--;
185 	*dst = '\0';
186 	if (lenp != NULL)
187 		*lenp = dst - odst;
188 }
189 
190 /*
191  * Determine which conversions to perform.
192  */
193 	static int
194 get_cvt_ops()
195 {
196 	int ops = 0;
197 	if (is_caseless || bs_mode == BS_SPECIAL)
198 	{
199 		if (is_caseless)
200 			ops |= CVT_TO_LC;
201 		if (bs_mode == BS_SPECIAL)
202 			ops |= CVT_BS;
203 		if (bs_mode != BS_CONTROL)
204 			ops |= CVT_CRLF;
205 	} else if (bs_mode != BS_CONTROL)
206 	{
207 		ops |= CVT_CRLF;
208 	}
209 	if (ctldisp == OPT_ONPLUS)
210 		ops |= CVT_ANSI;
211 	return (ops);
212 }
213 
214 /*
215  * Are there any uppercase letters in this string?
216  */
217 	static int
218 is_ucase(str)
219 	char *str;
220 {
221 	char *str_end = str + strlen(str);
222 	LWCHAR ch;
223 
224 	while (str < str_end)
225 	{
226 		ch = step_char(&str, +1, str_end);
227 		if (IS_UPPER(ch))
228 			return (1);
229 	}
230 	return (0);
231 }
232 
233 /*
234  * Is there a previous (remembered) search pattern?
235  */
236 	static int
237 prev_pattern()
238 {
239 	if (last_search_type & SRCH_NO_REGEX)
240 		return (last_pattern != NULL);
241 #if HAVE_POSIX_REGCOMP
242 	return (search_pattern != NULL);
243 #endif
244 #if HAVE_PCRE
245 	return (search_pattern != NULL);
246 #endif
247 #if HAVE_RE_COMP
248 	return (search_pattern != 0);
249 #endif
250 #if HAVE_REGCMP
251 	return (search_pattern != NULL);
252 #endif
253 #if HAVE_V8_REGCOMP
254 	return (search_pattern != NULL);
255 #endif
256 #if NO_REGEX
257 	return (search_pattern != NULL);
258 #endif
259 }
260 
261 #if HILITE_SEARCH
262 /*
263  * Repaint the hilites currently displayed on the screen.
264  * Repaint each line which contains highlighted text.
265  * If on==0, force all hilites off.
266  */
267 	public void
268 repaint_hilite(on)
269 	int on;
270 {
271 	int slinenum;
272 	POSITION pos;
273 	POSITION epos;
274 	int save_hide_hilite;
275 
276 	if (squished)
277 		repaint();
278 
279 	save_hide_hilite = hide_hilite;
280 	if (!on)
281 	{
282 		if (hide_hilite)
283 			return;
284 		hide_hilite = 1;
285 	}
286 
287 	if (!can_goto_line)
288 	{
289 		repaint();
290 		hide_hilite = save_hide_hilite;
291 		return;
292 	}
293 
294 	for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
295 	{
296 		pos = position(slinenum);
297 		if (pos == NULL_POSITION)
298 			continue;
299 		epos = position(slinenum+1);
300 #if 0
301 		/*
302 		 * If any character in the line is highlighted,
303 		 * repaint the line.
304 		 *
305 		 * {{ This doesn't work -- if line is drawn with highlights
306 		 * which should be erased (e.g. toggle -i with status column),
307 		 * we must redraw the line even if it has no highlights.
308 		 * For now, just repaint every line. }}
309 		 */
310 		if (is_hilited(pos, epos, 1, NULL))
311 #endif
312 		{
313 			(void) forw_line(pos);
314 			goto_line(slinenum);
315 			put_line();
316 		}
317 	}
318 	if (!oldbot)
319 		lower_left();
320 	hide_hilite = save_hide_hilite;
321 }
322 
323 /*
324  * Clear the attn hilite.
325  */
326 	public void
327 clear_attn()
328 {
329 	int slinenum;
330 	POSITION old_start_attnpos;
331 	POSITION old_end_attnpos;
332 	POSITION pos;
333 	POSITION epos;
334 	int moved = 0;
335 
336 	if (start_attnpos == NULL_POSITION)
337 		return;
338 	old_start_attnpos = start_attnpos;
339 	old_end_attnpos = end_attnpos;
340 	start_attnpos = end_attnpos = NULL_POSITION;
341 
342 	if (!can_goto_line)
343 	{
344 		repaint();
345 		return;
346 	}
347 	if (squished)
348 		repaint();
349 
350 	for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
351 	{
352 		pos = position(slinenum);
353 		if (pos == NULL_POSITION)
354 			continue;
355 		epos = position(slinenum+1);
356 		if (pos < old_end_attnpos &&
357 		     (epos == NULL_POSITION || epos > old_start_attnpos))
358 		{
359 			(void) forw_line(pos);
360 			goto_line(slinenum);
361 			put_line();
362 			moved = 1;
363 		}
364 	}
365 	if (moved)
366 		lower_left();
367 }
368 #endif
369 
370 /*
371  * Hide search string highlighting.
372  */
373 	public void
374 undo_search()
375 {
376 	if (!prev_pattern())
377 	{
378 		error("No previous regular expression", NULL_PARG);
379 		return;
380 	}
381 #if HILITE_SEARCH
382 	hide_hilite = !hide_hilite;
383 	repaint_hilite(1);
384 #endif
385 }
386 
387 /*
388  * Compile a search pattern, for future use by match_pattern.
389  */
390 	static int
391 compile_pattern2(pattern, search_type, comp_pattern)
392 	char *pattern;
393 	int search_type;
394 	void **comp_pattern;
395 {
396 	if ((search_type & SRCH_NO_REGEX) == 0)
397 	{
398 #if HAVE_POSIX_REGCOMP
399 		regex_t *comp = (regex_t *) ecalloc(1, sizeof(regex_t));
400 		regex_t **pcomp = (regex_t **) comp_pattern;
401 		if (regcomp(comp, pattern, REGCOMP_FLAG))
402 		{
403 			free(comp);
404 			error("Invalid pattern", NULL_PARG);
405 			return (-1);
406 		}
407 		if (*pcomp != NULL)
408 			regfree(*pcomp);
409 		*pcomp = comp;
410 #endif
411 #if HAVE_PCRE
412 		pcre *comp;
413 		pcre **pcomp = (pcre **) comp_pattern;
414 		const char *errstring;
415 		int erroffset;
416 		PARG parg;
417 		comp = pcre_compile(pattern, 0,
418 				&errstring, &erroffset, NULL);
419 		if (comp == NULL)
420 		{
421 			parg.p_string = (char *) errstring;
422 			error("%s", &parg);
423 			return (-1);
424 		}
425 		*pcomp = comp;
426 #endif
427 #if HAVE_RE_COMP
428 		PARG parg;
429 		int *pcomp = (int *) comp_pattern;
430 		if ((parg.p_string = re_comp(pattern)) != NULL)
431 		{
432 			error("%s", &parg);
433 			return (-1);
434 		}
435 		*pcomp = 1;
436 #endif
437 #if HAVE_REGCMP
438 		char *comp;
439 		char **pcomp = (char **) comp_pattern;
440 		if ((comp = regcmp(pattern, 0)) == NULL)
441 		{
442 			error("Invalid pattern", NULL_PARG);
443 			return (-1);
444 		}
445 		if (pcomp != NULL)
446 			free(*pcomp);
447 		*pcomp = comp;
448 #endif
449 #if HAVE_V8_REGCOMP
450 		struct regexp *comp;
451 		struct regexp **pcomp = (struct regexp **) comp_pattern;
452 		if ((comp = regcomp(pattern)) == NULL)
453 		{
454 			/*
455 			 * regcomp has already printed an error message
456 			 * via regerror().
457 			 */
458 			return (-1);
459 		}
460 		if (*pcomp != NULL)
461 			free(*pcomp);
462 		*pcomp = comp;
463 #endif
464 	}
465 
466 	if (comp_pattern == (void **) &search_pattern)
467 	{
468 		if (last_pattern != NULL)
469 			free(last_pattern);
470 		last_pattern = (char *) calloc(1, strlen(pattern)+1);
471 		if (last_pattern != NULL)
472 			strcpy(last_pattern, pattern);
473 		last_search_type = search_type;
474 	} else
475 	{
476 		last_filter_type = search_type;
477 	}
478 	return (0);
479 }
480 
481 /*
482  * Like compile_pattern2, but convert the pattern to lowercase if necessary.
483  */
484 	static int
485 compile_pattern(pattern, search_type, comp_pattern)
486 	char *pattern;
487 	int search_type;
488 	void **comp_pattern;
489 {
490 	char *cvt_pattern;
491 	int result;
492 
493 	if (caseless != OPT_ONPLUS)
494 		cvt_pattern = pattern;
495 	else
496 	{
497 		cvt_pattern = (char*) ecalloc(1, cvt_length(strlen(pattern), CVT_TO_LC));
498 		cvt_text(cvt_pattern, pattern, (int *)NULL, CVT_TO_LC);
499 	}
500 	result = compile_pattern2(cvt_pattern, search_type, comp_pattern);
501 	if (cvt_pattern != pattern)
502 		free(cvt_pattern);
503 	return (result);
504 }
505 
506 /*
507  * Forget that we have a compiled pattern.
508  */
509 	static void
510 uncompile_pattern(pattern)
511 	void **pattern;
512 {
513 #if HAVE_POSIX_REGCOMP
514 	regex_t **pcomp = (regex_t **) pattern;
515 	if (*pcomp != NULL)
516 		regfree(*pcomp);
517 	*pcomp = NULL;
518 #endif
519 #if HAVE_PCRE
520 	pcre **pcomp = (pcre **) pattern;
521 	if (*pcomp != NULL)
522 		pcre_free(*pcomp);
523 	*pcomp = NULL;
524 #endif
525 #if HAVE_RE_COMP
526 	int *pcomp = (int *) pattern;
527 	*pcomp = 0;
528 #endif
529 #if HAVE_REGCMP
530 	char **pcomp = (char **) pattern;
531 	if (*pcomp != NULL)
532 		free(*pcomp);
533 	*pcomp = NULL;
534 #endif
535 #if HAVE_V8_REGCOMP
536 	struct regexp **pcomp = (struct regexp **) pattern;
537 	if (*pcomp != NULL)
538 		free(*pcomp);
539 	*pcomp = NULL;
540 #endif
541 }
542 
543 	static void
544 uncompile_search_pattern()
545 {
546 	uncompile_pattern(&search_pattern);
547 	last_pattern = NULL;
548 }
549 
550 	static void
551 uncompile_filter_pattern()
552 {
553 	uncompile_pattern(&filter_pattern);
554 }
555 
556 /*
557  * Is a compiled pattern null?
558  */
559 	static int
560 is_null_pattern(pattern)
561 	void *pattern;
562 {
563 #if HAVE_POSIX_REGCOMP
564 	return (pattern == NULL);
565 #endif
566 #if HAVE_PCRE
567 	return (pattern == NULL);
568 #endif
569 #if HAVE_RE_COMP
570 	return (pattern == 0);
571 #endif
572 #if HAVE_REGCMP
573 	return (pattern == NULL);
574 #endif
575 #if HAVE_V8_REGCOMP
576 	return (pattern == NULL);
577 #endif
578 }
579 
580 /*
581  * Perform a pattern match with the previously compiled pattern.
582  * Set sp and ep to the start and end of the matched string.
583  */
584 	static int
585 match_pattern(pattern, line, line_len, sp, ep, notbol, search_type)
586 	void *pattern;
587 	char *line;
588 	int line_len;
589 	char **sp;
590 	char **ep;
591 	int notbol;
592 	int search_type;
593 {
594 	int matched;
595 #if HAVE_POSIX_REGCOMP
596 	regex_t *spattern = (regex_t *) pattern;
597 #endif
598 #if HAVE_PCRE
599 	pcre *spattern = (pcre *) pattern;
600 #endif
601 #if HAVE_RE_COMP
602 	int spattern = (int) pattern;
603 #endif
604 #if HAVE_REGCMP
605 	char *spattern = (char *) pattern;
606 #endif
607 #if HAVE_V8_REGCOMP
608 	struct regexp *spattern = (struct regexp *) pattern;
609 #endif
610 
611 	if (search_type & SRCH_NO_REGEX)
612 		return (match(last_pattern, strlen(last_pattern), line, line_len, sp, ep));
613 
614 #if HAVE_POSIX_REGCOMP
615 	{
616 		regmatch_t rm;
617 		int flags = (notbol) ? REG_NOTBOL : 0;
618 		matched = !regexec(spattern, line, 1, &rm, flags);
619 		if (matched)
620 		{
621 #ifndef __WATCOMC__
622 			*sp = line + rm.rm_so;
623 			*ep = line + rm.rm_eo;
624 #else
625 			*sp = rm.rm_sp;
626 			*ep = rm.rm_ep;
627 #endif
628 		}
629 	}
630 #endif
631 #if HAVE_PCRE
632 	{
633 		int flags = (notbol) ? PCRE_NOTBOL : 0;
634 		int ovector[3];
635 		matched = pcre_exec(spattern, NULL, line, line_len,
636 			0, flags, ovector, 3) >= 0;
637 		if (matched)
638 		{
639 			*sp = line + ovector[0];
640 			*ep = line + ovector[1];
641 		}
642 	}
643 #endif
644 #if HAVE_RE_COMP
645 	matched = (re_exec(line) == 1);
646 	/*
647 	 * re_exec doesn't seem to provide a way to get the matched string.
648 	 */
649 	*sp = *ep = NULL;
650 #endif
651 #if HAVE_REGCMP
652 	*ep = regex(spattern, line);
653 	matched = (*ep != NULL);
654 	if (matched)
655 		*sp = __loc1;
656 #endif
657 #if HAVE_V8_REGCOMP
658 #if HAVE_REGEXEC2
659 	matched = regexec2(spattern, line, notbol);
660 #else
661 	matched = regexec(spattern, line);
662 #endif
663 	if (matched)
664 	{
665 		*sp = spattern->startp[0];
666 		*ep = spattern->endp[0];
667 	}
668 #endif
669 #if NO_REGEX
670 	matched = match(last_pattern, strlen(last_pattern), line, line_len, sp, ep);
671 #endif
672 	matched = (!(search_type & SRCH_NO_MATCH) && matched) ||
673 			((search_type & SRCH_NO_MATCH) && !matched);
674 	return (matched);
675 }
676 
677 #if HILITE_SEARCH
678 /*
679  * Clear the hilite list.
680  */
681 	public void
682 clr_hlist(anchor)
683 	struct hilite *anchor;
684 {
685 	struct hilite *hl;
686 	struct hilite *nexthl;
687 
688 	for (hl = anchor->hl_first;  hl != NULL;  hl = nexthl)
689 	{
690 		nexthl = hl->hl_next;
691 		free((void*)hl);
692 	}
693 	anchor->hl_first = NULL;
694 	prep_startpos = prep_endpos = NULL_POSITION;
695 }
696 
697 	public void
698 clr_hilite()
699 {
700 	clr_hlist(&hilite_anchor);
701 }
702 
703 	public void
704 clr_filter()
705 {
706 	clr_hlist(&filter_anchor);
707 }
708 
709 /*
710  * Should any characters in a specified range be highlighted?
711  */
712 	static int
713 is_hilited_range(pos, epos)
714 	POSITION pos;
715 	POSITION epos;
716 {
717 	struct hilite *hl;
718 
719 	/*
720 	 * Look at each highlight and see if any part of it falls in the range.
721 	 */
722 	for (hl = hilite_anchor.hl_first;  hl != NULL;  hl = hl->hl_next)
723 	{
724 		if (hl->hl_endpos > pos &&
725 		    (epos == NULL_POSITION || epos > hl->hl_startpos))
726 			return (1);
727 	}
728 	return (0);
729 }
730 
731 /*
732  * Is a line "filtered" -- that is, should it be hidden?
733  */
734 	public int
735 is_filtered(pos)
736 	POSITION pos;
737 {
738 	struct hilite *hl;
739 
740 	if (ch_getflags() & CH_HELPFILE)
741 		return (0);
742 
743 	/*
744 	 * Look at each filter and see if the start position
745 	 * equals the start position of the line.
746 	 */
747 	for (hl = filter_anchor.hl_first;  hl != NULL;  hl = hl->hl_next)
748 	{
749 		if (hl->hl_startpos == pos)
750 			return (1);
751 	}
752 	return (0);
753 }
754 
755 /*
756  * Should any characters in a specified range be highlighted?
757  * If nohide is nonzero, don't consider hide_hilite.
758  */
759 	public int
760 is_hilited(pos, epos, nohide, p_matches)
761 	POSITION pos;
762 	POSITION epos;
763 	int nohide;
764 	int *p_matches;
765 {
766 	int match;
767 
768 	if (p_matches != NULL)
769 		*p_matches = 0;
770 
771 	if (!status_col &&
772 	    start_attnpos != NULL_POSITION &&
773 	    pos < end_attnpos &&
774 	     (epos == NULL_POSITION || epos > start_attnpos))
775 		/*
776 		 * The attn line overlaps this range.
777 		 */
778 		return (1);
779 
780 	match = is_hilited_range(pos, epos);
781 	if (!match)
782 		return (0);
783 
784 	if (p_matches != NULL)
785 		/*
786 		 * Report matches, even if we're hiding highlights.
787 		 */
788 		*p_matches = 1;
789 
790 	if (hilite_search == 0)
791 		/*
792 		 * Not doing highlighting.
793 		 */
794 		return (0);
795 
796 	if (!nohide && hide_hilite)
797 		/*
798 		 * Highlighting is hidden.
799 		 */
800 		return (0);
801 
802 	return (1);
803 }
804 
805 /*
806  * Add a new hilite to a hilite list.
807  */
808 	static void
809 add_hilite(anchor, hl)
810 	struct hilite *anchor;
811 	struct hilite *hl;
812 {
813 	struct hilite *ihl;
814 
815 	/*
816 	 * Hilites are sorted in the list; find where new one belongs.
817 	 * Insert new one after ihl.
818 	 */
819 	for (ihl = anchor;  ihl->hl_next != NULL;  ihl = ihl->hl_next)
820 	{
821 		if (ihl->hl_next->hl_startpos > hl->hl_startpos)
822 			break;
823 	}
824 
825 	/*
826 	 * Truncate hilite so it doesn't overlap any existing ones
827 	 * above and below it.
828 	 */
829 	if (ihl != anchor)
830 		hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos);
831 	if (ihl->hl_next != NULL)
832 		hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos);
833 	if (hl->hl_startpos >= hl->hl_endpos)
834 	{
835 		/*
836 		 * Hilite was truncated out of existence.
837 		 */
838 		free(hl);
839 		return;
840 	}
841 	hl->hl_next = ihl->hl_next;
842 	ihl->hl_next = hl;
843 }
844 
845 /*
846  * Adjust hl_startpos & hl_endpos to account for processing by cvt_text.
847  */
848 	static void
849 adj_hilite(anchor, linepos, cvt_ops)
850 	struct hilite *anchor;
851 	POSITION linepos;
852 	int cvt_ops;
853 {
854 	char *line;
855 	char *oline;
856 	int line_len;
857 	char *line_end;
858 	struct hilite *hl;
859 	int checkstart;
860 	POSITION opos;
861 	POSITION npos;
862 	POSITION hl_opos;
863 	POSITION hl_npos;
864 	LWCHAR ch;
865 	int ncwidth;
866 
867 	/*
868 	 * The line was already scanned and hilites were added (in hilite_line).
869 	 * But it was assumed that each char position in the line
870 	 * correponds to one char position in the file.
871 	 * This may not be true if cvt_text modified the line.
872 	 * Get the raw line again.  Look at each character.
873 	 */
874 	(void) forw_raw_line(linepos, &line, &line_len);
875 	line_end = line + line_len;
876 	opos = npos = linepos;
877 	hl = anchor->hl_first;
878     if (hl == NULL)
879         return;
880     hl_opos = hl_npos = hl->hl_startpos;
881 	checkstart = TRUE;
882 
883 	while (hl != NULL && line < line_end)
884 	{
885 		/*
886 		 * See if we need to adjust the current hl_startpos or
887 		 * hl_endpos.  After adjusting startpos[i], move to endpos[i].
888 		 * After adjusting endpos[i], move to startpos[i+1].
889 		 * The hilite list must be sorted thus:
890 		 * startpos[0] < endpos[0] <= startpos[1] < endpos[1] <= etc.
891 		 */
892 		oline = line;
893 		ch = step_char(&line, +1, line_end);
894 		ncwidth = line - oline;
895 		npos += ncwidth;
896 
897 		/* Figure out how this char was processed by cvt_text. */
898 		if ((cvt_ops & CVT_BS) && ch == '\b')
899 		{
900 			/* Skip the backspace and the following char. */
901 			oline = line;
902 			ch = step_char(&line, +1, line_end);
903 			ncwidth = line - oline;
904 			npos += ncwidth;
905 		} else if ((cvt_ops & CVT_TO_LC) && IS_UPPER(ch))
906 		{
907 			/* Converted uppercase to lower.
908 			 * Note that this may have changed the number of bytes
909 			 * that the character occupies. */
910 			char dbuf[6];
911 			char *dst = dbuf;
912 			put_wchar(&dst, TO_LOWER(ch));
913 			opos += dst - dbuf;
914 		} else if ((cvt_ops & CVT_ANSI) && IS_CSI_START(ch))
915 		{
916 			/* Skip to end of ANSI escape sequence. */
917 			line++;  /* skip the CSI start char */
918 			npos++;
919 			while (line < line_end)
920 			{
921 				npos++;
922 				if (!is_ansi_middle(*line++))
923 					break;
924 			}
925 		} else
926 		{
927 			/* Ordinary unprocessed character. */
928 			opos += ncwidth;
929 		}
930 
931         if (opos == hl_opos) {
932             /* Adjust highlight position. */
933             hl_npos = npos;
934         }
935         if (opos > hl_opos)
936         {
937             /*
938              * We've moved past the highlight position; store the
939              * adjusted highlight position and move to the next highlight.
940              */
941             if (checkstart)
942             {
943                 hl->hl_startpos = hl_npos;
944                 hl_opos = hl->hl_endpos;
945                 checkstart = FALSE;
946             } else
947             {
948                 hl->hl_endpos = hl_npos;
949                 hl = hl->hl_next;
950                 if (hl != NULL)
951                     hl_opos = hl->hl_startpos;
952                 checkstart = TRUE;
953             }
954             hl_npos = npos;
955         }
956 	}
957 }
958 
959 /*
960  * Make a hilite for each string in a physical line which matches
961  * the current pattern.
962  * sp,ep delimit the first match already found.
963  */
964 	static void
965 hilite_line(linepos, line, line_len, sp, ep, cvt_ops)
966 	POSITION linepos;
967 	char *line;
968 	int line_len;
969 	char *sp;
970 	char *ep;
971 	int cvt_ops;
972 {
973 	char *searchp;
974 	char *line_end = line + line_len;
975 	struct hilite *hl;
976 	struct hilite hilites;
977 
978 	if (sp == NULL || ep == NULL)
979 		return;
980 	/*
981 	 * sp and ep delimit the first match in the line.
982 	 * Mark the corresponding file positions, then
983 	 * look for further matches and mark them.
984 	 * {{ This technique, of calling match_pattern on subsequent
985 	 *    substrings of the line, may mark more than is correct
986 	 *    if the pattern starts with "^".  This bug is fixed
987 	 *    for those regex functions that accept a notbol parameter
988 	 *    (currently POSIX, PCRE and V8-with-regexec2). }}
989 	 */
990 	searchp = line;
991 	/*
992 	 * Put the hilites into a temporary list until they're adjusted.
993 	 */
994 	hilites.hl_first = NULL;
995 	do {
996 		if (ep > sp)
997 		{
998 			/*
999 			 * Assume that each char position in the "line"
1000 			 * buffer corresponds to one char position in the file.
1001 			 * This is not quite true; we need to adjust later.
1002 			 */
1003 			hl = (struct hilite *) ecalloc(1, sizeof(struct hilite));
1004 			hl->hl_startpos = linepos + (sp-line);
1005 			hl->hl_endpos = linepos + (ep-line);
1006 			add_hilite(&hilites, hl);
1007 		}
1008 		/*
1009 		 * If we matched more than zero characters,
1010 		 * move to the first char after the string we matched.
1011 		 * If we matched zero, just move to the next char.
1012 		 */
1013 		if (ep > searchp)
1014 			searchp = ep;
1015 		else if (searchp != line_end)
1016 			searchp++;
1017 		else /* end of line */
1018 			break;
1019 	} while (match_pattern(search_pattern, searchp, line_end - searchp, &sp, &ep, 1, last_search_type));
1020 
1021 	/*
1022 	 * If there were backspaces in the original line, they
1023 	 * were removed, and hl_startpos/hl_endpos are not correct.
1024 	 * {{ This is very ugly. }}
1025 	 */
1026 	adj_hilite(&hilites, linepos, cvt_ops);
1027 
1028 	/*
1029 	 * Now put the hilites into the real list.
1030 	 */
1031 	while ((hl = hilites.hl_next) != NULL)
1032 	{
1033 		hilites.hl_next = hl->hl_next;
1034 		add_hilite(&hilite_anchor, hl);
1035 	}
1036 }
1037 #endif
1038 
1039 /*
1040  * Change the caseless-ness of searches.
1041  * Updates the internal search state to reflect a change in the -i flag.
1042  */
1043 	public void
1044 chg_caseless()
1045 {
1046 	if (!is_ucase_pattern)
1047 		/*
1048 		 * Pattern did not have uppercase.
1049 		 * Just set the search caselessness to the global caselessness.
1050 		 */
1051 		is_caseless = caseless;
1052 	else
1053 		/*
1054 		 * Pattern did have uppercase.
1055 		 * Discard the pattern; we can't change search caselessness now.
1056 		 */
1057 		uncompile_search_pattern();
1058 }
1059 
1060 #if HILITE_SEARCH
1061 /*
1062  * Find matching text which is currently on screen and highlight it.
1063  */
1064 	static void
1065 hilite_screen()
1066 {
1067 	struct scrpos scrpos;
1068 
1069 	get_scrpos(&scrpos);
1070 	if (scrpos.pos == NULL_POSITION)
1071 		return;
1072 	prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1);
1073 	repaint_hilite(1);
1074 }
1075 
1076 /*
1077  * Change highlighting parameters.
1078  */
1079 	public void
1080 chg_hilite()
1081 {
1082 	/*
1083 	 * Erase any highlights currently on screen.
1084 	 */
1085 	clr_hilite();
1086 	hide_hilite = 0;
1087 
1088 	if (hilite_search == OPT_ONPLUS)
1089 		/*
1090 		 * Display highlights.
1091 		 */
1092 		hilite_screen();
1093 }
1094 #endif
1095 
1096 /*
1097  * Figure out where to start a search.
1098  */
1099 	static POSITION
1100 search_pos(search_type)
1101 	int search_type;
1102 {
1103 	POSITION pos;
1104 	int linenum;
1105 
1106 	if (empty_screen())
1107 	{
1108 		/*
1109 		 * Start at the beginning (or end) of the file.
1110 		 * The empty_screen() case is mainly for
1111 		 * command line initiated searches;
1112 		 * for example, "+/xyz" on the command line.
1113 		 * Also for multi-file (SRCH_PAST_EOF) searches.
1114 		 */
1115 		if (search_type & SRCH_FORW)
1116 		{
1117 			return (ch_zero());
1118 		} else
1119 		{
1120 			pos = ch_length();
1121 			if (pos == NULL_POSITION)
1122 			{
1123 				(void) ch_end_seek();
1124 				pos = ch_length();
1125 			}
1126 			return (pos);
1127 		}
1128 	}
1129 	if (how_search)
1130 	{
1131 		/*
1132 		 * Search does not include current screen.
1133 		 */
1134 		if (search_type & SRCH_FORW)
1135 			linenum = BOTTOM_PLUS_ONE;
1136 		else
1137 			linenum = TOP;
1138 		pos = position(linenum);
1139 	} else
1140 	{
1141 		/*
1142 		 * Search includes current screen.
1143 		 * It starts at the jump target (if searching backwards),
1144 		 * or at the jump target plus one (if forwards).
1145 		 */
1146 		linenum = adjsline(jump_sline);
1147 		pos = position(linenum);
1148 		if (search_type & SRCH_FORW)
1149 		{
1150 			pos = forw_raw_line(pos, (char **)NULL, (int *)NULL);
1151 			while (pos == NULL_POSITION)
1152 			{
1153 				if (++linenum >= sc_height)
1154 					break;
1155 				pos = position(linenum);
1156 			}
1157 		} else
1158 		{
1159 			while (pos == NULL_POSITION)
1160 			{
1161 				if (--linenum < 0)
1162 					break;
1163 				pos = position(linenum);
1164 			}
1165 		}
1166 	}
1167 	return (pos);
1168 }
1169 
1170 /*
1171  * Search a subset of the file, specified by start/end position.
1172  */
1173 	static int
1174 search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
1175 	POSITION pos;
1176 	POSITION endpos;
1177 	int search_type;
1178 	int matches;
1179 	int maxlines;
1180 	POSITION *plinepos;
1181 	POSITION *pendpos;
1182 {
1183 	char *line;
1184 	char *cline;
1185 	int line_len;
1186 	LINENUM linenum;
1187 	char *sp, *ep;
1188 	int line_match;
1189 	int cvt_ops;
1190 	POSITION linepos, oldpos;
1191 
1192 	linenum = find_linenum(pos);
1193 	oldpos = pos;
1194 	for (;;)
1195 	{
1196 		/*
1197 		 * Get lines until we find a matching one or until
1198 		 * we hit end-of-file (or beginning-of-file if we're
1199 		 * going backwards), or until we hit the end position.
1200 		 */
1201 		if (ABORT_SIGS())
1202 		{
1203 			/*
1204 			 * A signal aborts the search.
1205 			 */
1206 			return (-1);
1207 		}
1208 
1209 		if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0)
1210 		{
1211 			/*
1212 			 * Reached end position without a match.
1213 			 */
1214 			if (pendpos != NULL)
1215 				*pendpos = pos;
1216 			return (matches);
1217 		}
1218 		if (maxlines > 0)
1219 			maxlines--;
1220 
1221 		if (search_type & SRCH_FORW)
1222 		{
1223 			/*
1224 			 * Read the next line, and save the
1225 			 * starting position of that line in linepos.
1226 			 */
1227 			linepos = pos;
1228 			pos = forw_raw_line(pos, &line, &line_len);
1229 			if (linenum != 0)
1230 				linenum++;
1231 		} else
1232 		{
1233 			/*
1234 			 * Read the previous line and save the
1235 			 * starting position of that line in linepos.
1236 			 */
1237 			pos = back_raw_line(pos, &line, &line_len);
1238 			linepos = pos;
1239 			if (linenum != 0)
1240 				linenum--;
1241 		}
1242 
1243 		if (pos == NULL_POSITION)
1244 		{
1245 			/*
1246 			 * Reached EOF/BOF without a match.
1247 			 */
1248 			if (pendpos != NULL)
1249 				*pendpos = oldpos;
1250 			return (matches);
1251 		}
1252 
1253 		/*
1254 		 * If we're using line numbers, we might as well
1255 		 * remember the information we have now (the position
1256 		 * and line number of the current line).
1257 		 * Don't do it for every line because it slows down
1258 		 * the search.  Remember the line number only if
1259 		 * we're "far" from the last place we remembered it.
1260 		 */
1261 		if (linenums && abs((int)(pos - oldpos)) > 1024)
1262 			add_lnum(linenum, pos);
1263 		oldpos = pos;
1264 
1265 		if (is_filtered(linepos))
1266 			continue;
1267 
1268 		/*
1269 		 * If it's a caseless search, convert the line to lowercase.
1270 		 * If we're doing backspace processing, delete backspaces.
1271 		 */
1272 		cvt_ops = get_cvt_ops();
1273 		cline = calloc(1, cvt_length(line_len, cvt_ops));
1274 		cvt_text(cline, line, &line_len, cvt_ops);
1275 
1276 #if HILITE_SEARCH
1277 		/*
1278 		 * Check to see if the line matches the filter pattern.
1279 		 * If so, add an entry to the filter list.
1280 		 */
1281 		if ((search_type & SRCH_FIND_ALL) &&
1282 			!is_null_pattern(filter_pattern))
1283 		{
1284 			int line_filter = match_pattern(filter_pattern,
1285 				cline, line_len, &sp, &ep, 0, last_filter_type);
1286 			if (line_filter)
1287 			{
1288 				struct hilite *hl = (struct hilite *)
1289 					ecalloc(1, sizeof(struct hilite));
1290 				hl->hl_startpos = linepos;
1291 				hl->hl_endpos = pos;
1292 				add_hilite(&filter_anchor, hl);
1293 			}
1294 		}
1295 #endif
1296 
1297 		/*
1298 		 * Test the next line to see if we have a match.
1299 		 * We are successful if we either want a match and got one,
1300 		 * or if we want a non-match and got one.
1301 		 */
1302 		if (!is_null_pattern(search_pattern))
1303 		{
1304 			line_match = match_pattern(search_pattern,
1305 				cline, line_len, &sp, &ep, 0, search_type);
1306 			if (line_match)
1307 			{
1308 				/*
1309 				 * Got a match.
1310 				 */
1311 				if (search_type & SRCH_FIND_ALL)
1312 				{
1313 #if HILITE_SEARCH
1314 					/*
1315 					 * We are supposed to find all matches in the range.
1316 					 * Just add the matches in this line to the
1317 					 * hilite list and keep searching.
1318 					 */
1319 					hilite_line(linepos, cline, line_len, sp, ep, cvt_ops);
1320 #endif
1321 				} else if (--matches <= 0)
1322 				{
1323 					/*
1324 					 * Found the one match we're looking for.
1325 					 * Return it.
1326 					 */
1327 #if HILITE_SEARCH
1328 					if (hilite_search == OPT_ON)
1329 					{
1330 						/*
1331 						 * Clear the hilite list and add only
1332 						 * the matches in this one line.
1333 						 */
1334 						clr_hilite();
1335 						hilite_line(linepos, cline, line_len, sp, ep, cvt_ops);
1336 					}
1337 #endif
1338 					free(cline);
1339 					if (plinepos != NULL)
1340 						*plinepos = linepos;
1341 					return (0);
1342 				}
1343 			}
1344 		}
1345 		free(cline);
1346 	}
1347 }
1348 
1349 /*
1350  * search for a pattern in history. If found, compile that pattern.
1351  */
1352 	static int
1353 hist_pattern(search_type)
1354 	int search_type;
1355 {
1356 #if CMD_HISTORY
1357 	char *pattern;
1358 
1359 	set_mlist(ml_search, 0);
1360 	pattern = cmd_lastpattern();
1361 	if (pattern == NULL)
1362 		return (0);
1363 
1364 	if (compile_pattern(pattern, search_type, &search_pattern) < 0)
1365 		return (0);
1366 
1367 	is_ucase_pattern = is_ucase(pattern);
1368 	if (is_ucase_pattern && caseless != OPT_ONPLUS)
1369 		is_caseless = 0;
1370 	else
1371 		is_caseless = caseless;
1372 
1373 #if HILITE_SEARCH
1374 	if (hilite_search == OPT_ONPLUS && !hide_hilite)
1375 		hilite_screen();
1376 #endif
1377 
1378 	return (1);
1379 #else /* CMD_HISTORY */
1380 	return (0);
1381 #endif /* CMD_HISTORY */
1382 }
1383 
1384 /*
1385  * Search for the n-th occurrence of a specified pattern,
1386  * either forward or backward.
1387  * Return the number of matches not yet found in this file
1388  * (that is, n minus the number of matches found).
1389  * Return -1 if the search should be aborted.
1390  * Caller may continue the search in another file
1391  * if less than n matches are found in this file.
1392  */
1393 	public int
1394 search(search_type, pattern, n)
1395 	int search_type;
1396 	char *pattern;
1397 	int n;
1398 {
1399 	POSITION pos;
1400 
1401 	if (pattern == NULL || *pattern == '\0')
1402 	{
1403 		/*
1404 		 * A null pattern means use the previously compiled pattern.
1405 		 */
1406 		if (!prev_pattern() && !hist_pattern(search_type))
1407 		{
1408 			error("No previous regular expression", NULL_PARG);
1409 			return (-1);
1410 		}
1411 		if ((search_type & SRCH_NO_REGEX) !=
1412 		    (last_search_type & SRCH_NO_REGEX))
1413 		{
1414 			error("Please re-enter search pattern", NULL_PARG);
1415 			return -1;
1416 		}
1417 #if HILITE_SEARCH
1418 		if (hilite_search == OPT_ON)
1419 		{
1420 			/*
1421 			 * Erase the highlights currently on screen.
1422 			 * If the search fails, we'll redisplay them later.
1423 			 */
1424 			repaint_hilite(0);
1425 		}
1426 		if (hilite_search == OPT_ONPLUS && hide_hilite)
1427 		{
1428 			/*
1429 			 * Highlight any matches currently on screen,
1430 			 * before we actually start the search.
1431 			 */
1432 			hide_hilite = 0;
1433 			hilite_screen();
1434 		}
1435 		hide_hilite = 0;
1436 #endif
1437 	} else
1438 	{
1439 		/*
1440 		 * Compile the pattern.
1441 		 */
1442 		if (compile_pattern(pattern, search_type, &search_pattern) < 0)
1443 			return (-1);
1444 		/*
1445 		 * Ignore case if -I is set OR
1446 		 * -i is set AND the pattern is all lowercase.
1447 		 */
1448 		is_ucase_pattern = is_ucase(pattern);
1449 		if (is_ucase_pattern && caseless != OPT_ONPLUS)
1450 			is_caseless = 0;
1451 		else
1452 			is_caseless = caseless;
1453 #if HILITE_SEARCH
1454 		if (hilite_search)
1455 		{
1456 			/*
1457 			 * Erase the highlights currently on screen.
1458 			 * Also permanently delete them from the hilite list.
1459 			 */
1460 			repaint_hilite(0);
1461 			hide_hilite = 0;
1462 			clr_hilite();
1463 		}
1464 		if (hilite_search == OPT_ONPLUS)
1465 		{
1466 			/*
1467 			 * Highlight any matches currently on screen,
1468 			 * before we actually start the search.
1469 			 */
1470 			hilite_screen();
1471 		}
1472 #endif
1473 	}
1474 
1475 	/*
1476 	 * Figure out where to start the search.
1477 	 */
1478 	pos = search_pos(search_type);
1479 	if (pos == NULL_POSITION)
1480 	{
1481 		/*
1482 		 * Can't find anyplace to start searching from.
1483 		 */
1484 		if (search_type & SRCH_PAST_EOF)
1485 			return (n);
1486 		/* repaint(); -- why was this here? */
1487 		error("Nothing to search", NULL_PARG);
1488 		return (-1);
1489 	}
1490 
1491 	n = search_range(pos, NULL_POSITION, search_type, n, -1,
1492 			&pos, (POSITION*)NULL);
1493 	if (n != 0)
1494 	{
1495 		/*
1496 		 * Search was unsuccessful.
1497 		 */
1498 #if HILITE_SEARCH
1499 		if (hilite_search == OPT_ON && n > 0)
1500 			/*
1501 			 * Redisplay old hilites.
1502 			 */
1503 			repaint_hilite(1);
1504 #endif
1505 		return (n);
1506 	}
1507 
1508 	if (!(search_type & SRCH_NO_MOVE))
1509 	{
1510 		/*
1511 		 * Go to the matching line.
1512 		 */
1513 		jump_loc(pos, jump_sline);
1514 	}
1515 
1516 #if HILITE_SEARCH
1517 	if (hilite_search == OPT_ON)
1518 		/*
1519 		 * Display new hilites in the matching line.
1520 		 */
1521 		repaint_hilite(1);
1522 #endif
1523 	return (0);
1524 }
1525 
1526 
1527 #if HILITE_SEARCH
1528 /*
1529  * Prepare hilites in a given range of the file.
1530  *
1531  * The pair (prep_startpos,prep_endpos) delimits a contiguous region
1532  * of the file that has been "prepared"; that is, scanned for matches for
1533  * the current search pattern, and hilites have been created for such matches.
1534  * If prep_startpos == NULL_POSITION, the prep region is empty.
1535  * If prep_endpos == NULL_POSITION, the prep region extends to EOF.
1536  * prep_hilite asks that the range (spos,epos) be covered by the prep region.
1537  */
1538 	public void
1539 prep_hilite(spos, epos, maxlines)
1540 	POSITION spos;
1541 	POSITION epos;
1542 	int maxlines;
1543 {
1544 	POSITION nprep_startpos = prep_startpos;
1545 	POSITION nprep_endpos = prep_endpos;
1546 	POSITION new_epos;
1547 	POSITION max_epos;
1548 	int result;
1549 	int i;
1550 /*
1551  * Search beyond where we're asked to search, so the prep region covers
1552  * more than we need.  Do one big search instead of a bunch of small ones.
1553  */
1554 #define	SEARCH_MORE (3*size_linebuf)
1555 
1556 	if (!prev_pattern() && !is_filtering())
1557 		return;
1558 
1559 	/*
1560 	 * If we're limited to a max number of lines, figure out the
1561 	 * file position we should stop at.
1562 	 */
1563 	if (maxlines < 0)
1564 		max_epos = NULL_POSITION;
1565 	else
1566 	{
1567 		max_epos = spos;
1568 		for (i = 0;  i < maxlines;  i++)
1569 			max_epos = forw_raw_line(max_epos, (char **)NULL, (int *)NULL);
1570 	}
1571 
1572 	/*
1573 	 * Find two ranges:
1574 	 * The range that we need to search (spos,epos); and the range that
1575 	 * the "prep" region will then cover (nprep_startpos,nprep_endpos).
1576 	 */
1577 
1578 	if (prep_startpos == NULL_POSITION ||
1579 	    (epos != NULL_POSITION && epos < prep_startpos) ||
1580 	    spos > prep_endpos)
1581 	{
1582 		/*
1583 		 * New range is not contiguous with old prep region.
1584 		 * Discard the old prep region and start a new one.
1585 		 */
1586 		clr_hilite();
1587 		clr_filter();
1588 		if (epos != NULL_POSITION)
1589 			epos += SEARCH_MORE;
1590 		nprep_startpos = spos;
1591 	} else
1592 	{
1593 		/*
1594 		 * New range partially or completely overlaps old prep region.
1595 		 */
1596 		if (epos == NULL_POSITION)
1597 		{
1598 			/*
1599 			 * New range goes to end of file.
1600 			 */
1601 			;
1602 		} else if (epos > prep_endpos)
1603 		{
1604 			/*
1605 			 * New range ends after old prep region.
1606 			 * Extend prep region to end at end of new range.
1607 			 */
1608 			epos += SEARCH_MORE;
1609 		} else /* (epos <= prep_endpos) */
1610 		{
1611 			/*
1612 			 * New range ends within old prep region.
1613 			 * Truncate search to end at start of old prep region.
1614 			 */
1615 			epos = prep_startpos;
1616 		}
1617 
1618 		if (spos < prep_startpos)
1619 		{
1620 			/*
1621 			 * New range starts before old prep region.
1622 			 * Extend old prep region backwards to start at
1623 			 * start of new range.
1624 			 */
1625 			if (spos < SEARCH_MORE)
1626 				spos = 0;
1627 			else
1628 				spos -= SEARCH_MORE;
1629 			nprep_startpos = spos;
1630 		} else /* (spos >= prep_startpos) */
1631 		{
1632 			/*
1633 			 * New range starts within or after old prep region.
1634 			 * Trim search to start at end of old prep region.
1635 			 */
1636 			spos = prep_endpos;
1637 		}
1638 	}
1639 
1640 	if (epos != NULL_POSITION && max_epos != NULL_POSITION &&
1641 	    epos > max_epos)
1642 		/*
1643 		 * Don't go past the max position we're allowed.
1644 		 */
1645 		epos = max_epos;
1646 
1647 	if (epos == NULL_POSITION || epos > spos)
1648 	{
1649 		result = search_range(spos, epos, SRCH_FORW|SRCH_FIND_ALL, 0,
1650 				maxlines, (POSITION*)NULL, &new_epos);
1651 		if (result < 0)
1652 			return;
1653 		if (prep_endpos == NULL_POSITION || new_epos > prep_endpos)
1654 			nprep_endpos = new_epos;
1655 	}
1656 	prep_startpos = nprep_startpos;
1657 	prep_endpos = nprep_endpos;
1658 }
1659 
1660 /*
1661  * Set the pattern to be used for line filtering.
1662  */
1663 	public void
1664 set_filter_pattern(pattern, search_type)
1665 	char *pattern;
1666 	int search_type;
1667 {
1668 	clr_filter();
1669 	if (pattern == NULL || *pattern == '\0')
1670 		uncompile_filter_pattern();
1671 	else
1672 		compile_pattern(pattern, search_type, &filter_pattern);
1673 	screen_trashed = 1;
1674 }
1675 
1676 /*
1677  * Is there a line filter in effect?
1678  */
1679 	public int
1680 is_filtering()
1681 {
1682 	if (ch_getflags() & CH_HELPFILE)
1683 		return (0);
1684 	return !is_null_pattern(filter_pattern);
1685 }
1686 #endif
1687 
1688 /*
1689  * Simple pattern matching function.
1690  * It supports no metacharacters like *, etc.
1691  */
1692 	static int
1693 match(pattern, pattern_len, buf, buf_len, pfound, pend)
1694 	char *pattern;
1695 	int pattern_len;
1696 	char *buf;
1697 	int buf_len;
1698 	char **pfound, **pend;
1699 {
1700 	register char *pp, *lp;
1701 	register char *pattern_end = pattern + pattern_len;
1702 	register char *buf_end = buf + buf_len;
1703 
1704 	for ( ;  buf < buf_end;  buf++)
1705 	{
1706 		for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
1707 			if (pp == pattern_end || lp == buf_end)
1708 				break;
1709 		if (pp == pattern_end)
1710 		{
1711 			if (pfound != NULL)
1712 				*pfound = buf;
1713 			if (pend != NULL)
1714 				*pend = lp;
1715 			return (1);
1716 		}
1717 	}
1718 	return (0);
1719 }
1720 
1721 #if HAVE_V8_REGCOMP
1722 /*
1723  * This function is called by the V8 regcomp to report
1724  * errors in regular expressions.
1725  */
1726 	void
1727 regerror(s)
1728 	char *s;
1729 {
1730 	PARG parg;
1731 
1732 	parg.p_string = s;
1733 	error("%s", &parg);
1734 }
1735 #endif
1736 
1737