1 /* $NetBSD: pattern.c,v 1.3 2013/09/04 19:44:21 tron Exp $ */
2
3 /*
4 * Copyright (C) 1984-2012 Mark Nudelman
5 *
6 * You may distribute under the terms of either the GNU General Public
7 * License or the Less License, as specified in the README file.
8 *
9 * For more information, see the README file.
10 */
11
12 /*
13 * Routines to do pattern matching.
14 */
15
16 #include "less.h"
17 #include "pattern.h"
18
19 extern int caseless;
20
21 /*
22 * Compile a search pattern, for future use by match_pattern.
23 */
24 static int
compile_pattern2(pattern,search_type,comp_pattern)25 compile_pattern2(pattern, search_type, comp_pattern)
26 char *pattern;
27 int search_type;
28 void **comp_pattern;
29 {
30 if (search_type & SRCH_NO_REGEX)
31 return (0);
32 {
33 #if HAVE_GNU_REGEX
34 struct re_pattern_buffer *comp = (struct re_pattern_buffer *)
35 ecalloc(1, sizeof(struct re_pattern_buffer));
36 struct re_pattern_buffer **pcomp =
37 (struct re_pattern_buffer **) comp_pattern;
38 re_set_syntax(RE_SYNTAX_POSIX_EXTENDED);
39 if (re_compile_pattern(pattern, strlen(pattern), comp))
40 {
41 free(comp);
42 error("Invalid pattern", NULL_PARG);
43 return (-1);
44 }
45 if (*pcomp != NULL)
46 regfree(*pcomp);
47 *pcomp = comp;
48 #endif
49 #if HAVE_POSIX_REGCOMP
50 regex_t *comp = (regex_t *) ecalloc(1, sizeof(regex_t));
51 regex_t **pcomp = (regex_t **) comp_pattern;
52 if (regcomp(comp, pattern, REGCOMP_FLAG))
53 {
54 free(comp);
55 error("Invalid pattern", NULL_PARG);
56 return (-1);
57 }
58 if (*pcomp != NULL)
59 regfree(*pcomp);
60 *pcomp = comp;
61 #endif
62 #if HAVE_PCRE
63 pcre *comp;
64 pcre **pcomp = (pcre **) comp_pattern;
65 constant char *errstring;
66 int erroffset;
67 PARG parg;
68 comp = pcre_compile(pattern, 0,
69 &errstring, &erroffset, NULL);
70 if (comp == NULL)
71 {
72 parg.p_string = (char *) errstring;
73 error("%s", &parg);
74 return (-1);
75 }
76 *pcomp = comp;
77 #endif
78 #if HAVE_RE_COMP
79 PARG parg;
80 int *pcomp = (int *) comp_pattern;
81 if ((parg.p_string = re_comp(pattern)) != NULL)
82 {
83 error("%s", &parg);
84 return (-1);
85 }
86 *pcomp = 1;
87 #endif
88 #if HAVE_REGCMP
89 char *comp;
90 char **pcomp = (char **) comp_pattern;
91 if ((comp = regcmp(pattern, 0)) == NULL)
92 {
93 error("Invalid pattern", NULL_PARG);
94 return (-1);
95 }
96 if (pcomp != NULL)
97 free(*pcomp);
98 *pcomp = comp;
99 #endif
100 #if HAVE_V8_REGCOMP
101 struct regexp *comp;
102 struct regexp **pcomp = (struct regexp **) comp_pattern;
103 if ((comp = regcomp(pattern)) == NULL)
104 {
105 /*
106 * regcomp has already printed an error message
107 * via regerror().
108 */
109 return (-1);
110 }
111 if (*pcomp != NULL)
112 free(*pcomp);
113 *pcomp = comp;
114 #endif
115 }
116 return (0);
117 }
118
119 /*
120 * Like compile_pattern2, but convert the pattern to lowercase if necessary.
121 */
122 public int
compile_pattern(pattern,search_type,comp_pattern)123 compile_pattern(pattern, search_type, comp_pattern)
124 char *pattern;
125 int search_type;
126 void **comp_pattern;
127 {
128 char *cvt_pattern;
129 int result;
130
131 if (caseless != OPT_ONPLUS)
132 cvt_pattern = pattern;
133 else
134 {
135 cvt_pattern = (char*) ecalloc(1, cvt_length(strlen(pattern), CVT_TO_LC));
136 cvt_text(cvt_pattern, pattern, (int *)NULL, (int *)NULL, CVT_TO_LC);
137 }
138 result = compile_pattern2(cvt_pattern, search_type, comp_pattern);
139 if (cvt_pattern != pattern)
140 free(cvt_pattern);
141 return (result);
142 }
143
144 /*
145 * Forget that we have a compiled pattern.
146 */
147 public void
uncompile_pattern(pattern)148 uncompile_pattern(pattern)
149 void **pattern;
150 {
151 #if HAVE_GNU_REGEX
152 struct re_pattern_buffer **pcomp = (struct re_pattern_buffer **) pattern;
153 if (*pcomp != NULL)
154 regfree(*pcomp);
155 *pcomp = NULL;
156 #endif
157 #if HAVE_POSIX_REGCOMP
158 regex_t **pcomp = (regex_t **) pattern;
159 if (*pcomp != NULL)
160 regfree(*pcomp);
161 *pcomp = NULL;
162 #endif
163 #if HAVE_PCRE
164 pcre **pcomp = (pcre **) pattern;
165 if (*pcomp != NULL)
166 pcre_free(*pcomp);
167 *pcomp = NULL;
168 #endif
169 #if HAVE_RE_COMP
170 int *pcomp = (int *) pattern;
171 *pcomp = 0;
172 #endif
173 #if HAVE_REGCMP
174 char **pcomp = (char **) pattern;
175 if (*pcomp != NULL)
176 free(*pcomp);
177 *pcomp = NULL;
178 #endif
179 #if HAVE_V8_REGCOMP
180 struct regexp **pcomp = (struct regexp **) pattern;
181 if (*pcomp != NULL)
182 free(*pcomp);
183 *pcomp = NULL;
184 #endif
185 }
186
187 /*
188 * Is a compiled pattern null?
189 */
190 public int
is_null_pattern(pattern)191 is_null_pattern(pattern)
192 void *pattern;
193 {
194 #if HAVE_GNU_REGEX
195 return (pattern == NULL);
196 #endif
197 #if HAVE_POSIX_REGCOMP
198 return (pattern == NULL);
199 #endif
200 #if HAVE_PCRE
201 return (pattern == NULL);
202 #endif
203 #if HAVE_RE_COMP
204 return (pattern == 0);
205 #endif
206 #if HAVE_REGCMP
207 return (pattern == NULL);
208 #endif
209 #if HAVE_V8_REGCOMP
210 return (pattern == NULL);
211 #endif
212 }
213
214 /*
215 * Simple pattern matching function.
216 * It supports no metacharacters like *, etc.
217 */
218 static int
match(pattern,pattern_len,buf,buf_len,pfound,pend)219 match(pattern, pattern_len, buf, buf_len, pfound, pend)
220 char *pattern;
221 int pattern_len;
222 char *buf;
223 int buf_len;
224 char **pfound, **pend;
225 {
226 register char *pp, *lp;
227 register char *pattern_end = pattern + pattern_len;
228 register char *buf_end = buf + buf_len;
229
230 for ( ; buf < buf_end; buf++)
231 {
232 for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++)
233 if (pp == pattern_end || lp == buf_end)
234 break;
235 if (pp == pattern_end)
236 {
237 if (pfound != NULL)
238 *pfound = buf;
239 if (pend != NULL)
240 *pend = lp;
241 return (1);
242 }
243 }
244 return (0);
245 }
246
247 /*
248 * Perform a pattern match with the previously compiled pattern.
249 * Set sp and ep to the start and end of the matched string.
250 */
251 public int
match_pattern(pattern,tpattern,line,line_len,sp,ep,notbol,search_type)252 match_pattern(pattern, tpattern, line, line_len, sp, ep, notbol, search_type)
253 void *pattern;
254 char *tpattern;
255 char *line;
256 int line_len;
257 char **sp;
258 char **ep;
259 int notbol;
260 int search_type;
261 {
262 int matched;
263 #if HAVE_GNU_REGEX
264 struct re_pattern_buffer *spattern = (struct re_pattern_buffer *) pattern;
265 #endif
266 #if HAVE_POSIX_REGCOMP
267 regex_t *spattern = (regex_t *) pattern;
268 #endif
269 #if HAVE_PCRE
270 pcre *spattern = (pcre *) pattern;
271 #endif
272 #if HAVE_RE_COMP
273 int spattern = (int) pattern;
274 #endif
275 #if HAVE_REGCMP
276 char *spattern = (char *) pattern;
277 #endif
278 #if HAVE_V8_REGCOMP
279 struct regexp *spattern = (struct regexp *) pattern;
280 #endif
281
282 #if NO_REGEX
283 search_type |= SRCH_NO_REGEX;
284 #endif
285 if (search_type & SRCH_NO_REGEX)
286 matched = match(tpattern, strlen(tpattern), line, line_len, sp, ep);
287 else
288 {
289 #if HAVE_GNU_REGEX
290 {
291 struct re_registers search_regs;
292 regoff_t *starts = (regoff_t *) ecalloc(1, sizeof (regoff_t));
293 regoff_t *ends = (regoff_t *) ecalloc(1, sizeof (regoff_t));
294 spattern->not_bol = notbol;
295 re_set_registers(spattern, &search_regs, 1, starts, ends);
296 matched = re_search(spattern, line, line_len, 0, line_len, &search_regs) >= 0;
297 if (matched)
298 {
299 *sp = line + search_regs.start[0];
300 *ep = line + search_regs.end[0];
301 }
302 free(starts);
303 free(ends);
304 }
305 #endif
306 #if HAVE_POSIX_REGCOMP
307 {
308 regmatch_t rm;
309 int flags = (notbol) ? REG_NOTBOL : 0;
310 matched = !regexec(spattern, line, 1, &rm, flags);
311 if (matched)
312 {
313 #ifndef __WATCOMC__
314 *sp = line + rm.rm_so;
315 *ep = line + rm.rm_eo;
316 #else
317 *sp = rm.rm_sp;
318 *ep = rm.rm_ep;
319 #endif
320 }
321 }
322 #endif
323 #if HAVE_PCRE
324 {
325 int flags = (notbol) ? PCRE_NOTBOL : 0;
326 int ovector[3];
327 matched = pcre_exec(spattern, NULL, line, line_len,
328 0, flags, ovector, 3) >= 0;
329 if (matched)
330 {
331 *sp = line + ovector[0];
332 *ep = line + ovector[1];
333 }
334 }
335 #endif
336 #if HAVE_RE_COMP
337 matched = (re_exec(line) == 1);
338 /*
339 * re_exec doesn't seem to provide a way to get the matched string.
340 */
341 *sp = *ep = NULL;
342 #endif
343 #if HAVE_REGCMP
344 *ep = regex(spattern, line);
345 matched = (*ep != NULL);
346 if (matched)
347 *sp = __loc1;
348 #endif
349 #if HAVE_V8_REGCOMP
350 #if HAVE_REGEXEC2
351 matched = regexec2(spattern, line, notbol);
352 #else
353 matched = regexec(spattern, line);
354 #endif
355 if (matched)
356 {
357 *sp = spattern->startp[0];
358 *ep = spattern->endp[0];
359 }
360 #endif
361 }
362 matched = (!(search_type & SRCH_NO_MATCH) && matched) ||
363 ((search_type & SRCH_NO_MATCH) && !matched);
364 return (matched);
365 }
366
367