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