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