/src/libgit2/src/util/regexp.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) the libgit2 contributors. All rights reserved. |
3 | | * |
4 | | * This file is part of libgit2, distributed under the GNU GPL v2 with |
5 | | * a Linking Exception. For full terms see the included COPYING file. |
6 | | */ |
7 | | |
8 | | #include "regexp.h" |
9 | | |
10 | | #if defined(GIT_REGEX_BUILTIN) || defined(GIT_REGEX_PCRE) |
11 | | |
12 | | int git_regexp_compile(git_regexp *r, const char *pattern, int flags) |
13 | 0 | { |
14 | 0 | int erroffset, cflags = 0; |
15 | 0 | const char *error = NULL; |
16 | |
|
17 | 0 | if (flags & GIT_REGEXP_ICASE) |
18 | 0 | cflags |= PCRE_CASELESS; |
19 | |
|
20 | 0 | if ((*r = pcre_compile(pattern, cflags, &error, &erroffset, NULL)) == NULL) { |
21 | 0 | git_error_set_str(GIT_ERROR_REGEX, error); |
22 | 0 | return GIT_EINVALIDSPEC; |
23 | 0 | } |
24 | | |
25 | 0 | return 0; |
26 | 0 | } |
27 | | |
28 | | void git_regexp_dispose(git_regexp *r) |
29 | 0 | { |
30 | 0 | pcre_free(*r); |
31 | 0 | *r = NULL; |
32 | 0 | } |
33 | | |
34 | | int git_regexp_match(const git_regexp *r, const char *string) |
35 | 0 | { |
36 | 0 | int error; |
37 | 0 | if ((error = pcre_exec(*r, NULL, string, (int) strlen(string), 0, 0, NULL, 0)) < 0) |
38 | 0 | return (error == PCRE_ERROR_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC; |
39 | 0 | return 0; |
40 | 0 | } |
41 | | |
42 | | int git_regexp_search(const git_regexp *r, const char *string, size_t nmatches, git_regmatch *matches) |
43 | 0 | { |
44 | 0 | int static_ovec[9] = {0}, *ovec; |
45 | 0 | int error; |
46 | 0 | size_t i; |
47 | | |
48 | | /* The ovec array always needs to be a multiple of three */ |
49 | 0 | if (nmatches <= ARRAY_SIZE(static_ovec) / 3) |
50 | 0 | ovec = static_ovec; |
51 | 0 | else |
52 | 0 | ovec = git__calloc(nmatches * 3, sizeof(*ovec)); |
53 | 0 | GIT_ERROR_CHECK_ALLOC(ovec); |
54 | | |
55 | 0 | if ((error = pcre_exec(*r, NULL, string, (int) strlen(string), 0, 0, ovec, (int) nmatches * 3)) < 0) |
56 | 0 | goto out; |
57 | | |
58 | 0 | if (error == 0) |
59 | 0 | error = (int) nmatches; |
60 | |
|
61 | 0 | for (i = 0; i < (unsigned int) error; i++) { |
62 | 0 | matches[i].start = (ovec[i * 2] < 0) ? -1 : ovec[i * 2]; |
63 | 0 | matches[i].end = (ovec[i * 2 + 1] < 0) ? -1 : ovec[i * 2 + 1]; |
64 | 0 | } |
65 | 0 | for (i = (unsigned int) error; i < nmatches; i++) |
66 | 0 | matches[i].start = matches[i].end = -1; |
67 | |
|
68 | 0 | out: |
69 | 0 | if (nmatches > ARRAY_SIZE(static_ovec) / 3) |
70 | 0 | git__free(ovec); |
71 | 0 | if (error < 0) |
72 | 0 | return (error == PCRE_ERROR_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC; |
73 | 0 | return 0; |
74 | 0 | } |
75 | | |
76 | | #elif defined(GIT_REGEX_PCRE2) |
77 | | |
78 | | int git_regexp_compile(git_regexp *r, const char *pattern, int flags) |
79 | | { |
80 | | unsigned char errmsg[1024]; |
81 | | PCRE2_SIZE erroff; |
82 | | int error, cflags = 0; |
83 | | |
84 | | if (flags & GIT_REGEXP_ICASE) |
85 | | cflags |= PCRE2_CASELESS; |
86 | | |
87 | | if ((*r = pcre2_compile((const unsigned char *) pattern, PCRE2_ZERO_TERMINATED, |
88 | | cflags, &error, &erroff, NULL)) == NULL) { |
89 | | pcre2_get_error_message(error, errmsg, sizeof(errmsg)); |
90 | | git_error_set_str(GIT_ERROR_REGEX, (char *) errmsg); |
91 | | return GIT_EINVALIDSPEC; |
92 | | } |
93 | | |
94 | | return 0; |
95 | | } |
96 | | |
97 | | void git_regexp_dispose(git_regexp *r) |
98 | | { |
99 | | pcre2_code_free(*r); |
100 | | *r = NULL; |
101 | | } |
102 | | |
103 | | int git_regexp_match(const git_regexp *r, const char *string) |
104 | | { |
105 | | pcre2_match_data *data; |
106 | | int error; |
107 | | |
108 | | data = pcre2_match_data_create(1, NULL); |
109 | | GIT_ERROR_CHECK_ALLOC(data); |
110 | | |
111 | | error = pcre2_match(*r, (const unsigned char *) string, strlen(string), 0, 0, data, NULL); |
112 | | pcre2_match_data_free(data); |
113 | | if (error < 0) |
114 | | return (error == PCRE2_ERROR_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC; |
115 | | |
116 | | return 0; |
117 | | } |
118 | | |
119 | | int git_regexp_search(const git_regexp *r, const char *string, size_t nmatches, git_regmatch *matches) |
120 | | { |
121 | | pcre2_match_data *data = NULL; |
122 | | PCRE2_SIZE *ovec; |
123 | | int error; |
124 | | size_t i; |
125 | | |
126 | | if ((data = pcre2_match_data_create(nmatches, NULL)) == NULL) { |
127 | | git_error_set_oom(); |
128 | | return -1; |
129 | | } |
130 | | |
131 | | if ((error = pcre2_match(*r, (const unsigned char *) string, strlen(string), |
132 | | 0, 0, data, NULL)) < 0) |
133 | | goto out; |
134 | | |
135 | | if (error == 0 || (unsigned int) error > nmatches) |
136 | | error = nmatches; |
137 | | ovec = pcre2_get_ovector_pointer(data); |
138 | | |
139 | | for (i = 0; i < (unsigned int) error; i++) { |
140 | | matches[i].start = (ovec[i * 2] == PCRE2_UNSET) ? -1 : (ssize_t) ovec[i * 2]; |
141 | | matches[i].end = (ovec[i * 2 + 1] == PCRE2_UNSET) ? -1 : (ssize_t) ovec[i * 2 + 1]; |
142 | | } |
143 | | for (i = (unsigned int) error; i < nmatches; i++) |
144 | | matches[i].start = matches[i].end = -1; |
145 | | |
146 | | out: |
147 | | pcre2_match_data_free(data); |
148 | | if (error < 0) |
149 | | return (error == PCRE2_ERROR_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC; |
150 | | return 0; |
151 | | } |
152 | | |
153 | | #elif defined(GIT_REGEX_REGCOMP) || defined(GIT_REGEX_REGCOMP_L) |
154 | | |
155 | | #if defined(GIT_REGEX_REGCOMP_L) |
156 | | # include <xlocale.h> |
157 | | #endif |
158 | | |
159 | | int git_regexp_compile(git_regexp *r, const char *pattern, int flags) |
160 | | { |
161 | | int cflags = REG_EXTENDED, error; |
162 | | char errmsg[1024]; |
163 | | |
164 | | if (flags & GIT_REGEXP_ICASE) |
165 | | cflags |= REG_ICASE; |
166 | | |
167 | | # if defined(GIT_REGEX_REGCOMP) |
168 | | if ((error = regcomp(r, pattern, cflags)) != 0) |
169 | | # else |
170 | | if ((error = regcomp_l(r, pattern, cflags, (locale_t) 0)) != 0) |
171 | | # endif |
172 | | { |
173 | | regerror(error, r, errmsg, sizeof(errmsg)); |
174 | | git_error_set_str(GIT_ERROR_REGEX, errmsg); |
175 | | return GIT_EINVALIDSPEC; |
176 | | } |
177 | | |
178 | | return 0; |
179 | | } |
180 | | |
181 | | void git_regexp_dispose(git_regexp *r) |
182 | | { |
183 | | regfree(r); |
184 | | } |
185 | | |
186 | | int git_regexp_match(const git_regexp *r, const char *string) |
187 | | { |
188 | | int error; |
189 | | if ((error = regexec(r, string, 0, NULL, 0)) != 0) |
190 | | return (error == REG_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC; |
191 | | return 0; |
192 | | } |
193 | | |
194 | | int git_regexp_search(const git_regexp *r, const char *string, size_t nmatches, git_regmatch *matches) |
195 | | { |
196 | | regmatch_t static_m[3], *m; |
197 | | int error; |
198 | | size_t i; |
199 | | |
200 | | if (nmatches <= ARRAY_SIZE(static_m)) |
201 | | m = static_m; |
202 | | else |
203 | | m = git__calloc(nmatches, sizeof(*m)); |
204 | | |
205 | | if ((error = regexec(r, string, nmatches, m, 0)) != 0) |
206 | | goto out; |
207 | | |
208 | | for (i = 0; i < nmatches; i++) { |
209 | | matches[i].start = (m[i].rm_so < 0) ? -1 : m[i].rm_so; |
210 | | matches[i].end = (m[i].rm_eo < 0) ? -1 : m[i].rm_eo; |
211 | | } |
212 | | |
213 | | out: |
214 | | if (nmatches > ARRAY_SIZE(static_m)) |
215 | | git__free(m); |
216 | | if (error) |
217 | | return (error == REG_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC; |
218 | | return 0; |
219 | | } |
220 | | |
221 | | #endif |