blob: e23e311521cc38ec47b51f455157bb67114be226 [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * Regex and string management functions.
3 *
Willy Tarreauf4f04122010-01-28 18:10:50 +01004 * Copyright 2000-2010 Willy Tarreau <w@1wt.eu>
Willy Tarreaubaaee002006-06-26 02:48:02 +02005 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <ctype.h>
14#include <stdlib.h>
15#include <string.h>
16
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020017#include <haproxy/api.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020018#include <haproxy/errors.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020019#include <haproxy/global.h>
Willy Tarreau7cd8b6e2020-06-02 17:32:26 +020020#include <haproxy/regex.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020021#include <haproxy/tools.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020022
23/* regex trash buffer used by various regex tests */
Emeric Brun272e2522017-06-15 11:53:49 +020024THREAD_LOCAL regmatch_t pmatch[MAX_MATCH]; /* rm_so, rm_eo for regular expressions */
Willy Tarreaubaaee002006-06-26 02:48:02 +020025
Willy Tarreauc8746532014-05-28 23:05:07 +020026int exp_replace(char *dst, unsigned int dst_size, char *src, const char *str, const regmatch_t *matches)
Willy Tarreaubaaee002006-06-26 02:48:02 +020027{
28 char *old_dst = dst;
Sasha Pachevc6002042014-05-26 12:33:48 -060029 char* dst_end = dst + dst_size;
Willy Tarreaubaaee002006-06-26 02:48:02 +020030
31 while (*str) {
32 if (*str == '\\') {
33 str++;
Sasha Pachevc6002042014-05-26 12:33:48 -060034 if (!*str)
35 return -1;
36
Willy Tarreau8f8e6452007-06-17 21:51:38 +020037 if (isdigit((unsigned char)*str)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +020038 int len, num;
39
40 num = *str - '0';
41 str++;
42
43 if (matches[num].rm_eo > -1 && matches[num].rm_so > -1) {
44 len = matches[num].rm_eo - matches[num].rm_so;
Sasha Pachevc6002042014-05-26 12:33:48 -060045
46 if (dst + len >= dst_end)
47 return -1;
48
Willy Tarreaubaaee002006-06-26 02:48:02 +020049 memcpy(dst, src + matches[num].rm_so, len);
50 dst += len;
51 }
52
53 } else if (*str == 'x') {
54 unsigned char hex1, hex2;
55 str++;
56
Sasha Pachevc6002042014-05-26 12:33:48 -060057 if (!*str)
58 return -1;
59
Willy Tarreaubaaee002006-06-26 02:48:02 +020060 hex1 = toupper(*str++) - '0';
Sasha Pachevc6002042014-05-26 12:33:48 -060061
62 if (!*str)
63 return -1;
64
Willy Tarreaubaaee002006-06-26 02:48:02 +020065 hex2 = toupper(*str++) - '0';
66
67 if (hex1 > 9) hex1 -= 'A' - '9' - 1;
68 if (hex2 > 9) hex2 -= 'A' - '9' - 1;
Sasha Pachevc6002042014-05-26 12:33:48 -060069
70 if (dst >= dst_end)
71 return -1;
72
Willy Tarreaubaaee002006-06-26 02:48:02 +020073 *dst++ = (hex1<<4) + hex2;
74 } else {
Sasha Pachevc6002042014-05-26 12:33:48 -060075 if (dst >= dst_end)
76 return -1;
77
Willy Tarreaubaaee002006-06-26 02:48:02 +020078 *dst++ = *str++;
79 }
80 } else {
Sasha Pachevc6002042014-05-26 12:33:48 -060081 if (dst >= dst_end)
82 return -1;
83
Willy Tarreaubaaee002006-06-26 02:48:02 +020084 *dst++ = *str++;
85 }
86 }
Sasha Pachevc6002042014-05-26 12:33:48 -060087 if (dst >= dst_end)
88 return -1;
89
Willy Tarreaubaaee002006-06-26 02:48:02 +020090 *dst = '\0';
91 return dst - old_dst;
92}
93
94/* returns NULL if the replacement string <str> is valid, or the pointer to the first error */
Willy Tarreaub17916e2006-10-15 15:17:57 +020095const char *check_replace_string(const char *str)
Willy Tarreaubaaee002006-06-26 02:48:02 +020096{
Willy Tarreaub17916e2006-10-15 15:17:57 +020097 const char *err = NULL;
Willy Tarreaubaaee002006-06-26 02:48:02 +020098 while (*str) {
99 if (*str == '\\') {
100 err = str; /* in case of a backslash, we return the pointer to it */
101 str++;
102 if (!*str)
103 return err;
Willy Tarreau8f8e6452007-06-17 21:51:38 +0200104 else if (isdigit((unsigned char)*str))
Willy Tarreaubaaee002006-06-26 02:48:02 +0200105 err = NULL;
106 else if (*str == 'x') {
107 str++;
108 if (!ishex(*str))
109 return err;
110 str++;
111 if (!ishex(*str))
112 return err;
113 err = NULL;
114 }
115 else {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100116 ha_warning("'\\%c' : deprecated use of a backslash before something not '\\','x' or a digit.\n", *str);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200117 err = NULL;
118 }
119 }
120 str++;
121 }
122 return err;
123}
124
125
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200126/* This function apply regex. It take const null terminated char as input.
127 * If the function doesn't match, it returns false, else it returns true.
128 * When it is compiled with JIT, this function execute strlen on the subject.
Willy Tarreau15a53a42015-01-21 13:39:42 +0100129 * Currently the only supported flag is REG_NOTBOL.
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200130 */
131int regex_exec_match(const struct my_regex *preg, const char *subject,
Willy Tarreau15a53a42015-01-21 13:39:42 +0100132 size_t nmatch, regmatch_t pmatch[], int flags) {
David Carlierf2592b22016-11-21 21:25:58 +0000133#if defined(USE_PCRE) || defined(USE_PCRE_JIT) || defined(USE_PCRE2) || defined(USE_PCRE2_JIT)
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200134 int ret;
David Carlierf2592b22016-11-21 21:25:58 +0000135#ifdef USE_PCRE2
136 PCRE2_SIZE *matches;
137 pcre2_match_data *pm;
138#else
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200139 int matches[MAX_MATCH * 3];
David Carlierf2592b22016-11-21 21:25:58 +0000140#endif
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200141 int enmatch;
142 int i;
Willy Tarreau15a53a42015-01-21 13:39:42 +0100143 int options;
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200144
145 /* Silently limit the number of allowed matches. max
146 * match i the maximum value for match, in fact this
147 * limit is not applyied.
148 */
David Carlierf2592b22016-11-21 21:25:58 +0000149
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200150 enmatch = nmatch;
151 if (enmatch > MAX_MATCH)
152 enmatch = MAX_MATCH;
153
Willy Tarreau15a53a42015-01-21 13:39:42 +0100154 options = 0;
155 if (flags & REG_NOTBOL)
David Carlierf2592b22016-11-21 21:25:58 +0000156#ifdef USE_PCRE2
157 options |= PCRE2_NOTBOL;
158#else
Willy Tarreau15a53a42015-01-21 13:39:42 +0100159 options |= PCRE_NOTBOL;
David Carlierf2592b22016-11-21 21:25:58 +0000160#endif
Willy Tarreau15a53a42015-01-21 13:39:42 +0100161
David Carlierf2592b22016-11-21 21:25:58 +0000162 /* The value returned by pcre_exec()/pcre2_match() is one more than the highest numbered
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200163 * pair that has been set. For example, if two substrings have been captured,
164 * the returned value is 3. If there are no capturing subpatterns, the return
165 * value from a successful match is 1, indicating that just the first pair of
166 * offsets has been set.
167 *
Joseph Herlanteda75482018-11-15 14:46:29 -0800168 * It seems that this function returns 0 if it detects more matches than available
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200169 * space in the matches array.
170 */
David Carlierf2592b22016-11-21 21:25:58 +0000171#ifdef USE_PCRE2
172 pm = pcre2_match_data_create_from_pattern(preg->reg, NULL);
173 ret = pcre2_match(preg->reg, (PCRE2_SPTR)subject, (PCRE2_SIZE)strlen(subject), 0, options, pm, NULL);
174
175 if (ret < 0) {
176 pcre2_match_data_free(pm);
177 return 0;
178 }
179
180 matches = pcre2_get_ovector_pointer(pm);
181#else
Willy Tarreau15a53a42015-01-21 13:39:42 +0100182 ret = pcre_exec(preg->reg, preg->extra, subject, strlen(subject), 0, options, matches, enmatch * 3);
David Carlierf2592b22016-11-21 21:25:58 +0000183
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200184 if (ret < 0)
185 return 0;
David Carlierf2592b22016-11-21 21:25:58 +0000186#endif
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200187
188 if (ret == 0)
189 ret = enmatch;
190
191 for (i=0; i<nmatch; i++) {
192 /* Copy offset. */
193 if (i < ret) {
194 pmatch[i].rm_so = matches[(i*2)];
195 pmatch[i].rm_eo = matches[(i*2)+1];
196 continue;
197 }
198 /* Set the unmatvh flag (-1). */
199 pmatch[i].rm_so = -1;
200 pmatch[i].rm_eo = -1;
201 }
David Carlierf2592b22016-11-21 21:25:58 +0000202#ifdef USE_PCRE2
203 pcre2_match_data_free(pm);
204#endif
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200205 return 1;
206#else
207 int match;
Willy Tarreau15a53a42015-01-21 13:39:42 +0100208
209 flags &= REG_NOTBOL;
210 match = regexec(&preg->regex, subject, nmatch, pmatch, flags);
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200211 if (match == REG_NOMATCH)
212 return 0;
213 return 1;
214#endif
215}
216
217/* This function apply regex. It take a "char *" ans length as input. The
218 * <subject> can be modified during the processing. If the function doesn't
219 * match, it returns false, else it returns true.
220 * When it is compiled with standard POSIX regex or PCRE, this function add
221 * a temporary null chracters at the end of the <subject>. The <subject> must
Willy Tarreau15a53a42015-01-21 13:39:42 +0100222 * have a real length of <length> + 1. Currently the only supported flag is
223 * REG_NOTBOL.
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200224 */
225int regex_exec_match2(const struct my_regex *preg, char *subject, int length,
Willy Tarreau15a53a42015-01-21 13:39:42 +0100226 size_t nmatch, regmatch_t pmatch[], int flags) {
David Carlierf2592b22016-11-21 21:25:58 +0000227#if defined(USE_PCRE) || defined(USE_PCRE_JIT) || defined(USE_PCRE2) || defined(USE_PCRE2_JIT)
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200228 int ret;
David Carlierf2592b22016-11-21 21:25:58 +0000229#ifdef USE_PCRE2
230 PCRE2_SIZE *matches;
231 pcre2_match_data *pm;
232#else
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200233 int matches[MAX_MATCH * 3];
David Carlierf2592b22016-11-21 21:25:58 +0000234#endif
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200235 int enmatch;
236 int i;
Willy Tarreau15a53a42015-01-21 13:39:42 +0100237 int options;
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200238
239 /* Silently limit the number of allowed matches. max
240 * match i the maximum value for match, in fact this
241 * limit is not applyied.
242 */
243 enmatch = nmatch;
244 if (enmatch > MAX_MATCH)
245 enmatch = MAX_MATCH;
246
Willy Tarreau15a53a42015-01-21 13:39:42 +0100247 options = 0;
248 if (flags & REG_NOTBOL)
David Carlierf2592b22016-11-21 21:25:58 +0000249#ifdef USE_PCRE2
250 options |= PCRE2_NOTBOL;
251#else
Willy Tarreau15a53a42015-01-21 13:39:42 +0100252 options |= PCRE_NOTBOL;
David Carlierf2592b22016-11-21 21:25:58 +0000253#endif
Willy Tarreau15a53a42015-01-21 13:39:42 +0100254
David Carlierf2592b22016-11-21 21:25:58 +0000255 /* The value returned by pcre_exec()/pcre2_match() is one more than the highest numbered
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200256 * pair that has been set. For example, if two substrings have been captured,
257 * the returned value is 3. If there are no capturing subpatterns, the return
258 * value from a successful match is 1, indicating that just the first pair of
259 * offsets has been set.
260 *
Joseph Herlanteda75482018-11-15 14:46:29 -0800261 * It seems that this function returns 0 if it detects more matches than available
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200262 * space in the matches array.
263 */
David Carlierf2592b22016-11-21 21:25:58 +0000264#ifdef USE_PCRE2
265 pm = pcre2_match_data_create_from_pattern(preg->reg, NULL);
266 ret = pcre2_match(preg->reg, (PCRE2_SPTR)subject, (PCRE2_SIZE)length, 0, options, pm, NULL);
267
268 if (ret < 0) {
269 pcre2_match_data_free(pm);
270 return 0;
271 }
272
273 matches = pcre2_get_ovector_pointer(pm);
274#else
Willy Tarreau15a53a42015-01-21 13:39:42 +0100275 ret = pcre_exec(preg->reg, preg->extra, subject, length, 0, options, matches, enmatch * 3);
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200276 if (ret < 0)
277 return 0;
David Carlierf2592b22016-11-21 21:25:58 +0000278#endif
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200279
280 if (ret == 0)
281 ret = enmatch;
282
283 for (i=0; i<nmatch; i++) {
284 /* Copy offset. */
285 if (i < ret) {
286 pmatch[i].rm_so = matches[(i*2)];
287 pmatch[i].rm_eo = matches[(i*2)+1];
288 continue;
289 }
290 /* Set the unmatvh flag (-1). */
291 pmatch[i].rm_so = -1;
292 pmatch[i].rm_eo = -1;
293 }
David Carlierf2592b22016-11-21 21:25:58 +0000294#ifdef USE_PCRE2
295 pcre2_match_data_free(pm);
296#endif
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200297 return 1;
298#else
299 char old_char = subject[length];
300 int match;
Willy Tarreau15a53a42015-01-21 13:39:42 +0100301
302 flags &= REG_NOTBOL;
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200303 subject[length] = 0;
Willy Tarreau15a53a42015-01-21 13:39:42 +0100304 match = regexec(&preg->regex, subject, nmatch, pmatch, flags);
Thierry FOURNIERb8f980c2014-06-11 13:59:05 +0200305 subject[length] = old_char;
306 if (match == REG_NOMATCH)
307 return 0;
308 return 1;
309#endif
310}
311
Dragan Dosen26743032019-04-30 15:54:36 +0200312struct my_regex *regex_comp(const char *str, int cs, int cap, char **err)
Thierry FOURNIERed5a4ae2013-10-14 14:07:36 +0200313{
Dragan Dosen26743032019-04-30 15:54:36 +0200314 struct my_regex *regex = NULL;
Thierry FOURNIER26202762014-06-18 11:50:51 +0200315#if defined(USE_PCRE) || defined(USE_PCRE_JIT)
Thierry FOURNIERed5a4ae2013-10-14 14:07:36 +0200316 int flags = 0;
317 const char *error;
318 int erroffset;
Dragan Dosen26743032019-04-30 15:54:36 +0200319#elif defined(USE_PCRE2) || defined(USE_PCRE2_JIT)
320 int flags = 0;
321 int errn;
322#if defined(USE_PCRE2_JIT)
323 int jit;
324#endif
325 PCRE2_UCHAR error[256];
326 PCRE2_SIZE erroffset;
327#else
328 int flags = REG_EXTENDED;
329#endif
330
331 regex = calloc(1, sizeof(*regex));
332 if (!regex) {
333 memprintf(err, "not enough memory to build regex");
334 goto out_fail_alloc;
335 }
Thierry FOURNIERed5a4ae2013-10-14 14:07:36 +0200336
Dragan Dosen26743032019-04-30 15:54:36 +0200337#if defined(USE_PCRE) || defined(USE_PCRE_JIT)
Thierry FOURNIERed5a4ae2013-10-14 14:07:36 +0200338 if (!cs)
339 flags |= PCRE_CASELESS;
340 if (!cap)
341 flags |= PCRE_NO_AUTO_CAPTURE;
342
343 regex->reg = pcre_compile(str, flags, &error, &erroffset, NULL);
344 if (!regex->reg) {
345 memprintf(err, "regex '%s' is invalid (error=%s, erroffset=%d)", str, error, erroffset);
Dragan Dosen26743032019-04-30 15:54:36 +0200346 goto out_fail_alloc;
Thierry FOURNIERed5a4ae2013-10-14 14:07:36 +0200347 }
348
349 regex->extra = pcre_study(regex->reg, PCRE_STUDY_JIT_COMPILE, &error);
Christian Ruppert955f4612014-10-29 17:05:53 +0100350 if (!regex->extra && error != NULL) {
Thierry FOURNIERed5a4ae2013-10-14 14:07:36 +0200351 pcre_free(regex->reg);
352 memprintf(err, "failed to compile regex '%s' (error=%s)", str, error);
Dragan Dosen26743032019-04-30 15:54:36 +0200353 goto out_fail_alloc;
Thierry FOURNIERed5a4ae2013-10-14 14:07:36 +0200354 }
David Carlierf2592b22016-11-21 21:25:58 +0000355#elif defined(USE_PCRE2) || defined(USE_PCRE2_JIT)
David Carlierf2592b22016-11-21 21:25:58 +0000356 if (!cs)
357 flags |= PCRE2_CASELESS;
358 if (!cap)
359 flags |= PCRE2_NO_AUTO_CAPTURE;
360
361 regex->reg = pcre2_compile((PCRE2_SPTR)str, PCRE2_ZERO_TERMINATED, flags, &errn, &erroffset, NULL);
362 if (!regex->reg) {
363 pcre2_get_error_message(errn, error, sizeof(error));
364 memprintf(err, "regex '%s' is invalid (error=%s, erroffset=%zu)", str, error, erroffset);
Dragan Dosen26743032019-04-30 15:54:36 +0200365 goto out_fail_alloc;
David Carlierf2592b22016-11-21 21:25:58 +0000366 }
367
368#if defined(USE_PCRE2_JIT)
369 jit = pcre2_jit_compile(regex->reg, PCRE2_JIT_COMPLETE);
370 /*
371 * We end if it is an error not related to lack of JIT support
372 * in a case of JIT support missing pcre2_jit_compile is "no-op"
373 */
374 if (jit < 0 && jit != PCRE2_ERROR_JIT_BADOPTION) {
375 pcre2_code_free(regex->reg);
376 memprintf(err, "regex '%s' jit compilation failed", str);
Dragan Dosen26743032019-04-30 15:54:36 +0200377 goto out_fail_alloc;
David Carlierf2592b22016-11-21 21:25:58 +0000378 }
379#endif
380
Thierry FOURNIERed5a4ae2013-10-14 14:07:36 +0200381#else
Thierry FOURNIERed5a4ae2013-10-14 14:07:36 +0200382 if (!cs)
383 flags |= REG_ICASE;
384 if (!cap)
385 flags |= REG_NOSUB;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200386
Thierry FOURNIER799c0422013-12-06 20:36:20 +0100387 if (regcomp(&regex->regex, str, flags) != 0) {
Thierry FOURNIERed5a4ae2013-10-14 14:07:36 +0200388 memprintf(err, "regex '%s' is invalid", str);
Dragan Dosen26743032019-04-30 15:54:36 +0200389 goto out_fail_alloc;
Thierry FOURNIERed5a4ae2013-10-14 14:07:36 +0200390 }
391#endif
Dragan Dosen26743032019-04-30 15:54:36 +0200392 return regex;
393
394 out_fail_alloc:
395 free(regex);
396 return NULL;
Thierry FOURNIERed5a4ae2013-10-14 14:07:36 +0200397}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200398
Willy Tarreau80713382018-11-26 10:19:54 +0100399static void regex_register_build_options(void)
Willy Tarreau7a9ac6d2016-12-21 19:13:14 +0100400{
401 char *ptr = NULL;
402
403#ifdef USE_PCRE
404 memprintf(&ptr, "Built with PCRE version : %s", (HAP_XSTRING(Z PCRE_PRERELEASE)[1] == 0)?
405 HAP_XSTRING(PCRE_MAJOR.PCRE_MINOR PCRE_DATE) :
406 HAP_XSTRING(PCRE_MAJOR.PCRE_MINOR) HAP_XSTRING(PCRE_PRERELEASE PCRE_DATE));
407 memprintf(&ptr, "%s\nRunning on PCRE version : %s", ptr, pcre_version());
408
409 memprintf(&ptr, "%s\nPCRE library supports JIT : %s", ptr,
410#ifdef USE_PCRE_JIT
411 ({
412 int r;
413 pcre_config(PCRE_CONFIG_JIT, &r);
414 r ? "yes" : "no (libpcre build without JIT?)";
415 })
416#else
417 "no (USE_PCRE_JIT not set)"
418#endif
419 );
David Carlierf2592b22016-11-21 21:25:58 +0000420#endif /* USE_PCRE */
421
422#ifdef USE_PCRE2
423 memprintf(&ptr, "Built with PCRE2 version : %s", (HAP_XSTRING(Z PCRE2_PRERELEASE)[1] == 0) ?
424 HAP_XSTRING(PCRE2_MAJOR.PCRE2_MINOR PCRE2_DATE) :
425 HAP_XSTRING(PCRE2_MAJOR.PCRE2_MINOR) HAP_XSTRING(PCRE2_PRERELEASE PCRE2_DATE));
426 memprintf(&ptr, "%s\nPCRE2 library supports JIT : %s", ptr,
427#ifdef USE_PCRE2_JIT
428 ({
429 int r;
430 pcre2_config(PCRE2_CONFIG_JIT, &r);
431 r ? "yes" : "no (libpcre2 build without JIT?)";
432 })
Willy Tarreau7a9ac6d2016-12-21 19:13:14 +0100433#else
David Carlierf2592b22016-11-21 21:25:58 +0000434 "no (USE_PCRE2_JIT not set)"
435#endif
436 );
437#endif /* USE_PCRE2 */
438
439#if !defined(USE_PCRE) && !defined(USE_PCRE2)
440 memprintf(&ptr, "Built without PCRE or PCRE2 support (using libc's regex instead)");
Willy Tarreau7a9ac6d2016-12-21 19:13:14 +0100441#endif
442 hap_register_build_opts(ptr, 1);
443}
444
Willy Tarreau80713382018-11-26 10:19:54 +0100445INITCALL0(STG_REGISTER, regex_register_build_options);
446
Willy Tarreaubaaee002006-06-26 02:48:02 +0200447/*
448 * Local variables:
449 * c-indent-level: 8
450 * c-basic-offset: 8
451 * End:
452 */