xref: /openbsd/usr.bin/less/pattern.c (revision 4bdff4be)
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 
19 extern int caseless;
20 extern int less_is_more;
21 
22 /*
23  * Compile a search pattern, for future use by match_pattern.
24  */
25 static int
26 compile_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  */
47 int
48 compile_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  */
68 void
69 uncompile_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  */
80 static int
81 match(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  */
107 int
108 match_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