/src/njs/external/njs_regex.c
Line | Count | Source |
1 | | |
2 | | /* |
3 | | * Copyright (C) Igor Sysoev |
4 | | * Copyright (C) Dmitry Volyntsev |
5 | | * Copyright (C) NGINX, Inc. |
6 | | */ |
7 | | |
8 | | |
9 | | #include <njs_main.h> |
10 | | |
11 | | #ifdef NJS_HAVE_PCRE2 |
12 | | |
13 | | #define PCRE2_CODE_UNIT_WIDTH 8 |
14 | | #include <pcre2.h> |
15 | | |
16 | | |
17 | | static const u_char* njs_regex_pcre2_error(int errcode, u_char buffer[128]); |
18 | | |
19 | | #else |
20 | | |
21 | | #include <pcre.h> |
22 | | |
23 | | |
24 | | static void *njs_pcre_malloc(size_t size); |
25 | | static void njs_pcre_free(void *p); |
26 | | |
27 | | |
28 | | static njs_regex_generic_ctx_t *regex_context; |
29 | | |
30 | | #endif |
31 | | |
32 | | |
33 | | njs_regex_generic_ctx_t * |
34 | | njs_regex_generic_ctx_create(njs_pcre_malloc_t private_malloc, |
35 | | njs_pcre_free_t private_free, void *memory_data) |
36 | 1.08k | { |
37 | 1.08k | #ifdef NJS_HAVE_PCRE2 |
38 | | |
39 | 1.08k | return pcre2_general_context_create(private_malloc, private_free, |
40 | 1.08k | memory_data); |
41 | | #else |
42 | | |
43 | | njs_regex_generic_ctx_t *ctx; |
44 | | |
45 | | ctx = private_malloc(sizeof(njs_regex_generic_ctx_t), memory_data); |
46 | | |
47 | | if (njs_fast_path(ctx != NULL)) { |
48 | | ctx->private_malloc = private_malloc; |
49 | | ctx->private_free = private_free; |
50 | | ctx->memory_data = memory_data; |
51 | | } |
52 | | |
53 | | return ctx; |
54 | | |
55 | | #endif |
56 | 1.08k | } |
57 | | |
58 | | |
59 | | njs_regex_compile_ctx_t * |
60 | | njs_regex_compile_ctx_create(njs_regex_generic_ctx_t *ctx) |
61 | 1.08k | { |
62 | 1.08k | #ifdef NJS_HAVE_PCRE2 |
63 | 1.08k | pcre2_compile_context *cc; |
64 | | |
65 | 1.08k | cc = pcre2_compile_context_create(ctx); |
66 | | |
67 | 1.08k | #ifdef PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES |
68 | 1.08k | if (njs_fast_path(cc != NULL)) { |
69 | | /* Workaround for surrogate pairs in regular expressions |
70 | | * |
71 | | * This option is needed because njs, unlike the standard ECMAScript, |
72 | | * stores and processes strings in UTF-8 encoding. |
73 | | * PCRE2 does not support surrogate pairs by default when it |
74 | | * is compiled for UTF-8 only strings. But many polyfills |
75 | | * and transpilers use such surrogate pairs expressions. |
76 | | */ |
77 | 1.08k | pcre2_set_compile_extra_options(cc, |
78 | 1.08k | PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES); |
79 | 1.08k | } |
80 | 1.08k | #endif |
81 | | |
82 | 1.08k | return cc; |
83 | | |
84 | | #else |
85 | | |
86 | | return ctx; |
87 | | |
88 | | #endif |
89 | 1.08k | } |
90 | | |
91 | | |
92 | | |
93 | | njs_int_t |
94 | | njs_regex_escape(njs_mp_t *mp, njs_str_t *text) |
95 | 47.3k | { |
96 | 47.3k | #ifdef NJS_HAVE_PCRE2 |
97 | 47.3k | size_t anychars, nomatches; |
98 | 47.3k | u_char *p, *dst, *start, *end; |
99 | | |
100 | | /* |
101 | | * 1) [^] is a valid regexp expression in JavaScript, but PCRE2 |
102 | | * rejects it as invalid, replacing it with equivalent PCRE2 [\s\S] |
103 | | * expression. |
104 | | * 2) [] is a valid regexp expression in JavaScript, but PCRE2 |
105 | | * rejects it as invalid, replacing it with equivalent PCRE2 (?!) |
106 | | * expression which matches nothing. |
107 | | */ |
108 | | |
109 | 47.3k | start = text->start; |
110 | 47.3k | end = text->start + text->length; |
111 | | |
112 | 47.3k | anychars = 0; |
113 | 47.3k | nomatches = 0; |
114 | | |
115 | 32.1M | for (p = start; p < end; p++) { |
116 | 32.0M | switch (*p) { |
117 | 18.5k | case '\\': |
118 | 18.5k | p += 1; |
119 | | |
120 | 18.5k | break; |
121 | | |
122 | 11.1k | case '[': |
123 | 11.1k | if (p + 1 < end && p[1] == ']') { |
124 | 5.21k | p += 1; |
125 | 5.21k | nomatches += 1; |
126 | | |
127 | 5.96k | } else if (p + 2 < end && p[1] == '^' && p[2] == ']') { |
128 | 8 | p += 2; |
129 | 8 | anychars += 1; |
130 | | |
131 | 5.95k | } else { |
132 | 604k | while (p < end && *p != ']') { |
133 | 598k | p += 1; |
134 | 598k | } |
135 | 5.95k | } |
136 | | |
137 | 11.1k | break; |
138 | 32.0M | } |
139 | 32.0M | } |
140 | | |
141 | 47.3k | if (!anychars && !nomatches) { |
142 | 42.1k | return NJS_OK; |
143 | 42.1k | } |
144 | | |
145 | 5.22k | text->length = text->length |
146 | 5.22k | + anychars * (njs_length("\\s\\S") - njs_length("^")) |
147 | 5.22k | + nomatches * (njs_length("?!")); |
148 | | |
149 | 5.22k | text->start = njs_mp_alloc(mp, text->length); |
150 | 5.22k | if (njs_slow_path(text->start == NULL)) { |
151 | 0 | return NJS_ERROR; |
152 | 0 | } |
153 | | |
154 | 5.22k | dst = text->start; |
155 | | |
156 | 721k | for (p = start; p < end; p++) { |
157 | | |
158 | 715k | switch (*p) { |
159 | 3.68k | case '\\': |
160 | 3.68k | *dst++ = *p; |
161 | 3.68k | if (p + 1 < end) { |
162 | 3.68k | p += 1; |
163 | 3.68k | *dst++ = *p; |
164 | 3.68k | } |
165 | | |
166 | 3.68k | continue; |
167 | | |
168 | 10.5k | case '[': |
169 | 10.5k | if (p + 1 < end && p[1] == ']') { |
170 | 5.21k | p += 1; |
171 | 5.21k | dst = njs_cpymem(dst, "(?!)", 4); |
172 | 5.21k | continue; |
173 | | |
174 | 5.32k | } else if (p + 2 < end && p[1] == '^' && p[2] == ']') { |
175 | 8 | p += 2; |
176 | 8 | dst = njs_cpymem(dst, "[\\s\\S]", 6); |
177 | 8 | continue; |
178 | | |
179 | 5.31k | } else { |
180 | 5.31k | *dst++ = *p++; /* Copy '['. */ |
181 | | |
182 | 59.1k | while (p < end && *p != ']') { |
183 | 53.8k | *dst++ = *p++; |
184 | 53.8k | } |
185 | | |
186 | 5.31k | if (p < end) { |
187 | 5.31k | *dst++ = *p; /* Copy ']'. */ |
188 | 5.31k | } |
189 | | |
190 | 5.31k | continue; |
191 | 5.31k | } |
192 | 715k | } |
193 | | |
194 | 701k | *dst++ = *p; |
195 | 701k | } |
196 | | |
197 | 5.22k | njs_assert(dst == text->start + text->length); |
198 | | |
199 | 5.22k | return NJS_OK; |
200 | | |
201 | | #else |
202 | | |
203 | | /* |
204 | | * 1) PCRE with PCRE_JAVASCRIPT_COMPAT flag rejects regexps with |
205 | | * lone closing square brackets as invalid. Whereas according |
206 | | * to ES6: 11.8.5 it is a valid regexp expression. |
207 | | * |
208 | | * 2) escaping zero byte characters as "\u0000". |
209 | | * |
210 | | * Escaping it here as a workaround. |
211 | | */ |
212 | | |
213 | | size_t brackets, zeros; |
214 | | u_char *p, *dst, *start, *end; |
215 | | njs_bool_t in; |
216 | | |
217 | | start = text->start; |
218 | | end = text->start + text->length; |
219 | | |
220 | | in = 0; |
221 | | zeros = 0; |
222 | | brackets = 0; |
223 | | |
224 | | for (p = start; p < end; p++) { |
225 | | |
226 | | switch (*p) { |
227 | | case '[': |
228 | | in = 1; |
229 | | break; |
230 | | |
231 | | case ']': |
232 | | if (!in) { |
233 | | brackets++; |
234 | | } |
235 | | |
236 | | in = 0; |
237 | | break; |
238 | | |
239 | | case '\\': |
240 | | p++; |
241 | | |
242 | | if (p == end || *p != '\0') { |
243 | | break; |
244 | | } |
245 | | |
246 | | /* Fall through. */ |
247 | | |
248 | | case '\0': |
249 | | zeros++; |
250 | | break; |
251 | | } |
252 | | } |
253 | | |
254 | | if (!brackets && !zeros) { |
255 | | return NJS_OK; |
256 | | } |
257 | | |
258 | | text->length = text->length + brackets + zeros * njs_length("\\u0000"); |
259 | | |
260 | | text->start = njs_mp_alloc(mp, text->length); |
261 | | if (njs_slow_path(text->start == NULL)) { |
262 | | return NJS_ERROR; |
263 | | } |
264 | | |
265 | | in = 0; |
266 | | dst = text->start; |
267 | | |
268 | | for (p = start; p < end; p++) { |
269 | | |
270 | | switch (*p) { |
271 | | case '[': |
272 | | in = 1; |
273 | | break; |
274 | | |
275 | | case ']': |
276 | | if (!in) { |
277 | | *dst++ = '\\'; |
278 | | } |
279 | | |
280 | | in = 0; |
281 | | break; |
282 | | |
283 | | case '\\': |
284 | | *dst++ = *p++; |
285 | | |
286 | | if (p == end) { |
287 | | goto done; |
288 | | } |
289 | | |
290 | | if (*p != '\0') { |
291 | | break; |
292 | | } |
293 | | |
294 | | /* Fall through. */ |
295 | | |
296 | | case '\0': |
297 | | dst = njs_cpymem(dst, "\\u0000", 6); |
298 | | continue; |
299 | | } |
300 | | |
301 | | *dst++ = *p; |
302 | | } |
303 | | |
304 | | done: |
305 | | |
306 | | text->length = dst - text->start; |
307 | | |
308 | | return NJS_OK; |
309 | | |
310 | | #endif |
311 | 5.22k | } |
312 | | |
313 | | |
314 | | njs_int_t |
315 | | njs_regex_compile(njs_regex_t *regex, u_char *source, size_t len, |
316 | | njs_regex_flags_t flags, njs_regex_compile_ctx_t *cctx, njs_trace_t *trace) |
317 | 94.6k | { |
318 | 94.6k | #ifdef NJS_HAVE_PCRE2 |
319 | | |
320 | 94.6k | int ret; |
321 | 94.6k | u_char *error; |
322 | 94.6k | size_t erroff; |
323 | 94.6k | njs_uint_t options; |
324 | 94.6k | u_char errstr[128]; |
325 | | |
326 | 94.6k | options = PCRE2_ALT_BSUX | PCRE2_MATCH_UNSET_BACKREF; |
327 | | |
328 | 94.6k | if ((flags & NJS_REGEX_IGNORE_CASE)) { |
329 | 12.1k | options |= PCRE2_CASELESS; |
330 | 12.1k | } |
331 | | |
332 | 94.6k | if ((flags & NJS_REGEX_MULTILINE)) { |
333 | 0 | options |= PCRE2_MULTILINE; |
334 | 0 | } |
335 | | |
336 | 94.6k | if ((flags & NJS_REGEX_STICKY)) { |
337 | 8.15k | options |= PCRE2_ANCHORED; |
338 | 8.15k | } |
339 | | |
340 | 94.6k | if ((flags & NJS_REGEX_UTF8)) { |
341 | 47.3k | options |= PCRE2_UTF; |
342 | 47.3k | } |
343 | | |
344 | 94.6k | regex->code = pcre2_compile(source, len, options, &ret, &erroff, cctx); |
345 | | |
346 | 94.6k | if (njs_slow_path(regex->code == NULL)) { |
347 | 30.5k | error = &source[erroff]; |
348 | | |
349 | 30.5k | njs_alert(trace, NJS_LEVEL_ERROR, |
350 | 30.5k | "pcre_compile2(\"%s\") failed: %s at \"%s\"", |
351 | 30.5k | source, njs_regex_pcre2_error(ret, errstr), error); |
352 | | |
353 | 30.5k | return NJS_DECLINED; |
354 | 30.5k | } |
355 | | |
356 | 64.1k | ret = pcre2_pattern_info(regex->code, PCRE2_INFO_CAPTURECOUNT, |
357 | 64.1k | ®ex->ncaptures); |
358 | | |
359 | 64.1k | if (njs_slow_path(ret < 0)) { |
360 | 0 | njs_alert(trace, NJS_LEVEL_ERROR, |
361 | 0 | "pcre2_pattern_info(\"%s\", PCRE2_INFO_CAPTURECOUNT) failed: %s", |
362 | 0 | source, njs_regex_pcre2_error(ret, errstr)); |
363 | |
|
364 | 0 | return NJS_ERROR; |
365 | 0 | } |
366 | | |
367 | 64.1k | ret = pcre2_pattern_info(regex->code, PCRE2_INFO_BACKREFMAX, |
368 | 64.1k | ®ex->backrefmax); |
369 | | |
370 | 64.1k | if (njs_slow_path(ret < 0)) { |
371 | 0 | njs_alert(trace, NJS_LEVEL_ERROR, |
372 | 0 | "pcre2_pattern_info(\"%s\", PCRE2_INFO_BACKREFMAX) failed: %s", |
373 | 0 | source, njs_regex_pcre2_error(ret, errstr)); |
374 | |
|
375 | 0 | return NJS_ERROR; |
376 | 0 | } |
377 | | |
378 | | /* Reserve additional elements for the first "$0" capture. */ |
379 | 64.1k | regex->ncaptures++; |
380 | | |
381 | 64.1k | if (regex->ncaptures > 1) { |
382 | 10.7k | ret = pcre2_pattern_info(regex->code, PCRE2_INFO_NAMECOUNT, |
383 | 10.7k | ®ex->nentries); |
384 | | |
385 | 10.7k | if (njs_slow_path(ret < 0)) { |
386 | 0 | njs_alert(trace, NJS_LEVEL_ERROR, |
387 | 0 | "pcre2_pattern_info(\"%s\", PCRE2_INFO_NAMECOUNT) failed: %s", |
388 | 0 | source, njs_regex_pcre2_error(ret, errstr)); |
389 | |
|
390 | 0 | return NJS_ERROR; |
391 | 0 | } |
392 | | |
393 | 10.7k | if (regex->nentries != 0) { |
394 | 6.02k | ret = pcre2_pattern_info(regex->code, PCRE2_INFO_NAMEENTRYSIZE, |
395 | 6.02k | ®ex->entry_size); |
396 | | |
397 | 6.02k | if (njs_slow_path(ret < 0)) { |
398 | 0 | njs_alert(trace, NJS_LEVEL_ERROR, |
399 | 0 | "pcre2_pattern_info(\"%s\", PCRE2_INFO_NAMEENTRYSIZE)" |
400 | 0 | " failed: %s", source, |
401 | 0 | njs_regex_pcre2_error(ret, errstr)); |
402 | |
|
403 | 0 | return NJS_ERROR; |
404 | 0 | } |
405 | | |
406 | 6.02k | ret = pcre2_pattern_info(regex->code, PCRE2_INFO_NAMETABLE, |
407 | 6.02k | ®ex->entries); |
408 | | |
409 | 6.02k | if (njs_slow_path(ret < 0)) { |
410 | 0 | njs_alert(trace, NJS_LEVEL_ERROR, |
411 | 0 | "pcre2_pattern_info(\"%s\", PCRE2_INFO_NAMETABLE) " |
412 | 0 | "failed: %s", source, |
413 | 0 | njs_regex_pcre2_error(ret, errstr)); |
414 | |
|
415 | 0 | return NJS_ERROR; |
416 | 0 | } |
417 | 6.02k | } |
418 | 10.7k | } |
419 | | |
420 | 64.1k | return NJS_OK; |
421 | | |
422 | | #else |
423 | | |
424 | | int ret, err, erroff; |
425 | | char *pattern, *error; |
426 | | void *(*saved_malloc)(size_t size); |
427 | | void (*saved_free)(void *p); |
428 | | njs_uint_t options; |
429 | | const char *errstr; |
430 | | njs_regex_generic_ctx_t *ctx; |
431 | | |
432 | | ctx = cctx; |
433 | | |
434 | | ret = NJS_ERROR; |
435 | | |
436 | | saved_malloc = pcre_malloc; |
437 | | pcre_malloc = njs_pcre_malloc; |
438 | | saved_free = pcre_free; |
439 | | pcre_free = njs_pcre_free; |
440 | | regex_context = ctx; |
441 | | |
442 | | #ifdef PCRE_JAVASCRIPT_COMPAT |
443 | | /* JavaScript compatibility has been introduced in PCRE-7.7. */ |
444 | | options = PCRE_JAVASCRIPT_COMPAT; |
445 | | #else |
446 | | options = 0; |
447 | | #endif |
448 | | |
449 | | if ((flags & NJS_REGEX_IGNORE_CASE)) { |
450 | | options |= PCRE_CASELESS; |
451 | | } |
452 | | |
453 | | if ((flags & NJS_REGEX_MULTILINE)) { |
454 | | options |= PCRE_MULTILINE; |
455 | | } |
456 | | |
457 | | if ((flags & NJS_REGEX_STICKY)) { |
458 | | options |= PCRE_ANCHORED; |
459 | | } |
460 | | |
461 | | if ((flags & NJS_REGEX_UTF8)) { |
462 | | options |= PCRE_UTF8; |
463 | | } |
464 | | |
465 | | pattern = (char *) source; |
466 | | |
467 | | regex->code = pcre_compile(pattern, options, &errstr, &erroff, NULL); |
468 | | |
469 | | if (njs_slow_path(regex->code == NULL)) { |
470 | | error = pattern + erroff; |
471 | | |
472 | | if (*error != '\0') { |
473 | | njs_alert(trace, NJS_LEVEL_ERROR, |
474 | | "pcre_compile(\"%s\") failed: %s at \"%s\"", |
475 | | pattern, errstr, error); |
476 | | |
477 | | } else { |
478 | | njs_alert(trace, NJS_LEVEL_ERROR, |
479 | | "pcre_compile(\"%s\") failed: %s", pattern, errstr); |
480 | | } |
481 | | |
482 | | ret = NJS_DECLINED; |
483 | | |
484 | | goto done; |
485 | | } |
486 | | |
487 | | regex->extra = pcre_study(regex->code, 0, &errstr); |
488 | | |
489 | | if (njs_slow_path(errstr != NULL)) { |
490 | | njs_alert(trace, NJS_LEVEL_WARN, |
491 | | "pcre_study(\"%s\") failed: %s", pattern, errstr); |
492 | | } |
493 | | |
494 | | err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_CAPTURECOUNT, |
495 | | ®ex->ncaptures); |
496 | | |
497 | | if (njs_slow_path(err < 0)) { |
498 | | njs_alert(trace, NJS_LEVEL_ERROR, |
499 | | "pcre_fullinfo(\"%s\", PCRE_INFO_CAPTURECOUNT) failed: %d", |
500 | | pattern, err); |
501 | | |
502 | | goto done; |
503 | | } |
504 | | |
505 | | err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_BACKREFMAX, |
506 | | ®ex->backrefmax); |
507 | | |
508 | | if (njs_slow_path(err < 0)) { |
509 | | njs_alert(trace, NJS_LEVEL_ERROR, |
510 | | "pcre_fullinfo(\"%s\", PCRE_INFO_BACKREFMAX) failed: %d", |
511 | | pattern, err); |
512 | | |
513 | | goto done; |
514 | | } |
515 | | |
516 | | /* Reserve additional elements for the first "$0" capture. */ |
517 | | regex->ncaptures++; |
518 | | |
519 | | if (regex->ncaptures > 1) { |
520 | | err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_NAMECOUNT, |
521 | | ®ex->nentries); |
522 | | |
523 | | if (njs_slow_path(err < 0)) { |
524 | | njs_alert(trace, NJS_LEVEL_ERROR, |
525 | | "pcre_fullinfo(\"%s\", PCRE_INFO_NAMECOUNT) failed: %d", |
526 | | pattern, err); |
527 | | |
528 | | goto done; |
529 | | } |
530 | | |
531 | | if (regex->nentries != 0) { |
532 | | err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_NAMEENTRYSIZE, |
533 | | ®ex->entry_size); |
534 | | |
535 | | if (njs_slow_path(err < 0)) { |
536 | | njs_alert(trace, NJS_LEVEL_ERROR, "pcre_fullinfo(\"%s\", " |
537 | | "PCRE_INFO_NAMEENTRYSIZE) failed: %d", pattern, err); |
538 | | |
539 | | goto done; |
540 | | } |
541 | | |
542 | | err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_NAMETABLE, |
543 | | ®ex->entries); |
544 | | |
545 | | if (njs_slow_path(err < 0)) { |
546 | | njs_alert(trace, NJS_LEVEL_ERROR, "pcre_fullinfo(\"%s\", " |
547 | | "PCRE_INFO_NAMETABLE) failed: %d", pattern, err); |
548 | | |
549 | | goto done; |
550 | | } |
551 | | } |
552 | | } |
553 | | |
554 | | ret = NJS_OK; |
555 | | |
556 | | done: |
557 | | |
558 | | pcre_malloc = saved_malloc; |
559 | | pcre_free = saved_free; |
560 | | regex_context = NULL; |
561 | | |
562 | | return ret; |
563 | | |
564 | | #endif |
565 | 64.1k | } |
566 | | |
567 | | |
568 | | njs_bool_t |
569 | | njs_regex_is_valid(njs_regex_t *regex) |
570 | 96.0k | { |
571 | 96.0k | return (regex->code != NULL); |
572 | 96.0k | } |
573 | | |
574 | | |
575 | | njs_int_t |
576 | | njs_regex_named_capture(njs_regex_t *regex, njs_str_t *name, int n, |
577 | | uint32_t *capture) |
578 | 422k | { |
579 | 422k | u_char *entry; |
580 | | |
581 | 422k | if (name == NULL) { |
582 | 42.6k | return regex->nentries; |
583 | 42.6k | } |
584 | | |
585 | 379k | if (n >= regex->nentries) { |
586 | 0 | return NJS_ERROR; |
587 | 0 | } |
588 | | |
589 | 379k | entry = regex->entries + regex->entry_size * n; |
590 | | |
591 | 379k | name->start = entry + 2; |
592 | 379k | name->length = njs_strlen(name->start); |
593 | | |
594 | 379k | *capture = (entry[0] << 8) + entry[1]; |
595 | | |
596 | 379k | return NJS_OK; |
597 | 379k | } |
598 | | |
599 | | |
600 | | njs_regex_match_data_t * |
601 | | njs_regex_match_data(njs_regex_t *regex, njs_regex_generic_ctx_t *ctx) |
602 | 23.5k | { |
603 | 23.5k | #ifdef NJS_HAVE_PCRE2 |
604 | | |
605 | 23.5k | if (regex != NULL) { |
606 | 22.4k | return pcre2_match_data_create_from_pattern(regex->code, ctx); |
607 | 22.4k | } |
608 | | |
609 | 1.08k | return pcre2_match_data_create(0, ctx); |
610 | | |
611 | | #else |
612 | | |
613 | | size_t size; |
614 | | njs_uint_t ncaptures; |
615 | | njs_regex_match_data_t *match_data; |
616 | | |
617 | | if (regex != NULL) { |
618 | | ncaptures = regex->ncaptures - 1; |
619 | | |
620 | | } else { |
621 | | ncaptures = 0; |
622 | | } |
623 | | |
624 | | /* Each capture is stored in 3 "int" vector elements. */ |
625 | | ncaptures *= 3; |
626 | | size = sizeof(njs_regex_match_data_t) + ncaptures * sizeof(int); |
627 | | |
628 | | match_data = ctx->private_malloc(size, ctx->memory_data); |
629 | | |
630 | | if (njs_fast_path(match_data != NULL)) { |
631 | | match_data->ncaptures = ncaptures + 3; |
632 | | } |
633 | | |
634 | | return match_data; |
635 | | |
636 | | #endif |
637 | 23.5k | } |
638 | | |
639 | | |
640 | | void |
641 | | njs_regex_match_data_free(njs_regex_match_data_t *match_data, |
642 | | njs_regex_generic_ctx_t *ctx) |
643 | 22.4k | { |
644 | 22.4k | #ifdef NJS_HAVE_PCRE2 |
645 | | |
646 | 22.4k | pcre2_match_data_free(match_data); |
647 | | |
648 | | #else |
649 | | |
650 | | ctx->private_free(match_data, ctx->memory_data); |
651 | | |
652 | | #endif |
653 | 22.4k | } |
654 | | |
655 | | |
656 | | njs_int_t |
657 | | njs_regex_match(njs_regex_t *regex, const u_char *subject, size_t off, |
658 | | size_t len, njs_regex_match_data_t *match_data, njs_trace_t *trace) |
659 | 22.4k | { |
660 | 22.4k | #ifdef NJS_HAVE_PCRE2 |
661 | | |
662 | 22.4k | int ret; |
663 | 22.4k | u_char errstr[128]; |
664 | | |
665 | 22.4k | ret = pcre2_match(regex->code, subject, len, off, 0, match_data, NULL); |
666 | | |
667 | 22.4k | if (ret < 0) { |
668 | 19.3k | if (ret == PCRE2_ERROR_NOMATCH) { |
669 | 19.3k | return NJS_DECLINED; |
670 | 19.3k | } |
671 | | |
672 | 2 | njs_alert(trace, NJS_LEVEL_ERROR, "pcre2_match() failed: %s", |
673 | 2 | njs_regex_pcre2_error(ret, errstr)); |
674 | 2 | return NJS_ERROR; |
675 | 19.3k | } |
676 | | |
677 | 3.16k | return ret; |
678 | | |
679 | | #else |
680 | | |
681 | | int ret; |
682 | | |
683 | | ret = pcre_exec(regex->code, regex->extra, (const char *) subject, len, |
684 | | off, 0, match_data->captures, match_data->ncaptures); |
685 | | |
686 | | if (ret <= PCRE_ERROR_NOMATCH) { |
687 | | if (ret == PCRE_ERROR_NOMATCH) { |
688 | | return NJS_DECLINED; |
689 | | } |
690 | | |
691 | | njs_alert(trace, NJS_LEVEL_ERROR, "pcre_exec() failed: %d", ret); |
692 | | return NJS_ERROR; |
693 | | } |
694 | | |
695 | | return ret; |
696 | | |
697 | | #endif |
698 | 22.4k | } |
699 | | |
700 | | |
701 | | size_t |
702 | | njs_regex_capture(njs_regex_match_data_t *match_data, njs_uint_t n) |
703 | 14.3k | { |
704 | 14.3k | #ifdef NJS_HAVE_PCRE2 |
705 | | |
706 | 14.3k | size_t c; |
707 | | |
708 | 14.3k | c = pcre2_get_ovector_pointer(match_data)[n]; |
709 | | |
710 | 14.3k | if (c == PCRE2_UNSET) { |
711 | 1.69k | return NJS_REGEX_UNSET; |
712 | 1.69k | } |
713 | | |
714 | 12.6k | return c; |
715 | | |
716 | | #else |
717 | | |
718 | | return match_data->captures[n]; |
719 | | |
720 | | #endif |
721 | 14.3k | } |
722 | | |
723 | | #ifdef NJS_HAVE_PCRE2 |
724 | | |
725 | | static const u_char * |
726 | | njs_regex_pcre2_error(int errcode, u_char buffer[128]) |
727 | 30.5k | { |
728 | 30.5k | pcre2_get_error_message(errcode, buffer, 128); |
729 | | |
730 | 30.5k | return buffer; |
731 | 30.5k | } |
732 | | |
733 | | #else |
734 | | |
735 | | static void * |
736 | | njs_pcre_malloc(size_t size) |
737 | | { |
738 | | return regex_context->private_malloc(size, regex_context->memory_data); |
739 | | } |
740 | | |
741 | | |
742 | | static void |
743 | | njs_pcre_free(void *p) |
744 | | { |
745 | | regex_context->private_free(p, regex_context->memory_data); |
746 | | } |
747 | | |
748 | | #endif |
749 | | |