search.c revision 173685
160812Sps/* $FreeBSD: head/contrib/less/search.c 173685 2007-11-16 22:24:31Z delphij $ */
260786Sps/*
3170259Sdelphij * Copyright (C) 1984-2007  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"
19172471Sdelphij#include "charset.h"
2060786Sps
2160786Sps#define	MINPOS(a,b)	(((a) < (b)) ? (a) : (b))
2260786Sps#define	MAXPOS(a,b)	(((a) > (b)) ? (a) : (b))
2360786Sps
2460786Sps#if HAVE_POSIX_REGCOMP
2560786Sps#include <regex.h>
2660786Sps#ifdef REG_EXTENDED
27170259Sdelphij#define	REGCOMP_FLAG	(less_is_more ? 0 : REG_EXTENDED)
2860786Sps#else
2960786Sps#define	REGCOMP_FLAG	0
3060786Sps#endif
3160786Sps#endif
3260786Sps#if HAVE_PCRE
3360786Sps#include <pcre.h>
3460786Sps#endif
3560786Sps#if HAVE_RE_COMP
3660786Spschar *re_comp();
3760786Spsint re_exec();
3860786Sps#endif
3960786Sps#if HAVE_REGCMP
4060786Spschar *regcmp();
4160786Spschar *regex();
4260786Spsextern char *__loc1;
4360786Sps#endif
4460786Sps#if HAVE_V8_REGCOMP
4560786Sps#include "regexp.h"
4660786Sps#endif
4760786Sps
4860786Spsstatic int match();
4960786Sps
5060786Spsextern int sigs;
5160786Spsextern int how_search;
5260786Spsextern int caseless;
5360786Spsextern int linenums;
5460786Spsextern int sc_height;
5560786Spsextern int jump_sline;
5660786Spsextern int bs_mode;
57170259Sdelphijextern int less_is_more;
58128348Stjrextern int ctldisp;
5963131Spsextern int status_col;
60170259Sdelphijextern void * constant ml_search;
6160786Spsextern POSITION start_attnpos;
6260786Spsextern POSITION end_attnpos;
6360786Sps#if HILITE_SEARCH
6460786Spsextern int hilite_search;
6560786Spsextern int screen_trashed;
6660786Spsextern int size_linebuf;
6760786Spsextern int squished;
6860786Spsextern int can_goto_line;
69173685Sdelphijextern int utf_mode;
7060786Spsstatic int hide_hilite;
71170259Sdelphijstatic int oldbot;
7260786Spsstatic POSITION prep_startpos;
7360786Spsstatic POSITION prep_endpos;
7460786Sps
7560786Spsstruct hilite
7660786Sps{
7760786Sps	struct hilite *hl_next;
7860786Sps	POSITION hl_startpos;
7960786Sps	POSITION hl_endpos;
8060786Sps};
8160786Spsstatic struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION };
8260786Sps#define	hl_first	hl_next
8360786Sps#endif
8460786Sps
8560786Sps/*
8660786Sps * These are the static variables that represent the "remembered"
8760786Sps * search pattern.
8860786Sps */
8960786Sps#if HAVE_POSIX_REGCOMP
9060786Spsstatic regex_t *regpattern = NULL;
9160786Sps#endif
9260786Sps#if HAVE_PCRE
9360786Spspcre *regpattern = NULL;
9460786Sps#endif
9560786Sps#if HAVE_RE_COMP
9660786Spsint re_pattern = 0;
9760786Sps#endif
9860786Sps#if HAVE_REGCMP
9960786Spsstatic char *cpattern = NULL;
10060786Sps#endif
10160786Sps#if HAVE_V8_REGCOMP
10260786Spsstatic struct regexp *regpattern = NULL;
10360786Sps#endif
10460786Sps
10560786Spsstatic int is_caseless;
10660786Spsstatic int is_ucase_pattern;
10760786Spsstatic int last_search_type;
10860786Spsstatic char *last_pattern = NULL;
10960786Sps
11060786Sps#define	CVT_TO_LC	01	/* Convert upper-case to lower-case */
11160786Sps#define	CVT_BS		02	/* Do backspace processing */
11260786Sps#define	CVT_CRLF	04	/* Remove CR after LF */
113128348Stjr#define	CVT_ANSI	010	/* Remove ANSI escape sequences */
11460786Sps
115173685Sdelphij/*
116173685Sdelphij * Get the length of a buffer needed to convert a string.
117173685Sdelphij */
118173685Sdelphij	static int
119173685Sdelphijcvt_length(len, ops)
120173685Sdelphij	int len;
121173685Sdelphij	int ops;
122173685Sdelphij{
123173685Sdelphij	if (utf_mode && (ops & CVT_TO_LC))
124173685Sdelphij		/*
125173685Sdelphij		 * Converting case can cause a UTF-8 string to increase in length.
126173685Sdelphij		 * Multiplying by 3 is the worst case.
127173685Sdelphij		 */
128173685Sdelphij		len *= 3;
129173685Sdelphij	return len+1;
130173685Sdelphij}
131173685Sdelphij
132173685Sdelphij/*
133173685Sdelphij * Convert text.  Perform one or more of these transformations:
134173685Sdelphij */
13560786Sps	static void
136170259Sdelphijcvt_text(odst, osrc, lenp, ops)
13760786Sps	char *odst;
13860786Sps	char *osrc;
139170259Sdelphij	int *lenp;
14060786Sps	int ops;
14160786Sps{
142172471Sdelphij	char *dst;
143172471Sdelphij	char *src;
144170259Sdelphij	register char *src_end;
145172471Sdelphij	LWCHAR ch;
14660786Sps
147170259Sdelphij	if (lenp != NULL)
148170259Sdelphij		src_end = osrc + *lenp;
149170259Sdelphij	else
150170259Sdelphij		src_end = osrc + strlen(osrc);
151170259Sdelphij
152172471Sdelphij	for (src = osrc, dst = odst;  src < src_end;  )
15360786Sps	{
154172471Sdelphij		ch = step_char(&src, +1, src_end);
155172471Sdelphij		if ((ops & CVT_TO_LC) && IS_UPPER(ch))
156172471Sdelphij		{
15760786Sps			/* Convert uppercase to lowercase. */
158172471Sdelphij			put_wchar(&dst, TO_LOWER(ch));
159172471Sdelphij		} else if ((ops & CVT_BS) && ch == '\b' && dst > odst)
160172471Sdelphij		{
161173685Sdelphij			/* Delete backspace and preceding char. */
162172471Sdelphij			do {
163172471Sdelphij				dst--;
164172471Sdelphij			} while (dst > odst &&
165172471Sdelphij				!IS_ASCII_OCTET(*dst) && !IS_UTF8_LEAD(*dst));
166172471Sdelphij		} else if ((ops & CVT_ANSI) && IS_CSI_START(ch))
167128348Stjr		{
168128348Stjr			/* Skip to end of ANSI escape sequence. */
169170259Sdelphij			while (src + 1 != src_end)
170161478Sdelphij				if (!is_ansi_middle(*++src))
171128348Stjr					break;
172128348Stjr		} else
17360786Sps			/* Just copy. */
174172471Sdelphij			put_wchar(&dst, ch);
17560786Sps	}
17660786Sps	if ((ops & CVT_CRLF) && dst > odst && dst[-1] == '\r')
17760786Sps		dst--;
17860786Sps	*dst = '\0';
179170259Sdelphij	if (lenp != NULL)
180170259Sdelphij		*lenp = dst - odst;
18160786Sps}
18260786Sps
18360786Sps/*
184128348Stjr * Determine which conversions to perform.
185128348Stjr */
186128348Stjr	static int
187128348Stjrget_cvt_ops()
188128348Stjr{
189128348Stjr	int ops = 0;
190128348Stjr	if (is_caseless || bs_mode == BS_SPECIAL)
191128348Stjr	{
192128348Stjr		if (is_caseless)
193128348Stjr			ops |= CVT_TO_LC;
194128348Stjr		if (bs_mode == BS_SPECIAL)
195128348Stjr			ops |= CVT_BS;
196128348Stjr		if (bs_mode != BS_CONTROL)
197128348Stjr			ops |= CVT_CRLF;
198128348Stjr	} else if (bs_mode != BS_CONTROL)
199128348Stjr	{
200128348Stjr		ops |= CVT_CRLF;
201128348Stjr	}
202128348Stjr	if (ctldisp == OPT_ONPLUS)
203128348Stjr		ops |= CVT_ANSI;
204128348Stjr	return (ops);
205128348Stjr}
206128348Stjr
207128348Stjr/*
20860786Sps * Are there any uppercase letters in this string?
20960786Sps */
21060786Sps	static int
211172471Sdelphijis_ucase(str)
212172471Sdelphij	char *str;
21360786Sps{
214172471Sdelphij	char *str_end = str + strlen(str);
215172471Sdelphij	LWCHAR ch;
21660786Sps
217172471Sdelphij	while (str < str_end)
218172471Sdelphij	{
219172471Sdelphij		ch = step_char(&str, +1, str_end);
220172471Sdelphij		if (IS_UPPER(ch))
22160786Sps			return (1);
222172471Sdelphij	}
22360786Sps	return (0);
22460786Sps}
22560786Sps
22660786Sps/*
22760786Sps * Is there a previous (remembered) search pattern?
22860786Sps */
22960786Sps	static int
23060786Spsprev_pattern()
23160786Sps{
23260786Sps	if (last_search_type & SRCH_NO_REGEX)
23360786Sps		return (last_pattern != NULL);
23460786Sps#if HAVE_POSIX_REGCOMP
23560786Sps	return (regpattern != NULL);
23660786Sps#endif
23760786Sps#if HAVE_PCRE
23860786Sps	return (regpattern != NULL);
23960786Sps#endif
24060786Sps#if HAVE_RE_COMP
24160786Sps	return (re_pattern != 0);
24260786Sps#endif
24360786Sps#if HAVE_REGCMP
24460786Sps	return (cpattern != NULL);
24560786Sps#endif
24660786Sps#if HAVE_V8_REGCOMP
24760786Sps	return (regpattern != NULL);
24860786Sps#endif
24960786Sps#if NO_REGEX
25060786Sps	return (last_pattern != NULL);
25160786Sps#endif
25260786Sps}
25360786Sps
25460786Sps#if HILITE_SEARCH
25560786Sps/*
25660786Sps * Repaint the hilites currently displayed on the screen.
25760786Sps * Repaint each line which contains highlighted text.
25860786Sps * If on==0, force all hilites off.
25960786Sps */
26060786Sps	public void
26160786Spsrepaint_hilite(on)
26260786Sps	int on;
26360786Sps{
26460786Sps	int slinenum;
26560786Sps	POSITION pos;
26660786Sps	POSITION epos;
26760786Sps	int save_hide_hilite;
26860786Sps
26960786Sps	if (squished)
27060786Sps		repaint();
27160786Sps
27260786Sps	save_hide_hilite = hide_hilite;
27360786Sps	if (!on)
27460786Sps	{
27560786Sps		if (hide_hilite)
27660786Sps			return;
27760786Sps		hide_hilite = 1;
27860786Sps	}
27960786Sps
28060786Sps	if (!can_goto_line)
28160786Sps	{
28260786Sps		repaint();
28360786Sps		hide_hilite = save_hide_hilite;
28460786Sps		return;
28560786Sps	}
28660786Sps
28760786Sps	for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
28860786Sps	{
28960786Sps		pos = position(slinenum);
29060786Sps		if (pos == NULL_POSITION)
29160786Sps			continue;
29260786Sps		epos = position(slinenum+1);
293161478Sdelphij#if 0
29460786Sps		/*
29560786Sps		 * If any character in the line is highlighted,
29660786Sps		 * repaint the line.
297161478Sdelphij		 *
298161478Sdelphij		 * {{ This doesn't work -- if line is drawn with highlights
299161478Sdelphij		 * which should be erased (e.g. toggle -i with status column),
300161478Sdelphij		 * we must redraw the line even if it has no highlights.
301161478Sdelphij		 * For now, just repaint every line. }}
30260786Sps		 */
303161478Sdelphij		if (is_hilited(pos, epos, 1, NULL))
304161478Sdelphij#endif
30560786Sps		{
30660786Sps			(void) forw_line(pos);
30760786Sps			goto_line(slinenum);
30860786Sps			put_line();
30960786Sps		}
31060786Sps	}
311170259Sdelphij	if (!oldbot)
312170259Sdelphij		lower_left();
31360786Sps	hide_hilite = save_hide_hilite;
31460786Sps}
31560786Sps
31660786Sps/*
31760786Sps * Clear the attn hilite.
31860786Sps */
31960786Sps	public void
32060786Spsclear_attn()
32160786Sps{
32260786Sps	int slinenum;
32360786Sps	POSITION old_start_attnpos;
32460786Sps	POSITION old_end_attnpos;
32560786Sps	POSITION pos;
32660786Sps	POSITION epos;
327170898Sdelphij	int moved = 0;
32860786Sps
32960786Sps	if (start_attnpos == NULL_POSITION)
33060786Sps		return;
33160786Sps	old_start_attnpos = start_attnpos;
33260786Sps	old_end_attnpos = end_attnpos;
33360786Sps	start_attnpos = end_attnpos = NULL_POSITION;
33460786Sps
33560786Sps	if (!can_goto_line)
33660786Sps	{
33760786Sps		repaint();
33860786Sps		return;
33960786Sps	}
34060786Sps	if (squished)
34160786Sps		repaint();
34260786Sps
34360786Sps	for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
34460786Sps	{
34560786Sps		pos = position(slinenum);
34660786Sps		if (pos == NULL_POSITION)
34760786Sps			continue;
34860786Sps		epos = position(slinenum+1);
34960786Sps		if (pos < old_end_attnpos &&
35060786Sps		     (epos == NULL_POSITION || epos > old_start_attnpos))
35160786Sps		{
35260786Sps			(void) forw_line(pos);
35360786Sps			goto_line(slinenum);
35460786Sps			put_line();
355170898Sdelphij			moved = 1;
35660786Sps		}
35760786Sps	}
358170898Sdelphij	if (moved)
359170898Sdelphij		lower_left();
36060786Sps}
36160786Sps#endif
36260786Sps
36360786Sps/*
36460786Sps * Hide search string highlighting.
36560786Sps */
36660786Sps	public void
36760786Spsundo_search()
36860786Sps{
36960786Sps	if (!prev_pattern())
37060786Sps	{
37160786Sps		error("No previous regular expression", NULL_PARG);
37260786Sps		return;
37360786Sps	}
37460786Sps#if HILITE_SEARCH
37560786Sps	hide_hilite = !hide_hilite;
37660786Sps	repaint_hilite(1);
37760786Sps#endif
37860786Sps}
37960786Sps
38060786Sps/*
38160786Sps * Compile a search pattern, for future use by match_pattern.
38260786Sps */
38360786Sps	static int
384173685Sdelphijcompile_pattern2(pattern, search_type)
38560786Sps	char *pattern;
38660786Sps	int search_type;
38760786Sps{
38860786Sps	if ((search_type & SRCH_NO_REGEX) == 0)
38960786Sps	{
39060786Sps#if HAVE_POSIX_REGCOMP
39160786Sps		regex_t *s = (regex_t *) ecalloc(1, sizeof(regex_t));
39260786Sps		if (regcomp(s, pattern, REGCOMP_FLAG))
39360786Sps		{
39460786Sps			free(s);
39560786Sps			error("Invalid pattern", NULL_PARG);
39660786Sps			return (-1);
39760786Sps		}
39860786Sps		if (regpattern != NULL)
39960786Sps			regfree(regpattern);
40060786Sps		regpattern = s;
40160786Sps#endif
40260786Sps#if HAVE_PCRE
40360786Sps		pcre *comp;
40460786Sps		const char *errstring;
40560786Sps		int erroffset;
40660786Sps		PARG parg;
40760786Sps		comp = pcre_compile(pattern, 0,
40860786Sps				&errstring, &erroffset, NULL);
40960786Sps		if (comp == NULL)
41060786Sps		{
41160786Sps			parg.p_string = (char *) errstring;
41260786Sps			error("%s", &parg);
41360786Sps			return (-1);
41460786Sps		}
41560786Sps		regpattern = comp;
41660786Sps#endif
41760786Sps#if HAVE_RE_COMP
41860786Sps		PARG parg;
41960786Sps		if ((parg.p_string = re_comp(pattern)) != NULL)
42060786Sps		{
42160786Sps			error("%s", &parg);
42260786Sps			return (-1);
42360786Sps		}
42460786Sps		re_pattern = 1;
42560786Sps#endif
42660786Sps#if HAVE_REGCMP
42760786Sps		char *s;
42860786Sps		if ((s = regcmp(pattern, 0)) == NULL)
42960786Sps		{
43060786Sps			error("Invalid pattern", NULL_PARG);
43160786Sps			return (-1);
43260786Sps		}
43360786Sps		if (cpattern != NULL)
43460786Sps			free(cpattern);
43560786Sps		cpattern = s;
43660786Sps#endif
43760786Sps#if HAVE_V8_REGCOMP
43860786Sps		struct regexp *s;
43960786Sps		if ((s = regcomp(pattern)) == NULL)
44060786Sps		{
44160786Sps			/*
44260786Sps			 * regcomp has already printed an error message
44360786Sps			 * via regerror().
44460786Sps			 */
44560786Sps			return (-1);
44660786Sps		}
44760786Sps		if (regpattern != NULL)
44860786Sps			free(regpattern);
44960786Sps		regpattern = s;
45060786Sps#endif
45160786Sps	}
45260786Sps
45360786Sps	if (last_pattern != NULL)
45460786Sps		free(last_pattern);
45560786Sps	last_pattern = (char *) calloc(1, strlen(pattern)+1);
45660786Sps	if (last_pattern != NULL)
45760786Sps		strcpy(last_pattern, pattern);
45860786Sps
45960786Sps	last_search_type = search_type;
46060786Sps	return (0);
46160786Sps}
46260786Sps
46360786Sps/*
464173685Sdelphij * Like compile_pattern, but convert the pattern to lowercase if necessary.
465173685Sdelphij */
466173685Sdelphij	static int
467173685Sdelphijcompile_pattern(pattern, search_type)
468173685Sdelphij	char *pattern;
469173685Sdelphij	int search_type;
470173685Sdelphij{
471173685Sdelphij	char *cvt_pattern;
472173685Sdelphij	int result;
473173685Sdelphij
474173685Sdelphij	if (caseless != OPT_ONPLUS)
475173685Sdelphij		cvt_pattern = pattern;
476173685Sdelphij	else
477173685Sdelphij	{
478173685Sdelphij		cvt_pattern = (char*) ecalloc(1, cvt_length(strlen(pattern), CVT_TO_LC));
479173685Sdelphij		cvt_text(cvt_pattern, pattern, (int *)NULL, CVT_TO_LC);
480173685Sdelphij	}
481173685Sdelphij	result = compile_pattern2(cvt_pattern, search_type);
482173685Sdelphij	if (cvt_pattern != pattern)
483173685Sdelphij		free(cvt_pattern);
484173685Sdelphij	return (result);
485173685Sdelphij}
486173685Sdelphij
487173685Sdelphij/*
48860786Sps * Forget that we have a compiled pattern.
48960786Sps */
49060786Sps	static void
49160786Spsuncompile_pattern()
49260786Sps{
49360786Sps#if HAVE_POSIX_REGCOMP
49460786Sps	if (regpattern != NULL)
49560786Sps		regfree(regpattern);
49660786Sps	regpattern = NULL;
49760786Sps#endif
49860786Sps#if HAVE_PCRE
49960786Sps	if (regpattern != NULL)
50060786Sps		pcre_free(regpattern);
50160786Sps	regpattern = NULL;
50260786Sps#endif
50360786Sps#if HAVE_RE_COMP
50460786Sps	re_pattern = 0;
50560786Sps#endif
50660786Sps#if HAVE_REGCMP
50760786Sps	if (cpattern != NULL)
50860786Sps		free(cpattern);
50960786Sps	cpattern = NULL;
51060786Sps#endif
51160786Sps#if HAVE_V8_REGCOMP
51260786Sps	if (regpattern != NULL)
51360786Sps		free(regpattern);
51460786Sps	regpattern = NULL;
51560786Sps#endif
51660786Sps	last_pattern = NULL;
51760786Sps}
51860786Sps
51960786Sps/*
52060786Sps * Perform a pattern match with the previously compiled pattern.
52160786Sps * Set sp and ep to the start and end of the matched string.
52260786Sps */
52360786Sps	static int
524170259Sdelphijmatch_pattern(line, line_len, sp, ep, notbol)
52560786Sps	char *line;
526170259Sdelphij	int line_len;
52760786Sps	char **sp;
52860786Sps	char **ep;
52960786Sps	int notbol;
53060786Sps{
53160786Sps	int matched;
53260786Sps
53360786Sps	if (last_search_type & SRCH_NO_REGEX)
534170259Sdelphij		return (match(last_pattern, strlen(last_pattern), line, line_len, sp, ep));
53560786Sps
53660786Sps#if HAVE_POSIX_REGCOMP
53760786Sps	{
53860786Sps		regmatch_t rm;
53960786Sps		int flags = (notbol) ? REG_NOTBOL : 0;
54060786Sps		matched = !regexec(regpattern, line, 1, &rm, flags);
54160786Sps		if (!matched)
54260786Sps			return (0);
54360786Sps#ifndef __WATCOMC__
54460786Sps		*sp = line + rm.rm_so;
54560786Sps		*ep = line + rm.rm_eo;
54660786Sps#else
54760786Sps		*sp = rm.rm_sp;
54860786Sps		*ep = rm.rm_ep;
54960786Sps#endif
55060786Sps	}
55160786Sps#endif
55260786Sps#if HAVE_PCRE
55360786Sps	{
55460786Sps		int flags = (notbol) ? PCRE_NOTBOL : 0;
55560786Sps		int ovector[3];
556170259Sdelphij		matched = pcre_exec(regpattern, NULL, line, line_len,
55760786Sps			0, flags, ovector, 3) >= 0;
55860786Sps		if (!matched)
55960786Sps			return (0);
56060786Sps		*sp = line + ovector[0];
56160786Sps		*ep = line + ovector[1];
56260786Sps	}
56360786Sps#endif
56460786Sps#if HAVE_RE_COMP
56560786Sps	matched = (re_exec(line) == 1);
56660786Sps	/*
56760786Sps	 * re_exec doesn't seem to provide a way to get the matched string.
56860786Sps	 */
56960786Sps	*sp = *ep = NULL;
57060786Sps#endif
57160786Sps#if HAVE_REGCMP
57260786Sps	*ep = regex(cpattern, line);
57360786Sps	matched = (*ep != NULL);
57460786Sps	if (!matched)
57560786Sps		return (0);
57660786Sps	*sp = __loc1;
57760786Sps#endif
57860786Sps#if HAVE_V8_REGCOMP
57960786Sps#if HAVE_REGEXEC2
58060786Sps	matched = regexec2(regpattern, line, notbol);
58160786Sps#else
58260786Sps	matched = regexec(regpattern, line);
58360786Sps#endif
58460786Sps	if (!matched)
58560786Sps		return (0);
58660786Sps	*sp = regpattern->startp[0];
58760786Sps	*ep = regpattern->endp[0];
58860786Sps#endif
58960786Sps#if NO_REGEX
590170259Sdelphij	matched = match(last_pattern, strlen(last_pattern), line, line_len, sp, ep);
59160786Sps#endif
59260786Sps	return (matched);
59360786Sps}
59460786Sps
59560786Sps#if HILITE_SEARCH
59660786Sps/*
59760786Sps * Clear the hilite list.
59860786Sps */
59960786Sps	public void
60060786Spsclr_hilite()
60160786Sps{
60260786Sps	struct hilite *hl;
60360786Sps	struct hilite *nexthl;
60460786Sps
60560786Sps	for (hl = hilite_anchor.hl_first;  hl != NULL;  hl = nexthl)
60660786Sps	{
60760786Sps		nexthl = hl->hl_next;
60860786Sps		free((void*)hl);
60960786Sps	}
61060786Sps	hilite_anchor.hl_first = NULL;
61160786Sps	prep_startpos = prep_endpos = NULL_POSITION;
61260786Sps}
61360786Sps
61460786Sps/*
61560786Sps * Should any characters in a specified range be highlighted?
616161478Sdelphij */
617161478Sdelphij	static int
618161478Sdelphijis_hilited_range(pos, epos)
619161478Sdelphij	POSITION pos;
620161478Sdelphij	POSITION epos;
621161478Sdelphij{
622161478Sdelphij	struct hilite *hl;
623161478Sdelphij
624161478Sdelphij	/*
625161478Sdelphij	 * Look at each highlight and see if any part of it falls in the range.
626161478Sdelphij	 */
627161478Sdelphij	for (hl = hilite_anchor.hl_first;  hl != NULL;  hl = hl->hl_next)
628161478Sdelphij	{
629161478Sdelphij		if (hl->hl_endpos > pos &&
630161478Sdelphij		    (epos == NULL_POSITION || epos > hl->hl_startpos))
631161478Sdelphij			return (1);
632161478Sdelphij	}
633161478Sdelphij	return (0);
634161478Sdelphij}
635161478Sdelphij
636161478Sdelphij/*
637161478Sdelphij * Should any characters in a specified range be highlighted?
63860786Sps * If nohide is nonzero, don't consider hide_hilite.
63960786Sps */
64060786Sps	public int
641161478Sdelphijis_hilited(pos, epos, nohide, p_matches)
64260786Sps	POSITION pos;
64360786Sps	POSITION epos;
64460786Sps	int nohide;
645161478Sdelphij	int *p_matches;
64660786Sps{
647161478Sdelphij	int match;
64860786Sps
649161478Sdelphij	if (p_matches != NULL)
650161478Sdelphij		*p_matches = 0;
651161478Sdelphij
65263131Sps	if (!status_col &&
65363131Sps	    start_attnpos != NULL_POSITION &&
65460786Sps	    pos < end_attnpos &&
65560786Sps	     (epos == NULL_POSITION || epos > start_attnpos))
65660786Sps		/*
65760786Sps		 * The attn line overlaps this range.
65860786Sps		 */
65960786Sps		return (1);
66060786Sps
661161478Sdelphij	match = is_hilited_range(pos, epos);
662161478Sdelphij	if (!match)
663161478Sdelphij		return (0);
664161478Sdelphij
665161478Sdelphij	if (p_matches != NULL)
666161478Sdelphij		/*
667161478Sdelphij		 * Report matches, even if we're hiding highlights.
668161478Sdelphij		 */
669161478Sdelphij		*p_matches = 1;
670161478Sdelphij
67160786Sps	if (hilite_search == 0)
67260786Sps		/*
67360786Sps		 * Not doing highlighting.
67460786Sps		 */
67560786Sps		return (0);
67660786Sps
67760786Sps	if (!nohide && hide_hilite)
67860786Sps		/*
67960786Sps		 * Highlighting is hidden.
68060786Sps		 */
68160786Sps		return (0);
68260786Sps
683161478Sdelphij	return (1);
68460786Sps}
68560786Sps
68660786Sps/*
68760786Sps * Add a new hilite to a hilite list.
68860786Sps */
68960786Sps	static void
69060786Spsadd_hilite(anchor, hl)
69160786Sps	struct hilite *anchor;
69260786Sps	struct hilite *hl;
69360786Sps{
69460786Sps	struct hilite *ihl;
69560786Sps
69660786Sps	/*
69760786Sps	 * Hilites are sorted in the list; find where new one belongs.
69860786Sps	 * Insert new one after ihl.
69960786Sps	 */
70060786Sps	for (ihl = anchor;  ihl->hl_next != NULL;  ihl = ihl->hl_next)
70160786Sps	{
70260786Sps		if (ihl->hl_next->hl_startpos > hl->hl_startpos)
70360786Sps			break;
70460786Sps	}
70560786Sps
70660786Sps	/*
70760786Sps	 * Truncate hilite so it doesn't overlap any existing ones
70860786Sps	 * above and below it.
70960786Sps	 */
71060786Sps	if (ihl != anchor)
71160786Sps		hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos);
71260786Sps	if (ihl->hl_next != NULL)
71360786Sps		hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos);
71460786Sps	if (hl->hl_startpos >= hl->hl_endpos)
71560786Sps	{
71660786Sps		/*
71760786Sps		 * Hilite was truncated out of existence.
71860786Sps		 */
71960786Sps		free(hl);
72060786Sps		return;
72160786Sps	}
72260786Sps	hl->hl_next = ihl->hl_next;
72360786Sps	ihl->hl_next = hl;
72460786Sps}
72560786Sps
72660786Sps/*
727173685Sdelphij * Adjust hl_startpos & hl_endpos to account for processing by cvt_text.
72860786Sps */
72960786Sps	static void
730128348Stjradj_hilite(anchor, linepos, cvt_ops)
73160786Sps	struct hilite *anchor;
73260786Sps	POSITION linepos;
733128348Stjr	int cvt_ops;
73460786Sps{
73560786Sps	char *line;
736173685Sdelphij	char *oline;
737170259Sdelphij	int line_len;
738170259Sdelphij	char *line_end;
73960786Sps	struct hilite *hl;
74060786Sps	int checkstart;
74160786Sps	POSITION opos;
74260786Sps	POSITION npos;
743173685Sdelphij	LWCHAR ch;
744173685Sdelphij	int ncwidth;
74560786Sps
74660786Sps	/*
74760786Sps	 * The line was already scanned and hilites were added (in hilite_line).
74860786Sps	 * But it was assumed that each char position in the line
74960786Sps	 * correponds to one char position in the file.
750173685Sdelphij	 * This may not be true if cvt_text modified the line.
75160786Sps	 * Get the raw line again.  Look at each character.
75260786Sps	 */
753170259Sdelphij	(void) forw_raw_line(linepos, &line, &line_len);
754170259Sdelphij	line_end = line + line_len;
75560786Sps	opos = npos = linepos;
75660786Sps	hl = anchor->hl_first;
75760786Sps	checkstart = TRUE;
75860786Sps	while (hl != NULL)
75960786Sps	{
76060786Sps		/*
76160786Sps		 * See if we need to adjust the current hl_startpos or
76260786Sps		 * hl_endpos.  After adjusting startpos[i], move to endpos[i].
76360786Sps		 * After adjusting endpos[i], move to startpos[i+1].
76460786Sps		 * The hilite list must be sorted thus:
76560786Sps		 * startpos[0] < endpos[0] <= startpos[1] < endpos[1] <= etc.
76660786Sps		 */
76760786Sps		if (checkstart && hl->hl_startpos == opos)
76860786Sps		{
76960786Sps			hl->hl_startpos = npos;
77060786Sps			checkstart = FALSE;
77160786Sps			continue; /* {{ not really necessary }} */
77260786Sps		} else if (!checkstart && hl->hl_endpos == opos)
77360786Sps		{
77460786Sps			hl->hl_endpos = npos;
77560786Sps			checkstart = TRUE;
77660786Sps			hl = hl->hl_next;
77760786Sps			continue; /* {{ necessary }} */
77860786Sps		}
779170259Sdelphij		if (line == line_end)
78060786Sps			break;
781173685Sdelphij
782173685Sdelphij		/* Get the next char from the line. */
783173685Sdelphij		oline = line;
784173685Sdelphij		ch = step_char(&line, +1, line_end);
785173685Sdelphij		ncwidth = line - oline;
786173685Sdelphij		npos += ncwidth;
787173685Sdelphij
788173685Sdelphij		/* Figure out how this char was processed by cvt_text. */
789173685Sdelphij		if ((cvt_ops & CVT_BS) && ch == '\b')
79060786Sps		{
791173685Sdelphij			/* Skip the backspace and the following char. */
792173685Sdelphij			oline = line;
793173685Sdelphij			ch = step_char(&line, +1, line_end);
794173685Sdelphij			ncwidth = line - oline;
795173685Sdelphij			npos += ncwidth;
796173685Sdelphij		} else if ((cvt_ops & CVT_TO_LC) && IS_UPPER(ch))
797173685Sdelphij		{
798173685Sdelphij			/* Converted uppercase to lower.
799173685Sdelphij			 * Note that this may have changed the number of bytes
800173685Sdelphij			 * that the character occupies. */
801173685Sdelphij			char dbuf[6];
802173685Sdelphij			char *dst = dbuf;
803173685Sdelphij			put_wchar(&dst, TO_LOWER(ch));
804173685Sdelphij			opos += dst - dbuf;
805173685Sdelphij		} else if ((cvt_ops & CVT_ANSI) && IS_CSI_START(ch))
806173685Sdelphij		{
807173685Sdelphij			/* Skip to end of ANSI escape sequence. */
808173685Sdelphij			while (line < line_end)
809128348Stjr			{
810161478Sdelphij				npos++;
811173685Sdelphij				if (!is_ansi_middle(*++line))
812161478Sdelphij					break;
813128348Stjr			}
814173685Sdelphij		} else
815173685Sdelphij		{
816173685Sdelphij			/* Ordinary unprocessed character. */
817173685Sdelphij			opos += ncwidth;
81860786Sps		}
81960786Sps	}
82060786Sps}
82160786Sps
82260786Sps/*
82360786Sps * Make a hilite for each string in a physical line which matches
82460786Sps * the current pattern.
82560786Sps * sp,ep delimit the first match already found.
82660786Sps */
82760786Sps	static void
828170259Sdelphijhilite_line(linepos, line, line_len, sp, ep, cvt_ops)
82960786Sps	POSITION linepos;
83060786Sps	char *line;
831170259Sdelphij	int line_len;
83260786Sps	char *sp;
83360786Sps	char *ep;
834128348Stjr	int cvt_ops;
83560786Sps{
83660786Sps	char *searchp;
837170259Sdelphij	char *line_end = line + line_len;
83860786Sps	struct hilite *hl;
83960786Sps	struct hilite hilites;
84060786Sps
84160786Sps	if (sp == NULL || ep == NULL)
84260786Sps		return;
84360786Sps	/*
84460786Sps	 * sp and ep delimit the first match in the line.
84560786Sps	 * Mark the corresponding file positions, then
84660786Sps	 * look for further matches and mark them.
84760786Sps	 * {{ This technique, of calling match_pattern on subsequent
84860786Sps	 *    substrings of the line, may mark more than is correct
84960786Sps	 *    if the pattern starts with "^".  This bug is fixed
85060786Sps	 *    for those regex functions that accept a notbol parameter
851170259Sdelphij	 *    (currently POSIX, PCRE and V8-with-regexec2). }}
85260786Sps	 */
85360786Sps	searchp = line;
85460786Sps	/*
85560786Sps	 * Put the hilites into a temporary list until they're adjusted.
85660786Sps	 */
85760786Sps	hilites.hl_first = NULL;
85860786Sps	do {
85960786Sps		if (ep > sp)
86060786Sps		{
86160786Sps			/*
86260786Sps			 * Assume that each char position in the "line"
86360786Sps			 * buffer corresponds to one char position in the file.
86460786Sps			 * This is not quite true; we need to adjust later.
86560786Sps			 */
86660786Sps			hl = (struct hilite *) ecalloc(1, sizeof(struct hilite));
86760786Sps			hl->hl_startpos = linepos + (sp-line);
86860786Sps			hl->hl_endpos = linepos + (ep-line);
86960786Sps			add_hilite(&hilites, hl);
87060786Sps		}
87160786Sps		/*
87260786Sps		 * If we matched more than zero characters,
87360786Sps		 * move to the first char after the string we matched.
87460786Sps		 * If we matched zero, just move to the next char.
87560786Sps		 */
87660786Sps		if (ep > searchp)
87760786Sps			searchp = ep;
878170259Sdelphij		else if (searchp != line_end)
87960786Sps			searchp++;
88060786Sps		else /* end of line */
88160786Sps			break;
882170259Sdelphij	} while (match_pattern(searchp, line_end - searchp, &sp, &ep, 1));
88360786Sps
88460786Sps	/*
885128348Stjr	 * If there were backspaces in the original line, they
886128348Stjr	 * were removed, and hl_startpos/hl_endpos are not correct.
887128348Stjr	 * {{ This is very ugly. }}
888128348Stjr	 */
889128348Stjr	adj_hilite(&hilites, linepos, cvt_ops);
890128348Stjr
891128348Stjr	/*
89260786Sps	 * Now put the hilites into the real list.
89360786Sps	 */
89460786Sps	while ((hl = hilites.hl_next) != NULL)
89560786Sps	{
89660786Sps		hilites.hl_next = hl->hl_next;
89760786Sps		add_hilite(&hilite_anchor, hl);
89860786Sps	}
89960786Sps}
90060786Sps#endif
90160786Sps
90260786Sps/*
90360786Sps * Change the caseless-ness of searches.
90460786Sps * Updates the internal search state to reflect a change in the -i flag.
90560786Sps */
90660786Sps	public void
90760786Spschg_caseless()
90860786Sps{
90960786Sps	if (!is_ucase_pattern)
91060786Sps		/*
91160786Sps		 * Pattern did not have uppercase.
91260786Sps		 * Just set the search caselessness to the global caselessness.
91360786Sps		 */
91460786Sps		is_caseless = caseless;
91560786Sps	else
91660786Sps		/*
91760786Sps		 * Pattern did have uppercase.
91860786Sps		 * Discard the pattern; we can't change search caselessness now.
91960786Sps		 */
92060786Sps		uncompile_pattern();
92160786Sps}
92260786Sps
92360786Sps#if HILITE_SEARCH
92460786Sps/*
92560786Sps * Find matching text which is currently on screen and highlight it.
92660786Sps */
92760786Sps	static void
92860786Spshilite_screen()
92960786Sps{
93060786Sps	struct scrpos scrpos;
93160786Sps
93260786Sps	get_scrpos(&scrpos);
93360786Sps	if (scrpos.pos == NULL_POSITION)
93460786Sps		return;
93560786Sps	prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1);
93660786Sps	repaint_hilite(1);
93760786Sps}
93860786Sps
93960786Sps/*
94060786Sps * Change highlighting parameters.
94160786Sps */
94260786Sps	public void
94360786Spschg_hilite()
94460786Sps{
94560786Sps	/*
94660786Sps	 * Erase any highlights currently on screen.
94760786Sps	 */
94860786Sps	clr_hilite();
94960786Sps	hide_hilite = 0;
95060786Sps
95160786Sps	if (hilite_search == OPT_ONPLUS)
95260786Sps		/*
95360786Sps		 * Display highlights.
95460786Sps		 */
95560786Sps		hilite_screen();
95660786Sps}
95760786Sps#endif
95860786Sps
95960786Sps/*
96060786Sps * Figure out where to start a search.
96160786Sps */
96260786Sps	static POSITION
96360786Spssearch_pos(search_type)
96460786Sps	int search_type;
96560786Sps{
96660786Sps	POSITION pos;
96760786Sps	int linenum;
96860786Sps
96960786Sps	if (empty_screen())
97060786Sps	{
97160786Sps		/*
97260786Sps		 * Start at the beginning (or end) of the file.
97360786Sps		 * The empty_screen() case is mainly for
97460786Sps		 * command line initiated searches;
97560786Sps		 * for example, "+/xyz" on the command line.
97660786Sps		 * Also for multi-file (SRCH_PAST_EOF) searches.
97760786Sps		 */
97860786Sps		if (search_type & SRCH_FORW)
97960786Sps		{
98060786Sps			return (ch_zero());
98160786Sps		} else
98260786Sps		{
98360786Sps			pos = ch_length();
98460786Sps			if (pos == NULL_POSITION)
98560786Sps			{
98660786Sps				(void) ch_end_seek();
98760786Sps				pos = ch_length();
98860786Sps			}
98960786Sps			return (pos);
99060786Sps		}
99160786Sps	}
99260786Sps	if (how_search)
99360786Sps	{
99460786Sps		/*
99560786Sps		 * Search does not include current screen.
99660786Sps		 */
99760786Sps		if (search_type & SRCH_FORW)
99860786Sps			linenum = BOTTOM_PLUS_ONE;
99960786Sps		else
100060786Sps			linenum = TOP;
100160786Sps		pos = position(linenum);
100260786Sps	} else
100360786Sps	{
100460786Sps		/*
100560786Sps		 * Search includes current screen.
100660786Sps		 * It starts at the jump target (if searching backwards),
100760786Sps		 * or at the jump target plus one (if forwards).
100860786Sps		 */
100960786Sps		linenum = adjsline(jump_sline);
101060786Sps		pos = position(linenum);
101160786Sps		if (search_type & SRCH_FORW)
101260786Sps		{
1013170259Sdelphij			pos = forw_raw_line(pos, (char **)NULL, (int *)NULL);
101460786Sps			while (pos == NULL_POSITION)
101560786Sps			{
101660786Sps				if (++linenum >= sc_height)
101760786Sps					break;
101860786Sps				pos = position(linenum);
101960786Sps			}
102060786Sps		} else
102160786Sps		{
102260786Sps			while (pos == NULL_POSITION)
102360786Sps			{
102460786Sps				if (--linenum < 0)
102560786Sps					break;
102660786Sps				pos = position(linenum);
102760786Sps			}
102860786Sps		}
102960786Sps	}
103060786Sps	return (pos);
103160786Sps}
103260786Sps
103360786Sps/*
103460786Sps * Search a subset of the file, specified by start/end position.
103560786Sps */
103660786Sps	static int
103760786Spssearch_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
103860786Sps	POSITION pos;
103960786Sps	POSITION endpos;
104060786Sps	int search_type;
104160786Sps	int matches;
104260786Sps	int maxlines;
104360786Sps	POSITION *plinepos;
104460786Sps	POSITION *pendpos;
104560786Sps{
104660786Sps	char *line;
1047173685Sdelphij	char *cline;
1048170259Sdelphij	int line_len;
1049128348Stjr	LINENUM linenum;
105060786Sps	char *sp, *ep;
105160786Sps	int line_match;
1052128348Stjr	int cvt_ops;
105360786Sps	POSITION linepos, oldpos;
105460786Sps
105560786Sps	linenum = find_linenum(pos);
105660786Sps	oldpos = pos;
105760786Sps	for (;;)
105860786Sps	{
105960786Sps		/*
106060786Sps		 * Get lines until we find a matching one or until
106160786Sps		 * we hit end-of-file (or beginning-of-file if we're
106260786Sps		 * going backwards), or until we hit the end position.
106360786Sps		 */
106460786Sps		if (ABORT_SIGS())
106560786Sps		{
106660786Sps			/*
106760786Sps			 * A signal aborts the search.
106860786Sps			 */
106960786Sps			return (-1);
107060786Sps		}
107160786Sps
107260786Sps		if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0)
107360786Sps		{
107460786Sps			/*
107560786Sps			 * Reached end position without a match.
107660786Sps			 */
107760786Sps			if (pendpos != NULL)
107860786Sps				*pendpos = pos;
107960786Sps			return (matches);
108060786Sps		}
108160786Sps		if (maxlines > 0)
108260786Sps			maxlines--;
108360786Sps
108460786Sps		if (search_type & SRCH_FORW)
108560786Sps		{
108660786Sps			/*
108760786Sps			 * Read the next line, and save the
108860786Sps			 * starting position of that line in linepos.
108960786Sps			 */
109060786Sps			linepos = pos;
1091170259Sdelphij			pos = forw_raw_line(pos, &line, &line_len);
109260786Sps			if (linenum != 0)
109360786Sps				linenum++;
109460786Sps		} else
109560786Sps		{
109660786Sps			/*
109760786Sps			 * Read the previous line and save the
109860786Sps			 * starting position of that line in linepos.
109960786Sps			 */
1100170259Sdelphij			pos = back_raw_line(pos, &line, &line_len);
110160786Sps			linepos = pos;
110260786Sps			if (linenum != 0)
110360786Sps				linenum--;
110460786Sps		}
110560786Sps
110660786Sps		if (pos == NULL_POSITION)
110760786Sps		{
110860786Sps			/*
110960786Sps			 * Reached EOF/BOF without a match.
111060786Sps			 */
111160786Sps			if (pendpos != NULL)
111260786Sps				*pendpos = oldpos;
111360786Sps			return (matches);
111460786Sps		}
111560786Sps
111660786Sps		/*
111760786Sps		 * If we're using line numbers, we might as well
111860786Sps		 * remember the information we have now (the position
111960786Sps		 * and line number of the current line).
112060786Sps		 * Don't do it for every line because it slows down
112160786Sps		 * the search.  Remember the line number only if
112260786Sps		 * we're "far" from the last place we remembered it.
112360786Sps		 */
112460786Sps		if (linenums && abs((int)(pos - oldpos)) > 1024)
112560786Sps			add_lnum(linenum, pos);
112660786Sps		oldpos = pos;
112760786Sps
112860786Sps		/*
112960786Sps		 * If it's a caseless search, convert the line to lowercase.
113060786Sps		 * If we're doing backspace processing, delete backspaces.
113160786Sps		 */
1132128348Stjr		cvt_ops = get_cvt_ops();
1133173685Sdelphij		cline = calloc(1, cvt_length(line_len, cvt_ops));
1134173685Sdelphij		cvt_text(cline, line, &line_len, cvt_ops);
113560786Sps
113660786Sps		/*
113760786Sps		 * Test the next line to see if we have a match.
113860786Sps		 * We are successful if we either want a match and got one,
113960786Sps		 * or if we want a non-match and got one.
114060786Sps		 */
1141173685Sdelphij		line_match = match_pattern(cline, line_len, &sp, &ep, 0);
114260786Sps		line_match = (!(search_type & SRCH_NO_MATCH) && line_match) ||
114360786Sps				((search_type & SRCH_NO_MATCH) && !line_match);
114460786Sps		if (!line_match)
1145173685Sdelphij		{
1146173685Sdelphij			free(cline);
114760786Sps			continue;
1148173685Sdelphij		}
114960786Sps		/*
115060786Sps		 * Got a match.
115160786Sps		 */
115260786Sps		if (search_type & SRCH_FIND_ALL)
115360786Sps		{
115460786Sps#if HILITE_SEARCH
115560786Sps			/*
115660786Sps			 * We are supposed to find all matches in the range.
115760786Sps			 * Just add the matches in this line to the
115860786Sps			 * hilite list and keep searching.
115960786Sps			 */
116060786Sps			if (line_match)
1161173685Sdelphij				hilite_line(linepos, cline, line_len, sp, ep, cvt_ops);
116260786Sps#endif
1163173685Sdelphij			free(cline);
116460786Sps		} else if (--matches <= 0)
116560786Sps		{
116660786Sps			/*
116760786Sps			 * Found the one match we're looking for.
116860786Sps			 * Return it.
116960786Sps			 */
117060786Sps#if HILITE_SEARCH
1171161478Sdelphij			if (hilite_search == OPT_ON)
117260786Sps			{
117360786Sps				/*
117460786Sps				 * Clear the hilite list and add only
117560786Sps				 * the matches in this one line.
117660786Sps				 */
117760786Sps				clr_hilite();
117860786Sps				if (line_match)
1179173685Sdelphij					hilite_line(linepos, cline, line_len, sp, ep, cvt_ops);
118060786Sps			}
118160786Sps#endif
1182173685Sdelphij			free(cline);
118360786Sps			if (plinepos != NULL)
118460786Sps				*plinepos = linepos;
118560786Sps			return (0);
118660786Sps		}
118760786Sps	}
118860786Sps}
118960786Sps
1190170259Sdelphij /*
1191170259Sdelphij * search for a pattern in history. If found, compile that pattern.
1192170259Sdelphij */
1193170259Sdelphij	static int
1194170259Sdelphijhist_pattern(search_type)
1195170259Sdelphij	int search_type;
1196170259Sdelphij{
1197170259Sdelphij#if CMD_HISTORY
1198170259Sdelphij	char *pattern;
1199170259Sdelphij
1200170259Sdelphij	set_mlist(ml_search, 0);
1201170259Sdelphij	pattern = cmd_lastpattern();
1202170259Sdelphij	if (pattern == NULL)
1203170259Sdelphij		return (0);
1204170259Sdelphij
1205170259Sdelphij	if (compile_pattern(pattern, search_type) < 0)
1206170259Sdelphij		return (0);
1207170259Sdelphij
1208170259Sdelphij	is_ucase_pattern = is_ucase(pattern);
1209170259Sdelphij	if (is_ucase_pattern && caseless != OPT_ONPLUS)
1210170259Sdelphij		is_caseless = 0;
1211170259Sdelphij	else
1212170259Sdelphij		is_caseless = caseless;
1213170259Sdelphij
1214170259Sdelphij#if HILITE_SEARCH
1215170259Sdelphij	if (hilite_search == OPT_ONPLUS && !hide_hilite)
1216170259Sdelphij		hilite_screen();
1217170259Sdelphij#endif
1218170259Sdelphij
1219170259Sdelphij	return (1);
1220170259Sdelphij#else /* CMD_HISTORY */
1221170259Sdelphij	return (0);
1222170259Sdelphij#endif /* CMD_HISTORY */
1223170259Sdelphij}
1224170259Sdelphij
122560786Sps/*
122660786Sps * Search for the n-th occurrence of a specified pattern,
122760786Sps * either forward or backward.
122860786Sps * Return the number of matches not yet found in this file
122960786Sps * (that is, n minus the number of matches found).
123060786Sps * Return -1 if the search should be aborted.
123160786Sps * Caller may continue the search in another file
123260786Sps * if less than n matches are found in this file.
123360786Sps */
123460786Sps	public int
123560786Spssearch(search_type, pattern, n)
123660786Sps	int search_type;
123760786Sps	char *pattern;
123860786Sps	int n;
123960786Sps{
124060786Sps	POSITION pos;
1241173685Sdelphij	int result;
124260786Sps
124360786Sps	if (pattern == NULL || *pattern == '\0')
124460786Sps	{
124560786Sps		/*
124660786Sps		 * A null pattern means use the previously compiled pattern.
124760786Sps		 */
1248170259Sdelphij		if (!prev_pattern() && !hist_pattern(search_type))
124960786Sps		{
125060786Sps			error("No previous regular expression", NULL_PARG);
125160786Sps			return (-1);
125260786Sps		}
125360786Sps		if ((search_type & SRCH_NO_REGEX) !=
125460786Sps		    (last_search_type & SRCH_NO_REGEX))
125560786Sps		{
125660786Sps			error("Please re-enter search pattern", NULL_PARG);
125760786Sps			return -1;
125860786Sps		}
125960786Sps#if HILITE_SEARCH
126060786Sps		if (hilite_search == OPT_ON)
126160786Sps		{
126260786Sps			/*
126360786Sps			 * Erase the highlights currently on screen.
126460786Sps			 * If the search fails, we'll redisplay them later.
126560786Sps			 */
126660786Sps			repaint_hilite(0);
126760786Sps		}
126860786Sps		if (hilite_search == OPT_ONPLUS && hide_hilite)
126960786Sps		{
127060786Sps			/*
127160786Sps			 * Highlight any matches currently on screen,
127260786Sps			 * before we actually start the search.
127360786Sps			 */
127460786Sps			hide_hilite = 0;
127560786Sps			hilite_screen();
127660786Sps		}
127760786Sps		hide_hilite = 0;
127860786Sps#endif
127960786Sps	} else
128060786Sps	{
128160786Sps		/*
128260786Sps		 * Compile the pattern.
128360786Sps		 */
128460786Sps		if (compile_pattern(pattern, search_type) < 0)
128560786Sps			return (-1);
128660786Sps		/*
128760786Sps		 * Ignore case if -I is set OR
128860786Sps		 * -i is set AND the pattern is all lowercase.
128960786Sps		 */
1290173685Sdelphij		is_ucase_pattern = is_ucase(pattern);
129160786Sps		if (is_ucase_pattern && caseless != OPT_ONPLUS)
129260786Sps			is_caseless = 0;
129360786Sps		else
129460786Sps			is_caseless = caseless;
129560786Sps#if HILITE_SEARCH
129660786Sps		if (hilite_search)
129760786Sps		{
129860786Sps			/*
129960786Sps			 * Erase the highlights currently on screen.
130060786Sps			 * Also permanently delete them from the hilite list.
130160786Sps			 */
130260786Sps			repaint_hilite(0);
130360786Sps			hide_hilite = 0;
130460786Sps			clr_hilite();
130560786Sps		}
130660786Sps		if (hilite_search == OPT_ONPLUS)
130760786Sps		{
130860786Sps			/*
130960786Sps			 * Highlight any matches currently on screen,
131060786Sps			 * before we actually start the search.
131160786Sps			 */
131260786Sps			hilite_screen();
131360786Sps		}
131460786Sps#endif
131560786Sps	}
131660786Sps
131760786Sps	/*
131860786Sps	 * Figure out where to start the search.
131960786Sps	 */
132060786Sps	pos = search_pos(search_type);
132160786Sps	if (pos == NULL_POSITION)
132260786Sps	{
132360786Sps		/*
132460786Sps		 * Can't find anyplace to start searching from.
132560786Sps		 */
132660786Sps		if (search_type & SRCH_PAST_EOF)
132760786Sps			return (n);
132860786Sps		/* repaint(); -- why was this here? */
132960786Sps		error("Nothing to search", NULL_PARG);
133060786Sps		return (-1);
133160786Sps	}
133260786Sps
133360786Sps	n = search_range(pos, NULL_POSITION, search_type, n, -1,
133460786Sps			&pos, (POSITION*)NULL);
133560786Sps	if (n != 0)
133660786Sps	{
133760786Sps		/*
133860786Sps		 * Search was unsuccessful.
133960786Sps		 */
134060786Sps#if HILITE_SEARCH
134160786Sps		if (hilite_search == OPT_ON && n > 0)
134260786Sps			/*
134360786Sps			 * Redisplay old hilites.
134460786Sps			 */
134560786Sps			repaint_hilite(1);
134660786Sps#endif
134760786Sps		return (n);
134860786Sps	}
134960786Sps
135060786Sps	if (!(search_type & SRCH_NO_MOVE))
135160786Sps	{
135260786Sps		/*
135360786Sps		 * Go to the matching line.
135460786Sps		 */
135560786Sps		jump_loc(pos, jump_sline);
135660786Sps	}
135760786Sps
135860786Sps#if HILITE_SEARCH
135960786Sps	if (hilite_search == OPT_ON)
136060786Sps		/*
136160786Sps		 * Display new hilites in the matching line.
136260786Sps		 */
136360786Sps		repaint_hilite(1);
136460786Sps#endif
136560786Sps	return (0);
136660786Sps}
136760786Sps
136860786Sps
136960786Sps#if HILITE_SEARCH
137060786Sps/*
137160786Sps * Prepare hilites in a given range of the file.
137260786Sps *
137360786Sps * The pair (prep_startpos,prep_endpos) delimits a contiguous region
137460786Sps * of the file that has been "prepared"; that is, scanned for matches for
137560786Sps * the current search pattern, and hilites have been created for such matches.
137660786Sps * If prep_startpos == NULL_POSITION, the prep region is empty.
137760786Sps * If prep_endpos == NULL_POSITION, the prep region extends to EOF.
137860786Sps * prep_hilite asks that the range (spos,epos) be covered by the prep region.
137960786Sps */
138060786Sps	public void
138160786Spsprep_hilite(spos, epos, maxlines)
138260786Sps	POSITION spos;
138360786Sps	POSITION epos;
138460786Sps	int maxlines;
138560786Sps{
138660786Sps	POSITION nprep_startpos = prep_startpos;
138760786Sps	POSITION nprep_endpos = prep_endpos;
138860786Sps	POSITION new_epos;
138960786Sps	POSITION max_epos;
139060786Sps	int result;
139160786Sps	int i;
139260786Sps/*
139360786Sps * Search beyond where we're asked to search, so the prep region covers
139460786Sps * more than we need.  Do one big search instead of a bunch of small ones.
139560786Sps */
139660786Sps#define	SEARCH_MORE (3*size_linebuf)
139760786Sps
139860786Sps	if (!prev_pattern())
139960786Sps		return;
140060786Sps
140160786Sps	/*
140260786Sps	 * If we're limited to a max number of lines, figure out the
140360786Sps	 * file position we should stop at.
140460786Sps	 */
140560786Sps	if (maxlines < 0)
140660786Sps		max_epos = NULL_POSITION;
140760786Sps	else
140860786Sps	{
140960786Sps		max_epos = spos;
141060786Sps		for (i = 0;  i < maxlines;  i++)
1411170259Sdelphij			max_epos = forw_raw_line(max_epos, (char **)NULL, (int *)NULL);
141260786Sps	}
141360786Sps
141460786Sps	/*
141560786Sps	 * Find two ranges:
141660786Sps	 * The range that we need to search (spos,epos); and the range that
141760786Sps	 * the "prep" region will then cover (nprep_startpos,nprep_endpos).
141860786Sps	 */
141960786Sps
142060786Sps	if (prep_startpos == NULL_POSITION ||
142160786Sps	    (epos != NULL_POSITION && epos < prep_startpos) ||
142260786Sps	    spos > prep_endpos)
142360786Sps	{
142460786Sps		/*
142560786Sps		 * New range is not contiguous with old prep region.
142660786Sps		 * Discard the old prep region and start a new one.
142760786Sps		 */
142860786Sps		clr_hilite();
142960786Sps		if (epos != NULL_POSITION)
143060786Sps			epos += SEARCH_MORE;
143160786Sps		nprep_startpos = spos;
143260786Sps	} else
143360786Sps	{
143460786Sps		/*
143560786Sps		 * New range partially or completely overlaps old prep region.
143660786Sps		 */
143760786Sps		if (epos == NULL_POSITION)
143860786Sps		{
143960786Sps			/*
144060786Sps			 * New range goes to end of file.
144160786Sps			 */
144260786Sps			;
144360786Sps		} else if (epos > prep_endpos)
144460786Sps		{
144560786Sps			/*
144660786Sps			 * New range ends after old prep region.
144760786Sps			 * Extend prep region to end at end of new range.
144860786Sps			 */
144960786Sps			epos += SEARCH_MORE;
145060786Sps		} else /* (epos <= prep_endpos) */
145160786Sps		{
145260786Sps			/*
145360786Sps			 * New range ends within old prep region.
145460786Sps			 * Truncate search to end at start of old prep region.
145560786Sps			 */
145660786Sps			epos = prep_startpos;
145760786Sps		}
145860786Sps
145960786Sps		if (spos < prep_startpos)
146060786Sps		{
146160786Sps			/*
146260786Sps			 * New range starts before old prep region.
146360786Sps			 * Extend old prep region backwards to start at
146460786Sps			 * start of new range.
146560786Sps			 */
146660786Sps			if (spos < SEARCH_MORE)
146760786Sps				spos = 0;
146860786Sps			else
146960786Sps				spos -= SEARCH_MORE;
147060786Sps			nprep_startpos = spos;
147160786Sps		} else /* (spos >= prep_startpos) */
147260786Sps		{
147360786Sps			/*
147460786Sps			 * New range starts within or after old prep region.
147560786Sps			 * Trim search to start at end of old prep region.
147660786Sps			 */
147760786Sps			spos = prep_endpos;
147860786Sps		}
147960786Sps	}
148060786Sps
148160786Sps	if (epos != NULL_POSITION && max_epos != NULL_POSITION &&
148260786Sps	    epos > max_epos)
148360786Sps		/*
148460786Sps		 * Don't go past the max position we're allowed.
148560786Sps		 */
148660786Sps		epos = max_epos;
148760786Sps
148860786Sps	if (epos == NULL_POSITION || epos > spos)
148960786Sps	{
149060786Sps		result = search_range(spos, epos, SRCH_FORW|SRCH_FIND_ALL, 0,
149160786Sps				maxlines, (POSITION*)NULL, &new_epos);
149260786Sps		if (result < 0)
149360786Sps			return;
149460786Sps		if (prep_endpos == NULL_POSITION || new_epos > prep_endpos)
149560786Sps			nprep_endpos = new_epos;
149660786Sps	}
149760786Sps	prep_startpos = nprep_startpos;
149860786Sps	prep_endpos = nprep_endpos;
149960786Sps}
150060786Sps#endif
150160786Sps
150260786Sps/*
150360786Sps * Simple pattern matching function.
150460786Sps * It supports no metacharacters like *, etc.
150560786Sps */
150660786Sps	static int
1507170259Sdelphijmatch(pattern, pattern_len, buf, buf_len, pfound, pend)
1508170259Sdelphij	char *pattern;
1509170259Sdelphij	int pattern_len;
1510170259Sdelphij	char *buf;
1511170259Sdelphij	int buf_len;
151260786Sps	char **pfound, **pend;
151360786Sps{
151460786Sps	register char *pp, *lp;
1515170259Sdelphij	register char *pattern_end = pattern + pattern_len;
1516170259Sdelphij	register char *buf_end = buf + buf_len;
151760786Sps
1518170259Sdelphij	for ( ;  buf < buf_end;  buf++)
151960786Sps	{
152060786Sps		for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
1521170259Sdelphij			if (pp == pattern_end || lp == buf_end)
152260786Sps				break;
1523170259Sdelphij		if (pp == pattern_end)
152460786Sps		{
152560786Sps			if (pfound != NULL)
152660786Sps				*pfound = buf;
152760786Sps			if (pend != NULL)
152860786Sps				*pend = lp;
152960786Sps			return (1);
153060786Sps		}
153160786Sps	}
153260786Sps	return (0);
153360786Sps}
153460786Sps
153560786Sps#if HAVE_V8_REGCOMP
153660786Sps/*
153760786Sps * This function is called by the V8 regcomp to report
153860786Sps * errors in regular expressions.
153960786Sps */
154060786Sps	void
154160786Spsregerror(s)
154260786Sps	char *s;
154360786Sps{
154460786Sps	PARG parg;
154560786Sps
154660786Sps	parg.p_string = s;
154760786Sps	error("%s", &parg);
154860786Sps}
154960786Sps#endif
155060786Sps
1551