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