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