pattern.c revision 225736
1/*
2 * Copyright (C) 1984-2011  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
18extern int caseless;
19
20/*
21 * Compile a search pattern, for future use by match_pattern.
22 */
23	static int
24compile_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
105compile_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
130uncompile_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
167is_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
195match(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
228match_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