Coverage Report

Created: 2025-07-11 06:40

/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
12.1k
#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
5.98k
{
157
5.98k
#ifdef HAVE_PCRE2
158
5.98k
    pcre2_code_free(preg->re_pcre);
159
#else
160
    (pcre_free)(preg->re_pcre);
161
#endif
162
5.98k
}
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
5.99k
{
214
5.99k
#ifdef HAVE_PCRE2
215
5.99k
    uint32_t capcount;
216
5.99k
    PCRE2_SIZE erroffset;
217
#else
218
    const char *errorptr;
219
    int erroffset;
220
#endif
221
5.99k
    int errcode = 0;
222
5.99k
    int options = PCREn(DUPNAMES);
223
224
5.99k
    if ((cflags & AP_REG_NO_DEFAULT) == 0)
225
5.99k
        cflags |= default_cflags;
226
227
5.99k
    if ((cflags & AP_REG_ICASE) != 0)
228
1
        options |= PCREn(CASELESS);
229
5.99k
    if ((cflags & AP_REG_NEWLINE) != 0)
230
128
        options |= PCREn(MULTILINE);
231
5.99k
    if ((cflags & AP_REG_DOTALL) != 0)
232
5.99k
        options |= PCREn(DOTALL);
233
5.99k
    if ((cflags & AP_REG_DOLLAR_ENDONLY) != 0)
234
5.99k
        options |= PCREn(DOLLAR_ENDONLY);
235
236
5.99k
#ifdef HAVE_PCRE2
237
5.99k
    preg->re_pcre = pcre2_compile((const unsigned char *)pattern,
238
5.99k
                                  PCRE2_ZERO_TERMINATED, options, &errcode,
239
5.99k
                                  &erroffset, NULL);
240
#else
241
    preg->re_pcre = pcre_compile2(pattern, options, &errcode,
242
                                  &errorptr, &erroffset, NULL);
243
#endif
244
245
5.99k
    preg->re_erroffset = erroffset;
246
5.99k
    if (preg->re_pcre == NULL) {
247
        /* Internal ERR21 is "failed to get memory" according to pcreapi(3) */
248
1
        if (errcode == 21)
249
0
            return AP_REG_ESPACE;
250
1
        return AP_REG_INVARG;
251
1
    }
252
253
5.98k
#ifdef HAVE_PCRE2
254
5.98k
    pcre2_pattern_info((const pcre2_code *)preg->re_pcre,
255
5.98k
                       PCRE2_INFO_CAPTURECOUNT, &capcount);
256
5.98k
    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
5.98k
    return 0;
262
5.99k
}
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_ex(preg, string, strlen(string), 0,
431
0
                         nmatch, pmatch, eflags);
432
0
}
433
434
AP_DECLARE(int) ap_regexec_len(const ap_regex_t *preg,
435
                               const char *buff, apr_size_t len,
436
                               apr_size_t nmatch, ap_regmatch_t *pmatch,
437
                               int eflags)
438
0
{
439
0
    return ap_regexec_ex(preg, buff, len, 0, nmatch, pmatch, eflags);
440
0
}
441
442
AP_DECLARE(int) ap_regexec_ex(const ap_regex_t *preg,
443
                              const char *buff, apr_size_t len, apr_size_t pos,
444
                              apr_size_t nmatch, ap_regmatch_t *pmatch,
445
                              int eflags)
446
0
{
447
0
    int rc;
448
0
    int options = 0;
449
0
    struct match_data_state state;
450
0
    match_vector_pt ovector = NULL;
451
0
    apr_uint32_t ncaps = (apr_uint32_t)preg->re_nsub + 1;
452
453
#ifndef HAVE_PCRE2
454
    /* PCRE1 uses ints, reject overflowing values */
455
    if (len > APR_INT32_MAX || pos > APR_INT32_MAX) {
456
        return AP_REG_INVARG;
457
    }
458
459
    /* This is fine if pcre_exec() gets a vector size smaller than the
460
     * number of capturing groups (it will treat the remaining ones as
461
     * non-capturing), but if the vector is too small to keep track of
462
     * the potential backrefs within the pattern, it will temporarily
463
     * malloc()ate the necessary space anyway. So let's provide a vector
464
     * of at least PCRE_INFO_BACKREFMAX entries (likely zero, otherwise
465
     * the vector is most likely cached already anyway).
466
     * Note that if no captures are to be used by the caller, passing an
467
     * nmatch of zero (thus forcing all groups to be non-capturing) may
468
     * allow for some optimizations and/or less recursion (stack usage)
469
     * with PCRE1, unless backrefs..
470
     */
471
    if (ncaps > nmatch) {
472
        int backrefmax = 0;
473
        pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
474
                      PCRE_INFO_BACKREFMAX, &backrefmax);
475
        if (backrefmax > 0 && (apr_uint32_t)backrefmax >= nmatch) {
476
            ncaps = (apr_uint32_t)backrefmax + 1;
477
        }
478
        else {
479
            ncaps = nmatch;
480
        }
481
    }
482
#endif
483
484
0
    if (!setup_state(&state, ncaps)) {
485
0
        return AP_REG_ESPACE;
486
0
    }
487
488
0
    if ((eflags & AP_REG_NOTBOL) != 0)
489
0
        options |= PCREn(NOTBOL);
490
0
    if ((eflags & AP_REG_NOTEOL) != 0)
491
0
        options |= PCREn(NOTEOL);
492
0
    if ((eflags & AP_REG_NOTEMPTY) != 0)
493
0
        options |= PCREn(NOTEMPTY);
494
0
    if ((eflags & AP_REG_NOTEMPTY_ATSTART) != 0)
495
0
        options |= PCREn(NOTEMPTY_ATSTART);
496
0
    if ((eflags & AP_REG_ANCHORED) != 0)
497
0
        options |= PCREn(ANCHORED);
498
499
0
#ifdef HAVE_PCRE2
500
0
    rc = pcre2_match((const pcre2_code *)preg->re_pcre,
501
0
                     (const unsigned char *)buff, len, pos, options,
502
0
                     state.match_data, NULL);
503
0
    ovector = pcre2_get_ovector_pointer(state.match_data);
504
#else
505
    ovector = state.match_data;
506
    rc = pcre_exec((const pcre *)preg->re_pcre, NULL,
507
                   buff, (int)len, (int)pos, options,
508
                   ovector, ncaps * 3);
509
#endif
510
511
0
    if (rc >= 0) {
512
0
        apr_size_t n = rc, i;
513
0
        if (n == 0 || n > nmatch)
514
0
            rc = n = nmatch; /* All capture slots were filled in */
515
0
        for (i = 0; i < n; i++) {
516
0
            pmatch[i].rm_so = ovector[i * 2];
517
0
            pmatch[i].rm_eo = ovector[i * 2 + 1];
518
0
        }
519
0
        for (; i < nmatch; i++)
520
0
            pmatch[i].rm_so = pmatch[i].rm_eo = -1;
521
0
        cleanup_state(&state);
522
0
        return 0;
523
0
    }
524
0
    else {
525
0
        cleanup_state(&state);
526
0
#ifdef HAVE_PCRE2
527
0
        if (rc <= PCRE2_ERROR_UTF8_ERR1 && rc >= PCRE2_ERROR_UTF8_ERR21)
528
0
            return AP_REG_INVARG;
529
0
#endif
530
0
        switch (rc) {
531
0
        case PCREn(ERROR_NOMATCH):
532
0
            return AP_REG_NOMATCH;
533
0
        case PCREn(ERROR_NULL):
534
0
            return AP_REG_INVARG;
535
0
        case PCREn(ERROR_BADOPTION):
536
0
            return AP_REG_INVARG;
537
0
        case PCREn(ERROR_BADMAGIC):
538
0
            return AP_REG_INVARG;
539
0
        case PCREn(ERROR_NOMEMORY):
540
0
            return AP_REG_ESPACE;
541
0
#if defined(HAVE_PCRE2) || defined(PCRE_ERROR_MATCHLIMIT)
542
0
        case PCREn(ERROR_MATCHLIMIT):
543
0
            return AP_REG_ESPACE;
544
0
#endif
545
#if defined(PCRE_ERROR_UNKNOWN_NODE)
546
        case PCRE_ERROR_UNKNOWN_NODE:
547
            return AP_REG_ASSERT;
548
#endif
549
#if defined(PCRE_ERROR_BADUTF8)
550
        case PCREn(ERROR_BADUTF8):
551
            return AP_REG_INVARG;
552
#endif
553
#if defined(PCRE_ERROR_BADUTF8_OFFSET)
554
        case PCREn(ERROR_BADUTF8_OFFSET):
555
            return AP_REG_INVARG;
556
#endif
557
0
        default:
558
0
            return AP_REG_ASSERT;
559
0
        }
560
0
    }
561
0
}
562
563
AP_DECLARE(int) ap_regname(const ap_regex_t *preg,
564
                           apr_array_header_t *names, const char *prefix,
565
                           int upper)
566
0
{
567
0
    char *nametable;
568
569
0
#ifdef HAVE_PCRE2
570
0
    uint32_t namecount;
571
0
    uint32_t nameentrysize;
572
0
    uint32_t i;
573
0
    pcre2_pattern_info((const pcre2_code *)preg->re_pcre,
574
0
                       PCRE2_INFO_NAMECOUNT, &namecount);
575
0
    pcre2_pattern_info((const pcre2_code *)preg->re_pcre,
576
0
                       PCRE2_INFO_NAMEENTRYSIZE, &nameentrysize);
577
0
    pcre2_pattern_info((const pcre2_code *)preg->re_pcre,
578
0
                       PCRE2_INFO_NAMETABLE, &nametable);
579
#else
580
    int namecount;
581
    int nameentrysize;
582
    int i;
583
    pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
584
                  PCRE_INFO_NAMECOUNT, &namecount);
585
    pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
586
                  PCRE_INFO_NAMEENTRYSIZE, &nameentrysize);
587
    pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
588
                  PCRE_INFO_NAMETABLE, &nametable);
589
#endif
590
591
0
    for (i = 0; i < namecount; i++) {
592
0
        const char *offset = nametable + i * nameentrysize;
593
0
        int capture = ((offset[0] << 8) + offset[1]);
594
0
        while (names->nelts <= capture) {
595
0
            apr_array_push(names);
596
0
        }
597
0
        if (upper || prefix) {
598
0
            char *name = ((char **) names->elts)[capture] =
599
0
                    prefix ? apr_pstrcat(names->pool, prefix, offset + 2,
600
0
                            NULL) :
601
0
                            apr_pstrdup(names->pool, offset + 2);
602
0
            if (upper) {
603
0
                ap_str_toupper(name);
604
0
            }
605
0
        }
606
0
        else {
607
0
            ((const char **)names->elts)[capture] = offset + 2;
608
0
        }
609
0
    }
610
611
0
    return namecount;
612
0
}
613
614
#endif /* PCRE_DUPNAMES defined */
615
616
/* End of pcreposix.c */