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