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