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