/src/httpd/server/util_pcre.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Licensed to the Apache Software Foundation (ASF) under one or more |
2 | | * contributor license agreements. See the NOTICE file distributed with |
3 | | * this work for additional information regarding copyright ownership. |
4 | | * The ASF licenses this file to You under the Apache License, Version 2.0 |
5 | | * (the "License"); you may not use this file except in compliance with |
6 | | * the License. You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | /* This code is based on pcreposix.c from the PCRE Library distribution, |
18 | | * as originally written by Philip Hazel <ph10@cam.ac.uk>, and forked by |
19 | | * the Apache HTTP Server project to provide POSIX-style regex function |
20 | | * wrappers around underlying PCRE library functions for httpd. |
21 | | * |
22 | | * The original source file pcreposix.c is copyright and licensed as follows; |
23 | | |
24 | | Copyright (c) 1997-2004 University of Cambridge |
25 | | |
26 | | ----------------------------------------------------------------------------- |
27 | | Redistribution and use in source and binary forms, with or without |
28 | | modification, are permitted provided that the following conditions are met: |
29 | | |
30 | | * Redistributions of source code must retain the above copyright notice, |
31 | | this list of conditions and the following disclaimer. |
32 | | |
33 | | * Redistributions in binary form must reproduce the above copyright |
34 | | notice, this list of conditions and the following disclaimer in the |
35 | | documentation and/or other materials provided with the distribution. |
36 | | |
37 | | * Neither the name of the University of Cambridge nor the names of its |
38 | | contributors may be used to endorse or promote products derived from |
39 | | this software without specific prior written permission. |
40 | | |
41 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
42 | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
43 | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
44 | | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
45 | | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
46 | | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
47 | | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
48 | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
49 | | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
50 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
51 | | POSSIBILITY OF SUCH DAMAGE. |
52 | | ----------------------------------------------------------------------------- |
53 | | */ |
54 | | |
55 | | #include "httpd.h" |
56 | | #include "apr_strings.h" |
57 | | #include "apr_tables.h" |
58 | | |
59 | | #ifdef HAVE_PCRE2 |
60 | | #define PCRE2_CODE_UNIT_WIDTH 8 |
61 | | #include "pcre2.h" |
62 | 0 | #define PCREn(x) PCRE2_ ## x |
63 | | #else |
64 | | #include "pcre.h" |
65 | | #define PCREn(x) PCRE_ ## x |
66 | | #endif |
67 | | |
68 | | /* PCRE_DUPNAMES is only present since version 6.7 of PCRE */ |
69 | | #if !defined(PCRE_DUPNAMES) && !defined(HAVE_PCRE2) |
70 | | #error PCRE Version 6.7 or later required! |
71 | | #else |
72 | | |
73 | | #define APR_WANT_STRFUNC |
74 | | #include "apr_want.h" |
75 | | |
76 | | #ifndef POSIX_MALLOC_THRESHOLD |
77 | | #define POSIX_MALLOC_THRESHOLD (10) |
78 | | #endif |
79 | | |
80 | | #ifdef HAVE_PCRE2 |
81 | | /* Reserve 128 bytes for the PCRE2 structs, that is (a bit above): |
82 | | * sizeof(pcre2_general_context) + offsetof(pcre2_match_data, ovector) |
83 | | */ |
84 | | #define AP_PCRE_STACKBUF_SIZE \ |
85 | | APR_ALIGN_DEFAULT(128 + POSIX_MALLOC_THRESHOLD * sizeof(PCRE2_SIZE) * 2) |
86 | | #else |
87 | | #define AP_PCRE_STACKBUF_SIZE \ |
88 | | APR_ALIGN_DEFAULT(POSIX_MALLOC_THRESHOLD * sizeof(int) * 3) |
89 | | #endif |
90 | | |
91 | | /* Table of error strings corresponding to POSIX error codes; must be |
92 | | * kept in synch with include/ap_regex.h's AP_REG_E* definitions. |
93 | | */ |
94 | | |
95 | | static const char *const pstring[] = { |
96 | | "", /* Dummy for value 0 */ |
97 | | "internal error", /* AP_REG_ASSERT */ |
98 | | "failed to get memory", /* AP_REG_ESPACE */ |
99 | | "bad argument", /* AP_REG_INVARG */ |
100 | | "match failed" /* AP_REG_NOMATCH */ |
101 | | }; |
102 | | |
103 | | AP_DECLARE(const char *) ap_pcre_version_string(int which) |
104 | 0 | { |
105 | 0 | #ifdef HAVE_PCRE2 |
106 | 0 | static char buf[80]; |
107 | 0 | #endif |
108 | 0 | switch (which) { |
109 | 0 | case AP_REG_PCRE_COMPILED: |
110 | 0 | return APR_STRINGIFY(PCREn(MAJOR)) "." APR_STRINGIFY(PCREn(MINOR)) " " APR_STRINGIFY(PCREn(DATE)); |
111 | 0 | case AP_REG_PCRE_LOADED: |
112 | 0 | #ifdef HAVE_PCRE2 |
113 | 0 | pcre2_config(PCRE2_CONFIG_VERSION, buf); |
114 | 0 | return buf; |
115 | | #else |
116 | | return pcre_version(); |
117 | | #endif |
118 | 0 | default: |
119 | 0 | return "Unknown"; |
120 | 0 | } |
121 | 0 | } |
122 | | |
123 | | AP_DECLARE(apr_size_t) ap_regerror(int errcode, const ap_regex_t *preg, |
124 | | char *errbuf, apr_size_t errbuf_size) |
125 | 0 | { |
126 | 0 | const char *message, *addmessage; |
127 | 0 | apr_size_t length, addlength; |
128 | |
|
129 | 0 | message = (errcode >= (int)(sizeof(pstring) / sizeof(char *))) ? |
130 | 0 | "unknown error code" : pstring[errcode]; |
131 | 0 | length = strlen(message) + 1; |
132 | |
|
133 | 0 | addmessage = " at offset "; |
134 | 0 | addlength = (preg != NULL && (int)preg->re_erroffset != -1) ? |
135 | 0 | strlen(addmessage) + 6 : 0; |
136 | |
|
137 | 0 | if (errbuf_size > 0) { |
138 | 0 | if (addlength > 0 && errbuf_size >= length + addlength) |
139 | 0 | apr_snprintf(errbuf, errbuf_size, "%s%s%-6d", message, addmessage, |
140 | 0 | (int)preg->re_erroffset); |
141 | 0 | else |
142 | 0 | apr_cpystrn(errbuf, message, errbuf_size); |
143 | 0 | } |
144 | |
|
145 | 0 | return length + addlength; |
146 | 0 | } |
147 | | |
148 | | |
149 | | |
150 | | |
151 | | /************************************************* |
152 | | * Free store held by a regex * |
153 | | *************************************************/ |
154 | | |
155 | | AP_DECLARE(void) ap_regfree(ap_regex_t *preg) |
156 | 0 | { |
157 | 0 | #ifdef HAVE_PCRE2 |
158 | 0 | pcre2_code_free(preg->re_pcre); |
159 | | #else |
160 | | (pcre_free)(preg->re_pcre); |
161 | | #endif |
162 | 0 | } |
163 | | |
164 | | |
165 | | |
166 | | |
167 | | /************************************************* |
168 | | * Compile a regular expression * |
169 | | *************************************************/ |
170 | | |
171 | | static int default_cflags = AP_REG_DEFAULT; |
172 | | |
173 | | AP_DECLARE(int) ap_regcomp_get_default_cflags(void) |
174 | 0 | { |
175 | 0 | return default_cflags; |
176 | 0 | } |
177 | | |
178 | | AP_DECLARE(void) ap_regcomp_set_default_cflags(int cflags) |
179 | 0 | { |
180 | 0 | default_cflags = cflags; |
181 | 0 | } |
182 | | |
183 | | AP_DECLARE(int) ap_regcomp_default_cflag_by_name(const char *name) |
184 | 0 | { |
185 | 0 | int cflag = 0; |
186 | |
|
187 | 0 | if (ap_cstr_casecmp(name, "ICASE") == 0) { |
188 | 0 | cflag = AP_REG_ICASE; |
189 | 0 | } |
190 | 0 | else if (ap_cstr_casecmp(name, "DOTALL") == 0) { |
191 | 0 | cflag = AP_REG_DOTALL; |
192 | 0 | } |
193 | 0 | else if (ap_cstr_casecmp(name, "DOLLAR_ENDONLY") == 0) { |
194 | 0 | cflag = AP_REG_DOLLAR_ENDONLY; |
195 | 0 | } |
196 | 0 | else if (ap_cstr_casecmp(name, "EXTENDED") == 0) { |
197 | 0 | cflag = AP_REG_EXTENDED; |
198 | 0 | } |
199 | |
|
200 | 0 | return cflag; |
201 | 0 | } |
202 | | |
203 | | /* |
204 | | * Arguments: |
205 | | * preg points to a structure for recording the compiled expression |
206 | | * pattern the pattern to compile |
207 | | * cflags compilation flags |
208 | | * |
209 | | * Returns: 0 on success |
210 | | * various non-zero codes on failure |
211 | | */ |
212 | | AP_DECLARE(int) ap_regcomp(ap_regex_t * preg, const char *pattern, int cflags) |
213 | 0 | { |
214 | 0 | #ifdef HAVE_PCRE2 |
215 | 0 | uint32_t capcount; |
216 | 0 | PCRE2_SIZE erroffset; |
217 | | #else |
218 | | const char *errorptr; |
219 | | int erroffset; |
220 | | #endif |
221 | 0 | int errcode = 0; |
222 | 0 | int options = PCREn(DUPNAMES); |
223 | |
|
224 | 0 | if ((cflags & AP_REG_NO_DEFAULT) == 0) |
225 | 0 | cflags |= default_cflags; |
226 | |
|
227 | 0 | if ((cflags & AP_REG_ICASE) != 0) |
228 | 0 | options |= PCREn(CASELESS); |
229 | 0 | if ((cflags & AP_REG_NEWLINE) != 0) |
230 | 0 | options |= PCREn(MULTILINE); |
231 | 0 | if ((cflags & AP_REG_DOTALL) != 0) |
232 | 0 | options |= PCREn(DOTALL); |
233 | 0 | if ((cflags & AP_REG_DOLLAR_ENDONLY) != 0) |
234 | 0 | options |= PCREn(DOLLAR_ENDONLY); |
235 | |
|
236 | 0 | #ifdef HAVE_PCRE2 |
237 | 0 | preg->re_pcre = pcre2_compile((const unsigned char *)pattern, |
238 | 0 | PCRE2_ZERO_TERMINATED, options, &errcode, |
239 | 0 | &erroffset, NULL); |
240 | | #else |
241 | | preg->re_pcre = pcre_compile2(pattern, options, &errcode, |
242 | | &errorptr, &erroffset, NULL); |
243 | | #endif |
244 | |
|
245 | 0 | preg->re_erroffset = erroffset; |
246 | 0 | if (preg->re_pcre == NULL) { |
247 | | /* Internal ERR21 is "failed to get memory" according to pcreapi(3) */ |
248 | 0 | if (errcode == 21) |
249 | 0 | return AP_REG_ESPACE; |
250 | 0 | return AP_REG_INVARG; |
251 | 0 | } |
252 | | |
253 | 0 | #ifdef HAVE_PCRE2 |
254 | 0 | pcre2_pattern_info((const pcre2_code *)preg->re_pcre, |
255 | 0 | PCRE2_INFO_CAPTURECOUNT, &capcount); |
256 | 0 | preg->re_nsub = capcount; |
257 | | #else |
258 | | pcre_fullinfo((const pcre *)preg->re_pcre, NULL, |
259 | | PCRE_INFO_CAPTURECOUNT, &(preg->re_nsub)); |
260 | | #endif |
261 | 0 | return 0; |
262 | 0 | } |
263 | | |
264 | | |
265 | | |
266 | | |
267 | | /************************************************* |
268 | | * Match a regular expression * |
269 | | *************************************************/ |
270 | | |
271 | | /* Unfortunately, PCRE1 requires 3 ints of working space for each captured |
272 | | * substring, so we have to get and release working store instead of just using |
273 | | * the POSIX structures as was done in earlier releases when PCRE needed only 2 |
274 | | * ints. However, if the number of possible capturing brackets is small, use a |
275 | | * block of store on the stack, to reduce the use of malloc/free. The threshold |
276 | | * is in POSIX_MALLOC_THRESHOLD macro that can be changed at configure time. |
277 | | * PCRE2 takes an opaque match context and lets us provide the callbacks to |
278 | | * manage the memory needed during the match, so we can still use a small stack |
279 | | * space that will suffice for the match context struct and a single frame of |
280 | | * POSIX_MALLOC_THRESHOLD captures, above that either use a thread local |
281 | | * subpool cache (#if AP_HAS_THREAD_LOCAL) or fall back to malloc()/free(). |
282 | | */ |
283 | | |
284 | | #if AP_HAS_THREAD_LOCAL && !defined(APREG_NO_THREAD_LOCAL) |
285 | | #define APREG_USE_THREAD_LOCAL 1 |
286 | | #else |
287 | | #define APREG_USE_THREAD_LOCAL 0 |
288 | | #endif |
289 | | |
290 | | #ifdef HAVE_PCRE2 |
291 | | typedef PCRE2_SIZE* match_vector_pt; |
292 | | #else |
293 | | typedef int* match_vector_pt; |
294 | | #endif |
295 | | |
296 | | #if APREG_USE_THREAD_LOCAL |
297 | | static AP_THREAD_LOCAL apr_pool_t *thread_pool; |
298 | | #endif |
299 | | |
300 | | struct match_data_state { |
301 | | /* keep first, struct aligned */ |
302 | | char buf[AP_PCRE_STACKBUF_SIZE]; |
303 | | apr_size_t buf_used; |
304 | | |
305 | | #if APREG_USE_THREAD_LOCAL |
306 | | apr_thread_t *thd; |
307 | | apr_pool_t *pool; |
308 | | #endif |
309 | | |
310 | | #ifdef HAVE_PCRE2 |
311 | | pcre2_general_context *pcre2_ctx; |
312 | | pcre2_match_data* match_data; |
313 | | #else |
314 | | int *match_data; |
315 | | #endif |
316 | | }; |
317 | | |
318 | | static void * private_malloc(size_t size, void *ctx) |
319 | 0 | { |
320 | 0 | struct match_data_state *state = ctx; |
321 | |
|
322 | 0 | if (size <= sizeof(state->buf) - state->buf_used) { |
323 | 0 | void *p = state->buf + state->buf_used; |
324 | 0 | state->buf_used += APR_ALIGN_DEFAULT(size); |
325 | 0 | return p; |
326 | 0 | } |
327 | | |
328 | 0 | #if APREG_USE_THREAD_LOCAL |
329 | 0 | if (state->thd) { |
330 | 0 | apr_pool_t *pool = state->pool; |
331 | 0 | if (pool == NULL) { |
332 | 0 | pool = thread_pool; |
333 | 0 | if (pool == NULL) { |
334 | 0 | apr_pool_create(&pool, apr_thread_pool_get(state->thd)); |
335 | 0 | thread_pool = pool; |
336 | 0 | } |
337 | 0 | state->pool = pool; |
338 | 0 | } |
339 | 0 | return apr_palloc(pool, size); |
340 | 0 | } |
341 | 0 | #endif |
342 | | |
343 | 0 | return malloc(size); |
344 | 0 | } |
345 | | |
346 | | static void private_free(void *block, void *ctx) |
347 | 0 | { |
348 | 0 | struct match_data_state *state = ctx; |
349 | 0 | char *p = block; |
350 | |
|
351 | 0 | if (p >= state->buf && p < state->buf + sizeof(state->buf)) { |
352 | | /* This block allocated from stack buffer. Do nothing. */ |
353 | 0 | return; |
354 | 0 | } |
355 | | |
356 | 0 | #if APREG_USE_THREAD_LOCAL |
357 | 0 | if (state->thd) { |
358 | | /* Freed in cleanup_state() eventually. */ |
359 | 0 | return; |
360 | 0 | } |
361 | 0 | #endif |
362 | | |
363 | 0 | free(block); |
364 | 0 | } |
365 | | |
366 | | static APR_INLINE |
367 | | int setup_state(struct match_data_state *state, apr_uint32_t ncaps) |
368 | 0 | { |
369 | 0 | state->buf_used = 0; |
370 | |
|
371 | 0 | #if APREG_USE_THREAD_LOCAL |
372 | 0 | state->thd = ap_thread_current(); |
373 | 0 | state->pool = NULL; |
374 | 0 | #endif |
375 | |
|
376 | 0 | #ifdef HAVE_PCRE2 |
377 | 0 | state->pcre2_ctx = pcre2_general_context_create(private_malloc, |
378 | 0 | private_free, state); |
379 | 0 | if (!state->pcre2_ctx) { |
380 | 0 | return 0; |
381 | 0 | } |
382 | | |
383 | 0 | state->match_data = pcre2_match_data_create(ncaps, state->pcre2_ctx); |
384 | 0 | if (!state->match_data) { |
385 | 0 | pcre2_general_context_free(state->pcre2_ctx); |
386 | 0 | return 0; |
387 | 0 | } |
388 | | #else |
389 | | if (ncaps) { |
390 | | state->match_data = private_malloc(ncaps * sizeof(int) * 3, state); |
391 | | if (!state->match_data) { |
392 | | return 0; |
393 | | } |
394 | | } |
395 | | else { |
396 | | /* Fine with PCRE1 */ |
397 | | state->match_data = NULL; |
398 | | } |
399 | | #endif |
400 | | |
401 | 0 | return 1; |
402 | 0 | } |
403 | | |
404 | | static APR_INLINE |
405 | | void cleanup_state(struct match_data_state *state) |
406 | 0 | { |
407 | 0 | #ifdef HAVE_PCRE2 |
408 | 0 | pcre2_match_data_free(state->match_data); |
409 | 0 | pcre2_general_context_free(state->pcre2_ctx); |
410 | | #else |
411 | | if (state->match_data) { |
412 | | private_free(state->match_data, state); |
413 | | } |
414 | | #endif |
415 | |
|
416 | 0 | #if APREG_USE_THREAD_LOCAL |
417 | 0 | if (state->pool) { |
418 | | /* Let the thread's pool allocator recycle or free according |
419 | | * to its max_free setting. |
420 | | */ |
421 | 0 | apr_pool_clear(state->pool); |
422 | 0 | } |
423 | 0 | #endif |
424 | 0 | } |
425 | | |
426 | | AP_DECLARE(int) ap_regexec(const ap_regex_t *preg, const char *string, |
427 | | apr_size_t nmatch, ap_regmatch_t *pmatch, |
428 | | int eflags) |
429 | 0 | { |
430 | 0 | return ap_regexec_len(preg, string, strlen(string), nmatch, pmatch, |
431 | 0 | eflags); |
432 | 0 | } |
433 | | |
434 | | AP_DECLARE(int) ap_regexec_len(const ap_regex_t *preg, const char *buff, |
435 | | apr_size_t len, apr_size_t nmatch, |
436 | | ap_regmatch_t *pmatch, int eflags) |
437 | 0 | { |
438 | 0 | int rc; |
439 | 0 | int options = 0; |
440 | 0 | struct match_data_state state; |
441 | 0 | match_vector_pt ovector = NULL; |
442 | 0 | apr_uint32_t ncaps = (apr_uint32_t)preg->re_nsub + 1; |
443 | |
|
444 | | #ifndef HAVE_PCRE2 |
445 | | /* This is fine if pcre_exec() gets a vector size smaller than the |
446 | | * number of capturing groups (it will treat the remaining ones as |
447 | | * non-capturing), but if the vector is too small to keep track of |
448 | | * the potential backrefs within the pattern, it will temporarily |
449 | | * malloc()ate the necessary space anyway. So let's provide a vector |
450 | | * of at least PCRE_INFO_BACKREFMAX entries (likely zero, otherwise |
451 | | * the vector is most likely cached already anyway). |
452 | | * Note that if no captures are to be used by the caller, passing an |
453 | | * nmatch of zero (thus forcing all groups to be non-capturing) may |
454 | | * allow for some optimizations and/or less recursion (stack usage) |
455 | | * with PCRE1, unless backrefs.. |
456 | | */ |
457 | | if (ncaps > nmatch) { |
458 | | int backrefmax = 0; |
459 | | pcre_fullinfo((const pcre *)preg->re_pcre, NULL, |
460 | | PCRE_INFO_BACKREFMAX, &backrefmax); |
461 | | if (backrefmax > 0 && (apr_uint32_t)backrefmax >= nmatch) { |
462 | | ncaps = (apr_uint32_t)backrefmax + 1; |
463 | | } |
464 | | else { |
465 | | ncaps = nmatch; |
466 | | } |
467 | | } |
468 | | #endif |
469 | |
|
470 | 0 | if (!setup_state(&state, ncaps)) { |
471 | 0 | return AP_REG_ESPACE; |
472 | 0 | } |
473 | | |
474 | 0 | if ((eflags & AP_REG_NOTBOL) != 0) |
475 | 0 | options |= PCREn(NOTBOL); |
476 | 0 | if ((eflags & AP_REG_NOTEOL) != 0) |
477 | 0 | options |= PCREn(NOTEOL); |
478 | 0 | if ((eflags & AP_REG_NOTEMPTY) != 0) |
479 | 0 | options |= PCREn(NOTEMPTY); |
480 | 0 | if ((eflags & AP_REG_ANCHORED) != 0) |
481 | 0 | options |= PCREn(ANCHORED); |
482 | |
|
483 | 0 | #ifdef HAVE_PCRE2 |
484 | 0 | rc = pcre2_match((const pcre2_code *)preg->re_pcre, |
485 | 0 | (const unsigned char *)buff, len, 0, options, |
486 | 0 | state.match_data, NULL); |
487 | 0 | ovector = pcre2_get_ovector_pointer(state.match_data); |
488 | | #else |
489 | | ovector = state.match_data; |
490 | | rc = pcre_exec((const pcre *)preg->re_pcre, NULL, buff, (int)len, |
491 | | 0, options, ovector, ncaps * 3); |
492 | | #endif |
493 | |
|
494 | 0 | if (rc >= 0) { |
495 | 0 | apr_size_t n = rc, i; |
496 | 0 | if (n == 0 || n > nmatch) |
497 | 0 | rc = n = nmatch; /* All capture slots were filled in */ |
498 | 0 | for (i = 0; i < n; i++) { |
499 | 0 | pmatch[i].rm_so = ovector[i * 2]; |
500 | 0 | pmatch[i].rm_eo = ovector[i * 2 + 1]; |
501 | 0 | } |
502 | 0 | for (; i < nmatch; i++) |
503 | 0 | pmatch[i].rm_so = pmatch[i].rm_eo = -1; |
504 | 0 | cleanup_state(&state); |
505 | 0 | return 0; |
506 | 0 | } |
507 | 0 | else { |
508 | 0 | cleanup_state(&state); |
509 | 0 | #ifdef HAVE_PCRE2 |
510 | 0 | if (rc <= PCRE2_ERROR_UTF8_ERR1 && rc >= PCRE2_ERROR_UTF8_ERR21) |
511 | 0 | return AP_REG_INVARG; |
512 | 0 | #endif |
513 | 0 | switch (rc) { |
514 | 0 | case PCREn(ERROR_NOMATCH): |
515 | 0 | return AP_REG_NOMATCH; |
516 | 0 | case PCREn(ERROR_NULL): |
517 | 0 | return AP_REG_INVARG; |
518 | 0 | case PCREn(ERROR_BADOPTION): |
519 | 0 | return AP_REG_INVARG; |
520 | 0 | case PCREn(ERROR_BADMAGIC): |
521 | 0 | return AP_REG_INVARG; |
522 | 0 | case PCREn(ERROR_NOMEMORY): |
523 | 0 | return AP_REG_ESPACE; |
524 | 0 | #if defined(HAVE_PCRE2) || defined(PCRE_ERROR_MATCHLIMIT) |
525 | 0 | case PCREn(ERROR_MATCHLIMIT): |
526 | 0 | return AP_REG_ESPACE; |
527 | 0 | #endif |
528 | | #if defined(PCRE_ERROR_UNKNOWN_NODE) |
529 | | case PCRE_ERROR_UNKNOWN_NODE: |
530 | | return AP_REG_ASSERT; |
531 | | #endif |
532 | | #if defined(PCRE_ERROR_BADUTF8) |
533 | | case PCREn(ERROR_BADUTF8): |
534 | | return AP_REG_INVARG; |
535 | | #endif |
536 | | #if defined(PCRE_ERROR_BADUTF8_OFFSET) |
537 | | case PCREn(ERROR_BADUTF8_OFFSET): |
538 | | return AP_REG_INVARG; |
539 | | #endif |
540 | 0 | default: |
541 | 0 | return AP_REG_ASSERT; |
542 | 0 | } |
543 | 0 | } |
544 | 0 | } |
545 | | |
546 | | AP_DECLARE(int) ap_regname(const ap_regex_t *preg, |
547 | | apr_array_header_t *names, const char *prefix, |
548 | | int upper) |
549 | 0 | { |
550 | 0 | char *nametable; |
551 | |
|
552 | 0 | #ifdef HAVE_PCRE2 |
553 | 0 | uint32_t namecount; |
554 | 0 | uint32_t nameentrysize; |
555 | 0 | uint32_t i; |
556 | 0 | pcre2_pattern_info((const pcre2_code *)preg->re_pcre, |
557 | 0 | PCRE2_INFO_NAMECOUNT, &namecount); |
558 | 0 | pcre2_pattern_info((const pcre2_code *)preg->re_pcre, |
559 | 0 | PCRE2_INFO_NAMEENTRYSIZE, &nameentrysize); |
560 | 0 | pcre2_pattern_info((const pcre2_code *)preg->re_pcre, |
561 | 0 | PCRE2_INFO_NAMETABLE, &nametable); |
562 | | #else |
563 | | int namecount; |
564 | | int nameentrysize; |
565 | | int i; |
566 | | pcre_fullinfo((const pcre *)preg->re_pcre, NULL, |
567 | | PCRE_INFO_NAMECOUNT, &namecount); |
568 | | pcre_fullinfo((const pcre *)preg->re_pcre, NULL, |
569 | | PCRE_INFO_NAMEENTRYSIZE, &nameentrysize); |
570 | | pcre_fullinfo((const pcre *)preg->re_pcre, NULL, |
571 | | PCRE_INFO_NAMETABLE, &nametable); |
572 | | #endif |
573 | |
|
574 | 0 | for (i = 0; i < namecount; i++) { |
575 | 0 | const char *offset = nametable + i * nameentrysize; |
576 | 0 | int capture = ((offset[0] << 8) + offset[1]); |
577 | 0 | while (names->nelts <= capture) { |
578 | 0 | apr_array_push(names); |
579 | 0 | } |
580 | 0 | if (upper || prefix) { |
581 | 0 | char *name = ((char **) names->elts)[capture] = |
582 | 0 | prefix ? apr_pstrcat(names->pool, prefix, offset + 2, |
583 | 0 | NULL) : |
584 | 0 | apr_pstrdup(names->pool, offset + 2); |
585 | 0 | if (upper) { |
586 | 0 | ap_str_toupper(name); |
587 | 0 | } |
588 | 0 | } |
589 | 0 | else { |
590 | 0 | ((const char **)names->elts)[capture] = offset + 2; |
591 | 0 | } |
592 | 0 | } |
593 | |
|
594 | 0 | return namecount; |
595 | 0 | } |
596 | | |
597 | | #endif /* PCRE_DUPNAMES defined */ |
598 | | |
599 | | /* End of pcreposix.c */ |