search.c revision 161478
160812Sps/* $FreeBSD: head/contrib/less/search.c 161478 2006-08-20 15:50:51Z delphij $ */
260786Sps/*
3161478Sdelphij * Copyright (C) 1984-2005  Mark Nudelman
460786Sps *
560786Sps * You may distribute under the terms of either the GNU General Public
660786Sps * License or the Less License, as specified in the README file.
760786Sps *
860786Sps * For more information about less, or for information on how to
960786Sps * contact the author, see the README file.
1060786Sps */
1160786Sps
1260786Sps
1360786Sps/*
1460786Sps * Routines to search a file for a pattern.
1560786Sps */
1660786Sps
1760786Sps#include "less.h"
1860786Sps#include "position.h"
1960786Sps
2060786Sps#define	MINPOS(a,b)	(((a) < (b)) ? (a) : (b))
2160786Sps#define	MAXPOS(a,b)	(((a) > (b)) ? (a) : (b))
2260786Sps
2360786Sps#if HAVE_POSIX_REGCOMP
2460786Sps#include <regex.h>
2560786Sps#ifdef REG_EXTENDED
2660812Sps#define	REGCOMP_FLAG	(more_mode ? 0 : REG_EXTENDED)
2760786Sps#else
2860786Sps#define	REGCOMP_FLAG	0
2960786Sps#endif
3060786Sps#endif
3160786Sps#if HAVE_PCRE
3260786Sps#include <pcre.h>
3360786Sps#endif
3460786Sps#if HAVE_RE_COMP
3560786Spschar *re_comp();
3660786Spsint re_exec();
3760786Sps#endif
3860786Sps#if HAVE_REGCMP
3960786Spschar *regcmp();
4060786Spschar *regex();
4160786Spsextern char *__loc1;
4260786Sps#endif
4360786Sps#if HAVE_V8_REGCOMP
4460786Sps#include "regexp.h"
4560786Sps#endif
4660786Sps
4760786Spsstatic int match();
4860786Sps
4960786Spsextern int sigs;
5060786Spsextern int how_search;
5160786Spsextern int caseless;
5260786Spsextern int linenums;
5360786Spsextern int sc_height;
5460786Spsextern int jump_sline;
5560786Spsextern int bs_mode;
5660812Spsextern int more_mode;
57128348Stjrextern int ctldisp;
5863131Spsextern int status_col;
5960786Spsextern POSITION start_attnpos;
6060786Spsextern POSITION end_attnpos;
6160786Sps#if HILITE_SEARCH
6260786Spsextern int hilite_search;
6360786Spsextern int screen_trashed;
6460786Spsextern int size_linebuf;
6560786Spsextern int squished;
6660786Spsextern int can_goto_line;
6760786Spsstatic int hide_hilite;
6860786Spsstatic POSITION prep_startpos;
6960786Spsstatic POSITION prep_endpos;
7060786Sps
7160786Spsstruct hilite
7260786Sps{
7360786Sps	struct hilite *hl_next;
7460786Sps	POSITION hl_startpos;
7560786Sps	POSITION hl_endpos;
7660786Sps};
7760786Spsstatic struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION };
7860786Sps#define	hl_first	hl_next
7960786Sps#endif
8060786Sps
8160786Sps/*
8260786Sps * These are the static variables that represent the "remembered"
8360786Sps * search pattern.
8460786Sps */
8560786Sps#if HAVE_POSIX_REGCOMP
8660786Spsstatic regex_t *regpattern = NULL;
8760786Sps#endif
8860786Sps#if HAVE_PCRE
8960786Spspcre *regpattern = NULL;
9060786Sps#endif
9160786Sps#if HAVE_RE_COMP
9260786Spsint re_pattern = 0;
9360786Sps#endif
9460786Sps#if HAVE_REGCMP
9560786Spsstatic char *cpattern = NULL;
9660786Sps#endif
9760786Sps#if HAVE_V8_REGCOMP
9860786Spsstatic struct regexp *regpattern = NULL;
9960786Sps#endif
10060786Sps
10160786Spsstatic int is_caseless;
10260786Spsstatic int is_ucase_pattern;
10360786Spsstatic int last_search_type;
10460786Spsstatic char *last_pattern = NULL;
10560786Sps
10660786Sps/*
10760786Sps * Convert text.  Perform one or more of these transformations:
10860786Sps */
10960786Sps#define	CVT_TO_LC	01	/* Convert upper-case to lower-case */
11060786Sps#define	CVT_BS		02	/* Do backspace processing */
11160786Sps#define	CVT_CRLF	04	/* Remove CR after LF */
112128348Stjr#define	CVT_ANSI	010	/* Remove ANSI escape sequences */
11360786Sps
11460786Sps	static void
11560786Spscvt_text(odst, osrc, ops)
11660786Sps	char *odst;
11760786Sps	char *osrc;
11860786Sps	int ops;
11960786Sps{
12060786Sps	register char *dst;
12160786Sps	register char *src;
12260786Sps
123128348Stjr	for (src = osrc, dst = odst;  *src != '\0';  src++)
12460786Sps	{
125161478Sdelphij		if ((ops & CVT_TO_LC) && IS_UPPER(*src))
12660786Sps			/* Convert uppercase to lowercase. */
127161478Sdelphij			*dst++ = TO_LOWER(*src);
12860786Sps		else if ((ops & CVT_BS) && *src == '\b' && dst > odst)
12960786Sps			/* Delete BS and preceding char. */
130128348Stjr			dst--;
131128348Stjr		else if ((ops & CVT_ANSI) && *src == ESC)
132128348Stjr		{
133128348Stjr			/* Skip to end of ANSI escape sequence. */
134128348Stjr			while (src[1] != '\0')
135161478Sdelphij				if (!is_ansi_middle(*++src))
136128348Stjr					break;
137128348Stjr		} else
13860786Sps			/* Just copy. */
139128348Stjr			*dst++ = *src;
14060786Sps	}
14160786Sps	if ((ops & CVT_CRLF) && dst > odst && dst[-1] == '\r')
14260786Sps		dst--;
14360786Sps	*dst = '\0';
14460786Sps}
14560786Sps
14660786Sps/*
147128348Stjr * Determine which conversions to perform.
148128348Stjr */
149128348Stjr	static int
150128348Stjrget_cvt_ops()
151128348Stjr{
152128348Stjr	int ops = 0;
153128348Stjr	if (is_caseless || bs_mode == BS_SPECIAL)
154128348Stjr	{
155128348Stjr		if (is_caseless)
156128348Stjr			ops |= CVT_TO_LC;
157128348Stjr		if (bs_mode == BS_SPECIAL)
158128348Stjr			ops |= CVT_BS;
159128348Stjr		if (bs_mode != BS_CONTROL)
160128348Stjr			ops |= CVT_CRLF;
161128348Stjr	} else if (bs_mode != BS_CONTROL)
162128348Stjr	{
163128348Stjr		ops |= CVT_CRLF;
164128348Stjr	}
165128348Stjr	if (ctldisp == OPT_ONPLUS)
166128348Stjr		ops |= CVT_ANSI;
167128348Stjr	return (ops);
168128348Stjr}
169128348Stjr
170128348Stjr/*
17160786Sps * Are there any uppercase letters in this string?
17260786Sps */
17360786Sps	static int
17460786Spsis_ucase(s)
17560786Sps	char *s;
17660786Sps{
17760786Sps	register char *p;
17860786Sps
17960786Sps	for (p = s;  *p != '\0';  p++)
180161478Sdelphij		if (IS_UPPER(*p))
18160786Sps			return (1);
18260786Sps	return (0);
18360786Sps}
18460786Sps
18560786Sps/*
18660786Sps * Is there a previous (remembered) search pattern?
18760786Sps */
18860786Sps	static int
18960786Spsprev_pattern()
19060786Sps{
19160786Sps	if (last_search_type & SRCH_NO_REGEX)
19260786Sps		return (last_pattern != NULL);
19360786Sps#if HAVE_POSIX_REGCOMP
19460786Sps	return (regpattern != NULL);
19560786Sps#endif
19660786Sps#if HAVE_PCRE
19760786Sps	return (regpattern != NULL);
19860786Sps#endif
19960786Sps#if HAVE_RE_COMP
20060786Sps	return (re_pattern != 0);
20160786Sps#endif
20260786Sps#if HAVE_REGCMP
20360786Sps	return (cpattern != NULL);
20460786Sps#endif
20560786Sps#if HAVE_V8_REGCOMP
20660786Sps	return (regpattern != NULL);
20760786Sps#endif
20860786Sps#if NO_REGEX
20960786Sps	return (last_pattern != NULL);
21060786Sps#endif
21160786Sps}
21260786Sps
21360786Sps#if HILITE_SEARCH
21460786Sps/*
21560786Sps * Repaint the hilites currently displayed on the screen.
21660786Sps * Repaint each line which contains highlighted text.
21760786Sps * If on==0, force all hilites off.
21860786Sps */
21960786Sps	public void
22060786Spsrepaint_hilite(on)
22160786Sps	int on;
22260786Sps{
22360786Sps	int slinenum;
22460786Sps	POSITION pos;
22560786Sps	POSITION epos;
22660786Sps	int save_hide_hilite;
22760786Sps
22860786Sps	if (squished)
22960786Sps		repaint();
23060786Sps
23160786Sps	save_hide_hilite = hide_hilite;
23260786Sps	if (!on)
23360786Sps	{
23460786Sps		if (hide_hilite)
23560786Sps			return;
23660786Sps		hide_hilite = 1;
23760786Sps	}
23860786Sps
23960786Sps	if (!can_goto_line)
24060786Sps	{
24160786Sps		repaint();
24260786Sps		hide_hilite = save_hide_hilite;
24360786Sps		return;
24460786Sps	}
24560786Sps
24660786Sps	for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
24760786Sps	{
24860786Sps		pos = position(slinenum);
24960786Sps		if (pos == NULL_POSITION)
25060786Sps			continue;
25160786Sps		epos = position(slinenum+1);
252161478Sdelphij#if 0
25360786Sps		/*
25460786Sps		 * If any character in the line is highlighted,
25560786Sps		 * repaint the line.
256161478Sdelphij		 *
257161478Sdelphij		 * {{ This doesn't work -- if line is drawn with highlights
258161478Sdelphij		 * which should be erased (e.g. toggle -i with status column),
259161478Sdelphij		 * we must redraw the line even if it has no highlights.
260161478Sdelphij		 * For now, just repaint every line. }}
26160786Sps		 */
262161478Sdelphij		if (is_hilited(pos, epos, 1, NULL))
263161478Sdelphij#endif
26460786Sps		{
26560786Sps			(void) forw_line(pos);
26660786Sps			goto_line(slinenum);
26760786Sps			put_line();
26860786Sps		}
26960786Sps	}
27060786Sps	hide_hilite = save_hide_hilite;
27160786Sps}
27260786Sps
27360786Sps/*
27460786Sps * Clear the attn hilite.
27560786Sps */
27660786Sps	public void
27760786Spsclear_attn()
27860786Sps{
27960786Sps	int slinenum;
28060786Sps	POSITION old_start_attnpos;
28160786Sps	POSITION old_end_attnpos;
28260786Sps	POSITION pos;
28360786Sps	POSITION epos;
28460786Sps
28560786Sps	if (start_attnpos == NULL_POSITION)
28660786Sps		return;
28760786Sps	old_start_attnpos = start_attnpos;
28860786Sps	old_end_attnpos = end_attnpos;
28960786Sps	start_attnpos = end_attnpos = NULL_POSITION;
29060786Sps
29160786Sps	if (!can_goto_line)
29260786Sps	{
29360786Sps		repaint();
29460786Sps		return;
29560786Sps	}
29660786Sps	if (squished)
29760786Sps		repaint();
29860786Sps
29960786Sps	for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
30060786Sps	{
30160786Sps		pos = position(slinenum);
30260786Sps		if (pos == NULL_POSITION)
30360786Sps			continue;
30460786Sps		epos = position(slinenum+1);
30560786Sps		if (pos < old_end_attnpos &&
30660786Sps		     (epos == NULL_POSITION || epos > old_start_attnpos))
30760786Sps		{
30860786Sps			(void) forw_line(pos);
30960786Sps			goto_line(slinenum);
31060786Sps			put_line();
31160786Sps		}
31260786Sps	}
31360786Sps}
31460786Sps#endif
31560786Sps
31660786Sps/*
31760786Sps * Hide search string highlighting.
31860786Sps */
31960786Sps	public void
32060786Spsundo_search()
32160786Sps{
32260786Sps	if (!prev_pattern())
32360786Sps	{
32460786Sps		error("No previous regular expression", NULL_PARG);
32560786Sps		return;
32660786Sps	}
32760786Sps#if HILITE_SEARCH
32860786Sps	hide_hilite = !hide_hilite;
32960786Sps	repaint_hilite(1);
33060786Sps#endif
33160786Sps}
33260786Sps
33360786Sps/*
33460786Sps * Compile a search pattern, for future use by match_pattern.
33560786Sps */
33660786Sps	static int
33760786Spscompile_pattern(pattern, search_type)
33860786Sps	char *pattern;
33960786Sps	int search_type;
34060786Sps{
34160786Sps	if ((search_type & SRCH_NO_REGEX) == 0)
34260786Sps	{
34360786Sps#if HAVE_POSIX_REGCOMP
34460786Sps		regex_t *s = (regex_t *) ecalloc(1, sizeof(regex_t));
34560786Sps		if (regcomp(s, pattern, REGCOMP_FLAG))
34660786Sps		{
34760786Sps			free(s);
34860786Sps			error("Invalid pattern", NULL_PARG);
34960786Sps			return (-1);
35060786Sps		}
35160786Sps		if (regpattern != NULL)
35260786Sps			regfree(regpattern);
35360786Sps		regpattern = s;
35460786Sps#endif
35560786Sps#if HAVE_PCRE
35660786Sps		pcre *comp;
35760786Sps		const char *errstring;
35860786Sps		int erroffset;
35960786Sps		PARG parg;
36060786Sps		comp = pcre_compile(pattern, 0,
36160786Sps				&errstring, &erroffset, NULL);
36260786Sps		if (comp == NULL)
36360786Sps		{
36460786Sps			parg.p_string = (char *) errstring;
36560786Sps			error("%s", &parg);
36660786Sps			return (-1);
36760786Sps		}
36860786Sps		regpattern = comp;
36960786Sps#endif
37060786Sps#if HAVE_RE_COMP
37160786Sps		PARG parg;
37260786Sps		if ((parg.p_string = re_comp(pattern)) != NULL)
37360786Sps		{
37460786Sps			error("%s", &parg);
37560786Sps			return (-1);
37660786Sps		}
37760786Sps		re_pattern = 1;
37860786Sps#endif
37960786Sps#if HAVE_REGCMP
38060786Sps		char *s;
38160786Sps		if ((s = regcmp(pattern, 0)) == NULL)
38260786Sps		{
38360786Sps			error("Invalid pattern", NULL_PARG);
38460786Sps			return (-1);
38560786Sps		}
38660786Sps		if (cpattern != NULL)
38760786Sps			free(cpattern);
38860786Sps		cpattern = s;
38960786Sps#endif
39060786Sps#if HAVE_V8_REGCOMP
39160786Sps		struct regexp *s;
39260786Sps		if ((s = regcomp(pattern)) == NULL)
39360786Sps		{
39460786Sps			/*
39560786Sps			 * regcomp has already printed an error message
39660786Sps			 * via regerror().
39760786Sps			 */
39860786Sps			return (-1);
39960786Sps		}
40060786Sps		if (regpattern != NULL)
40160786Sps			free(regpattern);
40260786Sps		regpattern = s;
40360786Sps#endif
40460786Sps	}
40560786Sps
40660786Sps	if (last_pattern != NULL)
40760786Sps		free(last_pattern);
40860786Sps	last_pattern = (char *) calloc(1, strlen(pattern)+1);
40960786Sps	if (last_pattern != NULL)
41060786Sps		strcpy(last_pattern, pattern);
41160786Sps
41260786Sps	last_search_type = search_type;
41360786Sps	return (0);
41460786Sps}
41560786Sps
41660786Sps/*
41760786Sps * Forget that we have a compiled pattern.
41860786Sps */
41960786Sps	static void
42060786Spsuncompile_pattern()
42160786Sps{
42260786Sps#if HAVE_POSIX_REGCOMP
42360786Sps	if (regpattern != NULL)
42460786Sps		regfree(regpattern);
42560786Sps	regpattern = NULL;
42660786Sps#endif
42760786Sps#if HAVE_PCRE
42860786Sps	if (regpattern != NULL)
42960786Sps		pcre_free(regpattern);
43060786Sps	regpattern = NULL;
43160786Sps#endif
43260786Sps#if HAVE_RE_COMP
43360786Sps	re_pattern = 0;
43460786Sps#endif
43560786Sps#if HAVE_REGCMP
43660786Sps	if (cpattern != NULL)
43760786Sps		free(cpattern);
43860786Sps	cpattern = NULL;
43960786Sps#endif
44060786Sps#if HAVE_V8_REGCOMP
44160786Sps	if (regpattern != NULL)
44260786Sps		free(regpattern);
44360786Sps	regpattern = NULL;
44460786Sps#endif
44560786Sps	last_pattern = NULL;
44660786Sps}
44760786Sps
44860786Sps/*
44960786Sps * Perform a pattern match with the previously compiled pattern.
45060786Sps * Set sp and ep to the start and end of the matched string.
45160786Sps */
45260786Sps	static int
45360786Spsmatch_pattern(line, sp, ep, notbol)
45460786Sps	char *line;
45560786Sps	char **sp;
45660786Sps	char **ep;
45760786Sps	int notbol;
45860786Sps{
45960786Sps	int matched;
46060786Sps
46160786Sps	if (last_search_type & SRCH_NO_REGEX)
46260786Sps		return (match(last_pattern, line, sp, ep));
46360786Sps
46460786Sps#if HAVE_POSIX_REGCOMP
46560786Sps	{
46660786Sps		regmatch_t rm;
46760786Sps		int flags = (notbol) ? REG_NOTBOL : 0;
46860786Sps		matched = !regexec(regpattern, line, 1, &rm, flags);
46960786Sps		if (!matched)
47060786Sps			return (0);
47160786Sps#ifndef __WATCOMC__
47260786Sps		*sp = line + rm.rm_so;
47360786Sps		*ep = line + rm.rm_eo;
47460786Sps#else
47560786Sps		*sp = rm.rm_sp;
47660786Sps		*ep = rm.rm_ep;
47760786Sps#endif
47860786Sps	}
47960786Sps#endif
48060786Sps#if HAVE_PCRE
48160786Sps	{
48260786Sps		int flags = (notbol) ? PCRE_NOTBOL : 0;
48360786Sps		int ovector[3];
48460786Sps		matched = pcre_exec(regpattern, NULL, line, strlen(line),
48560786Sps			0, flags, ovector, 3) >= 0;
48660786Sps		if (!matched)
48760786Sps			return (0);
48860786Sps		*sp = line + ovector[0];
48960786Sps		*ep = line + ovector[1];
49060786Sps	}
49160786Sps#endif
49260786Sps#if HAVE_RE_COMP
49360786Sps	matched = (re_exec(line) == 1);
49460786Sps	/*
49560786Sps	 * re_exec doesn't seem to provide a way to get the matched string.
49660786Sps	 */
49760786Sps	*sp = *ep = NULL;
49860786Sps#endif
49960786Sps#if HAVE_REGCMP
50060786Sps	*ep = regex(cpattern, line);
50160786Sps	matched = (*ep != NULL);
50260786Sps	if (!matched)
50360786Sps		return (0);
50460786Sps	*sp = __loc1;
50560786Sps#endif
50660786Sps#if HAVE_V8_REGCOMP
50760786Sps#if HAVE_REGEXEC2
50860786Sps	matched = regexec2(regpattern, line, notbol);
50960786Sps#else
51060786Sps	matched = regexec(regpattern, line);
51160786Sps#endif
51260786Sps	if (!matched)
51360786Sps		return (0);
51460786Sps	*sp = regpattern->startp[0];
51560786Sps	*ep = regpattern->endp[0];
51660786Sps#endif
51760786Sps#if NO_REGEX
51860786Sps	matched = match(last_pattern, line, sp, ep);
51960786Sps#endif
52060786Sps	return (matched);
52160786Sps}
52260786Sps
52360786Sps#if HILITE_SEARCH
52460786Sps/*
52560786Sps * Clear the hilite list.
52660786Sps */
52760786Sps	public void
52860786Spsclr_hilite()
52960786Sps{
53060786Sps	struct hilite *hl;
53160786Sps	struct hilite *nexthl;
53260786Sps
53360786Sps	for (hl = hilite_anchor.hl_first;  hl != NULL;  hl = nexthl)
53460786Sps	{
53560786Sps		nexthl = hl->hl_next;
53660786Sps		free((void*)hl);
53760786Sps	}
53860786Sps	hilite_anchor.hl_first = NULL;
53960786Sps	prep_startpos = prep_endpos = NULL_POSITION;
54060786Sps}
54160786Sps
54260786Sps/*
54360786Sps * Should any characters in a specified range be highlighted?
544161478Sdelphij */
545161478Sdelphij	static int
546161478Sdelphijis_hilited_range(pos, epos)
547161478Sdelphij	POSITION pos;
548161478Sdelphij	POSITION epos;
549161478Sdelphij{
550161478Sdelphij	struct hilite *hl;
551161478Sdelphij
552161478Sdelphij	/*
553161478Sdelphij	 * Look at each highlight and see if any part of it falls in the range.
554161478Sdelphij	 */
555161478Sdelphij	for (hl = hilite_anchor.hl_first;  hl != NULL;  hl = hl->hl_next)
556161478Sdelphij	{
557161478Sdelphij		if (hl->hl_endpos > pos &&
558161478Sdelphij		    (epos == NULL_POSITION || epos > hl->hl_startpos))
559161478Sdelphij			return (1);
560161478Sdelphij	}
561161478Sdelphij	return (0);
562161478Sdelphij}
563161478Sdelphij
564161478Sdelphij/*
565161478Sdelphij * Should any characters in a specified range be highlighted?
56660786Sps * If nohide is nonzero, don't consider hide_hilite.
56760786Sps */
56860786Sps	public int
569161478Sdelphijis_hilited(pos, epos, nohide, p_matches)
57060786Sps	POSITION pos;
57160786Sps	POSITION epos;
57260786Sps	int nohide;
573161478Sdelphij	int *p_matches;
57460786Sps{
575161478Sdelphij	int match;
57660786Sps
577161478Sdelphij	if (p_matches != NULL)
578161478Sdelphij		*p_matches = 0;
579161478Sdelphij
58063131Sps	if (!status_col &&
58163131Sps	    start_attnpos != NULL_POSITION &&
58260786Sps	    pos < end_attnpos &&
58360786Sps	     (epos == NULL_POSITION || epos > start_attnpos))
58460786Sps		/*
58560786Sps		 * The attn line overlaps this range.
58660786Sps		 */
58760786Sps		return (1);
58860786Sps
589161478Sdelphij	match = is_hilited_range(pos, epos);
590161478Sdelphij	if (!match)
591161478Sdelphij		return (0);
592161478Sdelphij
593161478Sdelphij	if (p_matches != NULL)
594161478Sdelphij		/*
595161478Sdelphij		 * Report matches, even if we're hiding highlights.
596161478Sdelphij		 */
597161478Sdelphij		*p_matches = 1;
598161478Sdelphij
59960786Sps	if (hilite_search == 0)
60060786Sps		/*
60160786Sps		 * Not doing highlighting.
60260786Sps		 */
60360786Sps		return (0);
60460786Sps
60560786Sps	if (!nohide && hide_hilite)
60660786Sps		/*
60760786Sps		 * Highlighting is hidden.
60860786Sps		 */
60960786Sps		return (0);
61060786Sps
611161478Sdelphij	return (1);
61260786Sps}
61360786Sps
61460786Sps/*
61560786Sps * Add a new hilite to a hilite list.
61660786Sps */
61760786Sps	static void
61860786Spsadd_hilite(anchor, hl)
61960786Sps	struct hilite *anchor;
62060786Sps	struct hilite *hl;
62160786Sps{
62260786Sps	struct hilite *ihl;
62360786Sps
62460786Sps	/*
62560786Sps	 * Hilites are sorted in the list; find where new one belongs.
62660786Sps	 * Insert new one after ihl.
62760786Sps	 */
62860786Sps	for (ihl = anchor;  ihl->hl_next != NULL;  ihl = ihl->hl_next)
62960786Sps	{
63060786Sps		if (ihl->hl_next->hl_startpos > hl->hl_startpos)
63160786Sps			break;
63260786Sps	}
63360786Sps
63460786Sps	/*
63560786Sps	 * Truncate hilite so it doesn't overlap any existing ones
63660786Sps	 * above and below it.
63760786Sps	 */
63860786Sps	if (ihl != anchor)
63960786Sps		hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos);
64060786Sps	if (ihl->hl_next != NULL)
64160786Sps		hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos);
64260786Sps	if (hl->hl_startpos >= hl->hl_endpos)
64360786Sps	{
64460786Sps		/*
64560786Sps		 * Hilite was truncated out of existence.
64660786Sps		 */
64760786Sps		free(hl);
64860786Sps		return;
64960786Sps	}
65060786Sps	hl->hl_next = ihl->hl_next;
65160786Sps	ihl->hl_next = hl;
65260786Sps}
65360786Sps
654161478Sdelphij	static void
655161478Sdelphijadj_hilite_ansi(cvt_ops, line, npos)
656161478Sdelphij	int cvt_ops;
657161478Sdelphij	char **line;
658161478Sdelphij	POSITION *npos;
659161478Sdelphij{
660161478Sdelphij	if (cvt_ops & CVT_ANSI)
661161478Sdelphij		while (**line == ESC)
662161478Sdelphij		{
663161478Sdelphij			/*
664161478Sdelphij			 * Found an ESC.  The file position moves
665161478Sdelphij			 * forward past the entire ANSI escape sequence.
666161478Sdelphij			 */
667161478Sdelphij			(*line)++;
668161478Sdelphij			(*npos)++;
669161478Sdelphij			while (**line != '\0')
670161478Sdelphij			{
671161478Sdelphij				(*npos)++;
672161478Sdelphij				if (!is_ansi_middle(*(*line)++))
673161478Sdelphij					break;
674161478Sdelphij			}
675161478Sdelphij		}
676161478Sdelphij}
677161478Sdelphij
67860786Sps/*
67960786Sps * Adjust hl_startpos & hl_endpos to account for backspace processing.
68060786Sps */
68160786Sps	static void
682128348Stjradj_hilite(anchor, linepos, cvt_ops)
68360786Sps	struct hilite *anchor;
68460786Sps	POSITION linepos;
685128348Stjr	int cvt_ops;
68660786Sps{
68760786Sps	char *line;
68860786Sps	struct hilite *hl;
68960786Sps	int checkstart;
69060786Sps	POSITION opos;
69160786Sps	POSITION npos;
69260786Sps
69360786Sps	/*
69460786Sps	 * The line was already scanned and hilites were added (in hilite_line).
69560786Sps	 * But it was assumed that each char position in the line
69660786Sps	 * correponds to one char position in the file.
69760786Sps	 * This may not be true if there are backspaces in the line.
69860786Sps	 * Get the raw line again.  Look at each character.
69960786Sps	 */
70060786Sps	(void) forw_raw_line(linepos, &line);
70160786Sps	opos = npos = linepos;
70260786Sps	hl = anchor->hl_first;
70360786Sps	checkstart = TRUE;
70460786Sps	while (hl != NULL)
70560786Sps	{
70660786Sps		/*
70760786Sps		 * See if we need to adjust the current hl_startpos or
70860786Sps		 * hl_endpos.  After adjusting startpos[i], move to endpos[i].
70960786Sps		 * After adjusting endpos[i], move to startpos[i+1].
71060786Sps		 * The hilite list must be sorted thus:
71160786Sps		 * startpos[0] < endpos[0] <= startpos[1] < endpos[1] <= etc.
71260786Sps		 */
71360786Sps		if (checkstart && hl->hl_startpos == opos)
71460786Sps		{
71560786Sps			hl->hl_startpos = npos;
71660786Sps			checkstart = FALSE;
71760786Sps			continue; /* {{ not really necessary }} */
71860786Sps		} else if (!checkstart && hl->hl_endpos == opos)
71960786Sps		{
72060786Sps			hl->hl_endpos = npos;
72160786Sps			checkstart = TRUE;
72260786Sps			hl = hl->hl_next;
72360786Sps			continue; /* {{ necessary }} */
72460786Sps		}
72560786Sps		if (*line == '\0')
72660786Sps			break;
727161478Sdelphij		adj_hilite_ansi(cvt_ops, &line, &npos);
72860786Sps		opos++;
72960786Sps		npos++;
73060786Sps		line++;
731128348Stjr		if (cvt_ops & CVT_BS)
73260786Sps		{
733161478Sdelphij			while (*line == '\b')
734128348Stjr			{
735161478Sdelphij				npos++;
736161478Sdelphij				line++;
737161478Sdelphij				adj_hilite_ansi(cvt_ops, &line, &npos);
738161478Sdelphij				if (*line == '\0')
739161478Sdelphij				{
740161478Sdelphij					--npos;
741161478Sdelphij					--line;
742161478Sdelphij					break;
743161478Sdelphij				}
744128348Stjr				/*
745128348Stjr				 * Found a backspace.  The file position moves
746128348Stjr				 * forward by 2 relative to the processed line
747128348Stjr				 * which was searched in hilite_line.
748128348Stjr				 */
749161478Sdelphij				npos++;
750161478Sdelphij				line++;
751128348Stjr			}
75260786Sps		}
75360786Sps	}
75460786Sps}
75560786Sps
75660786Sps/*
75760786Sps * Make a hilite for each string in a physical line which matches
75860786Sps * the current pattern.
75960786Sps * sp,ep delimit the first match already found.
76060786Sps */
76160786Sps	static void
762128348Stjrhilite_line(linepos, line, sp, ep, cvt_ops)
76360786Sps	POSITION linepos;
76460786Sps	char *line;
76560786Sps	char *sp;
76660786Sps	char *ep;
767128348Stjr	int cvt_ops;
76860786Sps{
76960786Sps	char *searchp;
77060786Sps	struct hilite *hl;
77160786Sps	struct hilite hilites;
77260786Sps
77360786Sps	if (sp == NULL || ep == NULL)
77460786Sps		return;
77560786Sps	/*
77660786Sps	 * sp and ep delimit the first match in the line.
77760786Sps	 * Mark the corresponding file positions, then
77860786Sps	 * look for further matches and mark them.
77960786Sps	 * {{ This technique, of calling match_pattern on subsequent
78060786Sps	 *    substrings of the line, may mark more than is correct
78160786Sps	 *    if the pattern starts with "^".  This bug is fixed
78260786Sps	 *    for those regex functions that accept a notbol parameter
78360786Sps	 *    (currently POSIX and V8-with-regexec2). }}
78460786Sps	 */
78560786Sps	searchp = line;
78660786Sps	/*
78760786Sps	 * Put the hilites into a temporary list until they're adjusted.
78860786Sps	 */
78960786Sps	hilites.hl_first = NULL;
79060786Sps	do {
79160786Sps		if (ep > sp)
79260786Sps		{
79360786Sps			/*
79460786Sps			 * Assume that each char position in the "line"
79560786Sps			 * buffer corresponds to one char position in the file.
79660786Sps			 * This is not quite true; we need to adjust later.
79760786Sps			 */
79860786Sps			hl = (struct hilite *) ecalloc(1, sizeof(struct hilite));
79960786Sps			hl->hl_startpos = linepos + (sp-line);
80060786Sps			hl->hl_endpos = linepos + (ep-line);
80160786Sps			add_hilite(&hilites, hl);
80260786Sps		}
80360786Sps		/*
80460786Sps		 * If we matched more than zero characters,
80560786Sps		 * move to the first char after the string we matched.
80660786Sps		 * If we matched zero, just move to the next char.
80760786Sps		 */
80860786Sps		if (ep > searchp)
80960786Sps			searchp = ep;
81060786Sps		else if (*searchp != '\0')
81160786Sps			searchp++;
81260786Sps		else /* end of line */
81360786Sps			break;
81460786Sps	} while (match_pattern(searchp, &sp, &ep, 1));
81560786Sps
81660786Sps	/*
817128348Stjr	 * If there were backspaces in the original line, they
818128348Stjr	 * were removed, and hl_startpos/hl_endpos are not correct.
819128348Stjr	 * {{ This is very ugly. }}
820128348Stjr	 */
821128348Stjr	adj_hilite(&hilites, linepos, cvt_ops);
822128348Stjr
823128348Stjr	/*
82460786Sps	 * Now put the hilites into the real list.
82560786Sps	 */
82660786Sps	while ((hl = hilites.hl_next) != NULL)
82760786Sps	{
82860786Sps		hilites.hl_next = hl->hl_next;
82960786Sps		add_hilite(&hilite_anchor, hl);
83060786Sps	}
83160786Sps}
83260786Sps#endif
83360786Sps
83460786Sps/*
83560786Sps * Change the caseless-ness of searches.
83660786Sps * Updates the internal search state to reflect a change in the -i flag.
83760786Sps */
83860786Sps	public void
83960786Spschg_caseless()
84060786Sps{
84160786Sps	if (!is_ucase_pattern)
84260786Sps		/*
84360786Sps		 * Pattern did not have uppercase.
84460786Sps		 * Just set the search caselessness to the global caselessness.
84560786Sps		 */
84660786Sps		is_caseless = caseless;
84760786Sps	else
84860786Sps		/*
84960786Sps		 * Pattern did have uppercase.
85060786Sps		 * Discard the pattern; we can't change search caselessness now.
85160786Sps		 */
85260786Sps		uncompile_pattern();
85360786Sps}
85460786Sps
85560786Sps#if HILITE_SEARCH
85660786Sps/*
85760786Sps * Find matching text which is currently on screen and highlight it.
85860786Sps */
85960786Sps	static void
86060786Spshilite_screen()
86160786Sps{
86260786Sps	struct scrpos scrpos;
86360786Sps
86460786Sps	get_scrpos(&scrpos);
86560786Sps	if (scrpos.pos == NULL_POSITION)
86660786Sps		return;
86760786Sps	prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1);
86860786Sps	repaint_hilite(1);
86960786Sps}
87060786Sps
87160786Sps/*
87260786Sps * Change highlighting parameters.
87360786Sps */
87460786Sps	public void
87560786Spschg_hilite()
87660786Sps{
87760786Sps	/*
87860786Sps	 * Erase any highlights currently on screen.
87960786Sps	 */
88060786Sps	clr_hilite();
88160786Sps	hide_hilite = 0;
88260786Sps
88360786Sps	if (hilite_search == OPT_ONPLUS)
88460786Sps		/*
88560786Sps		 * Display highlights.
88660786Sps		 */
88760786Sps		hilite_screen();
88860786Sps}
88960786Sps#endif
89060786Sps
89160786Sps/*
89260786Sps * Figure out where to start a search.
89360786Sps */
89460786Sps	static POSITION
89560786Spssearch_pos(search_type)
89660786Sps	int search_type;
89760786Sps{
89860786Sps	POSITION pos;
89960786Sps	int linenum;
90060786Sps
90160786Sps	if (empty_screen())
90260786Sps	{
90360786Sps		/*
90460786Sps		 * Start at the beginning (or end) of the file.
90560786Sps		 * The empty_screen() case is mainly for
90660786Sps		 * command line initiated searches;
90760786Sps		 * for example, "+/xyz" on the command line.
90860786Sps		 * Also for multi-file (SRCH_PAST_EOF) searches.
90960786Sps		 */
91060786Sps		if (search_type & SRCH_FORW)
91160786Sps		{
91260786Sps			return (ch_zero());
91360786Sps		} else
91460786Sps		{
91560786Sps			pos = ch_length();
91660786Sps			if (pos == NULL_POSITION)
91760786Sps			{
91860786Sps				(void) ch_end_seek();
91960786Sps				pos = ch_length();
92060786Sps			}
92160786Sps			return (pos);
92260786Sps		}
92360786Sps	}
92460786Sps	if (how_search)
92560786Sps	{
92660786Sps		/*
92760786Sps		 * Search does not include current screen.
92860786Sps		 */
92960786Sps		if (search_type & SRCH_FORW)
93060786Sps			linenum = BOTTOM_PLUS_ONE;
93160786Sps		else
93260786Sps			linenum = TOP;
93360786Sps		pos = position(linenum);
93460786Sps	} else
93560786Sps	{
93660786Sps		/*
93760786Sps		 * Search includes current screen.
93860786Sps		 * It starts at the jump target (if searching backwards),
93960786Sps		 * or at the jump target plus one (if forwards).
94060786Sps		 */
94160786Sps		linenum = adjsline(jump_sline);
94260786Sps		pos = position(linenum);
94360786Sps		if (search_type & SRCH_FORW)
94460786Sps		{
94560786Sps			pos = forw_raw_line(pos, (char **)NULL);
94660786Sps			while (pos == NULL_POSITION)
94760786Sps			{
94860786Sps				if (++linenum >= sc_height)
94960786Sps					break;
95060786Sps				pos = position(linenum);
95160786Sps			}
95260786Sps		} else
95360786Sps		{
95460786Sps			while (pos == NULL_POSITION)
95560786Sps			{
95660786Sps				if (--linenum < 0)
95760786Sps					break;
95860786Sps				pos = position(linenum);
95960786Sps			}
96060786Sps		}
96160786Sps	}
96260786Sps	return (pos);
96360786Sps}
96460786Sps
96560786Sps/*
96660786Sps * Search a subset of the file, specified by start/end position.
96760786Sps */
96860786Sps	static int
96960786Spssearch_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
97060786Sps	POSITION pos;
97160786Sps	POSITION endpos;
97260786Sps	int search_type;
97360786Sps	int matches;
97460786Sps	int maxlines;
97560786Sps	POSITION *plinepos;
97660786Sps	POSITION *pendpos;
97760786Sps{
97860786Sps	char *line;
979128348Stjr	LINENUM linenum;
98060786Sps	char *sp, *ep;
98160786Sps	int line_match;
982128348Stjr	int cvt_ops;
98360786Sps	POSITION linepos, oldpos;
98460786Sps
98560786Sps	linenum = find_linenum(pos);
98660786Sps	oldpos = pos;
98760786Sps	for (;;)
98860786Sps	{
98960786Sps		/*
99060786Sps		 * Get lines until we find a matching one or until
99160786Sps		 * we hit end-of-file (or beginning-of-file if we're
99260786Sps		 * going backwards), or until we hit the end position.
99360786Sps		 */
99460786Sps		if (ABORT_SIGS())
99560786Sps		{
99660786Sps			/*
99760786Sps			 * A signal aborts the search.
99860786Sps			 */
99960786Sps			return (-1);
100060786Sps		}
100160786Sps
100260786Sps		if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0)
100360786Sps		{
100460786Sps			/*
100560786Sps			 * Reached end position without a match.
100660786Sps			 */
100760786Sps			if (pendpos != NULL)
100860786Sps				*pendpos = pos;
100960786Sps			return (matches);
101060786Sps		}
101160786Sps		if (maxlines > 0)
101260786Sps			maxlines--;
101360786Sps
101460786Sps		if (search_type & SRCH_FORW)
101560786Sps		{
101660786Sps			/*
101760786Sps			 * Read the next line, and save the
101860786Sps			 * starting position of that line in linepos.
101960786Sps			 */
102060786Sps			linepos = pos;
102160786Sps			pos = forw_raw_line(pos, &line);
102260786Sps			if (linenum != 0)
102360786Sps				linenum++;
102460786Sps		} else
102560786Sps		{
102660786Sps			/*
102760786Sps			 * Read the previous line and save the
102860786Sps			 * starting position of that line in linepos.
102960786Sps			 */
103060786Sps			pos = back_raw_line(pos, &line);
103160786Sps			linepos = pos;
103260786Sps			if (linenum != 0)
103360786Sps				linenum--;
103460786Sps		}
103560786Sps
103660786Sps		if (pos == NULL_POSITION)
103760786Sps		{
103860786Sps			/*
103960786Sps			 * Reached EOF/BOF without a match.
104060786Sps			 */
104160786Sps			if (pendpos != NULL)
104260786Sps				*pendpos = oldpos;
104360786Sps			return (matches);
104460786Sps		}
104560786Sps
104660786Sps		/*
104760786Sps		 * If we're using line numbers, we might as well
104860786Sps		 * remember the information we have now (the position
104960786Sps		 * and line number of the current line).
105060786Sps		 * Don't do it for every line because it slows down
105160786Sps		 * the search.  Remember the line number only if
105260786Sps		 * we're "far" from the last place we remembered it.
105360786Sps		 */
105460786Sps		if (linenums && abs((int)(pos - oldpos)) > 1024)
105560786Sps			add_lnum(linenum, pos);
105660786Sps		oldpos = pos;
105760786Sps
105860786Sps		/*
105960786Sps		 * If it's a caseless search, convert the line to lowercase.
106060786Sps		 * If we're doing backspace processing, delete backspaces.
106160786Sps		 */
1062128348Stjr		cvt_ops = get_cvt_ops();
1063128348Stjr		cvt_text(line, line, cvt_ops);
106460786Sps
106560786Sps		/*
106660786Sps		 * Test the next line to see if we have a match.
106760786Sps		 * We are successful if we either want a match and got one,
106860786Sps		 * or if we want a non-match and got one.
106960786Sps		 */
107060786Sps		line_match = match_pattern(line, &sp, &ep, 0);
107160786Sps		line_match = (!(search_type & SRCH_NO_MATCH) && line_match) ||
107260786Sps				((search_type & SRCH_NO_MATCH) && !line_match);
107360786Sps		if (!line_match)
107460786Sps			continue;
107560786Sps		/*
107660786Sps		 * Got a match.
107760786Sps		 */
107860786Sps		if (search_type & SRCH_FIND_ALL)
107960786Sps		{
108060786Sps#if HILITE_SEARCH
108160786Sps			/*
108260786Sps			 * We are supposed to find all matches in the range.
108360786Sps			 * Just add the matches in this line to the
108460786Sps			 * hilite list and keep searching.
108560786Sps			 */
108660786Sps			if (line_match)
1087128348Stjr				hilite_line(linepos, line, sp, ep, cvt_ops);
108860786Sps#endif
108960786Sps		} else if (--matches <= 0)
109060786Sps		{
109160786Sps			/*
109260786Sps			 * Found the one match we're looking for.
109360786Sps			 * Return it.
109460786Sps			 */
109560786Sps#if HILITE_SEARCH
1096161478Sdelphij			if (hilite_search == OPT_ON)
109760786Sps			{
109860786Sps				/*
109960786Sps				 * Clear the hilite list and add only
110060786Sps				 * the matches in this one line.
110160786Sps				 */
110260786Sps				clr_hilite();
110360786Sps				if (line_match)
1104128348Stjr					hilite_line(linepos, line, sp, ep, cvt_ops);
110560786Sps			}
110660786Sps#endif
110760786Sps			if (plinepos != NULL)
110860786Sps				*plinepos = linepos;
110960786Sps			return (0);
111060786Sps		}
111160786Sps	}
111260786Sps}
111360786Sps
111460786Sps/*
111560786Sps * Search for the n-th occurrence of a specified pattern,
111660786Sps * either forward or backward.
111760786Sps * Return the number of matches not yet found in this file
111860786Sps * (that is, n minus the number of matches found).
111960786Sps * Return -1 if the search should be aborted.
112060786Sps * Caller may continue the search in another file
112160786Sps * if less than n matches are found in this file.
112260786Sps */
112360786Sps	public int
112460786Spssearch(search_type, pattern, n)
112560786Sps	int search_type;
112660786Sps	char *pattern;
112760786Sps	int n;
112860786Sps{
112960786Sps	POSITION pos;
113060786Sps	int ucase;
113160786Sps
113260786Sps	if (pattern == NULL || *pattern == '\0')
113360786Sps	{
113460786Sps		/*
113560786Sps		 * A null pattern means use the previously compiled pattern.
113660786Sps		 */
113760786Sps		if (!prev_pattern())
113860786Sps		{
113960786Sps			error("No previous regular expression", NULL_PARG);
114060786Sps			return (-1);
114160786Sps		}
114260786Sps		if ((search_type & SRCH_NO_REGEX) !=
114360786Sps		    (last_search_type & SRCH_NO_REGEX))
114460786Sps		{
114560786Sps			error("Please re-enter search pattern", NULL_PARG);
114660786Sps			return -1;
114760786Sps		}
114860786Sps#if HILITE_SEARCH
114960786Sps		if (hilite_search == OPT_ON)
115060786Sps		{
115160786Sps			/*
115260786Sps			 * Erase the highlights currently on screen.
115360786Sps			 * If the search fails, we'll redisplay them later.
115460786Sps			 */
115560786Sps			repaint_hilite(0);
115660786Sps		}
115760786Sps		if (hilite_search == OPT_ONPLUS && hide_hilite)
115860786Sps		{
115960786Sps			/*
116060786Sps			 * Highlight any matches currently on screen,
116160786Sps			 * before we actually start the search.
116260786Sps			 */
116360786Sps			hide_hilite = 0;
116460786Sps			hilite_screen();
116560786Sps		}
116660786Sps		hide_hilite = 0;
116760786Sps#endif
116860786Sps	} else
116960786Sps	{
117060786Sps		/*
117160786Sps		 * Compile the pattern.
117260786Sps		 */
117360786Sps		ucase = is_ucase(pattern);
117460786Sps		if (caseless == OPT_ONPLUS)
117560786Sps			cvt_text(pattern, pattern, CVT_TO_LC);
117660786Sps		if (compile_pattern(pattern, search_type) < 0)
117760786Sps			return (-1);
117860786Sps		/*
117960786Sps		 * Ignore case if -I is set OR
118060786Sps		 * -i is set AND the pattern is all lowercase.
118160786Sps		 */
118260786Sps		is_ucase_pattern = ucase;
118360786Sps		if (is_ucase_pattern && caseless != OPT_ONPLUS)
118460786Sps			is_caseless = 0;
118560786Sps		else
118660786Sps			is_caseless = caseless;
118760786Sps#if HILITE_SEARCH
118860786Sps		if (hilite_search)
118960786Sps		{
119060786Sps			/*
119160786Sps			 * Erase the highlights currently on screen.
119260786Sps			 * Also permanently delete them from the hilite list.
119360786Sps			 */
119460786Sps			repaint_hilite(0);
119560786Sps			hide_hilite = 0;
119660786Sps			clr_hilite();
119760786Sps		}
119860786Sps		if (hilite_search == OPT_ONPLUS)
119960786Sps		{
120060786Sps			/*
120160786Sps			 * Highlight any matches currently on screen,
120260786Sps			 * before we actually start the search.
120360786Sps			 */
120460786Sps			hilite_screen();
120560786Sps		}
120660786Sps#endif
120760786Sps	}
120860786Sps
120960786Sps	/*
121060786Sps	 * Figure out where to start the search.
121160786Sps	 */
121260786Sps	pos = search_pos(search_type);
121360786Sps	if (pos == NULL_POSITION)
121460786Sps	{
121560786Sps		/*
121660786Sps		 * Can't find anyplace to start searching from.
121760786Sps		 */
121860786Sps		if (search_type & SRCH_PAST_EOF)
121960786Sps			return (n);
122060786Sps		/* repaint(); -- why was this here? */
122160786Sps		error("Nothing to search", NULL_PARG);
122260786Sps		return (-1);
122360786Sps	}
122460786Sps
122560786Sps	n = search_range(pos, NULL_POSITION, search_type, n, -1,
122660786Sps			&pos, (POSITION*)NULL);
122760786Sps	if (n != 0)
122860786Sps	{
122960786Sps		/*
123060786Sps		 * Search was unsuccessful.
123160786Sps		 */
123260786Sps#if HILITE_SEARCH
123360786Sps		if (hilite_search == OPT_ON && n > 0)
123460786Sps			/*
123560786Sps			 * Redisplay old hilites.
123660786Sps			 */
123760786Sps			repaint_hilite(1);
123860786Sps#endif
123960786Sps		return (n);
124060786Sps	}
124160786Sps
124260786Sps	if (!(search_type & SRCH_NO_MOVE))
124360786Sps	{
124460786Sps		/*
124560786Sps		 * Go to the matching line.
124660786Sps		 */
124760786Sps		jump_loc(pos, jump_sline);
124860786Sps	}
124960786Sps
125060786Sps#if HILITE_SEARCH
125160786Sps	if (hilite_search == OPT_ON)
125260786Sps		/*
125360786Sps		 * Display new hilites in the matching line.
125460786Sps		 */
125560786Sps		repaint_hilite(1);
125660786Sps#endif
125760786Sps	return (0);
125860786Sps}
125960786Sps
126060786Sps
126160786Sps#if HILITE_SEARCH
126260786Sps/*
126360786Sps * Prepare hilites in a given range of the file.
126460786Sps *
126560786Sps * The pair (prep_startpos,prep_endpos) delimits a contiguous region
126660786Sps * of the file that has been "prepared"; that is, scanned for matches for
126760786Sps * the current search pattern, and hilites have been created for such matches.
126860786Sps * If prep_startpos == NULL_POSITION, the prep region is empty.
126960786Sps * If prep_endpos == NULL_POSITION, the prep region extends to EOF.
127060786Sps * prep_hilite asks that the range (spos,epos) be covered by the prep region.
127160786Sps */
127260786Sps	public void
127360786Spsprep_hilite(spos, epos, maxlines)
127460786Sps	POSITION spos;
127560786Sps	POSITION epos;
127660786Sps	int maxlines;
127760786Sps{
127860786Sps	POSITION nprep_startpos = prep_startpos;
127960786Sps	POSITION nprep_endpos = prep_endpos;
128060786Sps	POSITION new_epos;
128160786Sps	POSITION max_epos;
128260786Sps	int result;
128360786Sps	int i;
128460786Sps/*
128560786Sps * Search beyond where we're asked to search, so the prep region covers
128660786Sps * more than we need.  Do one big search instead of a bunch of small ones.
128760786Sps */
128860786Sps#define	SEARCH_MORE (3*size_linebuf)
128960786Sps
129060786Sps	if (!prev_pattern())
129160786Sps		return;
129260786Sps
129360786Sps	/*
129460786Sps	 * If we're limited to a max number of lines, figure out the
129560786Sps	 * file position we should stop at.
129660786Sps	 */
129760786Sps	if (maxlines < 0)
129860786Sps		max_epos = NULL_POSITION;
129960786Sps	else
130060786Sps	{
130160786Sps		max_epos = spos;
130260786Sps		for (i = 0;  i < maxlines;  i++)
130360786Sps			max_epos = forw_raw_line(max_epos, (char **)NULL);
130460786Sps	}
130560786Sps
130660786Sps	/*
130760786Sps	 * Find two ranges:
130860786Sps	 * The range that we need to search (spos,epos); and the range that
130960786Sps	 * the "prep" region will then cover (nprep_startpos,nprep_endpos).
131060786Sps	 */
131160786Sps
131260786Sps	if (prep_startpos == NULL_POSITION ||
131360786Sps	    (epos != NULL_POSITION && epos < prep_startpos) ||
131460786Sps	    spos > prep_endpos)
131560786Sps	{
131660786Sps		/*
131760786Sps		 * New range is not contiguous with old prep region.
131860786Sps		 * Discard the old prep region and start a new one.
131960786Sps		 */
132060786Sps		clr_hilite();
132160786Sps		if (epos != NULL_POSITION)
132260786Sps			epos += SEARCH_MORE;
132360786Sps		nprep_startpos = spos;
132460786Sps	} else
132560786Sps	{
132660786Sps		/*
132760786Sps		 * New range partially or completely overlaps old prep region.
132860786Sps		 */
132960786Sps		if (epos == NULL_POSITION)
133060786Sps		{
133160786Sps			/*
133260786Sps			 * New range goes to end of file.
133360786Sps			 */
133460786Sps			;
133560786Sps		} else if (epos > prep_endpos)
133660786Sps		{
133760786Sps			/*
133860786Sps			 * New range ends after old prep region.
133960786Sps			 * Extend prep region to end at end of new range.
134060786Sps			 */
134160786Sps			epos += SEARCH_MORE;
134260786Sps		} else /* (epos <= prep_endpos) */
134360786Sps		{
134460786Sps			/*
134560786Sps			 * New range ends within old prep region.
134660786Sps			 * Truncate search to end at start of old prep region.
134760786Sps			 */
134860786Sps			epos = prep_startpos;
134960786Sps		}
135060786Sps
135160786Sps		if (spos < prep_startpos)
135260786Sps		{
135360786Sps			/*
135460786Sps			 * New range starts before old prep region.
135560786Sps			 * Extend old prep region backwards to start at
135660786Sps			 * start of new range.
135760786Sps			 */
135860786Sps			if (spos < SEARCH_MORE)
135960786Sps				spos = 0;
136060786Sps			else
136160786Sps				spos -= SEARCH_MORE;
136260786Sps			nprep_startpos = spos;
136360786Sps		} else /* (spos >= prep_startpos) */
136460786Sps		{
136560786Sps			/*
136660786Sps			 * New range starts within or after old prep region.
136760786Sps			 * Trim search to start at end of old prep region.
136860786Sps			 */
136960786Sps			spos = prep_endpos;
137060786Sps		}
137160786Sps	}
137260786Sps
137360786Sps	if (epos != NULL_POSITION && max_epos != NULL_POSITION &&
137460786Sps	    epos > max_epos)
137560786Sps		/*
137660786Sps		 * Don't go past the max position we're allowed.
137760786Sps		 */
137860786Sps		epos = max_epos;
137960786Sps
138060786Sps	if (epos == NULL_POSITION || epos > spos)
138160786Sps	{
138260786Sps		result = search_range(spos, epos, SRCH_FORW|SRCH_FIND_ALL, 0,
138360786Sps				maxlines, (POSITION*)NULL, &new_epos);
138460786Sps		if (result < 0)
138560786Sps			return;
138660786Sps		if (prep_endpos == NULL_POSITION || new_epos > prep_endpos)
138760786Sps			nprep_endpos = new_epos;
138860786Sps	}
138960786Sps	prep_startpos = nprep_startpos;
139060786Sps	prep_endpos = nprep_endpos;
139160786Sps}
139260786Sps#endif
139360786Sps
139460786Sps/*
139560786Sps * Simple pattern matching function.
139660786Sps * It supports no metacharacters like *, etc.
139760786Sps */
139860786Sps	static int
139960786Spsmatch(pattern, buf, pfound, pend)
140060786Sps	char *pattern, *buf;
140160786Sps	char **pfound, **pend;
140260786Sps{
140360786Sps	register char *pp, *lp;
140460786Sps
140560786Sps	for ( ;  *buf != '\0';  buf++)
140660786Sps	{
140760786Sps		for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
140860786Sps			if (*pp == '\0' || *lp == '\0')
140960786Sps				break;
141060786Sps		if (*pp == '\0')
141160786Sps		{
141260786Sps			if (pfound != NULL)
141360786Sps				*pfound = buf;
141460786Sps			if (pend != NULL)
141560786Sps				*pend = lp;
141660786Sps			return (1);
141760786Sps		}
141860786Sps	}
141960786Sps	return (0);
142060786Sps}
142160786Sps
142260786Sps#if HAVE_V8_REGCOMP
142360786Sps/*
142460786Sps * This function is called by the V8 regcomp to report
142560786Sps * errors in regular expressions.
142660786Sps */
142760786Sps	void
142860786Spsregerror(s)
142960786Sps	char *s;
143060786Sps{
143160786Sps	PARG parg;
143260786Sps
143360786Sps	parg.p_string = s;
143460786Sps	error("%s", &parg);
143560786Sps}
143660786Sps#endif
143760786Sps
1438