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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "apr_pools.h"
20 #include "apr_strings.h"
21 #include "ap_config.h"
25 AP_DECLARE(ap_rxplus_t*) ap_rxplus_compile(apr_pool_t *pool,
28 /* perl style patterns
29 * add support for more as and when wanted
30 * substitute: s/rx/subs/
31 * match: m/rx/ or just /rx/
34 /* allow any nonalnum delimiter as first or second char.
35 * If we ever use this with non-string pattern we'll need an extra check
38 const char *str = pattern;
40 ap_rxplus_t *ret = apr_pcalloc(pool, sizeof(ap_rxplus_t));
42 enum { SUBSTITUTE = 's', MATCH = 'm'} action = MATCH;
43 if (!apr_isalnum(pattern[0])) {
46 else if (pattern[0] == 's' && !apr_isalnum(pattern[1])) {
51 else if (pattern[0] == 'm' && !apr_isalnum(pattern[1])) {
55 /* TODO: support perl's after/before */
56 /* FIXME: fix these simplminded delims */
58 /* we think there's a delimiter. Allow for it not to be if unmatched */
60 endp = ap_strchr_c(str, delim);
62 if (!endp) { /* there's no delim or flags */
63 if (ap_regcomp(&ret->rx, pattern, 0) == 0) {
64 apr_pool_cleanup_register(pool, &ret->rx, (void*) ap_regfree,
65 apr_pool_cleanup_null);
73 /* We have a delimiter. Use it to extract the regexp */
74 rxstr = apr_pstrndup(pool, str, endp-str);
76 /* If it's a substitution, we need the replacement string
77 * TODO: possible future enhancement - support other parsing
78 * in the replacement string.
80 if (action == SUBSTITUTE) {
82 if (!*str || (endp = ap_strchr_c(str, delim), !endp)) {
83 /* missing replacement string is an error */
86 ret->subs = apr_pstrndup(pool, str, (endp-str));
89 /* anything after the current delimiter is flags */
92 case 'i': ret->flags |= AP_REG_ICASE; break;
93 case 'm': ret->flags |= AP_REG_NEWLINE; break;
94 case 'n': ret->flags |= AP_REG_NOMEM; break;
95 case 'g': ret->flags |= AP_REG_MULTI; break;
96 case 's': ret->flags |= AP_REG_DOTALL; break;
97 case '^': ret->flags |= AP_REG_NOTBOL; break;
98 case '$': ret->flags |= AP_REG_NOTEOL; break;
99 default: break; /* we should probably be stricter here */
102 if (ap_regcomp(&ret->rx, rxstr, ret->flags) == 0) {
103 apr_pool_cleanup_register(pool, &ret->rx, (void*) ap_regfree,
104 apr_pool_cleanup_null);
109 if (!(ret->flags & AP_REG_NOMEM)) {
110 /* count size of memory required, starting at 1 for the whole-match
111 * Simpleminded should be fine 'cos regcomp already checked syntax
116 case '\\': /* next char is escaped - skip it */
121 case '(': /* unescaped bracket implies memory */
128 ret->pmatch = apr_palloc(pool, ret->nmatch*sizeof(ap_regmatch_t));
133 AP_DECLARE(int) ap_rxplus_exec(apr_pool_t *pool, ap_rxplus_t *rx,
134 const char *pattern, char **newpattern)
137 int startl, oldl, newl, diffsz;
138 const char *remainder;
140 /* snrf process_regexp from mod_headers */
141 if (ap_regexec(&rx->rx, pattern, rx->nmatch, rx->pmatch, rx->flags) != 0) {
143 return 0; /* no match, nothing to do */
147 *newpattern = ap_pregsub(pool, rx->subs, pattern,
148 rx->nmatch, rx->pmatch);
150 return 0; /* FIXME - should we do more to handle error? */
152 startl = rx->pmatch[0].rm_so;
153 oldl = rx->pmatch[0].rm_eo - startl;
154 newl = strlen(*newpattern);
155 diffsz = newl - oldl;
156 remainder = pattern + startl + oldl;
157 if (rx->flags & AP_REG_MULTI) {
158 /* recurse to do any further matches */
160 ret += ap_rxplus_exec(pool, rx, remainder, &subs);
162 /* a further substitution happened */
163 diffsz += strlen(subs) - strlen(remainder);
167 subs = apr_palloc(pool, strlen(pattern) + 1 + diffsz);
168 memcpy(subs, pattern, startl);
169 memcpy(subs+startl, *newpattern, newl);
170 strcpy(subs+startl+newl, remainder);
176 AP_DECLARE(int) ap_rxplus_nmatch(ap_rxplus_t *rx)
178 return (rx->match != NULL) ? rx->nmatch : 0;
182 /* If this blows up on you, see the notes in the header/apidoc
183 * rx->match is a pointer and it's your responsibility to ensure
184 * it hasn't gone out-of-scope since the last ap_rxplus_exec
186 AP_DECLARE(void) ap_rxplus_match(ap_rxplus_t *rx, int n, int *len,
189 if (n >= 0 && n < ap_rxplus_nmatch(rx)) {
190 *match = rx->match + rx->pmatch[n].rm_so;
191 *len = rx->pmatch[n].rm_eo - rx->pmatch[n].rm_so;
198 AP_DECLARE(char*) ap_rxplus_pmatch(apr_pool_t *pool, ap_rxplus_t *rx, int n)
202 ap_rxplus_match(rx, n, &len, &match);
203 return (match != NULL) ? apr_pstrndup(pool, match, len) : NULL;