]> granicus.if.org Git - apache/blob - server/util_pcre.c
Add compiled and loaded PCRE version numbers
[apache] / server / util_pcre.c
1 /*************************************************
2  *      Perl-Compatible Regular Expressions      *
3  *************************************************/
4
5 /*
6 This is a library of functions to support regular expressions whose syntax
7 and semantics are as close as possible to those of the Perl 5 language. See
8 the file Tech.Notes for some information on the internals.
9
10 This module is a wrapper that provides a POSIX API to the underlying PCRE
11 functions.
12
13 Written by: Philip Hazel <ph10@cam.ac.uk>
14
15            Copyright (c) 1997-2004 University of Cambridge
16
17 -----------------------------------------------------------------------------
18 Redistribution and use in source and binary forms, with or without
19 modification, are permitted provided that the following conditions are met:
20
21     * Redistributions of source code must retain the above copyright notice,
22       this list of conditions and the following disclaimer.
23
24     * Redistributions in binary form must reproduce the above copyright
25       notice, this list of conditions and the following disclaimer in the
26       documentation and/or other materials provided with the distribution.
27
28     * Neither the name of the University of Cambridge nor the names of its
29       contributors may be used to endorse or promote products derived from
30       this software without specific prior written permission.
31
32 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
33 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
36 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 POSSIBILITY OF SUCH DAMAGE.
43 -----------------------------------------------------------------------------
44 */
45
46 #include "httpd.h"
47 #include "apr_strings.h"
48 #include "apr_tables.h"
49 #include "pcre.h"
50
51 #define APR_WANT_STRFUNC
52 #include "apr_want.h"
53
54 #ifndef POSIX_MALLOC_THRESHOLD
55 #define POSIX_MALLOC_THRESHOLD (10)
56 #endif
57
58 /* Table of error strings corresponding to POSIX error codes; must be
59  * kept in synch with include/ap_regex.h's AP_REG_E* definitions.
60  */
61
62 static const char *const pstring[] = {
63     "",                         /* Dummy for value 0 */
64     "internal error",           /* AP_REG_ASSERT */
65     "failed to get memory",     /* AP_REG_ESPACE */
66     "bad argument",             /* AP_REG_INVARG */
67     "match failed"              /* AP_REG_NOMATCH */
68 };
69
70 AP_DECLARE(const char *) ap_pcre_version_string(int which)
71 {
72     switch (which) {
73     case AP_REG_PCRE_COMPILED:
74         return APR_STRINGIFY(PCRE_MAJOR) "." APR_STRINGIFY(PCRE_MINOR) " " APR_STRINGIFY(PCRE_DATE);
75     case AP_REG_PCRE_LOADED:
76         return pcre_version();
77     default:
78         return "Unknown";
79     }
80 }
81
82 AP_DECLARE(apr_size_t) ap_regerror(int errcode, const ap_regex_t *preg,
83                                    char *errbuf, apr_size_t errbuf_size)
84 {
85     const char *message, *addmessage;
86     apr_size_t length, addlength;
87
88     message = (errcode >= (int)(sizeof(pstring) / sizeof(char *))) ?
89               "unknown error code" : pstring[errcode];
90     length = strlen(message) + 1;
91
92     addmessage = " at offset ";
93     addlength = (preg != NULL && (int)preg->re_erroffset != -1) ?
94                 strlen(addmessage) + 6 : 0;
95
96     if (errbuf_size > 0) {
97         if (addlength > 0 && errbuf_size >= length + addlength)
98             apr_snprintf(errbuf, errbuf_size, "%s%s%-6d", message, addmessage,
99                          (int)preg->re_erroffset);
100         else
101             apr_cpystrn(errbuf, message, errbuf_size);
102     }
103
104     return length + addlength;
105 }
106
107
108
109
110 /*************************************************
111  *           Free store held by a regex          *
112  *************************************************/
113
114 AP_DECLARE(void) ap_regfree(ap_regex_t *preg)
115 {
116     (pcre_free)(preg->re_pcre);
117 }
118
119
120
121
122 /*************************************************
123  *            Compile a regular expression       *
124  *************************************************/
125
126 /*
127  * Arguments:
128  *  preg        points to a structure for recording the compiled expression
129  *  pattern     the pattern to compile
130  *  cflags      compilation flags
131  *
132  * Returns:      0 on success
133  *               various non-zero codes on failure
134 */
135 AP_DECLARE(int) ap_regcomp(ap_regex_t * preg, const char *pattern, int cflags)
136 {
137     const char *errorptr;
138     int erroffset;
139     int errcode = 0;
140     int options = PCRE_DUPNAMES;
141
142     if ((cflags & AP_REG_ICASE) != 0)
143         options |= PCRE_CASELESS;
144     if ((cflags & AP_REG_NEWLINE) != 0)
145         options |= PCRE_MULTILINE;
146     if ((cflags & AP_REG_DOTALL) != 0)
147         options |= PCRE_DOTALL;
148
149     preg->re_pcre =
150         pcre_compile2(pattern, options, &errcode, &errorptr, &erroffset, NULL);
151     preg->re_erroffset = erroffset;
152
153     if (preg->re_pcre == NULL) {
154         /*
155          * There doesn't seem to be constants defined for compile time error
156          * codes. 21 is "failed to get memory" according to pcreapi(3).
157          */
158         if (errcode == 21)
159             return AP_REG_ESPACE;
160         return AP_REG_INVARG;
161     }
162
163     pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
164                    PCRE_INFO_CAPTURECOUNT, &(preg->re_nsub));
165     return 0;
166 }
167
168
169
170
171 /*************************************************
172  *              Match a regular expression       *
173  *************************************************/
174
175 /* Unfortunately, PCRE requires 3 ints of working space for each captured
176  * substring, so we have to get and release working store instead of just using
177  * the POSIX structures as was done in earlier releases when PCRE needed only 2
178  * ints. However, if the number of possible capturing brackets is small, use a
179  * block of store on the stack, to reduce the use of malloc/free. The threshold
180  * is in a macro that can be changed at configure time.
181  */
182 AP_DECLARE(int) ap_regexec(const ap_regex_t *preg, const char *string,
183                            apr_size_t nmatch, ap_regmatch_t *pmatch,
184                            int eflags)
185 {
186     return ap_regexec_len(preg, string, strlen(string), nmatch, pmatch,
187                           eflags);
188 }
189
190 AP_DECLARE(int) ap_regexec_len(const ap_regex_t *preg, const char *buff,
191                                apr_size_t len, apr_size_t nmatch,
192                                ap_regmatch_t *pmatch, int eflags)
193 {
194     int rc;
195     int options = 0;
196     int *ovector = NULL;
197     int small_ovector[POSIX_MALLOC_THRESHOLD * 3];
198     int allocated_ovector = 0;
199
200     if ((eflags & AP_REG_NOTBOL) != 0)
201         options |= PCRE_NOTBOL;
202     if ((eflags & AP_REG_NOTEOL) != 0)
203         options |= PCRE_NOTEOL;
204
205     ((ap_regex_t *)preg)->re_erroffset = (apr_size_t)(-1);    /* Only has meaning after compile */
206
207     if (nmatch > 0) {
208         if (nmatch <= POSIX_MALLOC_THRESHOLD) {
209             ovector = &(small_ovector[0]);
210         }
211         else {
212             ovector = (int *)malloc(sizeof(int) * nmatch * 3);
213             if (ovector == NULL)
214                 return AP_REG_ESPACE;
215             allocated_ovector = 1;
216         }
217     }
218
219     rc = pcre_exec((const pcre *)preg->re_pcre, NULL, buff, (int)len,
220                    0, options, ovector, nmatch * 3);
221
222     if (rc == 0)
223         rc = nmatch;            /* All captured slots were filled in */
224
225     if (rc >= 0) {
226         apr_size_t i;
227         for (i = 0; i < (apr_size_t)rc; i++) {
228             pmatch[i].rm_so = ovector[i * 2];
229             pmatch[i].rm_eo = ovector[i * 2 + 1];
230         }
231         if (allocated_ovector)
232             free(ovector);
233         for (; i < nmatch; i++)
234             pmatch[i].rm_so = pmatch[i].rm_eo = -1;
235         return 0;
236     }
237
238     else {
239         if (allocated_ovector)
240             free(ovector);
241         switch (rc) {
242         case PCRE_ERROR_NOMATCH:
243             return AP_REG_NOMATCH;
244         case PCRE_ERROR_NULL:
245             return AP_REG_INVARG;
246         case PCRE_ERROR_BADOPTION:
247             return AP_REG_INVARG;
248         case PCRE_ERROR_BADMAGIC:
249             return AP_REG_INVARG;
250         case PCRE_ERROR_UNKNOWN_NODE:
251             return AP_REG_ASSERT;
252         case PCRE_ERROR_NOMEMORY:
253             return AP_REG_ESPACE;
254 #ifdef PCRE_ERROR_MATCHLIMIT
255         case PCRE_ERROR_MATCHLIMIT:
256             return AP_REG_ESPACE;
257 #endif
258 #ifdef PCRE_ERROR_BADUTF8
259         case PCRE_ERROR_BADUTF8:
260             return AP_REG_INVARG;
261 #endif
262 #ifdef PCRE_ERROR_BADUTF8_OFFSET
263         case PCRE_ERROR_BADUTF8_OFFSET:
264             return AP_REG_INVARG;
265 #endif
266         default:
267             return AP_REG_ASSERT;
268         }
269     }
270 }
271
272 AP_DECLARE(int) ap_regname(const ap_regex_t *preg,
273                            apr_array_header_t *names, const char *prefix,
274                            int upper)
275 {
276     int namecount;
277     int nameentrysize;
278     int i;
279     char *nametable;
280
281     pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
282                        PCRE_INFO_NAMECOUNT, &namecount);
283     pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
284                        PCRE_INFO_NAMEENTRYSIZE, &nameentrysize);
285     pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
286                        PCRE_INFO_NAMETABLE, &nametable);
287
288     for (i = 0; i < namecount; i++) {
289         const char *offset = nametable + i * nameentrysize;
290         int capture = ((offset[0] << 8) + offset[1]);
291         while (names->nelts <= capture) {
292             apr_array_push(names);
293         }
294         if (upper || prefix) {
295             char *name = ((char **) names->elts)[capture] =
296                     prefix ? apr_pstrcat(names->pool, prefix, offset + 2,
297                             NULL) :
298                             apr_pstrdup(names->pool, offset + 2);
299             if (upper) {
300                 ap_str_toupper(name);
301             }
302         }
303         else {
304             ((const char **)names->elts)[capture] = offset + 2;
305         }
306     }
307
308     return namecount;
309 }
310
311 /* End of pcreposix.c */