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