Fixed general memory corruption
[middleman.git] / src / regexp.c
1 /*
2     MiddleMan filtering proxy server
3     Copyright (C) 2002  Jason McLaughlin
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 #include <string.h>
21 #include "proto.h"
22
23 /*
24 compile a regular expression, or return NULL on failure
25 */
26 regex_t *reg_compile(char *pattern, int flags)
27 {
28         int rerr;
29         regex_t *re;
30
31         re = xmalloc(sizeof(regex_t));
32
33         rerr = regcomp2(re, pattern, flags);
34         if (rerr != 0) {
35                 putlog(MMLOG_ERROR, "failed to compile regular expression: %s", pattern);
36                 xfree(re);
37                 re = NULL;
38         }
39
40         return re;
41 }
42
43 /*
44 free a regular expression and the pointer it's stored in
45 */
46 void reg_free(regex_t * re)
47 {
48         ASSERT(re != NULL);
49
50         regfree2(re);
51         xfree(re);
52 }
53
54 /*
55 perform a regular expression match, return 0 on success
56 */
57 int reg_exec(regex_t * re, char *string)
58 {
59         int rerr;
60
61         rerr = regexec2(re, string, 0, NULL, 0);
62
63         return rerr;
64 }
65
66 regex_sub_t *reg_sub_compile(char *pattern, int flags)
67 {
68         int erroff;
69         const char *err;
70         regex_sub_t *ret;
71
72         ret = xmalloc(sizeof(regex_sub_t));
73
74         ret->pattern = pcre_compile(pattern, flags, &err, &erroff, NULL);
75         if (ret->pattern == NULL) {
76                 putlog(MMLOG_ERROR, "failed to compile regular expression: %s", pattern);
77
78                 xfree(ret);
79                 return NULL;
80         }
81
82         ret->extra = pcre_study(ret->pattern, 0, &err);
83
84         return ret;
85 }
86
87 /*
88 free a regex_sub_t structure
89 */
90 void reg_sub_free(regex_sub_t * re)
91 {
92         if (re->pattern != NULL)
93                 xfree(re->pattern);
94         if (re->extra != NULL)
95                 xfree(re->extra);
96         xfree(re);
97 }
98
99 /*
100 perform a regular expression match and store matching substrings
101 */
102 regmatch_sub_t *reg_sub_exec(regex_sub_t * pe, char *string)
103 {
104         int subcount, vectors[PCRE_MAX_SUBSTRING * 3];
105         regmatch_sub_t *ret;
106
107         subcount = pcre_exec(pe->pattern, pe->extra, string, strlen(string), 0, 0, vectors, PCRE_MAX_SUBSTRING * 3);
108         if (subcount > 0) {
109                 ret = xmalloc(sizeof(regmatch_sub_t));
110
111                 ret->rm_so = vectors[0];
112                 ret->rm_eo = vectors[1];
113                 ret->subcount = subcount;
114
115                 pcre_get_substring_list(string, vectors, subcount, &ret->substrings);
116
117                 return ret;
118         } else
119                 return NULL;
120 }
121
122 /*
123 free regmatch_sub_t structure and all substrings inside
124 */
125 void reg_sub_match_free(regmatch_sub_t * rm)
126 {
127         pcre_free_substring_list(rm->substrings);
128         xfree(rm);
129 }
130
131 /*
132 replace all backreferences in a string with the actual matching string
133 */
134 char *reg_sub_parse(regmatch_sub_t * rst, char *string)
135 {
136         int x;
137         char *ptr = string, *ret = NULL;
138         FILEBUF *filebuf;
139
140         filebuf = filebuf_new();
141
142         while (*ptr) {
143                 if (*ptr == '$') {
144                         if (!ptr[1] || ptr[1] == '$') {
145                                 filebuf_add(filebuf, "$", 1);
146
147                                 ptr += (ptr[1]) ? 2 : 1;
148                         } else {
149                                 x = ptr[1] - '0';
150                                 if (x >= 0 && x < rst->subcount)
151                                         filebuf_add(filebuf, rst->substrings[x], strlen(rst->substrings[x]));
152
153                                 ptr += 2;
154                         }
155                 } else if (*ptr == '\\') {
156                         /* handle escape sequences */
157
158                         switch (ptr[1]) {
159                         case '\\':
160                                 filebuf_add(filebuf, "\\", 1);
161                                 break;
162                         case 'n':
163                                 filebuf_add(filebuf, "\n", 1);
164                                 break;
165                         case 'r':
166                                 filebuf_add(filebuf, "\r", 1);
167                                 break;
168                         case 't':
169                                 filebuf_add(filebuf, "\t", 1);
170                                 break;
171                         case 'f':
172                                 filebuf_add(filebuf, "\f", 1);
173                                 break;
174                         case 'a':
175                                 filebuf_add(filebuf, "\a", 1);
176                                 break;
177                         case 'e':
178                                 filebuf_add(filebuf, "\e", 1);
179                                 break;
180                         default:
181                                 filebuf_add(filebuf, &ptr[1], 1);
182                                 break;
183                         }
184
185                         ptr += (ptr[1]) ? 2 : 1;
186                 } else {
187                         filebuf_add(filebuf, ptr, 1);
188
189                         ptr++;
190                 }
191         }
192
193         filebuf_add(filebuf, "", 1);
194         filebuf_shorten(filebuf);
195
196         ret = filebuf->data;
197
198         filebuf->data = NULL;
199         filebuf_free(filebuf);
200
201         return ret;
202 }
203
204 /*
205 replace all instances of text matching a regexp pattern with another string
206 */
207 void reg_replace(FILEBUF * filebuf, regex_sub_t * pe, char *replace)
208 {
209         char *r;
210         int len, e = 0;
211         regmatch_sub_t *rmatch;
212         FILEBUF *new = NULL;
213
214         filebuf_add(filebuf, "", 1);
215
216         while ((rmatch = reg_sub_exec(pe, &filebuf->data[e]))) {
217                 if (new == NULL)
218                         new = filebuf_new();
219
220                 filebuf_add(new, &filebuf->data[e], rmatch->rm_so);
221
222                 rmatch->rm_so += e;
223                 rmatch->rm_eo += e;
224                 len = rmatch->rm_eo - rmatch->rm_so;
225
226                 e = rmatch->rm_eo;
227
228                 if (replace != NULL) {
229                         r = reg_sub_parse(rmatch, replace);
230
231                         filebuf_add(new, r, strlen(r));
232
233                         xfree(r);
234                 }
235
236                 reg_sub_match_free(rmatch);
237         }
238
239         if (new != NULL) {
240                 filebuf_add(new, &filebuf->data[e], filebuf->size - e - 1);
241
242                 xfree(filebuf->data);
243                 filebuf->data = new->data;
244                 filebuf->size = new->size;
245
246                 new->data = NULL;
247                 filebuf_free(new);
248         } else
249                 filebuf_resize(filebuf, filebuf->size - 1);
250
251 }
252
253 /*
254 replace all instances of a plaintext string with another
255 */
256 void string_replace(FILEBUF * filebuf, char *string, char *replace)
257 {
258         char *ptr;
259         int len, len2 = 0, e = 0;
260
261         filebuf_add(filebuf, "", 1);
262
263         len = strlen(string);
264         if (replace != NULL)
265                 len2 = strlen(replace);
266
267         while ((ptr = strstr(&filebuf->data[e], string))) {
268                 e = (ptr - filebuf->data) + len;
269
270                 if (replace == NULL) {
271                         memcpy(ptr, ptr + len, ptr - filebuf->data - len);
272
273                         filebuf_resize(filebuf, filebuf->size - len);
274
275                         e -= len;
276                 } else {
277                         filebuf_replace(filebuf, ptr - filebuf->data, ptr - filebuf->data + len, replace);
278
279                         e += (len2 - len);
280                 }
281         }
282
283         filebuf_resize(filebuf, filebuf->size - 1);
284 }