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