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