1 /*
2 * pcre_rewrite.c
3 * (C)1999-2011 Marc Huber <Marc.Huber@web.de>
4 * All rights reserved.
5 *
6 * $Id: pcre_rewrite.c,v 1.16 2020/03/05 19:48:48 marc Exp marc $
7 *
8 */
9
10 /*
11 * This code was tested with version 2.08 of the PCRE library,
12 * available from:
13 * ftp://ftp.cus.cam.ac.uk/pub/software/programs/pcre/
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #ifdef WITH_PCRE
20 # include <pcre.h>
21 #endif
22 #ifdef WITH_PCRE2
23 # include <pcre2.h>
24 #endif
25 #include "headers.h"
26
27 static const char rcsid[] __attribute__ ((used)) = "$Id: pcre_rewrite.c,v 1.16 2020/03/05 19:48:48 marc Exp marc $";
28
29 #ifdef WITH_PCRE
30 struct pcre_rule {
31 pcre *p;
32 pcre_extra *pe;
33 struct pcre_rule *next;
34 char *flags;
35 char replacement[1];
36 };
37 #endif
38 #ifdef WITH_PCRE2
39 struct pcre_rule {
40 pcre2_code *p;
41 PCRE2_SIZE rlength; /* strlen(replacement string) */
42 struct pcre_rule *next;
43 char *flags;
44 char replacement[1];
45 };
46 #endif
47
48 static struct pcre_rule *pcre_start = NULL, *pcre_last = NULL;
49
PCRE_add(char * regex,char * replacement,char * flags)50 int PCRE_add(char *regex, char *replacement, char *flags)
51 {
52 struct pcre_rule *pr;
53 #ifdef WITH_PCRE
54 const char *errptr;
55 int erroffset;
56 #endif
57 #ifdef WITH_PCRE2
58 int errorcode;
59 PCRE2_SIZE erroffset;
60 #endif
61
62 if (!flags || !*flags)
63 flags = "L";
64
65 pr = Xcalloc(1, sizeof(struct pcre_rule)
66 + strlen(replacement) + strlen(flags) + 1);
67
68 #ifdef WITH_PCRE
69 pr->p = pcre_compile(regex, 0, &errptr, &erroffset, NULL);
70 if (!pr->p) {
71 logmsg("pcre_compile: %s: %s", regex, errptr ? errptr : "(NULL)");
72 free(pr);
73 return -1;
74 }
75
76 pr->pe = pcre_study(pr->p, 0, &errptr);
77 #endif
78 #ifdef WITH_PCRE2
79 pr->p = pcre2_compile((PCRE2_SPTR) regex, PCRE2_ZERO_TERMINATED, PCRE2_UTF, &errorcode, &erroffset, NULL);
80 if (!pr->p) {
81 PCRE2_UCHAR buffer[256];
82 pcre2_get_error_message(errorcode, buffer, sizeof(buffer));
83 logmsg("pcre2_compile: %s: %s", regex, buffer);
84 free(pr);
85 return -1;
86 }
87 #endif
88
89 strcpy(pr->replacement, replacement);
90 pr->flags = pr->replacement + strlen(pr->replacement) + 2;
91 strcpy(pr->flags, flags);
92 pr->next = NULL;
93
94 if (!pcre_start)
95 pcre_start = pcre_last = pr;
96 else {
97 pcre_last->next = pr;
98 pcre_last = pr;
99 }
100
101 Debug((DEBUG_PROC, " PCRE_add(%s, %s, %s)\n", regex, replacement, flags));
102 return 0;
103 }
104
PCRE_exec(const char * inbuf,char * outbuf,size_t outlen)105 int PCRE_exec(const char *inbuf, char *outbuf, size_t outlen)
106 {
107 int loopmax = 20;
108 #ifdef WITH_PCRE
109 #define OVECSIZE 100
110 int ovector[3 * (OVECSIZE + 1)];
111 #endif
112 struct pcre_rule *pr = pcre_start;
113 size_t tbuflen = 2 * outlen;
114 char *tbuf = alloca(tbuflen);
115
116 if (!pcre_start)
117 return 0; /* no match */
118
119 strncpy(tbuf, inbuf, tbuflen);
120 tbuf[tbuflen - 1] = 0;
121
122 while (loopmax--) {
123 int m;
124 #ifdef WITH_PCRE2
125 pcre2_match_data *match_data = NULL;
126 PCRE2_SIZE outlength = (PCRE2_SIZE) outlen - 1;
127 #endif
128 if (pr == NULL)
129 pr = pcre_start;
130 #ifdef WITH_PCRE
131 m = pcre_exec(pr->p, pr->pe, tbuf, (int) strlen(tbuf), 0, 0, ovector, OVECSIZE);
132 if (m > -1) {
133 char *o, *t;
134
135 for (o = outbuf, t = pr->replacement; *t && o < outbuf + tbuflen; t++)
136 switch (*t) {
137 case '\\': /* escape next character */
138 if (*(t + 1))
139 *o++ = *t++;
140 break;
141 case '$':
142 if (*(t + 1)) {
143 int num = 0;
144
145 if (isdigit((int) *++t))
146 num = *t - '0';
147 else if (*t == '{')
148 for (t++; *t && *t != '}'; t++) {
149 if (isdigit((int) *t))
150 num = num * 10 + *t - '0';
151 } else
152 continue;
153
154 pcre_copy_substring(tbuf, ovector, m, num, o, (int) (outbuf + outlen - o - 1));
155 while (*o)
156 o++;
157 }
158 break;
159 default:
160 *o++ = *t;
161 } /* switch */
162
163 *o = 0;
164 if (!strcmp(pr->flags, "N")) { /* Next */
165 pr = pcre_start;
166 strncpy(tbuf, outbuf, tbuflen);
167 tbuf[tbuflen - 1] = 0;
168 continue;
169 }
170 if (!strcmp(pr->flags, "R")) /* Reject */
171 *outbuf = 0;
172 Debug((DEBUG_PROC, " PCRE_exec(%s) = %s\n", inbuf, outbuf));
173 return -1;
174 }
175 #endif
176 #ifdef WITH_PCRE2
177 // caveat emptor: compiles, but untested
178 match_data = pcre2_match_data_create_from_pattern(pr->p, NULL);
179 m = pcre2_substitute(pr->p, (PCRE2_SPTR) tbuf, PCRE2_ZERO_TERMINATED, PCRE2_SUBSTITUTE_EXTENDED | PCRE2_UTF, 0, match_data,
180 NULL, (PCRE2_SPTR) pr->replacement, PCRE2_ZERO_TERMINATED, (PCRE2_UCHAR8 *) outbuf, &outlength);
181
182 if (match_data) {
183 pcre2_match_data_free(match_data);
184 match_data = NULL;
185 }
186
187 if (m > -1) {
188 if (outlength > 1 && outlength < (PCRE2_SIZE) outlen)
189 outbuf[outlen] = 0;
190
191 if (!strcmp(pr->flags, "N")) { /* Next */
192 pr = pcre_start;
193 strncpy(tbuf, outbuf, tbuflen);
194 tbuf[tbuflen - 1] = 0;
195 continue;
196 }
197 if (!strcmp(pr->flags, "R")) /* Reject */
198 *outbuf = 0;
199 Debug((DEBUG_PROC, " PCRE_exec(%s) = %s\n", inbuf, outbuf));
200 return -1;
201 }
202 #endif /* if */
203 pr = pr->next;
204 } /* while */
205 return 0;
206 }
207