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