1/*
2 * Copyright (C) 1984-2012  Mark Nudelman
3 * Modified for use with illumos by Garrett D'Amore.
4 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
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
19extern int caseless;
20extern int less_is_more;
21
22/*
23 * Compile a search pattern, for future use by match_pattern.
24 */
25static int
26compile_pattern2(char *pattern, int search_type, regex_t **comp_pattern)
27{
28	regex_t *comp;
29
30	if (search_type & SRCH_NO_REGEX)
31		return (0);
32	comp = ecalloc(1, sizeof (regex_t));
33	if (regcomp(comp, pattern, less_is_more ? 0 : REGCOMP_FLAG)) {
34		free(comp);
35		error("Invalid pattern", NULL);
36		return (-1);
37	}
38	if (*comp_pattern != NULL)
39		regfree(*comp_pattern);
40	*comp_pattern = comp;
41	return (0);
42}
43
44/*
45 * Like compile_pattern2, but convert the pattern to lowercase if necessary.
46 */
47int
48compile_pattern(char *pattern, int search_type, regex_t **comp_pattern)
49{
50	char *cvt_pattern;
51	int result;
52
53	if (caseless != OPT_ONPLUS) {
54		cvt_pattern = pattern;
55	} else {
56		cvt_pattern = ecalloc(1, cvt_length(strlen(pattern)));
57		cvt_text(cvt_pattern, pattern, NULL, NULL, CVT_TO_LC);
58	}
59	result = compile_pattern2(cvt_pattern, search_type, comp_pattern);
60	if (cvt_pattern != pattern)
61		free(cvt_pattern);
62	return (result);
63}
64
65/*
66 * Forget that we have a compiled pattern.
67 */
68void
69uncompile_pattern(regex_t **pattern)
70{
71	if (*pattern != NULL)
72		regfree(*pattern);
73	*pattern = NULL;
74}
75
76/*
77 * Simple pattern matching function.
78 * It supports no metacharacters like *, etc.
79 */
80static int
81match(char *pattern, int pattern_len, char *buf, int buf_len,
82    char **pfound, char **pend)
83{
84	char *pp, *lp;
85	char *pattern_end = pattern + pattern_len;
86	char *buf_end = buf + buf_len;
87
88	for (; buf < buf_end; buf++) {
89		for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++)
90			if (pp == pattern_end || lp == buf_end)
91				break;
92		if (pp == pattern_end) {
93			if (pfound != NULL)
94				*pfound = buf;
95			if (pend != NULL)
96				*pend = lp;
97			return (1);
98		}
99	}
100	return (0);
101}
102
103/*
104 * Perform a pattern match with the previously compiled pattern.
105 * Set sp and ep to the start and end of the matched string.
106 */
107int
108match_pattern(void *pattern, char *tpattern, char *line, int line_len,
109    char **sp, char **ep, int notbol, int search_type)
110{
111	int matched;
112	regex_t *spattern = (regex_t *)pattern;
113
114	if (search_type & SRCH_NO_REGEX) {
115		matched = match(tpattern, strlen(tpattern), line, line_len,
116		    sp, ep);
117	} else {
118		regmatch_t rm;
119		int flags = (notbol) ? REG_NOTBOL : 0;
120#ifdef	REG_STARTEND
121		flags |= REG_STARTEND;
122		rm.rm_so = 0;
123		rm.rm_eo = line_len;
124#endif
125		*sp = NULL;
126		*ep = NULL;
127		matched = !regexec(spattern, line, 1, &rm, flags);
128		if (matched) {
129			*sp = line + rm.rm_so;
130			*ep = line + rm.rm_eo;
131		}
132	}
133	matched = (!(search_type & SRCH_NO_MATCH) && matched) ||
134	    ((search_type & SRCH_NO_MATCH) && !matched);
135	return (matched);
136}
137