]> diplodocus.org Git - nmh/blob - docs/historical/mh-6.8.5/miscellany/less-177/search.c
ap: Fix write past end of addrs[] array.
[nmh] / docs / historical / mh-6.8.5 / miscellany / less-177 / search.c
1 /*
2 * Routines to search a file for a pattern.
3 */
4
5 #include "less.h"
6 #include "position.h"
7 #if REGCOMP
8 #include "regexp.h"
9 #endif
10
11 extern int sigs;
12 extern int how_search;
13 extern int caseless;
14 extern int linenums;
15 extern int jump_sline;
16
17 /*
18 * Search for the n-th occurrence of a specified pattern,
19 * either forward or backward.
20 * Return the number of matches not yet found in this file
21 * (that is, n minus the number of matches found).
22 * Return -1 if the search should be aborted.
23 * Caller may continue the search in another file
24 * if less than n matches are found in this file.
25 */
26 public int
27 search(search_type, pattern, n)
28 int search_type;
29 char *pattern;
30 int n;
31 {
32 POSITION pos, linepos, oldpos;
33 register char *p;
34 register char *q;
35 register int goforw;
36 register int want_match;
37 char *line;
38 int linenum;
39 int line_match;
40 static int is_caseless;
41 #if RECOMP
42 char *re_comp();
43 PARG parg;
44 #else
45 #if REGCMP
46 char *regcmp();
47 static char *cpattern = NULL;
48 #else
49 #if REGCOMP
50 static struct regexp *regpattern = NULL;
51 #else
52 static char lpbuf[100];
53 static char *last_pattern = NULL;
54 #endif
55 #endif
56 #endif
57
58 /*
59 * Extract flags and type of search.
60 */
61 goforw = (SRCH_DIR(search_type) == SRCH_FORW);
62 want_match = !(search_type & SRCH_NOMATCH);
63
64 if (pattern != NULL && *pattern != '\0' && (is_caseless = caseless))
65 {
66 /*
67 * Search will ignore case, unless
68 * there are any uppercase letters in the pattern.
69 */
70 for (p = pattern; *p != '\0'; p++)
71 if (*p >= 'A' && *p <= 'Z')
72 {
73 is_caseless = 0;
74 break;
75 }
76 }
77 #if RECOMP
78
79 /*
80 * (re_comp handles a null pattern internally,
81 * so there is no need to check for a null pattern here.)
82 */
83 if ((parg.p_string = re_comp(pattern)) != NULL)
84 {
85 error("%s", &parg);
86 return (-1);
87 }
88 #else
89 #if REGCMP
90 if (pattern == NULL || *pattern == '\0')
91 {
92 /*
93 * A null pattern means use the previous pattern.
94 * The compiled previous pattern is in cpattern, so just use it.
95 */
96 if (cpattern == NULL)
97 {
98 error("No previous regular expression", NULL_PARG);
99 return (-1);
100 }
101 } else
102 {
103 /*
104 * Otherwise compile the given pattern.
105 */
106 char *s;
107 if ((s = regcmp(pattern, 0)) == NULL)
108 {
109 error("Invalid pattern", NULL_PARG);
110 return (-1);
111 }
112 if (cpattern != NULL)
113 free(cpattern);
114 cpattern = s;
115 }
116 #else
117 #if REGCOMP
118 if (pattern == NULL || *pattern == '\0')
119 {
120 /*
121 * A null pattern means use the previous pattern.
122 * The compiled previous pattern is in regpattern,
123 * so just use it.
124 */
125 if (regpattern == NULL)
126 {
127 error("No previous regular expression", NULL_PARG);
128 return (-1);
129 }
130 } else
131 {
132 /*
133 * Otherwise compile the given pattern.
134 */
135 struct regexp *s;
136 if ((s = regcomp(pattern)) == NULL)
137 {
138 error("Invalid pattern", NULL_PARG);
139 return (-1);
140 }
141 if (regpattern != NULL)
142 free(regpattern);
143 regpattern = s;
144 }
145 #else
146 if (pattern == NULL || *pattern == '\0')
147 {
148 /*
149 * Null pattern means use the previous pattern.
150 */
151 if (last_pattern == NULL)
152 {
153 error("No previous regular expression", NULL_PARG);
154 return (-1);
155 }
156 pattern = last_pattern;
157 } else
158 {
159 strcpy(lpbuf, pattern);
160 last_pattern = lpbuf;
161 }
162 #endif
163 #endif
164 #endif
165
166 /*
167 * Figure out where to start the search.
168 */
169 if (empty_screen())
170 {
171 /*
172 * Start at the beginning (or end) of the file.
173 * (The empty_screen() case is mainly for
174 * command line initiated searches;
175 * for example, "+/xyz" on the command line.)
176 */
177 if (goforw)
178 pos = ch_zero();
179 else
180 {
181 pos = ch_length();
182 if (pos == NULL_POSITION)
183 pos = ch_zero();
184 }
185 } else
186 {
187 if (how_search)
188 {
189 if (goforw)
190 linenum = BOTTOM_PLUS_ONE;
191 else
192 linenum = TOP;
193 pos = position(linenum);
194 } else
195 {
196 linenum = adjsline(jump_sline);
197 pos = position(linenum);
198 if (goforw)
199 pos = forw_raw_line(pos, (char **)NULL);
200 }
201 }
202
203 if (pos == NULL_POSITION)
204 {
205 /*
206 * Can't find anyplace to start searching from.
207 */
208 error("Nothing to search", NULL_PARG);
209 return (-1);
210 }
211
212 linenum = find_linenum(pos);
213 oldpos = pos;
214 for (;;)
215 {
216 /*
217 * Get lines until we find a matching one or
218 * until we hit end-of-file (or beginning-of-file
219 * if we're going backwards).
220 */
221 if (sigs)
222 /*
223 * A signal aborts the search.
224 */
225 return (-1);
226
227 if (goforw)
228 {
229 /*
230 * Read the next line, and save the
231 * starting position of that line in linepos.
232 */
233 linepos = pos;
234 pos = forw_raw_line(pos, &line);
235 if (linenum != 0)
236 linenum++;
237 } else
238 {
239 /*
240 * Read the previous line and save the
241 * starting position of that line in linepos.
242 */
243 pos = back_raw_line(pos, &line);
244 linepos = pos;
245 if (linenum != 0)
246 linenum--;
247 }
248
249 if (pos == NULL_POSITION)
250 {
251 /*
252 * We hit EOF/BOF without a match.
253 */
254 return (n);
255 }
256
257 /*
258 * If we're using line numbers, we might as well
259 * remember the information we have now (the position
260 * and line number of the current line).
261 * Don't do it for every line because it slows down
262 * the search. Remember the line number only if
263 * we're "far" from the last place we remembered it.
264 */
265 if (linenums && abs(pos - oldpos) > 1024)
266 {
267 add_lnum(linenum, pos);
268 oldpos = pos;
269 }
270
271 if (is_caseless)
272 {
273 /*
274 * If this is a caseless search, convert
275 * uppercase in the input line to lowercase.
276 * While we're at it, remove any backspaces
277 * along with the preceding char.
278 * This allows us to match text which is
279 * underlined or overstruck.
280 */
281 for (p = q = line; *p != '\0'; p++, q++)
282 {
283 if (*p >= 'A' && *p <= 'Z')
284 /* Convert uppercase to lowercase. */
285 *q = *p + 'a' - 'A';
286 else if (q > line && *p == '\b')
287 /* Delete BS and preceding char. */
288 q -= 2;
289 else
290 /* Otherwise, just copy. */
291 *q = *p;
292 }
293 }
294
295 /*
296 * Test the next line to see if we have a match.
297 * This is done in a variety of ways, depending
298 * on what pattern matching functions are available.
299 */
300 #if REGCMP
301 line_match = (regex(cpattern, line) != NULL);
302 #else
303 #if RECOMP
304 line_match = (re_exec(line) == 1);
305 #else
306 #if REGCOMP
307 line_match = regexec(regpattern, line);
308 #else
309 line_match = match(pattern, line);
310 #endif
311 #endif
312 #endif
313 /*
314 * We are successful if want_match and line_match are
315 * both true (want a match and got it),
316 * or both false (want a non-match and got it).
317 */
318 if (((want_match && line_match) || (!want_match && !line_match)) &&
319 --n <= 0)
320 /*
321 * Found the line.
322 */
323 break;
324 }
325
326 jump_loc(linepos, jump_sline);
327 return (0);
328 }
329
330 #if (!REGCMP) && (!RECOMP) && (!REGCOMP)
331 /*
332 * We have neither regcmp() nor re_comp().
333 * We use this function to do simple pattern matching.
334 * It supports no metacharacters like *, etc.
335 */
336 static int
337 match(pattern, buf)
338 char *pattern, *buf;
339 {
340 register char *pp, *lp;
341
342 for ( ; *buf != '\0'; buf++)
343 {
344 for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++)
345 if (*pp == '\0' || *lp == '\0')
346 break;
347 if (*pp == '\0')
348 return (1);
349 }
350 return (0);
351 }
352 #endif