1 /*
2  * Copyright (c) 1988 Mark Nudleman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed by the University of
17  *	California, Berkeley and its contributors.
18  * 4. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #ifndef lint
36 static char sccsid[] = "@(#)prim.c	8.1 (Berkeley) 6/6/93";
37 #endif /* not lint */
38 
39 #ifndef lint
40 static const char rcsid[] =
41   "$FreeBSD: src/usr.bin/more/prim.c,v 1.10 2000/05/14 03:30:59 hoek Exp $";
42 #endif /* not lint */
43 
44 /*
45  * Primitives for displaying the file on the screen.
46  */
47 
48 #include <sys/types.h>
49 
50 #include <assert.h>
51 #include <ctype.h>
52 #include <limits.h>
53 #include <regex.h>
54 #include <stdio.h>
55 
56 #include "less.h"
57 
58 extern int sigs;
59 extern int top_scroll;
60 extern int sc_width, sc_height;
61 extern int horiz_off;
62 extern int wraplines;
63 extern int caseless;
64 extern int linenums;
65 extern int tagoption;
66 extern char *line;
67 extern int retain_below;
68 
69 /*
70  * Search for the n-th occurence of a specified pattern,
71  * either forward or backward.
72  */
search(search_forward,pattern,n,wantmatch)73 search(search_forward, pattern, n, wantmatch)
74 	register int search_forward;
75 	register char *pattern;
76 	register int n;
77 	int wantmatch;
78 {
79 	off_t pos, linepos;
80 	register char *p;
81 	register char *q;
82 	int linenum;
83 	int linematch;
84 	static regex_t rx;
85 	static int oncethru;
86 	int regerr;
87 	char errbuf[_POSIX2_LINE_MAX];
88 
89 	if (pattern && pattern[0]) {
90 		if (oncethru) {
91 			regfree(&rx);
92 		}
93 
94 		regerr = regcomp(&rx, pattern, (REG_EXTENDED | REG_NOSUB
95 						| (caseless ? REG_ICASE : 0)));
96 
97 		if (regerr) {
98 			regerror(regerr, &rx, errbuf, sizeof errbuf);
99 			error(errbuf);
100 			oncethru = 0;
101 			regfree(&rx);
102 			return (0);
103 		}
104 		oncethru = 1;
105 	} else if (!oncethru) {
106 		error("No previous regular expression");
107 		return (0);
108 	}
109 
110 	/*
111 	 * Figure out where to start the search.
112 	 *
113 	 * XXX This should probably be adapted to handle horizontal
114 	 * scrolling.  Consider a long line at the top of the screen
115 	 * that might be hiding more matches to its right (when doing
116 	 * successive searches).
117 	 */
118 
119 	if (position(TOP) == NULL_POSITION) {
120 		/*
121 		 * Nothing is currently displayed.  Start at the beginning
122 		 * of the file.  (This case is mainly for searches from the
123 		 * command line.
124 		 */
125 		pos = (off_t)0;
126 	} else if (!search_forward) {
127 		/*
128 		 * Backward search: start just before the top line
129 		 * displayed on the screen.
130 		 */
131 		pos = position(TOP);
132 	} else {
133 		/*
134 		 * Start at the second screen line displayed on the screen.
135 		 */
136 		pos = position(TOP_PLUS_ONE);
137 	}
138 
139 	if (pos == NULL_POSITION)
140 	{
141 		/*
142 		 * Can't find anyplace to start searching from.
143 		 */
144 		error("Nothing to search");
145 		return(0);
146 	}
147 
148 	linenum = find_linenum(pos);
149 	for (;;)
150 	{
151 		/*
152 		 * Get lines until we find a matching one or
153 		 * until we hit end-of-file (or beginning-of-file
154 		 * if we're going backwards).
155 		 */
156 		if (sigs)
157 			/*
158 			 * A signal aborts the search.
159 			 */
160 			return(0);
161 
162 		if (search_forward)
163 		{
164 			/*
165 			 * Read the next line, and save the
166 			 * starting position of that line in linepos.
167 			 */
168 			linepos = pos;
169 			pos = forw_raw_line(pos);
170 			if (linenum != 0)
171 				linenum++;
172 		} else
173 		{
174 			/*
175 			 * Read the previous line and save the
176 			 * starting position of that line in linepos.
177 			 */
178 			pos = back_raw_line(pos);
179 			linepos = pos;
180 			if (linenum != 0)
181 				linenum--;
182 		}
183 
184 		if (pos == NULL_POSITION)
185 		{
186 			/*
187 			 * We hit EOF/BOF without a match.
188 			 */
189 			error("Pattern not found");
190 			return(0);
191 		}
192 
193 		/*
194 		 * If we're using line numbers, we might as well
195 		 * remember the information we have now (the position
196 		 * and line number of the current line).
197 		 */
198 		if (linenums)
199 			add_lnum(linenum, pos);
200 
201 		/*
202 		 * Remove any backspaces along with the preceeding char.
203 		 * This allows us to match text which is underlined or
204 		 * overstruck.
205 		 */
206 		for (p = q = line;  *p;  p++, q++)
207 			if (q > line && *p == '\b')
208 				/* Delete BS and preceeding char. */
209 				q -= 2;
210 			else
211 				/* Otherwise, just copy. */
212 				*q = *p;
213 
214 		/*
215 		 * Test the next line to see if we have a match.
216 		 */
217 		linematch = !regexec(&rx, line, 0, 0, 0);
218 
219 		/*
220 		 * We are successful if wantmatch and linematch are
221 		 * both true (want a match and got it),
222 		 * or both false (want a non-match and got it).
223 		 */
224 		if (((wantmatch && linematch) || (!wantmatch && !linematch)) &&
225 		      --n <= 0)
226 			/*
227 			 * Found the line.
228 			 */
229 			break;
230 	}
231 	jump_loc(linepos);
232 	return(1);
233 }
234