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