xref: /freebsd/contrib/less/search.c (revision 145992504973bd16cf3518af9ba5ce185fefa82a)
1 /* $FreeBSD$ */
2 /*
3  * Copyright (C) 1984-2012  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, see the README file.
9  */
10 
11 
12 /*
13  * Routines to search a file for a pattern.
14  */
15 
16 #include "less.h"
17 #include "pattern.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 extern int sigs;
25 extern int how_search;
26 extern int caseless;
27 extern int linenums;
28 extern int sc_height;
29 extern int jump_sline;
30 extern int bs_mode;
31 extern int less_is_more;
32 extern int ctldisp;
33 extern int status_col;
34 extern void * constant ml_search;
35 extern POSITION start_attnpos;
36 extern POSITION end_attnpos;
37 extern int utf_mode;
38 extern int screen_trashed;
39 #if HILITE_SEARCH
40 extern int hilite_search;
41 extern int size_linebuf;
42 extern int squished;
43 extern int can_goto_line;
44 static int hide_hilite;
45 static POSITION prep_startpos;
46 static POSITION prep_endpos;
47 static int is_caseless;
48 static int is_ucase_pattern;
49 
50 struct hilite
51 {
52 	struct hilite *hl_next;
53 	POSITION hl_startpos;
54 	POSITION hl_endpos;
55 };
56 static struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION };
57 static struct hilite filter_anchor = { NULL, NULL_POSITION, NULL_POSITION };
58 #define	hl_first	hl_next
59 #endif
60 
61 /*
62  * These are the static variables that represent the "remembered"
63  * search pattern and filter pattern.
64  */
65 struct pattern_info {
66 	DEFINE_PATTERN(compiled);
67 	char* text;
68 	int search_type;
69 };
70 
71 #if NO_REGEX
72 #define info_compiled(info) ((void*)0)
73 #else
74 #define info_compiled(info) ((info)->compiled)
75 #endif
76 
77 static struct pattern_info search_info;
78 static struct pattern_info filter_info;
79 
80 /*
81  * Are there any uppercase letters in this string?
82  */
83 	static int
84 is_ucase(str)
85 	char *str;
86 {
87 	char *str_end = str + strlen(str);
88 	LWCHAR ch;
89 
90 	while (str < str_end)
91 	{
92 		ch = step_char(&str, +1, str_end);
93 		if (IS_UPPER(ch))
94 			return (1);
95 	}
96 	return (0);
97 }
98 
99 /*
100  * Compile and save a search pattern.
101  */
102 	static int
103 set_pattern(info, pattern, search_type)
104 	struct pattern_info *info;
105 	char *pattern;
106 	int search_type;
107 {
108 #if !NO_REGEX
109 	if (pattern == NULL)
110 		CLEAR_PATTERN(info->compiled);
111 	else if (compile_pattern(pattern, search_type, &info->compiled) < 0)
112 		return -1;
113 #endif
114 	/* Pattern compiled successfully; save the text too. */
115 	if (info->text != NULL)
116 		free(info->text);
117 	info->text = NULL;
118 	if (pattern != NULL)
119 	{
120 		info->text = (char *) ecalloc(1, strlen(pattern)+1);
121 		strcpy(info->text, pattern);
122 	}
123 	info->search_type = search_type;
124 
125 	/*
126 	 * Ignore case if -I is set OR
127 	 * -i is set AND the pattern is all lowercase.
128 	 */
129 	is_ucase_pattern = is_ucase(pattern);
130 	if (is_ucase_pattern && caseless != OPT_ONPLUS)
131 		is_caseless = 0;
132 	else
133 		is_caseless = caseless;
134 	return 0;
135 }
136 
137 /*
138  * Discard a saved pattern.
139  */
140 	static void
141 clear_pattern(info)
142 	struct pattern_info *info;
143 {
144 	if (info->text != NULL)
145 		free(info->text);
146 	info->text = NULL;
147 #if !NO_REGEX
148 	uncompile_pattern(&info->compiled);
149 #endif
150 }
151 
152 /*
153  * Initialize saved pattern to nothing.
154  */
155 	static void
156 init_pattern(info)
157 	struct pattern_info *info;
158 {
159 	CLEAR_PATTERN(info->compiled);
160 	info->text = NULL;
161 	info->search_type = 0;
162 }
163 
164 /*
165  * Initialize search variables.
166  */
167 	public void
168 init_search()
169 {
170 	init_pattern(&search_info);
171 	init_pattern(&filter_info);
172 }
173 
174 /*
175  * Determine which text conversions to perform before pattern matching.
176  */
177 	static int
178 get_cvt_ops()
179 {
180 	int ops = 0;
181 	if (is_caseless || bs_mode == BS_SPECIAL)
182 	{
183 		if (is_caseless)
184 			ops |= CVT_TO_LC;
185 		if (bs_mode == BS_SPECIAL)
186 			ops |= CVT_BS;
187 		if (bs_mode != BS_CONTROL)
188 			ops |= CVT_CRLF;
189 	} else if (bs_mode != BS_CONTROL)
190 	{
191 		ops |= CVT_CRLF;
192 	}
193 	if (ctldisp == OPT_ONPLUS)
194 		ops |= CVT_ANSI;
195 	return (ops);
196 }
197 
198 /*
199  * Is there a previous (remembered) search pattern?
200  */
201 	static int
202 prev_pattern(info)
203 	struct pattern_info *info;
204 {
205 #if !NO_REGEX
206 	if ((info->search_type & SRCH_NO_REGEX) == 0)
207 		return (!is_null_pattern(info->compiled));
208 #endif
209 	return (info->text != NULL);
210 }
211 
212 #if HILITE_SEARCH
213 /*
214  * Repaint the hilites currently displayed on the screen.
215  * Repaint each line which contains highlighted text.
216  * If on==0, force all hilites off.
217  */
218 	public void
219 repaint_hilite(on)
220 	int on;
221 {
222 	int slinenum;
223 	POSITION pos;
224 	POSITION epos;
225 	int save_hide_hilite;
226 
227 	if (squished)
228 		repaint();
229 
230 	save_hide_hilite = hide_hilite;
231 	if (!on)
232 	{
233 		if (hide_hilite)
234 			return;
235 		hide_hilite = 1;
236 	}
237 
238 	if (!can_goto_line)
239 	{
240 		repaint();
241 		hide_hilite = save_hide_hilite;
242 		return;
243 	}
244 
245 	for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
246 	{
247 		pos = position(slinenum);
248 		if (pos == NULL_POSITION)
249 			continue;
250 		epos = position(slinenum+1);
251 		(void) forw_line(pos);
252 		goto_line(slinenum);
253 		put_line();
254 	}
255 	lower_left();
256 	hide_hilite = save_hide_hilite;
257 }
258 
259 /*
260  * Clear the attn hilite.
261  */
262 	public void
263 clear_attn()
264 {
265 	int slinenum;
266 	POSITION old_start_attnpos;
267 	POSITION old_end_attnpos;
268 	POSITION pos;
269 	POSITION epos;
270 	int moved = 0;
271 
272 	if (start_attnpos == NULL_POSITION)
273 		return;
274 	old_start_attnpos = start_attnpos;
275 	old_end_attnpos = end_attnpos;
276 	start_attnpos = end_attnpos = NULL_POSITION;
277 
278 	if (!can_goto_line)
279 	{
280 		repaint();
281 		return;
282 	}
283 	if (squished)
284 		repaint();
285 
286 	for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
287 	{
288 		pos = position(slinenum);
289 		if (pos == NULL_POSITION)
290 			continue;
291 		epos = position(slinenum+1);
292 		if (pos < old_end_attnpos &&
293 		     (epos == NULL_POSITION || epos > old_start_attnpos))
294 		{
295 			(void) forw_line(pos);
296 			goto_line(slinenum);
297 			put_line();
298 			moved = 1;
299 		}
300 	}
301 	if (moved)
302 		lower_left();
303 }
304 #endif
305 
306 /*
307  * Hide search string highlighting.
308  */
309 	public void
310 undo_search()
311 {
312 	if (!prev_pattern(&search_info))
313 	{
314 		error("No previous regular expression", NULL_PARG);
315 		return;
316 	}
317 #if HILITE_SEARCH
318 	hide_hilite = !hide_hilite;
319 	repaint_hilite(1);
320 #endif
321 }
322 
323 #if HILITE_SEARCH
324 /*
325  * Clear the hilite list.
326  */
327 	public void
328 clr_hlist(anchor)
329 	struct hilite *anchor;
330 {
331 	struct hilite *hl;
332 	struct hilite *nexthl;
333 
334 	for (hl = anchor->hl_first;  hl != NULL;  hl = nexthl)
335 	{
336 		nexthl = hl->hl_next;
337 		free((void*)hl);
338 	}
339 	anchor->hl_first = NULL;
340 	prep_startpos = prep_endpos = NULL_POSITION;
341 }
342 
343 	public void
344 clr_hilite()
345 {
346 	clr_hlist(&hilite_anchor);
347 }
348 
349 	public void
350 clr_filter()
351 {
352 	clr_hlist(&filter_anchor);
353 }
354 
355 /*
356  * Should any characters in a specified range be highlighted?
357  */
358 	static int
359 is_hilited_range(pos, epos)
360 	POSITION pos;
361 	POSITION epos;
362 {
363 	struct hilite *hl;
364 
365 	/*
366 	 * Look at each highlight and see if any part of it falls in the range.
367 	 */
368 	for (hl = hilite_anchor.hl_first;  hl != NULL;  hl = hl->hl_next)
369 	{
370 		if (hl->hl_endpos > pos &&
371 		    (epos == NULL_POSITION || epos > hl->hl_startpos))
372 			return (1);
373 	}
374 	return (0);
375 }
376 
377 /*
378  * Is a line "filtered" -- that is, should it be hidden?
379  */
380 	public int
381 is_filtered(pos)
382 	POSITION pos;
383 {
384 	struct hilite *hl;
385 
386 	if (ch_getflags() & CH_HELPFILE)
387 		return (0);
388 
389 	/*
390 	 * Look at each filter and see if the start position
391 	 * equals the start position of the line.
392 	 */
393 	for (hl = filter_anchor.hl_first;  hl != NULL;  hl = hl->hl_next)
394 	{
395 		if (hl->hl_startpos == pos)
396 			return (1);
397 	}
398 	return (0);
399 }
400 
401 /*
402  * Should any characters in a specified range be highlighted?
403  * If nohide is nonzero, don't consider hide_hilite.
404  */
405 	public int
406 is_hilited(pos, epos, nohide, p_matches)
407 	POSITION pos;
408 	POSITION epos;
409 	int nohide;
410 	int *p_matches;
411 {
412 	int match;
413 
414 	if (p_matches != NULL)
415 		*p_matches = 0;
416 
417 	if (!status_col &&
418 	    start_attnpos != NULL_POSITION &&
419 	    pos < end_attnpos &&
420 	     (epos == NULL_POSITION || epos > start_attnpos))
421 		/*
422 		 * The attn line overlaps this range.
423 		 */
424 		return (1);
425 
426 	match = is_hilited_range(pos, epos);
427 	if (!match)
428 		return (0);
429 
430 	if (p_matches != NULL)
431 		/*
432 		 * Report matches, even if we're hiding highlights.
433 		 */
434 		*p_matches = 1;
435 
436 	if (hilite_search == 0)
437 		/*
438 		 * Not doing highlighting.
439 		 */
440 		return (0);
441 
442 	if (!nohide && hide_hilite)
443 		/*
444 		 * Highlighting is hidden.
445 		 */
446 		return (0);
447 
448 	return (1);
449 }
450 
451 /*
452  * Add a new hilite to a hilite list.
453  */
454 	static void
455 add_hilite(anchor, hl)
456 	struct hilite *anchor;
457 	struct hilite *hl;
458 {
459 	struct hilite *ihl;
460 
461 	/*
462 	 * Hilites are sorted in the list; find where new one belongs.
463 	 * Insert new one after ihl.
464 	 */
465 	for (ihl = anchor;  ihl->hl_next != NULL;  ihl = ihl->hl_next)
466 	{
467 		if (ihl->hl_next->hl_startpos > hl->hl_startpos)
468 			break;
469 	}
470 
471 	/*
472 	 * Truncate hilite so it doesn't overlap any existing ones
473 	 * above and below it.
474 	 */
475 	if (ihl != anchor)
476 		hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos);
477 	if (ihl->hl_next != NULL)
478 		hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos);
479 	if (hl->hl_startpos >= hl->hl_endpos)
480 	{
481 		/*
482 		 * Hilite was truncated out of existence.
483 		 */
484 		free(hl);
485 		return;
486 	}
487 	hl->hl_next = ihl->hl_next;
488 	ihl->hl_next = hl;
489 }
490 
491 /*
492  * Hilight every character in a range of displayed characters.
493  */
494 	static void
495 create_hilites(linepos, start_index, end_index, chpos)
496 	POSITION linepos;
497 	int start_index;
498 	int end_index;
499 	int *chpos;
500 {
501 	struct hilite *hl;
502 	int i;
503 
504 	/* Start the first hilite. */
505 	hl = (struct hilite *) ecalloc(1, sizeof(struct hilite));
506 	hl->hl_startpos = linepos + chpos[start_index];
507 
508 	/*
509 	 * Step through the displayed chars.
510 	 * If the source position (before cvt) of the char is one more
511 	 * than the source pos of the previous char (the usual case),
512 	 * just increase the size of the current hilite by one.
513 	 * Otherwise (there are backspaces or something involved),
514 	 * finish the current hilite and start a new one.
515 	 */
516 	for (i = start_index+1;  i <= end_index;  i++)
517 	{
518 		if (chpos[i] != chpos[i-1] + 1 || i == end_index)
519 		{
520 			hl->hl_endpos = linepos + chpos[i-1] + 1;
521 			add_hilite(&hilite_anchor, hl);
522 			/* Start new hilite unless this is the last char. */
523 			if (i < end_index)
524 			{
525 				hl = (struct hilite *) ecalloc(1, sizeof(struct hilite));
526 				hl->hl_startpos = linepos + chpos[i];
527 			}
528 		}
529 	}
530 }
531 
532 /*
533  * Make a hilite for each string in a physical line which matches
534  * the current pattern.
535  * sp,ep delimit the first match already found.
536  */
537 	static void
538 hilite_line(linepos, line, line_len, chpos, sp, ep, cvt_ops)
539 	POSITION linepos;
540 	char *line;
541 	int line_len;
542 	int *chpos;
543 	char *sp;
544 	char *ep;
545 	int cvt_ops;
546 {
547 	char *searchp;
548 	char *line_end = line + line_len;
549 
550 	if (sp == NULL || ep == NULL)
551 		return;
552 	/*
553 	 * sp and ep delimit the first match in the line.
554 	 * Mark the corresponding file positions, then
555 	 * look for further matches and mark them.
556 	 * {{ This technique, of calling match_pattern on subsequent
557 	 *    substrings of the line, may mark more than is correct
558 	 *    if the pattern starts with "^".  This bug is fixed
559 	 *    for those regex functions that accept a notbol parameter
560 	 *    (currently POSIX, PCRE and V8-with-regexec2). }}
561 	 */
562 	searchp = line;
563 	do {
564 		create_hilites(linepos, sp-line, ep-line, chpos);
565 		/*
566 		 * If we matched more than zero characters,
567 		 * move to the first char after the string we matched.
568 		 * If we matched zero, just move to the next char.
569 		 */
570 		if (ep > searchp)
571 			searchp = ep;
572 		else if (searchp != line_end)
573 			searchp++;
574 		else /* end of line */
575 			break;
576 	} while (match_pattern(info_compiled(&search_info), search_info.text,
577 			searchp, line_end - searchp, &sp, &ep, 1, search_info.search_type));
578 }
579 #endif
580 
581 /*
582  * Change the caseless-ness of searches.
583  * Updates the internal search state to reflect a change in the -i flag.
584  */
585 	public void
586 chg_caseless()
587 {
588 	if (!is_ucase_pattern)
589 		/*
590 		 * Pattern did not have uppercase.
591 		 * Just set the search caselessness to the global caselessness.
592 		 */
593 		is_caseless = caseless;
594 	else
595 		/*
596 		 * Pattern did have uppercase.
597 		 * Discard the pattern; we can't change search caselessness now.
598 		 */
599 		clear_pattern(&search_info);
600 }
601 
602 #if HILITE_SEARCH
603 /*
604  * Find matching text which is currently on screen and highlight it.
605  */
606 	static void
607 hilite_screen()
608 {
609 	struct scrpos scrpos;
610 
611 	get_scrpos(&scrpos);
612 	if (scrpos.pos == NULL_POSITION)
613 		return;
614 	prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1);
615 	repaint_hilite(1);
616 }
617 
618 /*
619  * Change highlighting parameters.
620  */
621 	public void
622 chg_hilite()
623 {
624 	/*
625 	 * Erase any highlights currently on screen.
626 	 */
627 	clr_hilite();
628 	hide_hilite = 0;
629 
630 	if (hilite_search == OPT_ONPLUS)
631 		/*
632 		 * Display highlights.
633 		 */
634 		hilite_screen();
635 }
636 #endif
637 
638 /*
639  * Figure out where to start a search.
640  */
641 	static POSITION
642 search_pos(search_type)
643 	int search_type;
644 {
645 	POSITION pos;
646 	int linenum;
647 
648 	if (empty_screen())
649 	{
650 		/*
651 		 * Start at the beginning (or end) of the file.
652 		 * The empty_screen() case is mainly for
653 		 * command line initiated searches;
654 		 * for example, "+/xyz" on the command line.
655 		 * Also for multi-file (SRCH_PAST_EOF) searches.
656 		 */
657 		if (search_type & SRCH_FORW)
658 		{
659 			pos = ch_zero();
660 		} else
661 		{
662 			pos = ch_length();
663 			if (pos == NULL_POSITION)
664 			{
665 				(void) ch_end_seek();
666 				pos = ch_length();
667 			}
668 		}
669 		linenum = 0;
670 	} else
671 	{
672 		int add_one = 0;
673 
674 		if (how_search == OPT_ON)
675 		{
676 			/*
677 			 * Search does not include current screen.
678 			 */
679 			if (search_type & SRCH_FORW)
680 				linenum = BOTTOM_PLUS_ONE;
681 			else
682 				linenum = TOP;
683 		} else if (how_search == OPT_ONPLUS && !(search_type & SRCH_AFTER_TARGET))
684 		{
685 			/*
686 			 * Search includes all of displayed screen.
687 			 */
688 			if (search_type & SRCH_FORW)
689 				linenum = TOP;
690 			else
691 				linenum = BOTTOM_PLUS_ONE;
692 		} else
693 		{
694 			/*
695 			 * Search includes the part of current screen beyond the jump target.
696 			 * It starts at the jump target (if searching backwards),
697 			 * or at the jump target plus one (if forwards).
698 			 */
699 			linenum = jump_sline;
700 			if (search_type & SRCH_FORW)
701 			    add_one = 1;
702 		}
703 		linenum = adjsline(linenum);
704 		pos = position(linenum);
705 		if (add_one)
706 			pos = forw_raw_line(pos, (char **)NULL, (int *)NULL);
707 	}
708 
709 	/*
710 	 * If the line is empty, look around for a plausible starting place.
711 	 */
712 	if (search_type & SRCH_FORW)
713 	{
714 	    while (pos == NULL_POSITION)
715 	    {
716 	        if (++linenum >= sc_height)
717 	            break;
718 	        pos = position(linenum);
719 	    }
720 	} else
721 	{
722 	    while (pos == NULL_POSITION)
723 	    {
724 	        if (--linenum < 0)
725 	            break;
726 	        pos = position(linenum);
727 	    }
728 	}
729 	return (pos);
730 }
731 
732 /*
733  * Search a subset of the file, specified by start/end position.
734  */
735 	static int
736 search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
737 	POSITION pos;
738 	POSITION endpos;
739 	int search_type;
740 	int matches;
741 	int maxlines;
742 	POSITION *plinepos;
743 	POSITION *pendpos;
744 {
745 	char *line;
746 	char *cline;
747 	int line_len;
748 	LINENUM linenum;
749 	char *sp, *ep;
750 	int line_match;
751 	int cvt_ops;
752 	int cvt_len;
753 	int *chpos;
754 	POSITION linepos, oldpos;
755 
756 	linenum = find_linenum(pos);
757 	oldpos = pos;
758 	for (;;)
759 	{
760 		/*
761 		 * Get lines until we find a matching one or until
762 		 * we hit end-of-file (or beginning-of-file if we're
763 		 * going backwards), or until we hit the end position.
764 		 */
765 		if (ABORT_SIGS())
766 		{
767 			/*
768 			 * A signal aborts the search.
769 			 */
770 			return (-1);
771 		}
772 
773 		if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0)
774 		{
775 			/*
776 			 * Reached end position without a match.
777 			 */
778 			if (pendpos != NULL)
779 				*pendpos = pos;
780 			return (matches);
781 		}
782 		if (maxlines > 0)
783 			maxlines--;
784 
785 		if (search_type & SRCH_FORW)
786 		{
787 			/*
788 			 * Read the next line, and save the
789 			 * starting position of that line in linepos.
790 			 */
791 			linepos = pos;
792 			pos = forw_raw_line(pos, &line, &line_len);
793 			if (linenum != 0)
794 				linenum++;
795 		} else
796 		{
797 			/*
798 			 * Read the previous line and save the
799 			 * starting position of that line in linepos.
800 			 */
801 			pos = back_raw_line(pos, &line, &line_len);
802 			linepos = pos;
803 			if (linenum != 0)
804 				linenum--;
805 		}
806 
807 		if (pos == NULL_POSITION)
808 		{
809 			/*
810 			 * Reached EOF/BOF without a match.
811 			 */
812 			if (pendpos != NULL)
813 				*pendpos = oldpos;
814 			return (matches);
815 		}
816 
817 		/*
818 		 * If we're using line numbers, we might as well
819 		 * remember the information we have now (the position
820 		 * and line number of the current line).
821 		 * Don't do it for every line because it slows down
822 		 * the search.  Remember the line number only if
823 		 * we're "far" from the last place we remembered it.
824 		 */
825 		if (linenums && abs((int)(pos - oldpos)) > 2048)
826 			add_lnum(linenum, pos);
827 		oldpos = pos;
828 
829 		if (is_filtered(linepos))
830 			continue;
831 
832 		/*
833 		 * If it's a caseless search, convert the line to lowercase.
834 		 * If we're doing backspace processing, delete backspaces.
835 		 */
836 		cvt_ops = get_cvt_ops();
837 		cvt_len = cvt_length(line_len, cvt_ops);
838 		cline = (char *) ecalloc(1, cvt_len);
839 		chpos = cvt_alloc_chpos(cvt_len);
840 		cvt_text(cline, line, chpos, &line_len, cvt_ops);
841 
842 #if HILITE_SEARCH
843 		/*
844 		 * Check to see if the line matches the filter pattern.
845 		 * If so, add an entry to the filter list.
846 		 */
847 		if ((search_type & SRCH_FIND_ALL) && prev_pattern(&filter_info)) {
848 			int line_filter = match_pattern(info_compiled(&filter_info), filter_info.text,
849 				cline, line_len, &sp, &ep, 0, filter_info.search_type);
850 			if (line_filter)
851 			{
852 				struct hilite *hl = (struct hilite *)
853 					ecalloc(1, sizeof(struct hilite));
854 				hl->hl_startpos = linepos;
855 				hl->hl_endpos = pos;
856 				add_hilite(&filter_anchor, hl);
857 			}
858 		}
859 #endif
860 
861 		/*
862 		 * Test the next line to see if we have a match.
863 		 * We are successful if we either want a match and got one,
864 		 * or if we want a non-match and got one.
865 		 */
866 		if (prev_pattern(&search_info))
867 		{
868 			line_match = match_pattern(info_compiled(&search_info), search_info.text,
869 				cline, line_len, &sp, &ep, 0, search_type);
870 			if (line_match)
871 			{
872 				/*
873 				 * Got a match.
874 				 */
875 				if (search_type & SRCH_FIND_ALL)
876 				{
877 #if HILITE_SEARCH
878 					/*
879 					 * We are supposed to find all matches in the range.
880 					 * Just add the matches in this line to the
881 					 * hilite list and keep searching.
882 					 */
883 					hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops);
884 #endif
885 				} else if (--matches <= 0)
886 				{
887 					/*
888 					 * Found the one match we're looking for.
889 					 * Return it.
890 					 */
891 #if HILITE_SEARCH
892 					if (hilite_search == OPT_ON)
893 					{
894 						/*
895 						 * Clear the hilite list and add only
896 						 * the matches in this one line.
897 						 */
898 						clr_hilite();
899 						hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops);
900 					}
901 #endif
902 					free(cline);
903 					free(chpos);
904 					if (plinepos != NULL)
905 						*plinepos = linepos;
906 					return (0);
907 				}
908 			}
909 		}
910 		free(cline);
911 		free(chpos);
912 	}
913 }
914 
915 /*
916  * search for a pattern in history. If found, compile that pattern.
917  */
918 	static int
919 hist_pattern(search_type)
920 	int search_type;
921 {
922 #if CMD_HISTORY
923 	char *pattern;
924 
925 	set_mlist(ml_search, 0);
926 	pattern = cmd_lastpattern();
927 	if (pattern == NULL)
928 		return (0);
929 
930 	if (set_pattern(&search_info, pattern, search_type) < 0)
931 		return (0);
932 
933 #if HILITE_SEARCH
934 	if (hilite_search == OPT_ONPLUS && !hide_hilite)
935 		hilite_screen();
936 #endif
937 
938 	return (1);
939 #else /* CMD_HISTORY */
940 	return (0);
941 #endif /* CMD_HISTORY */
942 }
943 
944 /*
945  * Search for the n-th occurrence of a specified pattern,
946  * either forward or backward.
947  * Return the number of matches not yet found in this file
948  * (that is, n minus the number of matches found).
949  * Return -1 if the search should be aborted.
950  * Caller may continue the search in another file
951  * if less than n matches are found in this file.
952  */
953 	public int
954 search(search_type, pattern, n)
955 	int search_type;
956 	char *pattern;
957 	int n;
958 {
959 	POSITION pos;
960 
961 	if (pattern == NULL || *pattern == '\0')
962 	{
963 		/*
964 		 * A null pattern means use the previously compiled pattern.
965 		 */
966 		search_type |= SRCH_AFTER_TARGET;
967 		if (!prev_pattern(&search_info) && !hist_pattern(search_type))
968 		{
969 			error("No previous regular expression", NULL_PARG);
970 			return (-1);
971 		}
972 		if ((search_type & SRCH_NO_REGEX) !=
973 		      (search_info.search_type & SRCH_NO_REGEX))
974 		{
975 			error("Please re-enter search pattern", NULL_PARG);
976 			return -1;
977 		}
978 #if HILITE_SEARCH
979 		if (hilite_search == OPT_ON)
980 		{
981 			/*
982 			 * Erase the highlights currently on screen.
983 			 * If the search fails, we'll redisplay them later.
984 			 */
985 			repaint_hilite(0);
986 		}
987 		if (hilite_search == OPT_ONPLUS && hide_hilite)
988 		{
989 			/*
990 			 * Highlight any matches currently on screen,
991 			 * before we actually start the search.
992 			 */
993 			hide_hilite = 0;
994 			hilite_screen();
995 		}
996 		hide_hilite = 0;
997 #endif
998 	} else
999 	{
1000 		/*
1001 		 * Compile the pattern.
1002 		 */
1003 		if (set_pattern(&search_info, pattern, search_type) < 0)
1004 			return (-1);
1005 #if HILITE_SEARCH
1006 		if (hilite_search)
1007 		{
1008 			/*
1009 			 * Erase the highlights currently on screen.
1010 			 * Also permanently delete them from the hilite list.
1011 			 */
1012 			repaint_hilite(0);
1013 			hide_hilite = 0;
1014 			clr_hilite();
1015 		}
1016 		if (hilite_search == OPT_ONPLUS)
1017 		{
1018 			/*
1019 			 * Highlight any matches currently on screen,
1020 			 * before we actually start the search.
1021 			 */
1022 			hilite_screen();
1023 		}
1024 #endif
1025 	}
1026 
1027 	/*
1028 	 * Figure out where to start the search.
1029 	 */
1030 	pos = search_pos(search_type);
1031 	if (pos == NULL_POSITION)
1032 	{
1033 		/*
1034 		 * Can't find anyplace to start searching from.
1035 		 */
1036 		if (search_type & SRCH_PAST_EOF)
1037 			return (n);
1038 		/* repaint(); -- why was this here? */
1039 		error("Nothing to search", NULL_PARG);
1040 		return (-1);
1041 	}
1042 
1043 	n = search_range(pos, NULL_POSITION, search_type, n, -1,
1044 			&pos, (POSITION*)NULL);
1045 	if (n != 0)
1046 	{
1047 		/*
1048 		 * Search was unsuccessful.
1049 		 */
1050 #if HILITE_SEARCH
1051 		if (hilite_search == OPT_ON && n > 0)
1052 			/*
1053 			 * Redisplay old hilites.
1054 			 */
1055 			repaint_hilite(1);
1056 #endif
1057 		return (n);
1058 	}
1059 
1060 	if (!(search_type & SRCH_NO_MOVE))
1061 	{
1062 		/*
1063 		 * Go to the matching line.
1064 		 */
1065 		jump_loc(pos, jump_sline);
1066 	}
1067 
1068 #if HILITE_SEARCH
1069 	if (hilite_search == OPT_ON)
1070 		/*
1071 		 * Display new hilites in the matching line.
1072 		 */
1073 		repaint_hilite(1);
1074 #endif
1075 	return (0);
1076 }
1077 
1078 
1079 #if HILITE_SEARCH
1080 /*
1081  * Prepare hilites in a given range of the file.
1082  *
1083  * The pair (prep_startpos,prep_endpos) delimits a contiguous region
1084  * of the file that has been "prepared"; that is, scanned for matches for
1085  * the current search pattern, and hilites have been created for such matches.
1086  * If prep_startpos == NULL_POSITION, the prep region is empty.
1087  * If prep_endpos == NULL_POSITION, the prep region extends to EOF.
1088  * prep_hilite asks that the range (spos,epos) be covered by the prep region.
1089  */
1090 	public void
1091 prep_hilite(spos, epos, maxlines)
1092 	POSITION spos;
1093 	POSITION epos;
1094 	int maxlines;
1095 {
1096 	POSITION nprep_startpos = prep_startpos;
1097 	POSITION nprep_endpos = prep_endpos;
1098 	POSITION new_epos;
1099 	POSITION max_epos;
1100 	int result;
1101 	int i;
1102 
1103 /*
1104  * Search beyond where we're asked to search, so the prep region covers
1105  * more than we need.  Do one big search instead of a bunch of small ones.
1106  */
1107 #define	SEARCH_MORE (3*size_linebuf)
1108 
1109 	if (!prev_pattern(&search_info) && !is_filtering())
1110 		return;
1111 
1112 	/*
1113 	 * If we're limited to a max number of lines, figure out the
1114 	 * file position we should stop at.
1115 	 */
1116 	if (maxlines < 0)
1117 		max_epos = NULL_POSITION;
1118 	else
1119 	{
1120 		max_epos = spos;
1121 		for (i = 0;  i < maxlines;  i++)
1122 			max_epos = forw_raw_line(max_epos, (char **)NULL, (int *)NULL);
1123 	}
1124 
1125 	/*
1126 	 * Find two ranges:
1127 	 * The range that we need to search (spos,epos); and the range that
1128 	 * the "prep" region will then cover (nprep_startpos,nprep_endpos).
1129 	 */
1130 
1131 	if (prep_startpos == NULL_POSITION ||
1132 	    (epos != NULL_POSITION && epos < prep_startpos) ||
1133 	    spos > prep_endpos)
1134 	{
1135 		/*
1136 		 * New range is not contiguous with old prep region.
1137 		 * Discard the old prep region and start a new one.
1138 		 */
1139 		clr_hilite();
1140 		clr_filter();
1141 		if (epos != NULL_POSITION)
1142 			epos += SEARCH_MORE;
1143 		nprep_startpos = spos;
1144 	} else
1145 	{
1146 		/*
1147 		 * New range partially or completely overlaps old prep region.
1148 		 */
1149 		if (epos == NULL_POSITION)
1150 		{
1151 			/*
1152 			 * New range goes to end of file.
1153 			 */
1154 			;
1155 		} else if (epos > prep_endpos)
1156 		{
1157 			/*
1158 			 * New range ends after old prep region.
1159 			 * Extend prep region to end at end of new range.
1160 			 */
1161 			epos += SEARCH_MORE;
1162 		} else /* (epos <= prep_endpos) */
1163 		{
1164 			/*
1165 			 * New range ends within old prep region.
1166 			 * Truncate search to end at start of old prep region.
1167 			 */
1168 			epos = prep_startpos;
1169 		}
1170 
1171 		if (spos < prep_startpos)
1172 		{
1173 			/*
1174 			 * New range starts before old prep region.
1175 			 * Extend old prep region backwards to start at
1176 			 * start of new range.
1177 			 */
1178 			if (spos < SEARCH_MORE)
1179 				spos = 0;
1180 			else
1181 				spos -= SEARCH_MORE;
1182 			nprep_startpos = spos;
1183 		} else /* (spos >= prep_startpos) */
1184 		{
1185 			/*
1186 			 * New range starts within or after old prep region.
1187 			 * Trim search to start at end of old prep region.
1188 			 */
1189 			spos = prep_endpos;
1190 		}
1191 	}
1192 
1193 	if (epos != NULL_POSITION && max_epos != NULL_POSITION &&
1194 	    epos > max_epos)
1195 		/*
1196 		 * Don't go past the max position we're allowed.
1197 		 */
1198 		epos = max_epos;
1199 
1200 	if (epos == NULL_POSITION || epos > spos)
1201 	{
1202 		int search_type = SRCH_FORW | SRCH_FIND_ALL;
1203 		search_type |= (search_info.search_type & SRCH_NO_REGEX);
1204 		result = search_range(spos, epos, search_type, 0,
1205 				maxlines, (POSITION*)NULL, &new_epos);
1206 		if (result < 0)
1207 			return;
1208 		if (prep_endpos == NULL_POSITION || new_epos > prep_endpos)
1209 			nprep_endpos = new_epos;
1210 	}
1211 	prep_startpos = nprep_startpos;
1212 	prep_endpos = nprep_endpos;
1213 }
1214 
1215 /*
1216  * Set the pattern to be used for line filtering.
1217  */
1218 	public void
1219 set_filter_pattern(pattern, search_type)
1220 	char *pattern;
1221 	int search_type;
1222 {
1223 	clr_filter();
1224 	if (pattern == NULL || *pattern == '\0')
1225 		clear_pattern(&filter_info);
1226 	else
1227 		set_pattern(&filter_info, pattern, search_type);
1228 	screen_trashed = 1;
1229 }
1230 
1231 /*
1232  * Is there a line filter in effect?
1233  */
1234 	public int
1235 is_filtering()
1236 {
1237 	if (ch_getflags() & CH_HELPFILE)
1238 		return (0);
1239 	return prev_pattern(&filter_info);
1240 }
1241 #endif
1242 
1243 #if HAVE_V8_REGCOMP
1244 /*
1245  * This function is called by the V8 regcomp to report
1246  * errors in regular expressions.
1247  */
1248 	void
1249 regerror(s)
1250 	char *s;
1251 {
1252 	PARG parg;
1253 
1254 	parg.p_string = s;
1255 	error("%s", &parg);
1256 }
1257 #endif
1258 
1259