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