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