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