xref: /freebsd/contrib/less/search.c (revision c77c488926555ca344ae3a417544cf7a720e1de1)
1a5f0fb15SPaul Saab /*
2*c77c4889SXin LI  * Copyright (C) 1984-2024  Mark Nudelman
3a5f0fb15SPaul Saab  *
4a5f0fb15SPaul Saab  * You may distribute under the terms of either the GNU General Public
5a5f0fb15SPaul Saab  * License or the Less License, as specified in the README file.
6a5f0fb15SPaul Saab  *
796e55cc7SXin LI  * For more information, see the README file.
8a5f0fb15SPaul Saab  */
9a5f0fb15SPaul Saab 
10a5f0fb15SPaul Saab 
11a5f0fb15SPaul Saab /*
12a5f0fb15SPaul Saab  * Routines to search a file for a pattern.
13a5f0fb15SPaul Saab  */
14a5f0fb15SPaul Saab 
15a5f0fb15SPaul Saab #include "less.h"
16a5f0fb15SPaul Saab #include "position.h"
1759a2d077SXin LI #include "charset.h"
18a5f0fb15SPaul Saab 
19a5f0fb15SPaul Saab #define MINPOS(a,b)     (((a) < (b)) ? (a) : (b))
20a5f0fb15SPaul Saab #define MAXPOS(a,b)     (((a) > (b)) ? (a) : (b))
21a5f0fb15SPaul Saab 
22a5f0fb15SPaul Saab extern int sigs;
23a5f0fb15SPaul Saab extern int how_search;
24a5f0fb15SPaul Saab extern int caseless;
25a5f0fb15SPaul Saab extern int linenums;
26a5f0fb15SPaul Saab extern int jump_sline;
27a5f0fb15SPaul Saab extern int bs_mode;
28d713e089SXin LI extern int proc_backspace;
29d713e089SXin LI extern int proc_return;
301ede1615STim J. Robbins extern int ctldisp;
3115596da4SPaul Saab extern int status_col;
32f6b74a7dSXin LI extern void *ml_search;
33a5f0fb15SPaul Saab extern POSITION start_attnpos;
34a5f0fb15SPaul Saab extern POSITION end_attnpos;
357374caaaSXin LI extern int utf_mode;
362235c7feSXin LI extern int sc_width;
372235c7feSXin LI extern int sc_height;
382235c7feSXin LI extern int hshift;
39*c77c4889SXin LI extern int match_shift;
40*c77c4889SXin LI extern int nosearch_header_lines;
41*c77c4889SXin LI extern int nosearch_header_cols;
42d713e089SXin LI extern int header_lines;
43d713e089SXin LI extern int header_cols;
44*c77c4889SXin LI extern LWCHAR rscroll_char;
45a5f0fb15SPaul Saab #if HILITE_SEARCH
46a5f0fb15SPaul Saab extern int hilite_search;
47*c77c4889SXin LI extern size_t size_linebuf;
48*c77c4889SXin LI extern lbool squished;
49a5f0fb15SPaul Saab extern int can_goto_line;
50*c77c4889SXin LI extern lbool no_eof_bell;
51*c77c4889SXin LI static lbool hide_hilite;
52a5f0fb15SPaul Saab static POSITION prep_startpos;
53a5f0fb15SPaul Saab static POSITION prep_endpos;
54*c77c4889SXin LI public POSITION header_start_pos = NULL_POSITION;
55*c77c4889SXin LI static POSITION header_end_pos;
56*c77c4889SXin LI public lbool search_wrapped = FALSE;
57*c77c4889SXin LI #if OSC8_LINK
58*c77c4889SXin LI public POSITION osc8_linepos = NULL_POSITION;
59*c77c4889SXin LI public POSITION osc8_match_start = NULL_POSITION;
60*c77c4889SXin LI public POSITION osc8_match_end = NULL_POSITION;
61*c77c4889SXin LI public POSITION osc8_params_start = NULL_POSITION;
62*c77c4889SXin LI public POSITION osc8_params_end = NULL_POSITION;
63*c77c4889SXin LI public POSITION osc8_uri_start = NULL_POSITION;
64*c77c4889SXin LI public POSITION osc8_uri_end = NULL_POSITION;
65*c77c4889SXin LI public POSITION osc8_text_start = NULL_POSITION;
66*c77c4889SXin LI public POSITION osc8_text_end = NULL_POSITION;
67*c77c4889SXin LI char *osc8_path = NULL;
68*c77c4889SXin LI char *osc8_uri = NULL;
69*c77c4889SXin LI constant char *osc8_search_param = NULL;
70*c77c4889SXin LI #endif
71a5f0fb15SPaul Saab 
72a15691bfSXin LI /*
73a15691bfSXin LI  * Structures for maintaining a set of ranges for hilites and filtered-out
74a15691bfSXin LI  * lines. Each range is stored as a node within a red-black tree, and we
75a15691bfSXin LI  * try to extend existing ranges (without creating overlaps) rather than
76a15691bfSXin LI  * create new nodes if possible. We remember the last node found by a
77a15691bfSXin LI  * search for constant-time lookup if the next search is near enough to
78a15691bfSXin LI  * the previous. To aid that, we overlay a secondary doubly-linked list
79a15691bfSXin LI  * on top of the red-black tree so we can find the preceding/succeeding
80a15691bfSXin LI  * nodes also in constant time.
81a15691bfSXin LI  *
82a15691bfSXin LI  * Each node is allocated from a series of pools, each pool double the size
83a15691bfSXin LI  * of the previous (for amortised constant time allocation). Since our only
84a15691bfSXin LI  * tree operations are clear and node insertion, not node removal, we don't
85a15691bfSXin LI  * need to maintain a usage bitmap or freelist and can just return nodes
86a15691bfSXin LI  * from the pool in-order until capacity is reached.
87a15691bfSXin LI  */
88a5f0fb15SPaul Saab struct hilite
89a5f0fb15SPaul Saab {
90a5f0fb15SPaul Saab 	POSITION hl_startpos;
91a5f0fb15SPaul Saab 	POSITION hl_endpos;
92d713e089SXin LI 	int hl_attr;
93a5f0fb15SPaul Saab };
94a15691bfSXin LI struct hilite_node
95a15691bfSXin LI {
96a15691bfSXin LI 	struct hilite_node *parent;
97a15691bfSXin LI 	struct hilite_node *left;
98a15691bfSXin LI 	struct hilite_node *right;
99a15691bfSXin LI 	struct hilite_node *prev;
100a15691bfSXin LI 	struct hilite_node *next;
101a15691bfSXin LI 	int red;
102a15691bfSXin LI 	struct hilite r;
103a15691bfSXin LI };
104a15691bfSXin LI struct hilite_storage
105a15691bfSXin LI {
106*c77c4889SXin LI 	size_t capacity;
107*c77c4889SXin LI 	size_t used;
108a15691bfSXin LI 	struct hilite_storage *next;
109a15691bfSXin LI 	struct hilite_node *nodes;
110a15691bfSXin LI };
111a15691bfSXin LI struct hilite_tree
112a15691bfSXin LI {
113a15691bfSXin LI 	struct hilite_storage *first;
114a15691bfSXin LI 	struct hilite_storage *current;
115a15691bfSXin LI 	struct hilite_node *root;
116a15691bfSXin LI 	struct hilite_node *lookaside;
117a15691bfSXin LI };
118a15691bfSXin LI #define HILITE_INITIALIZER() { NULL, NULL, NULL, NULL }
119a15691bfSXin LI #define HILITE_LOOKASIDE_STEPS 2
120a15691bfSXin LI 
121a15691bfSXin LI static struct hilite_tree hilite_anchor = HILITE_INITIALIZER();
122a15691bfSXin LI static struct hilite_tree filter_anchor = HILITE_INITIALIZER();
1232235c7feSXin LI static struct pattern_info *filter_infos = NULL;
124a15691bfSXin LI 
125a5f0fb15SPaul Saab #endif
126a5f0fb15SPaul Saab 
127a5f0fb15SPaul Saab /*
128a5f0fb15SPaul Saab  * These are the static variables that represent the "remembered"
129f0be0a1fSXin LI  * search pattern and filter pattern.
130a5f0fb15SPaul Saab  */
131f0be0a1fSXin LI struct pattern_info {
132f6b74a7dSXin LI 	PATTERN_TYPE compiled;
133f0be0a1fSXin LI 	char* text;
134f0be0a1fSXin LI 	int search_type;
135*c77c4889SXin LI 	lbool is_ucase_pattern;
1362235c7feSXin LI 	struct pattern_info *next;
137f0be0a1fSXin LI };
138a5f0fb15SPaul Saab 
13996e55cc7SXin LI #if NO_REGEX
14096e55cc7SXin LI #define info_compiled(info) ((void*)0)
14196e55cc7SXin LI #else
14296e55cc7SXin LI #define info_compiled(info) ((info)->compiled)
14396e55cc7SXin LI #endif
14496e55cc7SXin LI 
145f0be0a1fSXin LI static struct pattern_info search_info;
14695270f73SXin LI public int is_caseless;
147a5f0fb15SPaul Saab 
148423c5ce5SXin LI /*
14933096f16SXin LI  * Are there any uppercase letters in this string?
15033096f16SXin LI  */
151*c77c4889SXin LI static lbool is_ucase(constant char *str)
15233096f16SXin LI {
153*c77c4889SXin LI 	constant char *str_end = str + strlen(str);
15433096f16SXin LI 	LWCHAR ch;
15533096f16SXin LI 
15633096f16SXin LI 	while (str < str_end)
15733096f16SXin LI 	{
158*c77c4889SXin LI 		ch = step_charc(&str, +1, str_end);
15933096f16SXin LI 		if (IS_UPPER(ch))
160*c77c4889SXin LI 			return (TRUE);
16133096f16SXin LI 	}
162*c77c4889SXin LI 	return (FALSE);
16333096f16SXin LI }
16433096f16SXin LI 
16533096f16SXin LI /*
1662235c7feSXin LI  * Discard a saved pattern.
1672235c7feSXin LI  */
168d713e089SXin LI static void clear_pattern(struct pattern_info *info)
1692235c7feSXin LI {
1702235c7feSXin LI 	if (info->text != NULL)
1712235c7feSXin LI 		free(info->text);
1722235c7feSXin LI 	info->text = NULL;
1732235c7feSXin LI #if !NO_REGEX
1742235c7feSXin LI 	uncompile_pattern(&info->compiled);
1752235c7feSXin LI #endif
1762235c7feSXin LI }
1772235c7feSXin LI 
1782235c7feSXin LI /*
179f0be0a1fSXin LI  * Compile and save a search pattern.
180423c5ce5SXin LI  */
181*c77c4889SXin LI static int set_pattern(struct pattern_info *info, constant char *pattern, int search_type, int show_error)
182423c5ce5SXin LI {
18395270f73SXin LI 	/*
18495270f73SXin LI 	 * Ignore case if -I is set OR
18595270f73SXin LI 	 * -i is set AND the pattern is all lowercase.
18695270f73SXin LI 	 */
187d713e089SXin LI 	info->is_ucase_pattern = (pattern == NULL) ? FALSE : is_ucase(pattern);
188d713e089SXin LI 	is_caseless = (info->is_ucase_pattern && caseless != OPT_ONPLUS) ? 0 : caseless;
18996e55cc7SXin LI #if !NO_REGEX
190f0be0a1fSXin LI 	if (pattern == NULL)
1912235c7feSXin LI 		SET_NULL_PATTERN(info->compiled);
1922235c7feSXin LI 	else if (compile_pattern(pattern, search_type, show_error, &info->compiled) < 0)
193f0be0a1fSXin LI 		return -1;
19496e55cc7SXin LI #endif
195f0be0a1fSXin LI 	/* Pattern compiled successfully; save the text too. */
196f0be0a1fSXin LI 	if (info->text != NULL)
197f0be0a1fSXin LI 		free(info->text);
198f0be0a1fSXin LI 	info->text = NULL;
199f0be0a1fSXin LI 	if (pattern != NULL)
200f0be0a1fSXin LI 	{
201f0be0a1fSXin LI 		info->text = (char *) ecalloc(1, strlen(pattern)+1);
202f0be0a1fSXin LI 		strcpy(info->text, pattern);
203f0be0a1fSXin LI 	}
204f0be0a1fSXin LI 	info->search_type = search_type;
205f0be0a1fSXin LI 	return 0;
206423c5ce5SXin LI }
207423c5ce5SXin LI 
208423c5ce5SXin LI /*
209f0be0a1fSXin LI  * Initialize saved pattern to nothing.
210f0be0a1fSXin LI  */
211d713e089SXin LI static void init_pattern(struct pattern_info *info)
212f0be0a1fSXin LI {
2132235c7feSXin LI 	SET_NULL_PATTERN(info->compiled);
214f0be0a1fSXin LI 	info->text = NULL;
215f0be0a1fSXin LI 	info->search_type = 0;
2162235c7feSXin LI 	info->next = NULL;
217f0be0a1fSXin LI }
218f0be0a1fSXin LI 
219f0be0a1fSXin LI /*
220f0be0a1fSXin LI  * Initialize search variables.
221f0be0a1fSXin LI  */
222d713e089SXin LI public void init_search(void)
223f0be0a1fSXin LI {
224f0be0a1fSXin LI 	init_pattern(&search_info);
225f0be0a1fSXin LI }
226f0be0a1fSXin LI 
227f0be0a1fSXin LI /*
228f0be0a1fSXin LI  * Determine which text conversions to perform before pattern matching.
2291ede1615STim J. Robbins  */
230*c77c4889SXin LI public int get_cvt_ops(int search_type)
2311ede1615STim J. Robbins {
2321ede1615STim J. Robbins 	int ops = 0;
2332235c7feSXin LI 
234d713e089SXin LI 	if (is_caseless && (!re_handles_caseless || (search_type & SRCH_NO_REGEX)))
2351ede1615STim J. Robbins 		ops |= CVT_TO_LC;
236d713e089SXin LI 	if (proc_backspace == OPT_ON || (bs_mode == BS_SPECIAL && proc_backspace == OPT_OFF))
2371ede1615STim J. Robbins 		ops |= CVT_BS;
238d713e089SXin LI 	if (proc_return == OPT_ON || (bs_mode != BS_CONTROL && proc_backspace == OPT_OFF))
2391ede1615STim J. Robbins 		ops |= CVT_CRLF;
2401ede1615STim J. Robbins 	if (ctldisp == OPT_ONPLUS)
2411ede1615STim J. Robbins 		ops |= CVT_ANSI;
2421ede1615STim J. Robbins 	return (ops);
2431ede1615STim J. Robbins }
2441ede1615STim J. Robbins 
2451ede1615STim J. Robbins /*
246a5f0fb15SPaul Saab  * Is there a previous (remembered) search pattern?
247a5f0fb15SPaul Saab  */
248d713e089SXin LI static int prev_pattern(struct pattern_info *info)
249a5f0fb15SPaul Saab {
25096e55cc7SXin LI #if !NO_REGEX
25196e55cc7SXin LI 	if ((info->search_type & SRCH_NO_REGEX) == 0)
252f0be0a1fSXin LI 		return (!is_null_pattern(info->compiled));
25396e55cc7SXin LI #endif
25496e55cc7SXin LI 	return (info->text != NULL);
255a5f0fb15SPaul Saab }
256a5f0fb15SPaul Saab 
257a5f0fb15SPaul Saab #if HILITE_SEARCH
258a5f0fb15SPaul Saab /*
259a5f0fb15SPaul Saab  * Repaint the hilites currently displayed on the screen.
260a5f0fb15SPaul Saab  * Repaint each line which contains highlighted text.
261a5f0fb15SPaul Saab  * If on==0, force all hilites off.
262a5f0fb15SPaul Saab  */
263*c77c4889SXin LI public void repaint_hilite(lbool on)
264a5f0fb15SPaul Saab {
265b2ea2440SXin LI 	int sindex;
266a5f0fb15SPaul Saab 	POSITION pos;
267*c77c4889SXin LI 	lbool save_hide_hilite;
268a5f0fb15SPaul Saab 
269a5f0fb15SPaul Saab 	if (squished)
270a5f0fb15SPaul Saab 		repaint();
271a5f0fb15SPaul Saab 
272a5f0fb15SPaul Saab 	save_hide_hilite = hide_hilite;
273a5f0fb15SPaul Saab 	if (!on)
274a5f0fb15SPaul Saab 	{
275a5f0fb15SPaul Saab 		if (hide_hilite)
276a5f0fb15SPaul Saab 			return;
277*c77c4889SXin LI 		hide_hilite = TRUE;
278a5f0fb15SPaul Saab 	}
279a5f0fb15SPaul Saab 
280a5f0fb15SPaul Saab 	if (!can_goto_line)
281a5f0fb15SPaul Saab 	{
282a5f0fb15SPaul Saab 		repaint();
283a5f0fb15SPaul Saab 		hide_hilite = save_hide_hilite;
284a5f0fb15SPaul Saab 		return;
285a5f0fb15SPaul Saab 	}
286a5f0fb15SPaul Saab 
287b2ea2440SXin LI 	for (sindex = TOP;  sindex < TOP + sc_height-1;  sindex++)
288a5f0fb15SPaul Saab 	{
289b2ea2440SXin LI 		pos = position(sindex);
290a5f0fb15SPaul Saab 		if (pos == NULL_POSITION)
291a5f0fb15SPaul Saab 			continue;
292a5f0fb15SPaul Saab 		(void) forw_line(pos);
293b2ea2440SXin LI 		goto_line(sindex);
294d713e089SXin LI 		clear_eol();
295a5f0fb15SPaul Saab 		put_line();
296a5f0fb15SPaul Saab 	}
29795270f73SXin LI 	overlay_header();
29833096f16SXin LI 	lower_left();
299a5f0fb15SPaul Saab 	hide_hilite = save_hide_hilite;
300a5f0fb15SPaul Saab }
3012235c7feSXin LI #endif
302a5f0fb15SPaul Saab 
303a5f0fb15SPaul Saab /*
304a5f0fb15SPaul Saab  * Clear the attn hilite.
305a5f0fb15SPaul Saab  */
306d713e089SXin LI public void clear_attn(void)
307a5f0fb15SPaul Saab {
3082235c7feSXin LI #if HILITE_SEARCH
309b2ea2440SXin LI 	int sindex;
310a5f0fb15SPaul Saab 	POSITION old_start_attnpos;
311a5f0fb15SPaul Saab 	POSITION old_end_attnpos;
312a5f0fb15SPaul Saab 	POSITION pos;
313a5f0fb15SPaul Saab 	POSITION epos;
3147e990b09SXin LI 	int moved = 0;
315a5f0fb15SPaul Saab 
316a5f0fb15SPaul Saab 	if (start_attnpos == NULL_POSITION)
317a5f0fb15SPaul Saab 		return;
318a5f0fb15SPaul Saab 	old_start_attnpos = start_attnpos;
319a5f0fb15SPaul Saab 	old_end_attnpos = end_attnpos;
320a5f0fb15SPaul Saab 	start_attnpos = end_attnpos = NULL_POSITION;
321a5f0fb15SPaul Saab 
322a5f0fb15SPaul Saab 	if (!can_goto_line)
323a5f0fb15SPaul Saab 	{
324a5f0fb15SPaul Saab 		repaint();
325a5f0fb15SPaul Saab 		return;
326a5f0fb15SPaul Saab 	}
327a5f0fb15SPaul Saab 	if (squished)
328a5f0fb15SPaul Saab 		repaint();
329a5f0fb15SPaul Saab 
330b2ea2440SXin LI 	for (sindex = TOP;  sindex < TOP + sc_height-1;  sindex++)
331a5f0fb15SPaul Saab 	{
332b2ea2440SXin LI 		pos = position(sindex);
333a5f0fb15SPaul Saab 		if (pos == NULL_POSITION)
334a5f0fb15SPaul Saab 			continue;
335b2ea2440SXin LI 		epos = position(sindex+1);
336b2ea2440SXin LI 		if (pos <= old_end_attnpos &&
337a5f0fb15SPaul Saab 		     (epos == NULL_POSITION || epos > old_start_attnpos))
338a5f0fb15SPaul Saab 		{
339a5f0fb15SPaul Saab 			(void) forw_line(pos);
340b2ea2440SXin LI 			goto_line(sindex);
341d713e089SXin LI 			clear_eol();
342a5f0fb15SPaul Saab 			put_line();
3437e990b09SXin LI 			moved = 1;
344a5f0fb15SPaul Saab 		}
345a5f0fb15SPaul Saab 	}
34695270f73SXin LI 	if (overlay_header())
34795270f73SXin LI 		moved = 1;
3487e990b09SXin LI 	if (moved)
3497e990b09SXin LI 		lower_left();
350a5f0fb15SPaul Saab #endif
3512235c7feSXin LI }
352a5f0fb15SPaul Saab 
353a5f0fb15SPaul Saab /*
3542235c7feSXin LI  * Toggle or clear search string highlighting.
355a5f0fb15SPaul Saab  */
356*c77c4889SXin LI public void undo_search(lbool clear)
357a5f0fb15SPaul Saab {
3582235c7feSXin LI 	clear_pattern(&search_info);
359*c77c4889SXin LI #if OSC8_LINK
360*c77c4889SXin LI 	osc8_linepos = NULL_POSITION;
361*c77c4889SXin LI #endif
3622235c7feSXin LI #if HILITE_SEARCH
3632235c7feSXin LI 	if (clear)
3642235c7feSXin LI 	{
3652235c7feSXin LI 		clr_hilite();
3662235c7feSXin LI 	} else
367a5f0fb15SPaul Saab 	{
368b2ea2440SXin LI 		if (hilite_anchor.first == NULL)
369b2ea2440SXin LI 		{
370a5f0fb15SPaul Saab 			error("No previous regular expression", NULL_PARG);
371a5f0fb15SPaul Saab 			return;
372a5f0fb15SPaul Saab 		}
373a5f0fb15SPaul Saab 		hide_hilite = !hide_hilite;
3742235c7feSXin LI 	}
375*c77c4889SXin LI 	repaint_hilite(TRUE);
376a5f0fb15SPaul Saab #endif
377a5f0fb15SPaul Saab }
378a5f0fb15SPaul Saab 
379a5f0fb15SPaul Saab #if HILITE_SEARCH
380a5f0fb15SPaul Saab /*
381a5f0fb15SPaul Saab  * Clear the hilite list.
382a5f0fb15SPaul Saab  */
383d713e089SXin LI public void clr_hlist(struct hilite_tree *anchor)
384a5f0fb15SPaul Saab {
385a15691bfSXin LI 	struct hilite_storage *hls;
386a15691bfSXin LI 	struct hilite_storage *nexthls;
387a5f0fb15SPaul Saab 
388a15691bfSXin LI 	for (hls = anchor->first;  hls != NULL;  hls = nexthls)
389a5f0fb15SPaul Saab 	{
390a15691bfSXin LI 		nexthls = hls->next;
391a15691bfSXin LI 		free((void*)hls->nodes);
392a15691bfSXin LI 		free((void*)hls);
393a5f0fb15SPaul Saab 	}
394a15691bfSXin LI 	anchor->first = NULL;
395a15691bfSXin LI 	anchor->current = NULL;
396a15691bfSXin LI 	anchor->root = NULL;
397a15691bfSXin LI 
398a15691bfSXin LI 	anchor->lookaside = NULL;
399a15691bfSXin LI 
400a5f0fb15SPaul Saab 	prep_startpos = prep_endpos = NULL_POSITION;
401a5f0fb15SPaul Saab }
402a5f0fb15SPaul Saab 
403d713e089SXin LI public void clr_hilite(void)
4047374caaaSXin LI {
4057374caaaSXin LI 	clr_hlist(&hilite_anchor);
4067374caaaSXin LI }
4077374caaaSXin LI 
408d713e089SXin LI public void clr_filter(void)
4097374caaaSXin LI {
4107374caaaSXin LI 	clr_hlist(&filter_anchor);
4117374caaaSXin LI }
4127374caaaSXin LI 
413a15691bfSXin LI /*
414a15691bfSXin LI  * Find the node covering pos, or the node after it if no node covers it,
415a15691bfSXin LI  * or return NULL if pos is after the last range. Remember the found node,
416a15691bfSXin LI  * to speed up subsequent searches for the same or similar positions (if
417a15691bfSXin LI  * we return NULL, remember the last node.)
418a15691bfSXin LI  */
419d713e089SXin LI static struct hilite_node* hlist_find(struct hilite_tree *anchor, POSITION pos)
420a15691bfSXin LI {
421a15691bfSXin LI 	struct hilite_node *n, *m;
422a15691bfSXin LI 
423a15691bfSXin LI 	if (anchor->lookaside)
424a15691bfSXin LI 	{
425a15691bfSXin LI 		int steps = 0;
426a15691bfSXin LI 		int hit = 0;
427a15691bfSXin LI 
428a15691bfSXin LI 		n = anchor->lookaside;
429a15691bfSXin LI 
430a15691bfSXin LI 		for (;;)
431a15691bfSXin LI 		{
432a15691bfSXin LI 			if (pos < n->r.hl_endpos)
433a15691bfSXin LI 			{
434a15691bfSXin LI 				if (n->prev == NULL || pos >= n->prev->r.hl_endpos)
435a15691bfSXin LI 				{
436a15691bfSXin LI 					hit = 1;
437a15691bfSXin LI 					break;
438a15691bfSXin LI 				}
439a15691bfSXin LI 			} else if (n->next == NULL)
440a15691bfSXin LI 			{
441a15691bfSXin LI 				n = NULL;
442a15691bfSXin LI 				hit = 1;
443a15691bfSXin LI 				break;
444a15691bfSXin LI 			}
445a15691bfSXin LI 
446a15691bfSXin LI 			/*
447a15691bfSXin LI 			 * If we don't find the right node within a small
448a15691bfSXin LI 			 * distance, don't keep doing a linear search!
449a15691bfSXin LI 			 */
450a15691bfSXin LI 			if (steps >= HILITE_LOOKASIDE_STEPS)
451a15691bfSXin LI 				break;
452a15691bfSXin LI 			steps++;
453a15691bfSXin LI 
454a15691bfSXin LI 			if (pos < n->r.hl_endpos)
455a15691bfSXin LI 				anchor->lookaside = n = n->prev;
456a15691bfSXin LI 			else
457a15691bfSXin LI 				anchor->lookaside = n = n->next;
458a15691bfSXin LI 		}
459a15691bfSXin LI 
460a15691bfSXin LI 		if (hit)
461a15691bfSXin LI 			return n;
462a15691bfSXin LI 	}
463a15691bfSXin LI 
464a15691bfSXin LI 	n = anchor->root;
465a15691bfSXin LI 	m = NULL;
466a15691bfSXin LI 
467a15691bfSXin LI 	while (n != NULL)
468a15691bfSXin LI 	{
469a15691bfSXin LI 		if (pos < n->r.hl_startpos)
470a15691bfSXin LI 		{
471a15691bfSXin LI 			if (n->left != NULL)
472a15691bfSXin LI 			{
473a15691bfSXin LI 				m = n;
474a15691bfSXin LI 				n = n->left;
475a15691bfSXin LI 				continue;
476a15691bfSXin LI 			}
477a15691bfSXin LI 			break;
478a15691bfSXin LI 		}
479a15691bfSXin LI 		if (pos >= n->r.hl_endpos)
480a15691bfSXin LI 		{
481a15691bfSXin LI 			if (n->right != NULL)
482a15691bfSXin LI 			{
483a15691bfSXin LI 				n = n->right;
484a15691bfSXin LI 				continue;
485a15691bfSXin LI 			}
486a15691bfSXin LI 			if (m != NULL)
487a15691bfSXin LI 			{
488a15691bfSXin LI 				n = m;
489a15691bfSXin LI 			} else
490a15691bfSXin LI 			{
491a15691bfSXin LI 				m = n;
492a15691bfSXin LI 				n = NULL;
493a15691bfSXin LI 			}
494a15691bfSXin LI 		}
495a15691bfSXin LI 		break;
496a15691bfSXin LI 	}
497a15691bfSXin LI 
498a15691bfSXin LI 	if (n != NULL)
499a15691bfSXin LI 		anchor->lookaside = n;
500a15691bfSXin LI 	else if (m != NULL)
501a15691bfSXin LI 		anchor->lookaside = m;
502a15691bfSXin LI 
503a15691bfSXin LI 	return n;
504a15691bfSXin LI }
505a15691bfSXin LI 
506a5f0fb15SPaul Saab /*
507a5f0fb15SPaul Saab  * Should any characters in a specified range be highlighted?
50889dd99dcSXin LI  */
509d713e089SXin LI static int hilited_range_attr(POSITION pos, POSITION epos)
51089dd99dcSXin LI {
511a15691bfSXin LI 	struct hilite_node *n = hlist_find(&hilite_anchor, pos);
512d713e089SXin LI 	if (n == NULL)
513d713e089SXin LI 		return 0;
514d713e089SXin LI 	if (epos != NULL_POSITION && epos <= n->r.hl_startpos)
515d713e089SXin LI 		return 0;
516d713e089SXin LI 	return n->r.hl_attr;
51789dd99dcSXin LI }
51889dd99dcSXin LI 
51989dd99dcSXin LI /*
520*c77c4889SXin LI  * Set header parameters.
521*c77c4889SXin LI  */
522*c77c4889SXin LI public void set_header(POSITION pos)
523*c77c4889SXin LI {
524*c77c4889SXin LI 	header_start_pos = (header_lines == 0) ? NULL_POSITION : pos;
525*c77c4889SXin LI 	if (header_start_pos != NULL_POSITION)
526*c77c4889SXin LI 	{
527*c77c4889SXin LI 		int ln;
528*c77c4889SXin LI 		for (ln = 0; ln < header_lines; ++ln)
529*c77c4889SXin LI 		{
530*c77c4889SXin LI 			pos = forw_raw_line(pos, NULL, NULL);
531*c77c4889SXin LI 			if (pos == NULL_POSITION) break;
532*c77c4889SXin LI 		}
533*c77c4889SXin LI 		header_end_pos = pos;
534*c77c4889SXin LI 	}
535*c77c4889SXin LI }
536*c77c4889SXin LI 
537*c77c4889SXin LI /*
538*c77c4889SXin LI  * Is a position within the header lines?
539*c77c4889SXin LI  */
540*c77c4889SXin LI static int pos_in_header(POSITION pos)
541*c77c4889SXin LI {
542*c77c4889SXin LI 	return (header_start_pos != NULL_POSITION &&
543*c77c4889SXin LI 	        pos >= header_start_pos && pos < header_end_pos);
544*c77c4889SXin LI }
545*c77c4889SXin LI 
546*c77c4889SXin LI /*
5477374caaaSXin LI  * Is a line "filtered" -- that is, should it be hidden?
5487374caaaSXin LI  */
549*c77c4889SXin LI public lbool is_filtered(POSITION pos)
5507374caaaSXin LI {
551a15691bfSXin LI 	struct hilite_node *n;
5527374caaaSXin LI 
5537374caaaSXin LI 	if (ch_getflags() & CH_HELPFILE)
554*c77c4889SXin LI 		return (FALSE);
555*c77c4889SXin LI 	if (pos_in_header(pos))
556*c77c4889SXin LI 		return (FALSE);
5577374caaaSXin LI 
558a15691bfSXin LI 	n = hlist_find(&filter_anchor, pos);
559a15691bfSXin LI 	return (n != NULL && pos >= n->r.hl_startpos);
560a15691bfSXin LI }
561a15691bfSXin LI 
5627374caaaSXin LI /*
563a15691bfSXin LI  * If pos is hidden, return the next position which isn't, otherwise
564a15691bfSXin LI  * just return pos.
5657374caaaSXin LI  */
566d713e089SXin LI public POSITION next_unfiltered(POSITION pos)
5677374caaaSXin LI {
568a15691bfSXin LI 	struct hilite_node *n;
569a15691bfSXin LI 
570a15691bfSXin LI 	if (ch_getflags() & CH_HELPFILE)
571a15691bfSXin LI 		return (pos);
572*c77c4889SXin LI 	if (pos_in_header(pos))
573*c77c4889SXin LI 		return (pos);
574a15691bfSXin LI 
575a15691bfSXin LI 	n = hlist_find(&filter_anchor, pos);
576a15691bfSXin LI 	while (n != NULL && pos >= n->r.hl_startpos)
577a15691bfSXin LI 	{
578a15691bfSXin LI 		pos = n->r.hl_endpos;
579a15691bfSXin LI 		n = n->next;
5807374caaaSXin LI 	}
581a15691bfSXin LI 	return (pos);
5827374caaaSXin LI }
5837374caaaSXin LI 
5847374caaaSXin LI /*
585a15691bfSXin LI  * If pos is hidden, return the previous position which isn't or 0 if
586a15691bfSXin LI  * we're filtered right to the beginning, otherwise just return pos.
587a15691bfSXin LI  */
588d713e089SXin LI public POSITION prev_unfiltered(POSITION pos)
589a15691bfSXin LI {
590a15691bfSXin LI 	struct hilite_node *n;
591a15691bfSXin LI 
592a15691bfSXin LI 	if (ch_getflags() & CH_HELPFILE)
593a15691bfSXin LI 		return (pos);
594*c77c4889SXin LI 	if (pos_in_header(pos))
595*c77c4889SXin LI 		return (pos);
596a15691bfSXin LI 
597a15691bfSXin LI 	n = hlist_find(&filter_anchor, pos);
598a15691bfSXin LI 	while (n != NULL && pos >= n->r.hl_startpos)
599a15691bfSXin LI 	{
600a15691bfSXin LI 		pos = n->r.hl_startpos;
601a15691bfSXin LI 		if (pos == 0)
602a15691bfSXin LI 			break;
603a15691bfSXin LI 		pos--;
604a15691bfSXin LI 		n = n->prev;
605a15691bfSXin LI 	}
606a15691bfSXin LI 	return (pos);
607a15691bfSXin LI }
608a15691bfSXin LI 
609*c77c4889SXin LI /*
610*c77c4889SXin LI  * Set the hshift for the line starting at line_pos so that the string
611*c77c4889SXin LI  * between start_off and end_off is visible on the screen.
612*c77c4889SXin LI  */
613*c77c4889SXin LI static void shift_visible(POSITION line_pos, size_t start_off, size_t end_off)
614*c77c4889SXin LI {
615*c77c4889SXin LI 	POSITION start_pos = line_pos + start_off;
616*c77c4889SXin LI 	POSITION end_pos = line_pos + end_off;
617*c77c4889SXin LI 	int start_col = col_from_pos(line_pos, start_pos, NULL_POSITION, -1);
618*c77c4889SXin LI 	int end_col = col_from_pos(line_pos, end_pos, start_pos, start_col);
619*c77c4889SXin LI 	int swidth = sc_width - line_pfx_width() - (rscroll_char ? 1 : 0);
620*c77c4889SXin LI 	int new_hshift;
621*c77c4889SXin LI 	if (start_col < 0 || end_col < 0)
622*c77c4889SXin LI 		return;
623*c77c4889SXin LI 	if (end_col < swidth) /* whole string is in first screen */
624*c77c4889SXin LI 		new_hshift = 0;
625*c77c4889SXin LI 	else if (start_col > hshift && end_col < hshift + swidth)
626*c77c4889SXin LI 		new_hshift = hshift; /* already visible; leave hshift unchanged */
627*c77c4889SXin LI 	else
628*c77c4889SXin LI 	{
629*c77c4889SXin LI 		int eol_col = col_from_pos(line_pos, NULL_POSITION, end_pos, end_col) - swidth;
630*c77c4889SXin LI 		if (start_col >= eol_col) /* whole string is in last screen */
631*c77c4889SXin LI 			new_hshift = eol_col;
632*c77c4889SXin LI 		else /* shift it to column match_shift */
633*c77c4889SXin LI 			new_hshift = (start_col < match_shift) ? 0 : start_col - match_shift;
634*c77c4889SXin LI 	}
635*c77c4889SXin LI 	if (new_hshift != hshift)
636*c77c4889SXin LI 	{
637*c77c4889SXin LI 		hshift = new_hshift;
638*c77c4889SXin LI 		screen_trashed();
639*c77c4889SXin LI 	}
640*c77c4889SXin LI }
641a15691bfSXin LI 
642a15691bfSXin LI /*
64389dd99dcSXin LI  * Should any characters in a specified range be highlighted?
644a5f0fb15SPaul Saab  * If nohide is nonzero, don't consider hide_hilite.
645a5f0fb15SPaul Saab  */
646d713e089SXin LI public int is_hilited_attr(POSITION pos, POSITION epos, int nohide, int *p_matches)
647a5f0fb15SPaul Saab {
648d713e089SXin LI 	int attr;
64989dd99dcSXin LI 
65089dd99dcSXin LI 	if (p_matches != NULL)
65189dd99dcSXin LI 		*p_matches = 0;
652a5f0fb15SPaul Saab 
65315596da4SPaul Saab 	if (!status_col &&
65415596da4SPaul Saab 	    start_attnpos != NULL_POSITION &&
6552235c7feSXin LI 	    pos <= end_attnpos &&
656*c77c4889SXin LI 	     (epos == NULL_POSITION || epos > start_attnpos))
657a5f0fb15SPaul Saab 		/*
658a5f0fb15SPaul Saab 		 * The attn line overlaps this range.
659a5f0fb15SPaul Saab 		 */
6602235c7feSXin LI 		return (AT_HILITE|AT_COLOR_ATTN);
661a5f0fb15SPaul Saab 
662*c77c4889SXin LI #if OSC8_LINK
663*c77c4889SXin LI 	if (osc8_linepos != NULL_POSITION &&
664*c77c4889SXin LI 			pos <= osc8_text_end && (epos == NULL_POSITION || epos > osc8_text_start))
665*c77c4889SXin LI 		return (AT_HILITE|AT_COLOR_SEARCH);
666*c77c4889SXin LI #endif
667*c77c4889SXin LI 
668d713e089SXin LI 	attr = hilited_range_attr(pos, epos);
669d713e089SXin LI 	if (attr == 0)
67089dd99dcSXin LI 		return (0);
67189dd99dcSXin LI 
672b2ea2440SXin LI 	if (p_matches == NULL)
673b2ea2440SXin LI 		/*
674b2ea2440SXin LI 		 * Kinda kludgy way to recognize that caller is checking for
675b2ea2440SXin LI 		 * hilite in status column. In this case we want to return
676b2ea2440SXin LI 		 * hilite status even if hiliting is disabled or hidden.
677b2ea2440SXin LI 		 */
678d713e089SXin LI 		return (attr);
679b2ea2440SXin LI 
68089dd99dcSXin LI 	/*
68189dd99dcSXin LI 	 * Report matches, even if we're hiding highlights.
68289dd99dcSXin LI 	 */
68389dd99dcSXin LI 	*p_matches = 1;
68489dd99dcSXin LI 
685a5f0fb15SPaul Saab 	if (hilite_search == 0)
686a5f0fb15SPaul Saab 		/*
687a5f0fb15SPaul Saab 		 * Not doing highlighting.
688a5f0fb15SPaul Saab 		 */
689a5f0fb15SPaul Saab 		return (0);
690a5f0fb15SPaul Saab 
691a5f0fb15SPaul Saab 	if (!nohide && hide_hilite)
692a5f0fb15SPaul Saab 		/*
693a5f0fb15SPaul Saab 		 * Highlighting is hidden.
694a5f0fb15SPaul Saab 		 */
695a5f0fb15SPaul Saab 		return (0);
696a5f0fb15SPaul Saab 
697d713e089SXin LI 	return (attr);
698a5f0fb15SPaul Saab }
699a5f0fb15SPaul Saab 
700a5f0fb15SPaul Saab /*
701a15691bfSXin LI  * Tree node storage: get the current block of nodes if it has spare
702a15691bfSXin LI  * capacity, or create a new one if not.
703a15691bfSXin LI  */
704d713e089SXin LI static struct hilite_storage * hlist_getstorage(struct hilite_tree *anchor)
705a15691bfSXin LI {
706*c77c4889SXin LI 	size_t capacity = 1;
707a15691bfSXin LI 	struct hilite_storage *s;
708a15691bfSXin LI 
709a15691bfSXin LI 	if (anchor->current)
710a15691bfSXin LI 	{
711a15691bfSXin LI 		if (anchor->current->used < anchor->current->capacity)
712a15691bfSXin LI 			return anchor->current;
713a15691bfSXin LI 		capacity = anchor->current->capacity * 2;
714a15691bfSXin LI 	}
715a15691bfSXin LI 
716a15691bfSXin LI 	s = (struct hilite_storage *) ecalloc(1, sizeof(struct hilite_storage));
717a15691bfSXin LI 	s->nodes = (struct hilite_node *) ecalloc(capacity, sizeof(struct hilite_node));
718a15691bfSXin LI 	s->capacity = capacity;
719a15691bfSXin LI 	s->used = 0;
720a15691bfSXin LI 	s->next = NULL;
721a15691bfSXin LI 	if (anchor->current)
722a15691bfSXin LI 		anchor->current->next = s;
723a15691bfSXin LI 	else
724a15691bfSXin LI 		anchor->first = s;
725a15691bfSXin LI 	anchor->current = s;
726a15691bfSXin LI 	return s;
727a15691bfSXin LI }
728a15691bfSXin LI 
729a15691bfSXin LI /*
730a15691bfSXin LI  * Tree node storage: retrieve a new empty node to be inserted into the
731a15691bfSXin LI  * tree.
732a15691bfSXin LI  */
733d713e089SXin LI static struct hilite_node * hlist_getnode(struct hilite_tree *anchor)
734a15691bfSXin LI {
735a15691bfSXin LI 	struct hilite_storage *s = hlist_getstorage(anchor);
736a15691bfSXin LI 	return &s->nodes[s->used++];
737a15691bfSXin LI }
738a15691bfSXin LI 
739a15691bfSXin LI /*
740a15691bfSXin LI  * Rotate the tree left around a pivot node.
741a15691bfSXin LI  */
742d713e089SXin LI static void hlist_rotate_left(struct hilite_tree *anchor, struct hilite_node *n)
743a15691bfSXin LI {
744a15691bfSXin LI 	struct hilite_node *np = n->parent;
745a15691bfSXin LI 	struct hilite_node *nr = n->right;
746a15691bfSXin LI 	struct hilite_node *nrl = n->right->left;
747a15691bfSXin LI 
748a15691bfSXin LI 	if (np != NULL)
749a15691bfSXin LI 	{
750a15691bfSXin LI 		if (n == np->left)
751a15691bfSXin LI 			np->left = nr;
752a15691bfSXin LI 		else
753a15691bfSXin LI 			np->right = nr;
754a15691bfSXin LI 	} else
755a15691bfSXin LI 	{
756a15691bfSXin LI 		anchor->root = nr;
757a15691bfSXin LI 	}
758a15691bfSXin LI 	nr->left = n;
759a15691bfSXin LI 	n->right = nrl;
760a15691bfSXin LI 
761a15691bfSXin LI 	nr->parent = np;
762a15691bfSXin LI 	n->parent = nr;
763a15691bfSXin LI 	if (nrl != NULL)
764a15691bfSXin LI 		nrl->parent = n;
765a15691bfSXin LI }
766a15691bfSXin LI 
767a15691bfSXin LI /*
768a15691bfSXin LI  * Rotate the tree right around a pivot node.
769a15691bfSXin LI  */
770d713e089SXin LI static void hlist_rotate_right(struct hilite_tree *anchor, struct hilite_node *n)
771a15691bfSXin LI {
772a15691bfSXin LI 	struct hilite_node *np = n->parent;
773a15691bfSXin LI 	struct hilite_node *nl = n->left;
774a15691bfSXin LI 	struct hilite_node *nlr = n->left->right;
775a15691bfSXin LI 
776a15691bfSXin LI 	if (np != NULL)
777a15691bfSXin LI 	{
778a15691bfSXin LI 		if (n == np->right)
779a15691bfSXin LI 			np->right = nl;
780a15691bfSXin LI 		else
781a15691bfSXin LI 			np->left = nl;
782a15691bfSXin LI 	} else
783a15691bfSXin LI 	{
784a15691bfSXin LI 		anchor->root = nl;
785a15691bfSXin LI 	}
786a15691bfSXin LI 	nl->right = n;
787a15691bfSXin LI 	n->left = nlr;
788a15691bfSXin LI 
789a15691bfSXin LI 	nl->parent = np;
790a15691bfSXin LI 	n->parent = nl;
791a15691bfSXin LI 	if (nlr != NULL)
792a15691bfSXin LI 		nlr->parent = n;
793a15691bfSXin LI }
794a15691bfSXin LI 
795a15691bfSXin LI 
796a15691bfSXin LI /*
797a5f0fb15SPaul Saab  * Add a new hilite to a hilite list.
798a5f0fb15SPaul Saab  */
799d713e089SXin LI static void add_hilite(struct hilite_tree *anchor, struct hilite *hl)
800a5f0fb15SPaul Saab {
801a15691bfSXin LI 	struct hilite_node *p, *n, *u;
802a15691bfSXin LI 
803a15691bfSXin LI 	/* Ignore empty ranges. */
804a15691bfSXin LI 	if (hl->hl_startpos >= hl->hl_endpos)
805a15691bfSXin LI 		return;
806a15691bfSXin LI 
807a15691bfSXin LI 	p = anchor->root;
808a15691bfSXin LI 
809a15691bfSXin LI 	/* Inserting the very first node is trivial. */
810a15691bfSXin LI 	if (p == NULL)
811a15691bfSXin LI 	{
812a15691bfSXin LI 		n = hlist_getnode(anchor);
813a15691bfSXin LI 		n->r = *hl;
814a15691bfSXin LI 		anchor->root = n;
815a15691bfSXin LI 		anchor->lookaside = n;
816a15691bfSXin LI 		return;
817a15691bfSXin LI 	}
818a5f0fb15SPaul Saab 
819a5f0fb15SPaul Saab 	/*
820a15691bfSXin LI 	 * Find our insertion point. If we come across any overlapping
821a15691bfSXin LI 	 * or adjoining existing ranges, shrink our range and discard
822a15691bfSXin LI 	 * if it become empty.
823a5f0fb15SPaul Saab 	 */
824a15691bfSXin LI 	for (;;)
825a5f0fb15SPaul Saab 	{
826a15691bfSXin LI 		if (hl->hl_startpos < p->r.hl_startpos)
827a15691bfSXin LI 		{
828d713e089SXin LI 			if (hl->hl_endpos > p->r.hl_startpos && hl->hl_attr == p->r.hl_attr)
829a15691bfSXin LI 				hl->hl_endpos = p->r.hl_startpos;
830a15691bfSXin LI 			if (p->left != NULL)
831a15691bfSXin LI 			{
832a15691bfSXin LI 				p = p->left;
833a15691bfSXin LI 				continue;
834a15691bfSXin LI 			}
835a15691bfSXin LI 			break;
836a15691bfSXin LI 		}
837d713e089SXin LI 		if (hl->hl_startpos < p->r.hl_endpos && hl->hl_attr == p->r.hl_attr) {
838a15691bfSXin LI 			hl->hl_startpos = p->r.hl_endpos;
839a15691bfSXin LI 			if (hl->hl_startpos >= hl->hl_endpos)
840a15691bfSXin LI 				return;
841a15691bfSXin LI 		}
842a15691bfSXin LI 		if (p->right != NULL)
843a15691bfSXin LI 		{
844a15691bfSXin LI 			p = p->right;
845a15691bfSXin LI 			continue;
846a15691bfSXin LI 		}
847a5f0fb15SPaul Saab 		break;
848a5f0fb15SPaul Saab 	}
849a5f0fb15SPaul Saab 
850a5f0fb15SPaul Saab 	/*
851a15691bfSXin LI 	 * Now we're at the right leaf, again check for contiguous ranges
852a15691bfSXin LI 	 * and extend the existing node if possible to avoid the
853a15691bfSXin LI 	 * insertion. Otherwise insert a new node at the leaf.
854a5f0fb15SPaul Saab 	 */
855a15691bfSXin LI 	if (hl->hl_startpos < p->r.hl_startpos) {
856d713e089SXin LI 		if (hl->hl_attr == p->r.hl_attr)
857d713e089SXin LI 		{
858a15691bfSXin LI 			if (hl->hl_endpos == p->r.hl_startpos)
859a5f0fb15SPaul Saab 			{
860a15691bfSXin LI 				p->r.hl_startpos = hl->hl_startpos;
861a5f0fb15SPaul Saab 				return;
862a5f0fb15SPaul Saab 			}
863a15691bfSXin LI 			if (p->prev != NULL && p->prev->r.hl_endpos == hl->hl_startpos)
864a15691bfSXin LI 			{
865a15691bfSXin LI 				p->prev->r.hl_endpos = hl->hl_endpos;
866a15691bfSXin LI 				return;
867a15691bfSXin LI 			}
868d713e089SXin LI 		}
869a15691bfSXin LI 		p->left = n = hlist_getnode(anchor);
870a15691bfSXin LI 		n->next = p;
871a15691bfSXin LI 		if (p->prev != NULL)
872a15691bfSXin LI 		{
873a15691bfSXin LI 			n->prev = p->prev;
874a15691bfSXin LI 			p->prev->next = n;
875a15691bfSXin LI 		}
876a15691bfSXin LI 		p->prev = n;
877a15691bfSXin LI 	} else {
878d713e089SXin LI 		if (hl->hl_attr == p->r.hl_attr)
879d713e089SXin LI 		{
880a15691bfSXin LI 			if (p->r.hl_endpos == hl->hl_startpos)
881a15691bfSXin LI 			{
882a15691bfSXin LI 				p->r.hl_endpos = hl->hl_endpos;
883a15691bfSXin LI 				return;
884a15691bfSXin LI 			}
885a15691bfSXin LI 			if (p->next != NULL && hl->hl_endpos == p->next->r.hl_startpos) {
886a15691bfSXin LI 				p->next->r.hl_startpos = hl->hl_startpos;
887a15691bfSXin LI 				return;
888a15691bfSXin LI 			}
889d713e089SXin LI 		}
890a15691bfSXin LI 		p->right = n = hlist_getnode(anchor);
891a15691bfSXin LI 		n->prev = p;
892a15691bfSXin LI 		if (p->next != NULL)
893a15691bfSXin LI 		{
894a15691bfSXin LI 			n->next = p->next;
895a15691bfSXin LI 			p->next->prev = n;
896a15691bfSXin LI 		}
897a15691bfSXin LI 		p->next = n;
898a15691bfSXin LI 	}
899a15691bfSXin LI 	n->parent = p;
900a15691bfSXin LI 	n->red = 1;
901a15691bfSXin LI 	n->r = *hl;
902a15691bfSXin LI 
903a15691bfSXin LI 	/*
904a15691bfSXin LI 	 * The tree is in the correct order and covers the right ranges
905a15691bfSXin LI 	 * now, but may have become unbalanced. Rebalance it using the
906a15691bfSXin LI 	 * standard red-black tree constraints and operations.
907a15691bfSXin LI 	 */
908a15691bfSXin LI 	for (;;)
909a15691bfSXin LI 	{
910a15691bfSXin LI 		/* case 1 - current is root, root is always black */
911a15691bfSXin LI 		if (n->parent == NULL)
912a15691bfSXin LI 		{
913a15691bfSXin LI 			n->red = 0;
914a15691bfSXin LI 			break;
915a15691bfSXin LI 		}
916a15691bfSXin LI 
917a15691bfSXin LI 		/* case 2 - parent is black, we can always be red */
918a15691bfSXin LI 		if (!n->parent->red)
919a15691bfSXin LI 			break;
920a15691bfSXin LI 
921a15691bfSXin LI 		/*
922a15691bfSXin LI 		 * constraint: because the root must be black, if our
923a15691bfSXin LI 		 * parent is red it cannot be the root therefore we must
924a15691bfSXin LI 		 * have a grandparent
925a15691bfSXin LI 		 */
926a15691bfSXin LI 
927a15691bfSXin LI 		/*
928a15691bfSXin LI 		 * case 3 - parent and uncle are red, repaint them black,
929a15691bfSXin LI 		 * the grandparent red, and start again at the grandparent.
930a15691bfSXin LI 		 */
931a15691bfSXin LI 		u = n->parent->parent->left;
932a15691bfSXin LI 		if (n->parent == u)
933a15691bfSXin LI 			u = n->parent->parent->right;
934a15691bfSXin LI 		if (u != NULL && u->red)
935a15691bfSXin LI 		{
936a15691bfSXin LI 			n->parent->red = 0;
937a15691bfSXin LI 			u->red = 0;
938a15691bfSXin LI 			n = n->parent->parent;
939a15691bfSXin LI 			n->red = 1;
940a15691bfSXin LI 			continue;
941a15691bfSXin LI 		}
942a15691bfSXin LI 
943a15691bfSXin LI 		/*
944a15691bfSXin LI 		 * case 4 - parent is red but uncle is black, parent and
945a15691bfSXin LI 		 * grandparent on opposite sides. We need to start
946a15691bfSXin LI 		 * changing the structure now. This and case 5 will shorten
947a15691bfSXin LI 		 * our branch and lengthen the sibling, between them
948a15691bfSXin LI 		 * restoring balance.
949a15691bfSXin LI 		 */
950a15691bfSXin LI 		if (n == n->parent->right &&
951a15691bfSXin LI 		    n->parent == n->parent->parent->left)
952a15691bfSXin LI 		{
953a15691bfSXin LI 			hlist_rotate_left(anchor, n->parent);
954a15691bfSXin LI 			n = n->left;
955a15691bfSXin LI 		} else if (n == n->parent->left &&
956a15691bfSXin LI 			   n->parent == n->parent->parent->right)
957a15691bfSXin LI 		{
958a15691bfSXin LI 			hlist_rotate_right(anchor, n->parent);
959a15691bfSXin LI 			n = n->right;
960a15691bfSXin LI 		}
961a15691bfSXin LI 
962a15691bfSXin LI 		/*
963a15691bfSXin LI 		 * case 5 - parent is red but uncle is black, parent and
964a15691bfSXin LI 		 * grandparent on same side
965a15691bfSXin LI 		 */
966a15691bfSXin LI 		n->parent->red = 0;
967a15691bfSXin LI 		n->parent->parent->red = 1;
968a15691bfSXin LI 		if (n == n->parent->left)
969a15691bfSXin LI 			hlist_rotate_right(anchor, n->parent->parent);
970a15691bfSXin LI 		else
971a15691bfSXin LI 			hlist_rotate_left(anchor, n->parent->parent);
972a15691bfSXin LI 		break;
973a15691bfSXin LI 	}
974a5f0fb15SPaul Saab }
975a5f0fb15SPaul Saab 
97689dd99dcSXin LI /*
97795270f73SXin LI  * Highlight every character in a range of displayed characters.
97896e55cc7SXin LI  */
979*c77c4889SXin LI static void create_hilites(POSITION linepos, constant char *line, constant char *sp, constant char *ep, int attr, int *chpos)
98096e55cc7SXin LI {
981*c77c4889SXin LI 	size_t start_index = ptr_diff(sp, line); /*{{type-issue}}*/
982*c77c4889SXin LI 	size_t end_index = ptr_diff(ep, line);
983a15691bfSXin LI 	struct hilite hl;
984*c77c4889SXin LI 	size_t i;
98596e55cc7SXin LI 
98696e55cc7SXin LI 	/* Start the first hilite. */
987a15691bfSXin LI 	hl.hl_startpos = linepos + chpos[start_index];
988d713e089SXin LI 	hl.hl_attr = attr;
98996e55cc7SXin LI 
99096e55cc7SXin LI 	/*
99196e55cc7SXin LI 	 * Step through the displayed chars.
99296e55cc7SXin LI 	 * If the source position (before cvt) of the char is one more
99396e55cc7SXin LI 	 * than the source pos of the previous char (the usual case),
99496e55cc7SXin LI 	 * just increase the size of the current hilite by one.
99596e55cc7SXin LI 	 * Otherwise (there are backspaces or something involved),
99696e55cc7SXin LI 	 * finish the current hilite and start a new one.
99796e55cc7SXin LI 	 */
99896e55cc7SXin LI 	for (i = start_index+1;  i <= end_index;  i++)
99996e55cc7SXin LI 	{
100096e55cc7SXin LI 		if (chpos[i] != chpos[i-1] + 1 || i == end_index)
100196e55cc7SXin LI 		{
1002a15691bfSXin LI 			hl.hl_endpos = linepos + chpos[i-1] + 1;
1003a15691bfSXin LI 			add_hilite(&hilite_anchor, &hl);
100496e55cc7SXin LI 			/* Start new hilite unless this is the last char. */
100596e55cc7SXin LI 			if (i < end_index)
100696e55cc7SXin LI 			{
1007a15691bfSXin LI 				hl.hl_startpos = linepos + chpos[i];
100896e55cc7SXin LI 			}
100996e55cc7SXin LI 		}
101096e55cc7SXin LI 	}
101196e55cc7SXin LI }
101296e55cc7SXin LI 
101396e55cc7SXin LI /*
1014a5f0fb15SPaul Saab  * Make a hilite for each string in a physical line which matches
1015a5f0fb15SPaul Saab  * the current pattern.
1016a5f0fb15SPaul Saab  * sp,ep delimit the first match already found.
1017a5f0fb15SPaul Saab  */
1018*c77c4889SXin LI static void hilite_line(POSITION linepos, constant char *line, size_t line_len, int *chpos, constant char **sp, constant char **ep, int nsp)
1019a5f0fb15SPaul Saab {
1020*c77c4889SXin LI 	constant char *searchp;
1021*c77c4889SXin LI 	constant char *line_end = line + line_len;
1022a5f0fb15SPaul Saab 
1023a5f0fb15SPaul Saab 	/*
1024d713e089SXin LI 	 * sp[0] and ep[0] delimit the first match in the line.
1025a5f0fb15SPaul Saab 	 * Mark the corresponding file positions, then
1026a5f0fb15SPaul Saab 	 * look for further matches and mark them.
1027a5f0fb15SPaul Saab 	 * {{ This technique, of calling match_pattern on subsequent
1028a5f0fb15SPaul Saab 	 *    substrings of the line, may mark more than is correct
1029a5f0fb15SPaul Saab 	 *    if the pattern starts with "^".  This bug is fixed
1030a5f0fb15SPaul Saab 	 *    for those regex functions that accept a notbol parameter
1031720c436cSXin LI 	 *    (currently POSIX, PCRE and V8-with-regexec2). }}
1032d713e089SXin LI 	 * sp[i] and ep[i] for i>0 delimit subpattern matches.
1033d713e089SXin LI 	 * Color each of them with its unique color.
1034a5f0fb15SPaul Saab 	 */
1035a5f0fb15SPaul Saab 	searchp = line;
1036a5f0fb15SPaul Saab 	do {
1037*c77c4889SXin LI 		constant char *lep = sp[0];
1038d713e089SXin LI 		int i;
1039f80a33eaSXin LI 		if (sp[0] == NULL || ep[0] == NULL)
1040f80a33eaSXin LI 			break;
1041d713e089SXin LI 		for (i = 1;  i < nsp;  i++)
1042d713e089SXin LI 		{
1043d713e089SXin LI 			if (sp[i] == NULL || ep[i] == NULL)
1044d713e089SXin LI 				break;
1045d713e089SXin LI 			if (ep[i] > sp[i])
1046d713e089SXin LI 			{
1047d713e089SXin LI 				create_hilites(linepos, line, lep, sp[i],
1048d713e089SXin LI 					AT_HILITE | AT_COLOR_SEARCH, chpos);
1049d713e089SXin LI 				create_hilites(linepos, line, sp[i], ep[i],
1050d713e089SXin LI 					AT_HILITE | AT_COLOR_SUBSEARCH(i), chpos);
1051d713e089SXin LI 				lep = ep[i];
1052d713e089SXin LI 			}
1053d713e089SXin LI 		}
1054d713e089SXin LI 		create_hilites(linepos, line, lep, ep[0],
1055d713e089SXin LI 			AT_HILITE | AT_COLOR_SEARCH, chpos);
1056d713e089SXin LI 
1057a5f0fb15SPaul Saab 		/*
1058a5f0fb15SPaul Saab 		 * If we matched more than zero characters,
1059a5f0fb15SPaul Saab 		 * move to the first char after the string we matched.
1060a5f0fb15SPaul Saab 		 * If we matched zero, just move to the next char.
1061a5f0fb15SPaul Saab 		 */
1062d713e089SXin LI 		if (ep[0] > searchp)
1063d713e089SXin LI 			searchp = ep[0];
1064720c436cSXin LI 		else if (searchp != line_end)
1065a5f0fb15SPaul Saab 			searchp++;
1066a5f0fb15SPaul Saab 		else /* end of line */
1067a5f0fb15SPaul Saab 			break;
106896e55cc7SXin LI 	} while (match_pattern(info_compiled(&search_info), search_info.text,
1069*c77c4889SXin LI 			searchp, ptr_diff(line_end, searchp), sp, ep, nsp, 1, search_info.search_type));
1070a5f0fb15SPaul Saab }
1071a5f0fb15SPaul Saab #endif
1072a5f0fb15SPaul Saab 
1073a5f0fb15SPaul Saab #if HILITE_SEARCH
1074a5f0fb15SPaul Saab /*
1075a5f0fb15SPaul Saab  * Find matching text which is currently on screen and highlight it.
1076a5f0fb15SPaul Saab  */
1077d713e089SXin LI static void hilite_screen(void)
1078a5f0fb15SPaul Saab {
1079a5f0fb15SPaul Saab 	struct scrpos scrpos;
1080a5f0fb15SPaul Saab 
1081b2ea2440SXin LI 	get_scrpos(&scrpos, TOP);
1082a5f0fb15SPaul Saab 	if (scrpos.pos == NULL_POSITION)
1083a5f0fb15SPaul Saab 		return;
1084a5f0fb15SPaul Saab 	prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1);
1085*c77c4889SXin LI 	repaint_hilite(TRUE);
1086a5f0fb15SPaul Saab }
1087a5f0fb15SPaul Saab 
1088a5f0fb15SPaul Saab /*
1089a5f0fb15SPaul Saab  * Change highlighting parameters.
1090a5f0fb15SPaul Saab  */
1091d713e089SXin LI public void chg_hilite(void)
1092a5f0fb15SPaul Saab {
1093a5f0fb15SPaul Saab 	/*
1094a5f0fb15SPaul Saab 	 * Erase any highlights currently on screen.
1095a5f0fb15SPaul Saab 	 */
1096a5f0fb15SPaul Saab 	clr_hilite();
1097*c77c4889SXin LI 	hide_hilite = FALSE;
1098a5f0fb15SPaul Saab 
1099a5f0fb15SPaul Saab 	if (hilite_search == OPT_ONPLUS)
1100a5f0fb15SPaul Saab 		/*
1101a5f0fb15SPaul Saab 		 * Display highlights.
1102a5f0fb15SPaul Saab 		 */
1103a5f0fb15SPaul Saab 		hilite_screen();
1104a5f0fb15SPaul Saab }
1105a5f0fb15SPaul Saab #endif
1106a5f0fb15SPaul Saab 
1107a5f0fb15SPaul Saab /*
1108a5f0fb15SPaul Saab  * Figure out where to start a search.
1109a5f0fb15SPaul Saab  */
1110d713e089SXin LI static POSITION search_pos(int search_type)
1111a5f0fb15SPaul Saab {
1112a5f0fb15SPaul Saab 	POSITION pos;
1113b2ea2440SXin LI 	int sindex;
1114a5f0fb15SPaul Saab 
1115a5f0fb15SPaul Saab 	if (empty_screen())
1116a5f0fb15SPaul Saab 	{
1117a5f0fb15SPaul Saab 		/*
1118a5f0fb15SPaul Saab 		 * Start at the beginning (or end) of the file.
1119a5f0fb15SPaul Saab 		 * The empty_screen() case is mainly for
1120a5f0fb15SPaul Saab 		 * command line initiated searches;
1121a5f0fb15SPaul Saab 		 * for example, "+/xyz" on the command line.
1122a5f0fb15SPaul Saab 		 * Also for multi-file (SRCH_PAST_EOF) searches.
1123a5f0fb15SPaul Saab 		 */
1124a5f0fb15SPaul Saab 		if (search_type & SRCH_FORW)
1125a5f0fb15SPaul Saab 		{
112633096f16SXin LI 			pos = ch_zero();
1127a5f0fb15SPaul Saab 		} else
1128a5f0fb15SPaul Saab 		{
1129a5f0fb15SPaul Saab 			pos = ch_length();
1130a5f0fb15SPaul Saab 			if (pos == NULL_POSITION)
1131a5f0fb15SPaul Saab 			{
1132a5f0fb15SPaul Saab 				(void) ch_end_seek();
1133a5f0fb15SPaul Saab 				pos = ch_length();
1134a5f0fb15SPaul Saab 			}
1135a5f0fb15SPaul Saab 		}
1136b2ea2440SXin LI 		sindex = 0;
113733096f16SXin LI 	} else
113833096f16SXin LI 	{
1139*c77c4889SXin LI 		lbool add_one = FALSE;
114033096f16SXin LI 
114133096f16SXin LI 		if (how_search == OPT_ON)
1142a5f0fb15SPaul Saab 		{
1143a5f0fb15SPaul Saab 			/*
1144a5f0fb15SPaul Saab 			 * Search does not include current screen.
1145a5f0fb15SPaul Saab 			 */
1146a5f0fb15SPaul Saab 			if (search_type & SRCH_FORW)
1147b2ea2440SXin LI 				sindex = sc_height-1; /* BOTTOM_PLUS_ONE */
1148a5f0fb15SPaul Saab 			else
1149b2ea2440SXin LI 				sindex = 0; /* TOP */
115033096f16SXin LI 		} else if (how_search == OPT_ONPLUS && !(search_type & SRCH_AFTER_TARGET))
115133096f16SXin LI 		{
115233096f16SXin LI 			/*
115333096f16SXin LI 			 * Search includes all of displayed screen.
115433096f16SXin LI 			 */
115533096f16SXin LI 			if (search_type & SRCH_FORW)
1156b2ea2440SXin LI 				sindex = 0; /* TOP */
115733096f16SXin LI 			else
1158b2ea2440SXin LI 				sindex = sc_height-1; /* BOTTOM_PLUS_ONE */
1159a5f0fb15SPaul Saab 		} else
1160a5f0fb15SPaul Saab 		{
1161a5f0fb15SPaul Saab 			/*
116233096f16SXin LI 			 * Search includes the part of current screen beyond the jump target.
1163a5f0fb15SPaul Saab 			 * It starts at the jump target (if searching backwards),
1164a5f0fb15SPaul Saab 			 * or at the jump target plus one (if forwards).
1165a5f0fb15SPaul Saab 			 */
1166b2ea2440SXin LI 			sindex = sindex_from_sline(jump_sline);
116733096f16SXin LI 			if (search_type & SRCH_FORW)
1168*c77c4889SXin LI 				add_one = TRUE;
116933096f16SXin LI 		}
1170b2ea2440SXin LI 		pos = position(sindex);
117133096f16SXin LI 		if (add_one)
1172*c77c4889SXin LI 			pos = forw_raw_line(pos, NULL, NULL);
117333096f16SXin LI 	}
117433096f16SXin LI 
117533096f16SXin LI 	/*
117633096f16SXin LI 	 * If the line is empty, look around for a plausible starting place.
117733096f16SXin LI 	 */
1178a5f0fb15SPaul Saab 	if (search_type & SRCH_FORW)
1179a5f0fb15SPaul Saab 	{
1180a5f0fb15SPaul Saab 		while (pos == NULL_POSITION)
1181a5f0fb15SPaul Saab 		{
1182b2ea2440SXin LI 			if (++sindex >= sc_height)
1183a5f0fb15SPaul Saab 				break;
1184b2ea2440SXin LI 			pos = position(sindex);
1185a5f0fb15SPaul Saab 		}
1186a5f0fb15SPaul Saab 	} else
1187a5f0fb15SPaul Saab 	{
1188a5f0fb15SPaul Saab 		while (pos == NULL_POSITION)
1189a5f0fb15SPaul Saab 		{
1190b2ea2440SXin LI 			if (--sindex < 0)
1191a5f0fb15SPaul Saab 				break;
1192b2ea2440SXin LI 			pos = position(sindex);
1193a5f0fb15SPaul Saab 		}
1194a5f0fb15SPaul Saab 	}
1195a5f0fb15SPaul Saab 	return (pos);
1196a5f0fb15SPaul Saab }
1197a5f0fb15SPaul Saab 
1198a5f0fb15SPaul Saab /*
11992235c7feSXin LI  * Check to see if the line matches the filter pattern.
12002235c7feSXin LI  * If so, add an entry to the filter list.
12012235c7feSXin LI  */
12022235c7feSXin LI #if HILITE_SEARCH
1203*c77c4889SXin LI static int matches_filters(POSITION pos, char *cline, size_t line_len, int *chpos, POSITION linepos, constant char **sp, constant char **ep, int nsp)
12042235c7feSXin LI {
12052235c7feSXin LI 	struct pattern_info *filter;
12062235c7feSXin LI 
12072235c7feSXin LI 	for (filter = filter_infos; filter != NULL; filter = filter->next)
12082235c7feSXin LI 	{
12092235c7feSXin LI 		int line_filter = match_pattern(info_compiled(filter), filter->text,
1210d713e089SXin LI 			cline, line_len, sp, ep, nsp, 0, filter->search_type);
12112235c7feSXin LI 		if (line_filter)
12122235c7feSXin LI 		{
12132235c7feSXin LI 			struct hilite hl;
12142235c7feSXin LI 			hl.hl_startpos = linepos;
12152235c7feSXin LI 			hl.hl_endpos = pos;
12162235c7feSXin LI 			add_hilite(&filter_anchor, &hl);
12172235c7feSXin LI 			free(cline);
12182235c7feSXin LI 			free(chpos);
12192235c7feSXin LI 			return (1);
12202235c7feSXin LI 		}
12212235c7feSXin LI 	}
12222235c7feSXin LI 	return (0);
12232235c7feSXin LI }
12242235c7feSXin LI #endif
12252235c7feSXin LI 
12262235c7feSXin LI /*
12272235c7feSXin LI  * Get the position of the first char in the screen line which
12282235c7feSXin LI  * puts tpos on screen.
12292235c7feSXin LI  */
1230d713e089SXin LI static POSITION get_lastlinepos(POSITION pos, POSITION tpos, int sheight)
12312235c7feSXin LI {
12322235c7feSXin LI 	int nlines;
12332235c7feSXin LI 
12342235c7feSXin LI 	for (nlines = 0;;  nlines++)
12352235c7feSXin LI 	{
12362235c7feSXin LI 		POSITION npos = forw_line(pos);
12372235c7feSXin LI 		if (npos > tpos)
12382235c7feSXin LI 		{
12392235c7feSXin LI 			if (nlines < sheight)
12402235c7feSXin LI 				return NULL_POSITION;
12412235c7feSXin LI 			return pos;
12422235c7feSXin LI 		}
12432235c7feSXin LI 		pos = npos;
12442235c7feSXin LI 	}
12452235c7feSXin LI }
12462235c7feSXin LI 
1247*c77c4889SXin LI #if OSC8_LINK
12482235c7feSXin LI 
1249*c77c4889SXin LI /*
1250*c77c4889SXin LI  * osc8_parse_info points to the component fields in a parsed OSC8 sequence.
1251*c77c4889SXin LI  */
1252*c77c4889SXin LI struct osc8_parse_info {
1253*c77c4889SXin LI 	constant char *osc8_start;
1254*c77c4889SXin LI 	constant char *osc8_end;
1255*c77c4889SXin LI 	constant char *params_start;
1256*c77c4889SXin LI 	constant char *params_end;
1257*c77c4889SXin LI 	constant char *uri_start;
1258*c77c4889SXin LI 	constant char *uri_end;
1259*c77c4889SXin LI };
1260*c77c4889SXin LI 
1261*c77c4889SXin LI /*
1262*c77c4889SXin LI  * Parse an OSC8 sequence in a string.
1263*c77c4889SXin LI  */
1264*c77c4889SXin LI static lbool osc8_parse(constant char *line, constant char *line_end, struct osc8_parse_info *pop)
12652235c7feSXin LI {
1266*c77c4889SXin LI 	constant char *oline;
1267*c77c4889SXin LI 	pop->osc8_start = pop->osc8_end = pop->uri_start = pop->uri_end = pop->params_start = pop->params_end = NULL;
1268*c77c4889SXin LI 
1269*c77c4889SXin LI 	oline = line;
1270*c77c4889SXin LI 	LWCHAR ch = step_charc(&line, +1, line_end);
1271*c77c4889SXin LI 	/* oline points to character ch, line points to the one after it. */
1272*c77c4889SXin LI 	struct ansi_state *pansi = ansi_start(ch);
1273*c77c4889SXin LI 	if (pansi == NULL)
1274*c77c4889SXin LI 		return FALSE;
1275*c77c4889SXin LI 	pop->osc8_start = oline; /* start at the ESC */
1276*c77c4889SXin LI 	for (;;)
1277*c77c4889SXin LI 	{
1278*c77c4889SXin LI 		ansi_state astate = ansi_step(pansi, ch);
1279*c77c4889SXin LI 		osc8_state ostate = ansi_osc8_state(pansi);
1280*c77c4889SXin LI 		if (ostate == OSC8_NOT)
1281*c77c4889SXin LI 			break;
1282*c77c4889SXin LI 		switch (ostate)
1283*c77c4889SXin LI 		{
1284*c77c4889SXin LI 		case OSC8_PARAMS:
1285*c77c4889SXin LI 			if (pop->params_start == NULL)
1286*c77c4889SXin LI 				pop->params_start = line;
1287*c77c4889SXin LI 			break;
1288*c77c4889SXin LI 		case OSC8_URI:
1289*c77c4889SXin LI 			if (pop->uri_start == NULL)
1290*c77c4889SXin LI 			{
1291*c77c4889SXin LI 				pop->params_end = oline;
1292*c77c4889SXin LI 				pop->uri_start = line;
1293*c77c4889SXin LI 			}
1294*c77c4889SXin LI 			break;
1295*c77c4889SXin LI 		case OSC8_ST_ESC:
1296*c77c4889SXin LI 			if (pop->uri_end == NULL)
1297*c77c4889SXin LI 				pop->uri_end = oline;
1298*c77c4889SXin LI 			break;
1299*c77c4889SXin LI 		case OSC8_END:
1300*c77c4889SXin LI 			ansi_done(pansi);
1301*c77c4889SXin LI 			pop->osc8_end = line;
1302*c77c4889SXin LI 			if (pop->uri_end == NULL) /* happens when ST is "\7" */
1303*c77c4889SXin LI 				pop->uri_end = oline;
1304*c77c4889SXin LI 			if (pop->params_end == NULL) /* should not happen */
1305*c77c4889SXin LI 				pop->params_end = oline;
1306*c77c4889SXin LI 			return TRUE;
1307*c77c4889SXin LI 		default:
1308*c77c4889SXin LI 			break;
1309*c77c4889SXin LI 		}
1310*c77c4889SXin LI 		if (astate != ANSI_MID || line >= line_end)
1311*c77c4889SXin LI 			break;
1312*c77c4889SXin LI 		oline = line;
1313*c77c4889SXin LI 		ch = step_charc(&line, +1, line_end);
1314*c77c4889SXin LI 	}
1315*c77c4889SXin LI 	ansi_done(pansi);
1316*c77c4889SXin LI 	return FALSE;
1317*c77c4889SXin LI }
1318*c77c4889SXin LI 
1319*c77c4889SXin LI /*
1320*c77c4889SXin LI  * Does an OSC8 sequence contain a specified parameter?
1321*c77c4889SXin LI  */
1322*c77c4889SXin LI static lbool osc8_param_match(POSITION linepos, constant char *line, constant struct osc8_parse_info *op1, constant struct osc8_parse_info *op2, constant char *param, POSITION clickpos)
1323*c77c4889SXin LI {
1324*c77c4889SXin LI 	size_t param_len;
1325*c77c4889SXin LI 	constant char *p;
1326*c77c4889SXin LI 
1327*c77c4889SXin LI 	if (clickpos != NULL_POSITION)
1328*c77c4889SXin LI 	{
1329*c77c4889SXin LI 		return clickpos >= linepos + ptr_diff(op1->osc8_start, line) &&
1330*c77c4889SXin LI 		       clickpos < linepos + ptr_diff(op2->osc8_end, line);
1331*c77c4889SXin LI 	}
1332*c77c4889SXin LI 	if (param == NULL)
1333*c77c4889SXin LI 		return TRUE;
1334*c77c4889SXin LI 	param_len = strlen(param);
1335*c77c4889SXin LI 	/* Parameters are separated by colons. */
1336*c77c4889SXin LI 	for (p = op1->params_start;  p + param_len <= op1->params_end; )
1337*c77c4889SXin LI 	{
1338*c77c4889SXin LI 		if (strncmp(p, param, param_len) == 0)
1339*c77c4889SXin LI 		{
1340*c77c4889SXin LI 		    p += param_len;
1341*c77c4889SXin LI 			if (p == op1->params_end || *p == ':')
1342*c77c4889SXin LI 				return TRUE;
1343*c77c4889SXin LI 		}
1344*c77c4889SXin LI 		while (p < op1->params_end && *p != ':')
1345*c77c4889SXin LI 			++p;
1346*c77c4889SXin LI 		while (p < op1->params_end && *p == ':')
1347*c77c4889SXin LI 			++p;
1348*c77c4889SXin LI 	}
1349*c77c4889SXin LI 	return FALSE;
1350*c77c4889SXin LI }
1351*c77c4889SXin LI 
1352*c77c4889SXin LI /*
1353*c77c4889SXin LI  * Is the URI in an OSC8 sequence empty?
1354*c77c4889SXin LI  * "Empty" means zero length, or equal to "#".
1355*c77c4889SXin LI  */
1356*c77c4889SXin LI static lbool osc8_empty_uri(constant struct osc8_parse_info *op)
1357*c77c4889SXin LI {
1358*c77c4889SXin LI 	return op->uri_end == op->uri_start ||
1359*c77c4889SXin LI 	       (op->uri_end == op->uri_start+1 && op->uri_start[0] == '#');
1360*c77c4889SXin LI }
1361*c77c4889SXin LI 
1362*c77c4889SXin LI /*
1363*c77c4889SXin LI  * Find the next OSC8 hyperlink in a line.
1364*c77c4889SXin LI  * A hyperlink is two OSC8 sequences (the first with a nonempty URI)
1365*c77c4889SXin LI  * plus the non-empty text between them.
1366*c77c4889SXin LI  * But if searching for a parameter, allow URI and/or text to be empty.
1367*c77c4889SXin LI  */
1368*c77c4889SXin LI typedef enum { OSC8_NO_MATCH, OSC8_MATCH, OSC8_ALREADY } osc8_match;
1369*c77c4889SXin LI 
1370*c77c4889SXin LI static osc8_match osc8_search_line1(int search_type, POSITION linepos, POSITION spos, constant char *line, size_t line_len, constant char *param, POSITION clickpos)
1371*c77c4889SXin LI {
1372*c77c4889SXin LI 	constant char *line_end = &line[line_len];
1373*c77c4889SXin LI 	struct osc8_parse_info op1;
1374*c77c4889SXin LI 	struct osc8_parse_info op2;
1375*c77c4889SXin LI 	constant char *linep;
1376*c77c4889SXin LI 	constant size_t min_osc8_size = 6; /* "\e]8;;\7" */
1377*c77c4889SXin LI 
1378*c77c4889SXin LI 	if (search_type & SRCH_FORW)
1379*c77c4889SXin LI 	{
1380*c77c4889SXin LI 		for (linep = line; ; linep++)
1381*c77c4889SXin LI 		{
1382*c77c4889SXin LI 			if (linep + min_osc8_size > line_end)
1383*c77c4889SXin LI 				return OSC8_NO_MATCH;
1384*c77c4889SXin LI 			/* Find the first OSC8 sequence in the line with a nonempty URI,
1385*c77c4889SXin LI 			 * which begins the hypertext. */
1386*c77c4889SXin LI 			if (osc8_parse(linep, line_end, &op1) &&
1387*c77c4889SXin LI 			    (!osc8_empty_uri(&op1) || param != NULL))
1388*c77c4889SXin LI 			{
1389*c77c4889SXin LI 				/* Now find the next OSC8 sequence, which ends the hypertext. */
1390*c77c4889SXin LI 				constant char *linep2;
1391*c77c4889SXin LI 				for (linep2 = op1.osc8_end; linep2 < line_end; linep2++)
1392*c77c4889SXin LI 				{
1393*c77c4889SXin LI 					if (osc8_parse(linep2, line_end, &op2))
1394*c77c4889SXin LI 						break;
1395*c77c4889SXin LI 				}
1396*c77c4889SXin LI 				if (linep2 == line_end)
1397*c77c4889SXin LI 					op2.osc8_end = op2.osc8_start = line_end;
1398*c77c4889SXin LI 				if ((op2.osc8_start > op1.osc8_end || param != NULL) &&
1399*c77c4889SXin LI 				    osc8_param_match(linepos, line, &op1, &op2, param, clickpos))
1400*c77c4889SXin LI 					break;
14012235c7feSXin LI 			}
14022235c7feSXin LI 		}
1403*c77c4889SXin LI 	} else
1404*c77c4889SXin LI 	{
1405*c77c4889SXin LI 		op2.osc8_end = op2.osc8_start = line_end;
1406*c77c4889SXin LI 		for (linep = line_end - min_osc8_size; ; linep--)
1407*c77c4889SXin LI 		{
1408*c77c4889SXin LI 			if (linep < line)
1409*c77c4889SXin LI 				return OSC8_NO_MATCH;
1410*c77c4889SXin LI 			if (osc8_parse(linep, line_end, &op1))
1411*c77c4889SXin LI 			{
1412*c77c4889SXin LI 				if (((!osc8_empty_uri(&op1) && op2.osc8_start > op1.osc8_end) || param != NULL) &&
1413*c77c4889SXin LI 				    osc8_param_match(linepos, line, &op1, &op2, param, clickpos))
1414*c77c4889SXin LI 					break;
1415*c77c4889SXin LI 				op2 = op1;
1416*c77c4889SXin LI 			}
1417*c77c4889SXin LI 		}
1418*c77c4889SXin LI 	}
1419*c77c4889SXin LI 	if (param != NULL)
1420*c77c4889SXin LI 		/* Don't set osc8 globals if we're just searching for a parameter. */
1421*c77c4889SXin LI 		return OSC8_MATCH;
1422*c77c4889SXin LI 
1423*c77c4889SXin LI 	if (osc8_linepos == linepos && osc8_match_start == spos + ptr_diff(op1.osc8_start, line))
1424*c77c4889SXin LI 		return OSC8_ALREADY; /* already selected */
1425*c77c4889SXin LI 
1426*c77c4889SXin LI 	osc8_linepos = linepos;
1427*c77c4889SXin LI 	osc8_match_start  = spos + ptr_diff(op1.osc8_start,   line);
1428*c77c4889SXin LI 	osc8_match_end    = spos + ptr_diff(op2.osc8_start,   line);
1429*c77c4889SXin LI 	osc8_params_start = spos + ptr_diff(op1.params_start, line);
1430*c77c4889SXin LI 	osc8_params_end   = spos + ptr_diff(op1.params_end,   line);
1431*c77c4889SXin LI 	osc8_uri_start    = spos + ptr_diff(op1.uri_start,    line);
1432*c77c4889SXin LI 	osc8_uri_end      = spos + ptr_diff(op1.uri_end,      line);
1433*c77c4889SXin LI 	osc8_text_start   = spos + ptr_diff(op1.osc8_end,     line);
1434*c77c4889SXin LI 	osc8_text_end     = spos + ptr_diff(op2.osc8_start,   line);
1435*c77c4889SXin LI 
1436*c77c4889SXin LI 	/* Save URI for message in prompt(). */
1437*c77c4889SXin LI 	osc8_uri = saven(op1.uri_start, ptr_diff(op1.uri_end, op1.uri_start));
1438*c77c4889SXin LI 	return OSC8_MATCH;
1439*c77c4889SXin LI }
1440*c77c4889SXin LI 
1441*c77c4889SXin LI /*
1442*c77c4889SXin LI  * Find the N-th OSC8 hyperlink in a line.
1443*c77c4889SXin LI  */
1444*c77c4889SXin LI static osc8_match osc8_search_line(int search_type, POSITION linepos, constant char *line, size_t line_len, constant char *param, POSITION clickpos, int *matches)
1445*c77c4889SXin LI {
1446*c77c4889SXin LI 	while (*matches > 0)
1447*c77c4889SXin LI 	{
1448*c77c4889SXin LI 		POSITION spos = linepos;
1449*c77c4889SXin LI 		constant char *sline = line;
1450*c77c4889SXin LI 		size_t sline_len = line_len;
1451*c77c4889SXin LI 		osc8_match r;
1452*c77c4889SXin LI 		if (linepos == osc8_linepos && clickpos == NULL_POSITION)
1453*c77c4889SXin LI 		{
1454*c77c4889SXin LI 			/*
1455*c77c4889SXin LI 			 * Already have a hyperlink selected.
1456*c77c4889SXin LI 			 * Search for the next/previous one in the same line.
1457*c77c4889SXin LI 			 */
1458*c77c4889SXin LI 			if (search_type & SRCH_FORW)
1459*c77c4889SXin LI 			{
1460*c77c4889SXin LI 				size_t off = (size_t) (osc8_match_end - linepos);
1461*c77c4889SXin LI 				spos += off;
1462*c77c4889SXin LI 				sline += off;
1463*c77c4889SXin LI 				sline_len -= off;
1464*c77c4889SXin LI 			} else
1465*c77c4889SXin LI 			{
1466*c77c4889SXin LI 				sline_len = (size_t) (osc8_match_start - linepos);
1467*c77c4889SXin LI 			}
1468*c77c4889SXin LI 		}
1469*c77c4889SXin LI 		r = osc8_search_line1(search_type, linepos, spos, sline, sline_len, param, clickpos);
1470*c77c4889SXin LI 		if (r == OSC8_NO_MATCH)
1471*c77c4889SXin LI 			break;
1472*c77c4889SXin LI 		if (--*matches <= 0)
1473*c77c4889SXin LI 			return r;
1474*c77c4889SXin LI 	}
1475*c77c4889SXin LI 	return OSC8_NO_MATCH;
1476*c77c4889SXin LI }
1477*c77c4889SXin LI 
1478*c77c4889SXin LI /*
1479*c77c4889SXin LI  * Shift display to make the currently selected OSC8 hyperlink visible.
1480*c77c4889SXin LI  */
1481*c77c4889SXin LI static void osc8_shift_visible(void)
1482*c77c4889SXin LI {
1483*c77c4889SXin LI 	if (chop_line())
1484*c77c4889SXin LI 	{
1485*c77c4889SXin LI 		size_t start_off = (size_t)(osc8_match_start - osc8_linepos);
1486*c77c4889SXin LI 		size_t end_off = (size_t)(osc8_match_end - osc8_linepos);
1487*c77c4889SXin LI 		shift_visible(osc8_linepos, start_off, end_off);
1488*c77c4889SXin LI 	}
1489*c77c4889SXin LI 	/* {{ What about the (plastlinepos != NULL) case in search_range? }} */
1490*c77c4889SXin LI }
1491*c77c4889SXin LI 
1492*c77c4889SXin LI #endif /* OSC8_LINK */
14932235c7feSXin LI 
14942235c7feSXin LI /*
1495a5f0fb15SPaul Saab  * Search a subset of the file, specified by start/end position.
1496a5f0fb15SPaul Saab  */
1497d713e089SXin LI static int search_range(POSITION pos, POSITION endpos, int search_type, int matches, int maxlines, POSITION *plinepos, POSITION *pendpos, POSITION *plastlinepos)
1498a5f0fb15SPaul Saab {
1499*c77c4889SXin LI 	constant char *line;
1500423c5ce5SXin LI 	char *cline;
1501*c77c4889SXin LI 	size_t line_len;
15021ede1615STim J. Robbins 	LINENUM linenum;
1503d713e089SXin LI 	#define NSP (NUM_SEARCH_COLORS+2)
1504*c77c4889SXin LI 	constant char *sp[NSP];
1505*c77c4889SXin LI 	constant char *ep[NSP];
1506a5f0fb15SPaul Saab 	int line_match;
15071ede1615STim J. Robbins 	int cvt_ops;
1508*c77c4889SXin LI 	size_t cvt_len;
1509f0be0a1fSXin LI 	int *chpos;
1510a5f0fb15SPaul Saab 	POSITION linepos, oldpos;
1511d713e089SXin LI 	int skip_bytes = 0;
1512*c77c4889SXin LI 	size_t swidth = (size_t) (sc_width - line_pfx_width()); /*{{type-issue}}*/
1513*c77c4889SXin LI 	size_t sheight = (size_t) (sc_height - sindex_from_sline(jump_sline));
1514a5f0fb15SPaul Saab 
1515a5f0fb15SPaul Saab 	linenum = find_linenum(pos);
1516*c77c4889SXin LI 	if (nosearch_header_lines && linenum <= header_lines)
1517d713e089SXin LI 	{
1518d713e089SXin LI 		linenum = header_lines + 1;
1519d713e089SXin LI 		pos = find_pos(linenum);
1520d713e089SXin LI 	}
1521d713e089SXin LI 	if (pos == NULL_POSITION)
1522d713e089SXin LI 		return (-1);
1523a5f0fb15SPaul Saab 	oldpos = pos;
15242235c7feSXin LI 	/* When the search wraps around, end at starting position. */
15252235c7feSXin LI 	if ((search_type & SRCH_WRAP) && endpos == NULL_POSITION)
15262235c7feSXin LI 		endpos = pos;
1527a5f0fb15SPaul Saab 	for (;;)
1528a5f0fb15SPaul Saab 	{
1529a5f0fb15SPaul Saab 		/*
1530a5f0fb15SPaul Saab 		 * Get lines until we find a matching one or until
1531a5f0fb15SPaul Saab 		 * we hit end-of-file (or beginning-of-file if we're
1532a5f0fb15SPaul Saab 		 * going backwards), or until we hit the end position.
1533a5f0fb15SPaul Saab 		 */
1534a5f0fb15SPaul Saab 		if (ABORT_SIGS())
1535a5f0fb15SPaul Saab 		{
1536a5f0fb15SPaul Saab 			/*
1537a5f0fb15SPaul Saab 			 * A signal aborts the search.
1538a5f0fb15SPaul Saab 			 */
1539a5f0fb15SPaul Saab 			return (-1);
1540a5f0fb15SPaul Saab 		}
1541a5f0fb15SPaul Saab 
15422235c7feSXin LI 		if ((endpos != NULL_POSITION && !(search_type & SRCH_WRAP) &&
15432235c7feSXin LI 			(((search_type & SRCH_FORW) && pos >= endpos) ||
15442235c7feSXin LI 			 ((search_type & SRCH_BACK) && pos <= endpos))) || maxlines == 0)
1545a5f0fb15SPaul Saab 		{
1546a5f0fb15SPaul Saab 			/*
1547a5f0fb15SPaul Saab 			 * Reached end position without a match.
1548a5f0fb15SPaul Saab 			 */
1549a5f0fb15SPaul Saab 			if (pendpos != NULL)
1550a5f0fb15SPaul Saab 				*pendpos = pos;
1551a5f0fb15SPaul Saab 			return (matches);
1552a5f0fb15SPaul Saab 		}
1553a5f0fb15SPaul Saab 		if (maxlines > 0)
1554a5f0fb15SPaul Saab 			maxlines--;
1555a5f0fb15SPaul Saab 
1556a5f0fb15SPaul Saab 		if (search_type & SRCH_FORW)
1557a5f0fb15SPaul Saab 		{
1558a5f0fb15SPaul Saab 			/*
1559a5f0fb15SPaul Saab 			 * Read the next line, and save the
1560a5f0fb15SPaul Saab 			 * starting position of that line in linepos.
1561a5f0fb15SPaul Saab 			 */
1562a5f0fb15SPaul Saab 			linepos = pos;
1563720c436cSXin LI 			pos = forw_raw_line(pos, &line, &line_len);
1564a5f0fb15SPaul Saab 			if (linenum != 0)
1565a5f0fb15SPaul Saab 				linenum++;
1566a5f0fb15SPaul Saab 		} else
1567a5f0fb15SPaul Saab 		{
1568a5f0fb15SPaul Saab 			/*
1569a5f0fb15SPaul Saab 			 * Read the previous line and save the
1570a5f0fb15SPaul Saab 			 * starting position of that line in linepos.
1571a5f0fb15SPaul Saab 			 */
1572720c436cSXin LI 			pos = back_raw_line(pos, &line, &line_len);
1573a5f0fb15SPaul Saab 			linepos = pos;
1574a5f0fb15SPaul Saab 			if (linenum != 0)
1575a5f0fb15SPaul Saab 				linenum--;
1576a5f0fb15SPaul Saab 		}
1577a5f0fb15SPaul Saab 
1578a5f0fb15SPaul Saab 		if (pos == NULL_POSITION)
1579a5f0fb15SPaul Saab 		{
1580a5f0fb15SPaul Saab 			/*
1581a5f0fb15SPaul Saab 			 * Reached EOF/BOF without a match.
1582a5f0fb15SPaul Saab 			 */
15832235c7feSXin LI 			if (search_type & SRCH_WRAP)
15842235c7feSXin LI 			{
15852235c7feSXin LI 				/*
15862235c7feSXin LI 				 * The search wraps around the current file, so
15872235c7feSXin LI 				 * try to continue at BOF/EOF.
15882235c7feSXin LI 				 */
15892235c7feSXin LI 				if (search_type & SRCH_FORW)
15902235c7feSXin LI 				{
15912235c7feSXin LI 					pos = ch_zero();
15922235c7feSXin LI 				} else
15932235c7feSXin LI 				{
15942235c7feSXin LI 					pos = ch_length();
15952235c7feSXin LI 					if (pos == NULL_POSITION)
15962235c7feSXin LI 					{
15972235c7feSXin LI 						(void) ch_end_seek();
15982235c7feSXin LI 						pos = ch_length();
15992235c7feSXin LI 					}
16002235c7feSXin LI 				}
16012235c7feSXin LI 				if (pos != NULL_POSITION) {
16022235c7feSXin LI 					/*
16032235c7feSXin LI 					 * Wrap-around was successful. Clear
16042235c7feSXin LI 					 * the flag so we don't wrap again, and
16052235c7feSXin LI 					 * continue the search at new pos.
16062235c7feSXin LI 					 */
1607*c77c4889SXin LI 					search_wrapped = TRUE;
16082235c7feSXin LI 					search_type &= ~SRCH_WRAP;
16092235c7feSXin LI 					linenum = find_linenum(pos);
16102235c7feSXin LI 					continue;
16112235c7feSXin LI 				}
16122235c7feSXin LI 			}
1613a5f0fb15SPaul Saab 			if (pendpos != NULL)
1614a5f0fb15SPaul Saab 				*pendpos = oldpos;
1615a5f0fb15SPaul Saab 			return (matches);
1616a5f0fb15SPaul Saab 		}
1617a5f0fb15SPaul Saab 
1618a5f0fb15SPaul Saab 		/*
1619a5f0fb15SPaul Saab 		 * If we're using line numbers, we might as well
1620a5f0fb15SPaul Saab 		 * remember the information we have now (the position
1621a5f0fb15SPaul Saab 		 * and line number of the current line).
1622a5f0fb15SPaul Saab 		 * Don't do it for every line because it slows down
1623a5f0fb15SPaul Saab 		 * the search.  Remember the line number only if
1624a5f0fb15SPaul Saab 		 * we're "far" from the last place we remembered it.
1625a5f0fb15SPaul Saab 		 */
1626f0be0a1fSXin LI 		if (linenums && abs((int)(pos - oldpos)) > 2048)
1627a5f0fb15SPaul Saab 			add_lnum(linenum, pos);
1628a5f0fb15SPaul Saab 		oldpos = pos;
1629a5f0fb15SPaul Saab 
16302235c7feSXin LI #if HILITE_SEARCH
16317374caaaSXin LI 		if (is_filtered(linepos))
16327374caaaSXin LI 			continue;
16332235c7feSXin LI #endif
1634*c77c4889SXin LI 		if (nosearch_header_cols)
1635d713e089SXin LI 			skip_bytes = skip_columns(header_cols, &line, &line_len);
1636*c77c4889SXin LI #if OSC8_LINK
1637*c77c4889SXin LI 		if (search_type & SRCH_OSC8)
1638*c77c4889SXin LI 		{
1639*c77c4889SXin LI 			if (osc8_search_line(search_type, linepos, line, line_len, osc8_search_param, NULL_POSITION, &matches) != OSC8_NO_MATCH)
1640*c77c4889SXin LI 			{
1641*c77c4889SXin LI 				if (plinepos != NULL)
1642*c77c4889SXin LI 					*plinepos = linepos;
1643*c77c4889SXin LI 				osc8_shift_visible();
1644*c77c4889SXin LI 				return (0);
1645*c77c4889SXin LI 			}
1646*c77c4889SXin LI 			continue;
1647*c77c4889SXin LI 		}
1648*c77c4889SXin LI #endif
1649a5f0fb15SPaul Saab 		/*
1650a5f0fb15SPaul Saab 		 * If it's a caseless search, convert the line to lowercase.
1651a5f0fb15SPaul Saab 		 * If we're doing backspace processing, delete backspaces.
1652a5f0fb15SPaul Saab 		 */
1653d713e089SXin LI 		cvt_ops = get_cvt_ops(search_type);
1654f0be0a1fSXin LI 		cvt_len = cvt_length(line_len, cvt_ops);
1655f0be0a1fSXin LI 		cline = (char *) ecalloc(1, cvt_len);
1656f0be0a1fSXin LI 		chpos = cvt_alloc_chpos(cvt_len);
1657f0be0a1fSXin LI 		cvt_text(cline, line, chpos, &line_len, cvt_ops);
1658a5f0fb15SPaul Saab 
16597374caaaSXin LI #if HILITE_SEARCH
16607374caaaSXin LI 		/*
16612235c7feSXin LI 		 * If any filters are in effect, ignore non-matching lines.
16627374caaaSXin LI 		 */
16632235c7feSXin LI 		if (filter_infos != NULL &&
16642235c7feSXin LI 		   ((search_type & SRCH_FIND_ALL) ||
1665a15691bfSXin LI 		     prep_startpos == NULL_POSITION ||
16662235c7feSXin LI 		     linepos < prep_startpos || linepos >= prep_endpos)) {
1667d713e089SXin LI 			if (matches_filters(pos, cline, line_len, chpos, linepos, sp, ep, NSP))
1668a15691bfSXin LI 				continue;
16697374caaaSXin LI 		}
16707374caaaSXin LI #endif
16717374caaaSXin LI 
1672a5f0fb15SPaul Saab 		/*
1673a5f0fb15SPaul Saab 		 * Test the next line to see if we have a match.
1674a5f0fb15SPaul Saab 		 * We are successful if we either want a match and got one,
1675a5f0fb15SPaul Saab 		 * or if we want a non-match and got one.
1676a5f0fb15SPaul Saab 		 */
1677f0be0a1fSXin LI 		if (prev_pattern(&search_info))
1678423c5ce5SXin LI 		{
167996e55cc7SXin LI 			line_match = match_pattern(info_compiled(&search_info), search_info.text,
1680d713e089SXin LI 				cline, line_len, sp, ep, NSP, 0, search_type);
16817374caaaSXin LI 			if (line_match)
16827374caaaSXin LI 			{
1683a5f0fb15SPaul Saab 				/*
1684a5f0fb15SPaul Saab 				 * Got a match.
1685a5f0fb15SPaul Saab 				 */
1686a5f0fb15SPaul Saab 				if (search_type & SRCH_FIND_ALL)
1687a5f0fb15SPaul Saab 				{
1688a5f0fb15SPaul Saab #if HILITE_SEARCH
1689a5f0fb15SPaul Saab 					/*
1690a5f0fb15SPaul Saab 					 * We are supposed to find all matches in the range.
1691a5f0fb15SPaul Saab 					 * Just add the matches in this line to the
1692a5f0fb15SPaul Saab 					 * hilite list and keep searching.
1693a5f0fb15SPaul Saab 					 */
1694*c77c4889SXin LI 					hilite_line(linepos + skip_bytes, cline, line_len, chpos, sp, ep, NSP);
1695a5f0fb15SPaul Saab #endif
1696a5f0fb15SPaul Saab 				} else if (--matches <= 0)
1697a5f0fb15SPaul Saab 				{
1698a5f0fb15SPaul Saab 					/*
1699a5f0fb15SPaul Saab 					 * Found the one match we're looking for.
1700a5f0fb15SPaul Saab 					 * Return it.
1701a5f0fb15SPaul Saab 					 */
1702a5f0fb15SPaul Saab #if HILITE_SEARCH
170389dd99dcSXin LI 					if (hilite_search == OPT_ON)
1704a5f0fb15SPaul Saab 					{
1705a5f0fb15SPaul Saab 						/*
1706a5f0fb15SPaul Saab 						 * Clear the hilite list and add only
1707a5f0fb15SPaul Saab 						 * the matches in this one line.
1708a5f0fb15SPaul Saab 						 */
1709a5f0fb15SPaul Saab 						clr_hilite();
1710*c77c4889SXin LI 						hilite_line(linepos + skip_bytes, cline, line_len, chpos, sp, ep, NSP);
1711a5f0fb15SPaul Saab 					}
1712a5f0fb15SPaul Saab #endif
171395270f73SXin LI 					if (chop_line())
17142235c7feSXin LI 					{
17152235c7feSXin LI 						/*
17162235c7feSXin LI 						 * If necessary, shift horizontally to make sure
17172235c7feSXin LI 						 * search match is fully visible.
17182235c7feSXin LI 						 */
1719d713e089SXin LI 						if (sp[0] != NULL && ep[0] != NULL)
17202235c7feSXin LI 						{
1721*c77c4889SXin LI 							size_t start_off = ptr_diff(sp[0], cline);
1722*c77c4889SXin LI 							size_t end_off = ptr_diff(ep[0], cline);
1723*c77c4889SXin LI 							shift_visible(linepos, chpos[start_off], chpos[end_off]);
17242235c7feSXin LI 						}
17252235c7feSXin LI 					} else if (plastlinepos != NULL)
17262235c7feSXin LI 					{
17272235c7feSXin LI 						/*
17282235c7feSXin LI 						 * If the line is so long that the highlighted match
17292235c7feSXin LI 						 * won't be seen when the line is displayed normally
17302235c7feSXin LI 						 * (starting at the first char) because it fills the whole
17312235c7feSXin LI 						 * screen and more, scroll forward until the last char
17322235c7feSXin LI 						 * of the match appears in the last line on the screen.
17332235c7feSXin LI 						 * lastlinepos is the position of the first char of that last line.
17342235c7feSXin LI 						 */
1735d713e089SXin LI 						if (ep[0] != NULL)
17362235c7feSXin LI 						{
1737*c77c4889SXin LI 							size_t end_off = ptr_diff(ep[0], cline);
17382235c7feSXin LI 							if (end_off >= swidth * sheight / 4) /* heuristic */
1739*c77c4889SXin LI 								*plastlinepos = get_lastlinepos(linepos, linepos + chpos[end_off], (int) sheight);
17402235c7feSXin LI 						}
17412235c7feSXin LI 					}
1742423c5ce5SXin LI 					free(cline);
1743f0be0a1fSXin LI 					free(chpos);
1744a5f0fb15SPaul Saab 					if (plinepos != NULL)
1745a5f0fb15SPaul Saab 						*plinepos = linepos;
1746a5f0fb15SPaul Saab 					return (0);
1747a5f0fb15SPaul Saab 				}
1748a5f0fb15SPaul Saab 			}
1749a5f0fb15SPaul Saab 		}
17507374caaaSXin LI 		free(cline);
1751f0be0a1fSXin LI 		free(chpos);
17527374caaaSXin LI 	}
17537374caaaSXin LI }
1754a5f0fb15SPaul Saab 
1755*c77c4889SXin LI #if OSC8_LINK
1756*c77c4889SXin LI 
1757*c77c4889SXin LI /*
1758*c77c4889SXin LI  * Search for and select the next OSC8 sequence, forward or backward.
1759*c77c4889SXin LI  */
1760*c77c4889SXin LI public void osc8_search(int search_type, constant char *param, int matches)
1761*c77c4889SXin LI {
1762*c77c4889SXin LI 	POSITION pos;
1763*c77c4889SXin LI 	int match;
1764*c77c4889SXin LI 
1765*c77c4889SXin LI 	if (osc8_linepos != NULL_POSITION)
1766*c77c4889SXin LI 	{
1767*c77c4889SXin LI 		/* Continue search in same line as current match. */
1768*c77c4889SXin LI 		constant char *line;
1769*c77c4889SXin LI 		size_t line_len;
1770*c77c4889SXin LI 		pos = forw_raw_line(osc8_linepos, &line, &line_len);
1771*c77c4889SXin LI 		if (pos != NULL_POSITION)
1772*c77c4889SXin LI 		{
1773*c77c4889SXin LI 			if (osc8_search_line(search_type, osc8_linepos, line, line_len, param, NULL_POSITION, &matches) != OSC8_NO_MATCH)
1774*c77c4889SXin LI 			{
1775*c77c4889SXin LI 				no_eof_bell = TRUE;
1776*c77c4889SXin LI 				jump_loc(osc8_linepos, jump_sline);
1777*c77c4889SXin LI 				no_eof_bell = FALSE;
1778*c77c4889SXin LI 				osc8_shift_visible();
1779*c77c4889SXin LI #if HILITE_SEARCH
1780*c77c4889SXin LI 				repaint_hilite(TRUE);
1781*c77c4889SXin LI #endif
1782*c77c4889SXin LI 				return;
1783*c77c4889SXin LI 			}
1784*c77c4889SXin LI 		}
1785*c77c4889SXin LI 		search_type |= SRCH_AFTER_TARGET;
1786*c77c4889SXin LI 	}
1787*c77c4889SXin LI 	pos = search_pos(search_type);
1788*c77c4889SXin LI 	if (pos == NULL_POSITION)
1789*c77c4889SXin LI 	{
1790*c77c4889SXin LI 		error("Nothing to search", NULL_PARG);
1791*c77c4889SXin LI 		return;
1792*c77c4889SXin LI 	}
1793*c77c4889SXin LI 	osc8_search_param = param;
1794*c77c4889SXin LI 	match = search_range(pos, NULL_POSITION, search_type | SRCH_OSC8, matches, -1, &pos, NULL, NULL);
1795*c77c4889SXin LI 	osc8_search_param = NULL;
1796*c77c4889SXin LI 	if (match != 0)
1797*c77c4889SXin LI 	{
1798*c77c4889SXin LI 		error("OSC 8 link not found", NULL_PARG);
1799*c77c4889SXin LI 		return;
1800*c77c4889SXin LI 	}
1801*c77c4889SXin LI 	jump_loc(pos, jump_sline);
1802*c77c4889SXin LI #if HILITE_SEARCH
1803*c77c4889SXin LI 	repaint_hilite(TRUE);
1804*c77c4889SXin LI #endif
1805*c77c4889SXin LI }
1806*c77c4889SXin LI 
1807*c77c4889SXin LI /*
1808*c77c4889SXin LI  * If a mouse click is on an OSC 8 link, select the link.
1809*c77c4889SXin LI  */
1810*c77c4889SXin LI public lbool osc8_click(int sindex, int col)
1811*c77c4889SXin LI {
1812*c77c4889SXin LI #if OSC8_LINK
1813*c77c4889SXin LI 	POSITION linepos = position(sindex);
1814*c77c4889SXin LI 	POSITION clickpos;
1815*c77c4889SXin LI 	constant char *line;
1816*c77c4889SXin LI 	size_t line_len;
1817*c77c4889SXin LI 	int matches = 1;
1818*c77c4889SXin LI 	int r;
1819*c77c4889SXin LI 
1820*c77c4889SXin LI 	if (linepos == NULL_POSITION)
1821*c77c4889SXin LI 		return FALSE;
1822*c77c4889SXin LI 	clickpos = pos_from_col(linepos, col, NULL_POSITION, -1);
1823*c77c4889SXin LI 	if (clickpos == NULL_POSITION)
1824*c77c4889SXin LI 		return FALSE;
1825*c77c4889SXin LI 	if (forw_raw_line(linepos, &line, &line_len) == NULL_POSITION)
1826*c77c4889SXin LI 		return FALSE;
1827*c77c4889SXin LI 	r = osc8_search_line(SRCH_FORW|SRCH_OSC8, linepos, line, line_len, NULL, clickpos, &matches);
1828*c77c4889SXin LI 	if (r != OSC8_NO_MATCH)
1829*c77c4889SXin LI 	{
1830*c77c4889SXin LI #if HILITE_SEARCH
1831*c77c4889SXin LI 		repaint_hilite(TRUE);
1832*c77c4889SXin LI #endif
1833*c77c4889SXin LI 		if (r == OSC8_ALREADY)
1834*c77c4889SXin LI 			osc8_open();
1835*c77c4889SXin LI 		return TRUE;
1836*c77c4889SXin LI 	}
1837*c77c4889SXin LI #else
1838*c77c4889SXin LI 	(void) sindex; (void) col;
1839*c77c4889SXin LI #endif /* OSC8_LINK */
1840*c77c4889SXin LI 	return FALSE;
1841*c77c4889SXin LI }
1842*c77c4889SXin LI 
1843*c77c4889SXin LI /*
1844*c77c4889SXin LI  * Return the length of the scheme prefix in a URI.
1845*c77c4889SXin LI  */
1846*c77c4889SXin LI static int scheme_length(constant char *uri, size_t uri_len)
1847*c77c4889SXin LI {
1848*c77c4889SXin LI 	size_t plen;
1849*c77c4889SXin LI 	for (plen = 0;  plen < uri_len;  plen++)
1850*c77c4889SXin LI 		if (uri[plen] == ':')
1851*c77c4889SXin LI 			return plen;
1852*c77c4889SXin LI 	return 0;
1853*c77c4889SXin LI }
1854*c77c4889SXin LI 
1855*c77c4889SXin LI /*
1856*c77c4889SXin LI  * Does a URI contain any dangerous characters?
1857*c77c4889SXin LI  */
1858*c77c4889SXin LI static lbool bad_uri(constant char *uri, size_t uri_len)
1859*c77c4889SXin LI {
1860*c77c4889SXin LI 	size_t i;
1861*c77c4889SXin LI 	for (i = 0;  i < uri_len;  i++)
1862*c77c4889SXin LI 		if (strchr("'\"", uri[i]) != NULL)
1863*c77c4889SXin LI 			return TRUE;
1864*c77c4889SXin LI 	return FALSE;
1865*c77c4889SXin LI }
1866*c77c4889SXin LI 
1867*c77c4889SXin LI /*
1868*c77c4889SXin LI  * Re-read the line containing the selected OSC8 link.
1869*c77c4889SXin LI  */
1870*c77c4889SXin LI static lbool osc8_read_selected(struct osc8_parse_info *op)
1871*c77c4889SXin LI {
1872*c77c4889SXin LI 	constant char *line;
1873*c77c4889SXin LI 	size_t line_len;
1874*c77c4889SXin LI 	POSITION pos;
1875*c77c4889SXin LI 
1876*c77c4889SXin LI 	pos = forw_raw_line(osc8_linepos, &line, &line_len);
1877*c77c4889SXin LI 	if (pos == NULL_POSITION)
1878*c77c4889SXin LI 		return FALSE;
1879*c77c4889SXin LI 	op->osc8_start    = &line[osc8_match_start - osc8_linepos];
1880*c77c4889SXin LI 	op->osc8_end      = &line[osc8_match_end - osc8_linepos];
1881*c77c4889SXin LI 	op->params_start  = &line[osc8_params_start - osc8_linepos];
1882*c77c4889SXin LI 	op->params_end    = &line[osc8_params_end - osc8_linepos];
1883*c77c4889SXin LI 	op->uri_start     = &line[osc8_uri_start - osc8_linepos];
1884*c77c4889SXin LI 	op->uri_end       = &line[osc8_uri_end - osc8_linepos];
1885*c77c4889SXin LI 	return TRUE;
1886*c77c4889SXin LI }
1887*c77c4889SXin LI 
1888*c77c4889SXin LI /*
1889*c77c4889SXin LI  * Open the currently selected OSC8 link.
1890*c77c4889SXin LI  */
1891*c77c4889SXin LI public void osc8_open(void)
1892*c77c4889SXin LI {
1893*c77c4889SXin LI 	struct osc8_parse_info op;
1894*c77c4889SXin LI 	char env_name[64];
1895*c77c4889SXin LI 	size_t scheme_len;
1896*c77c4889SXin LI 	constant char *handler;
1897*c77c4889SXin LI 	char *open_cmd;
1898*c77c4889SXin LI 	size_t uri_len;
1899*c77c4889SXin LI 	FILE *hf;
1900*c77c4889SXin LI 	static constant char *env_name_pfx = "LESS_OSC8_";
1901*c77c4889SXin LI 
1902*c77c4889SXin LI 	if (osc8_linepos == NULL_POSITION)
1903*c77c4889SXin LI 	{
1904*c77c4889SXin LI 		error("No OSC8 link selected", NULL_PARG);
1905*c77c4889SXin LI 		return;
1906*c77c4889SXin LI 	}
1907*c77c4889SXin LI 	if (!osc8_read_selected(&op))
1908*c77c4889SXin LI 	{
1909*c77c4889SXin LI 		error("Cannot find OSC8 link", NULL_PARG);
1910*c77c4889SXin LI 		return;
1911*c77c4889SXin LI 	}
1912*c77c4889SXin LI 	/*
1913*c77c4889SXin LI 	 * Read a "handler" shell cmd from environment variable "LESS_OSC8_scheme".
1914*c77c4889SXin LI 	 * pr_expand the handler cmd (to expand %o -> osc8_path) and execute it.
1915*c77c4889SXin LI 	 * Handler's stdout is an "opener" shell cmd; execute opener to open the link.
1916*c77c4889SXin LI 	 */
1917*c77c4889SXin LI 	uri_len = ptr_diff(op.uri_end, op.uri_start);
1918*c77c4889SXin LI 	scheme_len = scheme_length(op.uri_start, uri_len);
1919*c77c4889SXin LI 	if (scheme_len == 0 && op.uri_start[0] == '#')
1920*c77c4889SXin LI 	{
1921*c77c4889SXin LI 		/* Link to "id=" in same file. */
1922*c77c4889SXin LI 		char *param = ecalloc(uri_len+3, sizeof(char));
1923*c77c4889SXin LI 		strcpy(param, "id=");
1924*c77c4889SXin LI 		strncpy(param+3, op.uri_start+1, uri_len-1);
1925*c77c4889SXin LI 		param[uri_len+2] = '\0';
1926*c77c4889SXin LI 		osc8_search(SRCH_FORW|SRCH_WRAP, param, 1);
1927*c77c4889SXin LI 		free(param);
1928*c77c4889SXin LI 		return;
1929*c77c4889SXin LI 	}
1930*c77c4889SXin LI #if HAVE_POPEN
1931*c77c4889SXin LI 	if (bad_uri(op.uri_start, uri_len))
1932*c77c4889SXin LI 	{
1933*c77c4889SXin LI 		error("Cannot open link containing dangerous characters", NULL_PARG);
1934*c77c4889SXin LI 		return;
1935*c77c4889SXin LI 	}
1936*c77c4889SXin LI 	SNPRINTF3(env_name, sizeof(env_name), "%s%.*s", env_name_pfx, (int) scheme_len, op.uri_start);
1937*c77c4889SXin LI 	handler = lgetenv(env_name);
1938*c77c4889SXin LI 	if (isnullenv(handler) || strcmp(handler, "-") == 0)
1939*c77c4889SXin LI         handler = lgetenv("LESS_OSC8_ANY");
1940*c77c4889SXin LI 	if (isnullenv(handler))
1941*c77c4889SXin LI 	{
1942*c77c4889SXin LI 		PARG parg;
1943*c77c4889SXin LI 		parg.p_string = env_name + strlen(env_name_pfx); /* {{ tricky }} */
1944*c77c4889SXin LI 		error("No handler for \"%s\" link type", &parg);
1945*c77c4889SXin LI 		return;
1946*c77c4889SXin LI 	}
1947*c77c4889SXin LI 	/* {{ ugly global osc8_path }} */
1948*c77c4889SXin LI 	osc8_path = saven(op.uri_start, uri_len);
1949*c77c4889SXin LI 	hf = popen(pr_expand(handler), "r");
1950*c77c4889SXin LI 	free(osc8_path);
1951*c77c4889SXin LI 	osc8_path = NULL;
1952*c77c4889SXin LI 	if (hf == NULL)
1953*c77c4889SXin LI 	{
1954*c77c4889SXin LI 		PARG parg;
1955*c77c4889SXin LI 		parg.p_string = env_name;
1956*c77c4889SXin LI 		error("Cannot execute protocol handler in %s", &parg);
1957*c77c4889SXin LI 		return;
1958*c77c4889SXin LI 	}
1959*c77c4889SXin LI 	open_cmd = readfd(hf);
1960*c77c4889SXin LI 	pclose(hf);
1961*c77c4889SXin LI 	if (strncmp(open_cmd, ":e", 2) == 0)
1962*c77c4889SXin LI 	{
1963*c77c4889SXin LI 		edit(skipsp(&open_cmd[2]));
1964*c77c4889SXin LI 	} else
1965*c77c4889SXin LI 	{
1966*c77c4889SXin LI 		lsystem(open_cmd, "link done");
1967*c77c4889SXin LI 	}
1968*c77c4889SXin LI 	free(open_cmd);
1969*c77c4889SXin LI #else
1970*c77c4889SXin LI 	error("Cannot open link because your system does not support popen", NULL_PARG);
1971*c77c4889SXin LI #endif /* HAVE_POPEN */
1972*c77c4889SXin LI }
1973*c77c4889SXin LI 
1974*c77c4889SXin LI /*
1975*c77c4889SXin LI  * Jump to the currently selected OSC8 link.
1976*c77c4889SXin LI  */
1977*c77c4889SXin LI public void osc8_jump(void)
1978*c77c4889SXin LI {
1979*c77c4889SXin LI 	if (osc8_linepos == NULL_POSITION)
1980*c77c4889SXin LI 	{
1981*c77c4889SXin LI 		error("No OSC8 link selected", NULL_PARG);
1982*c77c4889SXin LI 		return;
1983*c77c4889SXin LI 	}
1984*c77c4889SXin LI 	jump_loc(osc8_linepos, jump_sline);
1985*c77c4889SXin LI }
1986*c77c4889SXin LI 
1987*c77c4889SXin LI #endif /* OSC8_LINK */
1988*c77c4889SXin LI 
1989a5f0fb15SPaul Saab /*
1990720c436cSXin LI  * search for a pattern in history. If found, compile that pattern.
1991720c436cSXin LI  */
1992d713e089SXin LI static int hist_pattern(int search_type)
1993720c436cSXin LI {
1994720c436cSXin LI #if CMD_HISTORY
1995*c77c4889SXin LI 	constant char *pattern;
1996720c436cSXin LI 
1997720c436cSXin LI 	set_mlist(ml_search, 0);
1998720c436cSXin LI 	pattern = cmd_lastpattern();
1999720c436cSXin LI 	if (pattern == NULL)
2000720c436cSXin LI 		return (0);
2001720c436cSXin LI 
20022235c7feSXin LI 	if (set_pattern(&search_info, pattern, search_type, 1) < 0)
20032235c7feSXin LI 		return (-1);
2004720c436cSXin LI 
2005720c436cSXin LI #if HILITE_SEARCH
2006720c436cSXin LI 	if (hilite_search == OPT_ONPLUS && !hide_hilite)
2007720c436cSXin LI 		hilite_screen();
2008720c436cSXin LI #endif
2009720c436cSXin LI 
2010720c436cSXin LI 	return (1);
2011720c436cSXin LI #else /* CMD_HISTORY */
2012720c436cSXin LI 	return (0);
2013720c436cSXin LI #endif /* CMD_HISTORY */
2014720c436cSXin LI }
2015720c436cSXin LI 
2016720c436cSXin LI /*
2017f6b74a7dSXin LI  * Change the caseless-ness of searches.
2018f6b74a7dSXin LI  * Updates the internal search state to reflect a change in the -i flag.
2019f6b74a7dSXin LI  */
2020d713e089SXin LI public void chg_caseless(void)
2021f6b74a7dSXin LI {
2022d713e089SXin LI 	if (!search_info.is_ucase_pattern)
2023f6b74a7dSXin LI 	{
2024f6b74a7dSXin LI 		/*
202595270f73SXin LI 		 * Pattern did not have uppercase.
202695270f73SXin LI 		 * Set the search caselessness to the global caselessness.
202795270f73SXin LI 		 */
202895270f73SXin LI 		is_caseless = caseless;
202995270f73SXin LI 		/*
203095270f73SXin LI 		 * If regex handles caseless, we need to discard
203195270f73SXin LI 		 * the pattern which was compiled with the old caseless.
203295270f73SXin LI 		 */
203395270f73SXin LI 		if (!re_handles_caseless)
203495270f73SXin LI 			/* We handle caseless, so the pattern doesn't change. */
203595270f73SXin LI 			return;
203695270f73SXin LI 	}
203795270f73SXin LI 	/*
2038f6b74a7dSXin LI 	 * Regenerate the pattern using the new state.
2039f6b74a7dSXin LI 	 */
2040f6b74a7dSXin LI 	clear_pattern(&search_info);
20412235c7feSXin LI 	(void) hist_pattern(search_info.search_type);
2042f6b74a7dSXin LI }
2043f6b74a7dSXin LI 
2044f6b74a7dSXin LI /*
2045a5f0fb15SPaul Saab  * Search for the n-th occurrence of a specified pattern,
2046a5f0fb15SPaul Saab  * either forward or backward.
2047a5f0fb15SPaul Saab  * Return the number of matches not yet found in this file
2048a5f0fb15SPaul Saab  * (that is, n minus the number of matches found).
2049a5f0fb15SPaul Saab  * Return -1 if the search should be aborted.
2050a5f0fb15SPaul Saab  * Caller may continue the search in another file
2051a5f0fb15SPaul Saab  * if less than n matches are found in this file.
2052a5f0fb15SPaul Saab  */
2053*c77c4889SXin LI public int search(int search_type, constant char *pattern, int n)
2054a5f0fb15SPaul Saab {
2055a5f0fb15SPaul Saab 	POSITION pos;
20562235c7feSXin LI 	POSITION opos;
20572235c7feSXin LI 	POSITION lastlinepos = NULL_POSITION;
2058a5f0fb15SPaul Saab 
2059a5f0fb15SPaul Saab 	if (pattern == NULL || *pattern == '\0')
2060a5f0fb15SPaul Saab 	{
2061a5f0fb15SPaul Saab 		/*
2062a5f0fb15SPaul Saab 		 * A null pattern means use the previously compiled pattern.
2063a5f0fb15SPaul Saab 		 */
206433096f16SXin LI 		search_type |= SRCH_AFTER_TARGET;
20652235c7feSXin LI 		if (!prev_pattern(&search_info))
2066a5f0fb15SPaul Saab 		{
20672235c7feSXin LI 			int r = hist_pattern(search_type);
20682235c7feSXin LI 			if (r == 0)
2069a5f0fb15SPaul Saab 				error("No previous regular expression", NULL_PARG);
20702235c7feSXin LI 			if (r <= 0)
2071a5f0fb15SPaul Saab 				return (-1);
2072a5f0fb15SPaul Saab 		}
2073a5f0fb15SPaul Saab 		if ((search_type & SRCH_NO_REGEX) !=
2074f0be0a1fSXin LI 		      (search_info.search_type & SRCH_NO_REGEX))
2075a5f0fb15SPaul Saab 		{
2076a5f0fb15SPaul Saab 			error("Please re-enter search pattern", NULL_PARG);
2077a5f0fb15SPaul Saab 			return -1;
2078a5f0fb15SPaul Saab 		}
2079a5f0fb15SPaul Saab #if HILITE_SEARCH
2080b2ea2440SXin LI 		if (hilite_search == OPT_ON || status_col)
2081a5f0fb15SPaul Saab 		{
2082a5f0fb15SPaul Saab 			/*
2083a5f0fb15SPaul Saab 			 * Erase the highlights currently on screen.
2084a5f0fb15SPaul Saab 			 * If the search fails, we'll redisplay them later.
2085a5f0fb15SPaul Saab 			 */
2086*c77c4889SXin LI 			repaint_hilite(FALSE);
2087a5f0fb15SPaul Saab 		}
2088a5f0fb15SPaul Saab 		if (hilite_search == OPT_ONPLUS && hide_hilite)
2089a5f0fb15SPaul Saab 		{
2090a5f0fb15SPaul Saab 			/*
2091a5f0fb15SPaul Saab 			 * Highlight any matches currently on screen,
2092a5f0fb15SPaul Saab 			 * before we actually start the search.
2093a5f0fb15SPaul Saab 			 */
2094*c77c4889SXin LI 			hide_hilite = FALSE;
2095a5f0fb15SPaul Saab 			hilite_screen();
2096a5f0fb15SPaul Saab 		}
2097*c77c4889SXin LI 		hide_hilite = FALSE;
2098a5f0fb15SPaul Saab #endif
2099a5f0fb15SPaul Saab 	} else
2100a5f0fb15SPaul Saab 	{
2101a5f0fb15SPaul Saab 		/*
2102a5f0fb15SPaul Saab 		 * Compile the pattern.
2103a5f0fb15SPaul Saab 		 */
21042235c7feSXin LI 		int show_error = !(search_type & SRCH_INCR);
21052235c7feSXin LI 		if (set_pattern(&search_info, pattern, search_type, show_error) < 0)
2106a5f0fb15SPaul Saab 			return (-1);
2107a5f0fb15SPaul Saab #if HILITE_SEARCH
2108b2ea2440SXin LI 		if (hilite_search || status_col)
2109a5f0fb15SPaul Saab 		{
2110a5f0fb15SPaul Saab 			/*
2111a5f0fb15SPaul Saab 			 * Erase the highlights currently on screen.
2112a5f0fb15SPaul Saab 			 * Also permanently delete them from the hilite list.
2113a5f0fb15SPaul Saab 			 */
2114*c77c4889SXin LI 			repaint_hilite(FALSE);
2115*c77c4889SXin LI 			hide_hilite = FALSE;
2116a5f0fb15SPaul Saab 			clr_hilite();
2117a5f0fb15SPaul Saab 		}
2118b2ea2440SXin LI 		if (hilite_search == OPT_ONPLUS || status_col)
2119a5f0fb15SPaul Saab 		{
2120a5f0fb15SPaul Saab 			/*
2121a5f0fb15SPaul Saab 			 * Highlight any matches currently on screen,
2122a5f0fb15SPaul Saab 			 * before we actually start the search.
2123a5f0fb15SPaul Saab 			 */
2124a5f0fb15SPaul Saab 			hilite_screen();
2125a5f0fb15SPaul Saab 		}
2126a5f0fb15SPaul Saab #endif
2127a5f0fb15SPaul Saab 	}
2128a5f0fb15SPaul Saab 
2129a5f0fb15SPaul Saab 	/*
2130a5f0fb15SPaul Saab 	 * Figure out where to start the search.
2131a5f0fb15SPaul Saab 	 */
2132a5f0fb15SPaul Saab 	pos = search_pos(search_type);
21332235c7feSXin LI 	opos = position(sindex_from_sline(jump_sline));
2134a5f0fb15SPaul Saab 	if (pos == NULL_POSITION)
2135a5f0fb15SPaul Saab 	{
2136a5f0fb15SPaul Saab 		/*
2137a5f0fb15SPaul Saab 		 * Can't find anyplace to start searching from.
2138a5f0fb15SPaul Saab 		 */
2139a5f0fb15SPaul Saab 		if (search_type & SRCH_PAST_EOF)
2140a5f0fb15SPaul Saab 			return (n);
21412235c7feSXin LI #if HILITE_SEARCH
2142b2ea2440SXin LI 		if (hilite_search == OPT_ON || status_col)
2143*c77c4889SXin LI 			repaint_hilite(TRUE);
21442235c7feSXin LI #endif
2145a5f0fb15SPaul Saab 		error("Nothing to search", NULL_PARG);
2146a5f0fb15SPaul Saab 		return (-1);
2147a5f0fb15SPaul Saab 	}
2148a5f0fb15SPaul Saab 
2149a5f0fb15SPaul Saab 	n = search_range(pos, NULL_POSITION, search_type, n, -1,
21502235c7feSXin LI 			&pos, (POSITION*)NULL, &lastlinepos);
2151*c77c4889SXin LI 	/*
2152*c77c4889SXin LI 	 * This ABORT_SIGS check ensures that if the user presses interrupt,
2153*c77c4889SXin LI 	 * we don't continue and complete the search.
2154*c77c4889SXin LI 	 * That is, we leave the display unchanged.
2155*c77c4889SXin LI 	 * {{ Is this true? Do we always want to abort the search on interrupt? }}
2156*c77c4889SXin LI 	 */
2157*c77c4889SXin LI 	if (ABORT_SIGS())
2158*c77c4889SXin LI 		return (-1);
2159a5f0fb15SPaul Saab 	if (n != 0)
2160a5f0fb15SPaul Saab 	{
2161a5f0fb15SPaul Saab 		/*
2162a5f0fb15SPaul Saab 		 * Search was unsuccessful.
2163a5f0fb15SPaul Saab 		 */
2164a5f0fb15SPaul Saab #if HILITE_SEARCH
2165b2ea2440SXin LI 		if ((hilite_search == OPT_ON || status_col) && n > 0)
2166a5f0fb15SPaul Saab 			/*
2167a5f0fb15SPaul Saab 			 * Redisplay old hilites.
2168a5f0fb15SPaul Saab 			 */
2169*c77c4889SXin LI 			repaint_hilite(TRUE);
2170a5f0fb15SPaul Saab #endif
2171a5f0fb15SPaul Saab 		return (n);
2172a5f0fb15SPaul Saab 	}
2173a5f0fb15SPaul Saab 
2174a5f0fb15SPaul Saab 	if (!(search_type & SRCH_NO_MOVE))
2175a5f0fb15SPaul Saab 	{
2176a5f0fb15SPaul Saab 		/*
2177a5f0fb15SPaul Saab 		 * Go to the matching line.
2178a5f0fb15SPaul Saab 		 */
21792235c7feSXin LI 		if (lastlinepos != NULL_POSITION)
21802235c7feSXin LI 			jump_loc(lastlinepos, BOTTOM);
21812235c7feSXin LI 		else if (pos != opos)
2182a5f0fb15SPaul Saab 			jump_loc(pos, jump_sline);
2183a5f0fb15SPaul Saab 	}
2184a5f0fb15SPaul Saab 
2185a5f0fb15SPaul Saab #if HILITE_SEARCH
2186b2ea2440SXin LI 	if (hilite_search == OPT_ON || status_col)
2187a5f0fb15SPaul Saab 		/*
2188a5f0fb15SPaul Saab 		 * Display new hilites in the matching line.
2189a5f0fb15SPaul Saab 		 */
2190*c77c4889SXin LI 		repaint_hilite(TRUE);
2191a5f0fb15SPaul Saab #endif
2192a5f0fb15SPaul Saab 	return (0);
2193a5f0fb15SPaul Saab }
2194a5f0fb15SPaul Saab 
2195a5f0fb15SPaul Saab #if HILITE_SEARCH
2196a5f0fb15SPaul Saab /*
2197a5f0fb15SPaul Saab  * Prepare hilites in a given range of the file.
2198a5f0fb15SPaul Saab  *
2199a5f0fb15SPaul Saab  * The pair (prep_startpos,prep_endpos) delimits a contiguous region
2200a5f0fb15SPaul Saab  * of the file that has been "prepared"; that is, scanned for matches for
2201a5f0fb15SPaul Saab  * the current search pattern, and hilites have been created for such matches.
2202a5f0fb15SPaul Saab  * If prep_startpos == NULL_POSITION, the prep region is empty.
2203a5f0fb15SPaul Saab  * If prep_endpos == NULL_POSITION, the prep region extends to EOF.
2204a5f0fb15SPaul Saab  * prep_hilite asks that the range (spos,epos) be covered by the prep region.
2205a5f0fb15SPaul Saab  */
2206d713e089SXin LI public void prep_hilite(POSITION spos, POSITION epos, int maxlines)
2207a5f0fb15SPaul Saab {
2208a5f0fb15SPaul Saab 	POSITION nprep_startpos = prep_startpos;
2209a5f0fb15SPaul Saab 	POSITION nprep_endpos = prep_endpos;
2210a5f0fb15SPaul Saab 	POSITION new_epos;
2211a5f0fb15SPaul Saab 	POSITION max_epos;
2212a5f0fb15SPaul Saab 	int result;
2213a5f0fb15SPaul Saab 	int i;
2214f0be0a1fSXin LI 
2215a5f0fb15SPaul Saab 	/*
2216a5f0fb15SPaul Saab 	 * Search beyond where we're asked to search, so the prep region covers
2217a5f0fb15SPaul Saab 	 * more than we need.  Do one big search instead of a bunch of small ones.
2218a5f0fb15SPaul Saab 	 */
2219*c77c4889SXin LI 	POSITION SEARCH_MORE = (POSITION) (3*size_linebuf);
2220a5f0fb15SPaul Saab 
2221f0be0a1fSXin LI 	if (!prev_pattern(&search_info) && !is_filtering())
2222a5f0fb15SPaul Saab 		return;
2223a5f0fb15SPaul Saab 
2224a5f0fb15SPaul Saab 	/*
2225a15691bfSXin LI 	 * Make sure our prep region always starts at the beginning of
2226a15691bfSXin LI 	 * a line. (search_range takes care of the end boundary below.)
2227a15691bfSXin LI 	 */
2228*c77c4889SXin LI 	spos = back_raw_line(spos+1, NULL, NULL);
2229a15691bfSXin LI 
2230a15691bfSXin LI 	/*
2231a5f0fb15SPaul Saab 	 * If we're limited to a max number of lines, figure out the
2232a5f0fb15SPaul Saab 	 * file position we should stop at.
2233a5f0fb15SPaul Saab 	 */
2234a5f0fb15SPaul Saab 	if (maxlines < 0)
2235a5f0fb15SPaul Saab 		max_epos = NULL_POSITION;
2236a5f0fb15SPaul Saab 	else
2237a5f0fb15SPaul Saab 	{
2238a5f0fb15SPaul Saab 		max_epos = spos;
2239a5f0fb15SPaul Saab 		for (i = 0;  i < maxlines;  i++)
2240*c77c4889SXin LI 			max_epos = forw_raw_line(max_epos, NULL, NULL);
2241a5f0fb15SPaul Saab 	}
2242a5f0fb15SPaul Saab 
2243a5f0fb15SPaul Saab 	/*
2244a5f0fb15SPaul Saab 	 * Find two ranges:
2245a5f0fb15SPaul Saab 	 * The range that we need to search (spos,epos); and the range that
2246a5f0fb15SPaul Saab 	 * the "prep" region will then cover (nprep_startpos,nprep_endpos).
2247a5f0fb15SPaul Saab 	 */
2248a5f0fb15SPaul Saab 
2249a5f0fb15SPaul Saab 	if (prep_startpos == NULL_POSITION ||
2250a5f0fb15SPaul Saab 	    (epos != NULL_POSITION && epos < prep_startpos) ||
2251a5f0fb15SPaul Saab 	    spos > prep_endpos)
2252a5f0fb15SPaul Saab 	{
2253a5f0fb15SPaul Saab 		/*
2254a5f0fb15SPaul Saab 		 * New range is not contiguous with old prep region.
2255a5f0fb15SPaul Saab 		 * Discard the old prep region and start a new one.
2256a5f0fb15SPaul Saab 		 */
2257a5f0fb15SPaul Saab 		clr_hilite();
22587374caaaSXin LI 		clr_filter();
2259a5f0fb15SPaul Saab 		if (epos != NULL_POSITION)
2260a5f0fb15SPaul Saab 			epos += SEARCH_MORE;
2261a5f0fb15SPaul Saab 		nprep_startpos = spos;
2262a5f0fb15SPaul Saab 	} else
2263a5f0fb15SPaul Saab 	{
2264a5f0fb15SPaul Saab 		/*
2265a5f0fb15SPaul Saab 		 * New range partially or completely overlaps old prep region.
2266a5f0fb15SPaul Saab 		 */
2267a5f0fb15SPaul Saab 		if (epos == NULL_POSITION)
2268a5f0fb15SPaul Saab 		{
2269a5f0fb15SPaul Saab 			/*
2270a5f0fb15SPaul Saab 			 * New range goes to end of file.
2271a5f0fb15SPaul Saab 			 */
2272a5f0fb15SPaul Saab 			;
2273a5f0fb15SPaul Saab 		} else if (epos > prep_endpos)
2274a5f0fb15SPaul Saab 		{
2275a5f0fb15SPaul Saab 			/*
2276a5f0fb15SPaul Saab 			 * New range ends after old prep region.
2277a5f0fb15SPaul Saab 			 * Extend prep region to end at end of new range.
2278a5f0fb15SPaul Saab 			 */
2279a5f0fb15SPaul Saab 			epos += SEARCH_MORE;
2280a5f0fb15SPaul Saab 		} else /* (epos <= prep_endpos) */
2281a5f0fb15SPaul Saab 		{
2282a5f0fb15SPaul Saab 			/*
2283a5f0fb15SPaul Saab 			 * New range ends within old prep region.
2284a5f0fb15SPaul Saab 			 * Truncate search to end at start of old prep region.
2285a5f0fb15SPaul Saab 			 */
2286a5f0fb15SPaul Saab 			epos = prep_startpos;
2287a5f0fb15SPaul Saab 		}
2288a5f0fb15SPaul Saab 
2289a5f0fb15SPaul Saab 		if (spos < prep_startpos)
2290a5f0fb15SPaul Saab 		{
2291a5f0fb15SPaul Saab 			/*
2292a5f0fb15SPaul Saab 			 * New range starts before old prep region.
2293a5f0fb15SPaul Saab 			 * Extend old prep region backwards to start at
2294a5f0fb15SPaul Saab 			 * start of new range.
2295a5f0fb15SPaul Saab 			 */
2296a5f0fb15SPaul Saab 			if (spos < SEARCH_MORE)
2297a5f0fb15SPaul Saab 				spos = 0;
2298a5f0fb15SPaul Saab 			else
2299a5f0fb15SPaul Saab 				spos -= SEARCH_MORE;
2300a5f0fb15SPaul Saab 			nprep_startpos = spos;
2301a5f0fb15SPaul Saab 		} else /* (spos >= prep_startpos) */
2302a5f0fb15SPaul Saab 		{
2303a5f0fb15SPaul Saab 			/*
2304a5f0fb15SPaul Saab 			 * New range starts within or after old prep region.
2305a5f0fb15SPaul Saab 			 * Trim search to start at end of old prep region.
2306a5f0fb15SPaul Saab 			 */
2307a5f0fb15SPaul Saab 			spos = prep_endpos;
2308a5f0fb15SPaul Saab 		}
2309a5f0fb15SPaul Saab 	}
2310a5f0fb15SPaul Saab 
2311a5f0fb15SPaul Saab 	if (epos != NULL_POSITION && max_epos != NULL_POSITION &&
2312a5f0fb15SPaul Saab 	    epos > max_epos)
2313a5f0fb15SPaul Saab 		/*
2314a5f0fb15SPaul Saab 		 * Don't go past the max position we're allowed.
2315a5f0fb15SPaul Saab 		 */
2316a5f0fb15SPaul Saab 		epos = max_epos;
2317a5f0fb15SPaul Saab 
2318a5f0fb15SPaul Saab 	if (epos == NULL_POSITION || epos > spos)
2319a5f0fb15SPaul Saab 	{
2320f0be0a1fSXin LI 		int search_type = SRCH_FORW | SRCH_FIND_ALL;
2321d713e089SXin LI 		search_type |= (search_info.search_type & (SRCH_NO_REGEX|SRCH_SUBSEARCH_ALL));
2322a15691bfSXin LI 		for (;;)
2323a15691bfSXin LI 		{
23242235c7feSXin LI 			result = search_range(spos, epos, search_type, 0, maxlines, (POSITION*)NULL, &new_epos, (POSITION*)NULL);
2325a5f0fb15SPaul Saab 			if (result < 0)
2326a5f0fb15SPaul Saab 				return;
2327a5f0fb15SPaul Saab 			if (prep_endpos == NULL_POSITION || new_epos > prep_endpos)
2328a5f0fb15SPaul Saab 				nprep_endpos = new_epos;
2329a15691bfSXin LI 
2330a15691bfSXin LI 			/*
2331a15691bfSXin LI 			 * Check both ends of the resulting prep region to
2332a15691bfSXin LI 			 * make sure they're not filtered. If they are,
2333a15691bfSXin LI 			 * keep going at least one more line until we find
2334a15691bfSXin LI 			 * something that isn't filtered, or hit the end.
2335a15691bfSXin LI 			 */
2336a15691bfSXin LI 			if (prep_endpos == NULL_POSITION || nprep_endpos > prep_endpos)
2337a15691bfSXin LI 			{
2338a15691bfSXin LI 				if (new_epos >= nprep_endpos && is_filtered(new_epos-1))
2339a15691bfSXin LI 				{
2340a15691bfSXin LI 					spos = nprep_endpos;
2341*c77c4889SXin LI 					epos = forw_raw_line(nprep_endpos, NULL, NULL);
2342a15691bfSXin LI 					if (epos == NULL_POSITION)
2343a15691bfSXin LI 						break;
2344a15691bfSXin LI 					maxlines = 1;
2345a15691bfSXin LI 					continue;
2346a15691bfSXin LI 				}
2347a15691bfSXin LI 			}
2348a15691bfSXin LI 
2349a15691bfSXin LI 			if (prep_startpos == NULL_POSITION || nprep_startpos < prep_startpos)
2350a15691bfSXin LI 			{
2351a15691bfSXin LI 				if (nprep_startpos > 0 && is_filtered(nprep_startpos))
2352a15691bfSXin LI 				{
2353a15691bfSXin LI 					epos = nprep_startpos;
2354*c77c4889SXin LI 					spos = back_raw_line(nprep_startpos, NULL, NULL);
2355a15691bfSXin LI 					if (spos == NULL_POSITION)
2356a15691bfSXin LI 						break;
2357a15691bfSXin LI 					nprep_startpos = spos;
2358a15691bfSXin LI 					maxlines = 1;
2359a15691bfSXin LI 					continue;
2360a15691bfSXin LI 				}
2361a15691bfSXin LI 			}
2362a15691bfSXin LI 			break;
2363a15691bfSXin LI 		}
2364a5f0fb15SPaul Saab 	}
2365a5f0fb15SPaul Saab 	prep_startpos = nprep_startpos;
2366a5f0fb15SPaul Saab 	prep_endpos = nprep_endpos;
2367a5f0fb15SPaul Saab }
23687374caaaSXin LI 
23697374caaaSXin LI /*
23707374caaaSXin LI  * Set the pattern to be used for line filtering.
23717374caaaSXin LI  */
2372*c77c4889SXin LI public void set_filter_pattern(constant char *pattern, int search_type)
23737374caaaSXin LI {
23742235c7feSXin LI 	struct pattern_info *filter;
23752235c7feSXin LI 
23767374caaaSXin LI 	clr_filter();
23777374caaaSXin LI 	if (pattern == NULL || *pattern == '\0')
23782235c7feSXin LI 	{
23792235c7feSXin LI 		/* Clear and free all filters. */
23802235c7feSXin LI 		for (filter = filter_infos; filter != NULL; )
23812235c7feSXin LI 		{
23822235c7feSXin LI 			struct pattern_info *next_filter = filter->next;
23832235c7feSXin LI 			clear_pattern(filter);
23842235c7feSXin LI 			free(filter);
23852235c7feSXin LI 			filter = next_filter;
23862235c7feSXin LI 		}
23872235c7feSXin LI 		filter_infos = NULL;
23882235c7feSXin LI 	} else
23892235c7feSXin LI 	{
23902235c7feSXin LI 		/* Create a new filter and add it to the filter_infos list. */
23912235c7feSXin LI 		filter = ecalloc(1, sizeof(struct pattern_info));
23922235c7feSXin LI 		init_pattern(filter);
239395270f73SXin LI 		if (set_pattern(filter, pattern, search_type, 1) < 0)
239495270f73SXin LI 		{
239595270f73SXin LI 			free(filter);
239695270f73SXin LI 			return;
239795270f73SXin LI 		}
23982235c7feSXin LI 		filter->next = filter_infos;
23992235c7feSXin LI 		filter_infos = filter;
24002235c7feSXin LI 	}
2401*c77c4889SXin LI 	screen_trashed();
24027374caaaSXin LI }
24037374caaaSXin LI 
24047374caaaSXin LI /*
24057374caaaSXin LI  * Is there a line filter in effect?
24067374caaaSXin LI  */
2407*c77c4889SXin LI public lbool is_filtering(void)
24087374caaaSXin LI {
24097374caaaSXin LI 	if (ch_getflags() & CH_HELPFILE)
2410*c77c4889SXin LI 		return (FALSE);
24112235c7feSXin LI 	return (filter_infos != NULL);
24127374caaaSXin LI }
2413a5f0fb15SPaul Saab #endif
2414a5f0fb15SPaul Saab 
2415a5f0fb15SPaul Saab #if HAVE_V8_REGCOMP
2416a5f0fb15SPaul Saab /*
2417a5f0fb15SPaul Saab  * This function is called by the V8 regcomp to report
2418a5f0fb15SPaul Saab  * errors in regular expressions.
2419a5f0fb15SPaul Saab  */
2420a15691bfSXin LI public int reg_show_error = 1;
2421a15691bfSXin LI 
2422*c77c4889SXin LI void regerror(constant char *s)
2423a5f0fb15SPaul Saab {
2424a5f0fb15SPaul Saab 	PARG parg;
2425a5f0fb15SPaul Saab 
2426a15691bfSXin LI 	if (!reg_show_error)
2427a15691bfSXin LI 		return;
2428a5f0fb15SPaul Saab 	parg.p_string = s;
2429a5f0fb15SPaul Saab 	error("%s", &parg);
2430a5f0fb15SPaul Saab }
2431a5f0fb15SPaul Saab #endif
2432a5f0fb15SPaul Saab 
2433