]> mj.ucw.cz Git - libucw.git/blob - ucw/regex.c
Config parser: Let `:append' and `:prepend' on bitmaps be equivalent to `:set'.
[libucw.git] / ucw / regex.c
1 /*
2  *      UCW Library -- Interface to Regular Expression Libraries
3  *
4  *      (c) 1997--2004 Martin Mares <mj@ucw.cz>
5  *      (c) 2001 Robert Spalek <robert@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #include "ucw/lib.h"
12 #include "ucw/chartype.h"
13 #include "ucw/hashfunc.h"
14 #include "ucw/regex.h"
15
16 #include <stdio.h>
17 #include <string.h>
18
19 #ifdef CONFIG_POSIX_REGEX
20
21 /* POSIX regular expression library */
22
23 #include <regex.h>
24
25 struct regex {
26   regex_t rx;
27   regmatch_t matches[10];
28 };
29
30 regex *
31 rx_compile(const char *p, int icase)
32 {
33   regex *r = xmalloc_zero(sizeof(regex));
34
35   int err = regcomp(&r->rx, p, REG_EXTENDED | (icase ? REG_ICASE : 0));
36   if (err)
37     {
38       char msg[256];
39       regerror(err, &r->rx, msg, sizeof(msg)-1);
40       /* regfree(&r->rx) not needed */
41       die("Error parsing regular expression `%s': %s", p, msg);
42     }
43   return r;
44 }
45
46 void
47 rx_free(regex *r)
48 {
49   regfree(&r->rx);
50   xfree(r);
51 }
52
53 int
54 rx_match(regex *r, const char *s)
55 {
56   int err = regexec(&r->rx, s, 10, r->matches, 0);
57   if (!err)
58     {
59       /* regexec doesn't support anchored expressions, so we have to check ourselves that the full string is matched */
60       return !(r->matches[0].rm_so || s[r->matches[0].rm_eo]);
61     }
62   else if (err == REG_NOMATCH)
63     return 0;
64   else if (err == REG_ESPACE)
65     die("Regex matching ran out of memory");
66   else
67     die("Regex matching failed with unknown error %d", err);
68 }
69
70 int
71 rx_subst(regex *r, const char *by, const char *src, char *dest, uns destlen)
72 {
73   char *end = dest + destlen - 1;
74
75   if (!rx_match(r, src))
76     return 0;
77
78   while (*by)
79     {
80       if (*by == '\\')
81         {
82           by++;
83           if (*by >= '0' && *by <= '9') /* \0 gets replaced by entire pattern */
84             {
85               uns j = *by++ - '0';
86               if (j <= r->rx.re_nsub && r->matches[j].rm_so >= 0)
87                 {
88                   const char *s = src + r->matches[j].rm_so;
89                   uns i = r->matches[j].rm_eo - r->matches[j].rm_so;
90                   if (dest + i >= end)
91                     return -1;
92                   memcpy(dest, s, i);
93                   dest += i;
94                   continue;
95                 }
96             }
97         }
98       if (dest < end)
99         *dest++ = *by++;
100       else
101         return -1;
102     }
103   *dest = 0;
104   return 1;
105 }
106
107 #elif defined(CONFIG_PCRE)
108
109 /* PCRE library */
110
111 #include <pcre.h>
112
113 struct regex {
114   pcre *rx;
115   pcre_extra *extra;
116   uns match_array_size;
117   uns real_matches;
118   int matches[0];                       /* (max_matches+1) pairs (pos,len) plus some workspace */
119 };
120
121 regex *
122 rx_compile(const char *p, int icase)
123 {
124   const char *err;
125   int errpos, match_array_size, eno;
126
127   pcre *rx = pcre_compile(p, PCRE_ANCHORED | PCRE_EXTRA | (icase ? PCRE_CASELESS : 0), &err, &errpos, NULL);
128   if (!rx)
129     die("Error parsing regular expression `%s': %s at position %d", p, err, errpos);
130   eno = pcre_fullinfo(rx, NULL, PCRE_INFO_CAPTURECOUNT, &match_array_size);
131   if (eno)
132     die("Internal error: pcre_fullinfo() failed with error %d", eno);
133   match_array_size = 3*(match_array_size+1);
134   regex *r = xmalloc_zero(sizeof(regex) + match_array_size * sizeof(int));
135   r->rx = rx;
136   r->match_array_size = match_array_size;
137   r->extra = pcre_study(r->rx, 0, &err);
138   if (err)
139     die("Error studying regular expression `%s': %s", p, err);
140   return r;
141 }
142
143 void
144 rx_free(regex *r)
145 {
146   xfree(r->rx);
147   xfree(r->extra);
148   xfree(r);
149 }
150
151 int
152 rx_match(regex *r, const char *s)
153 {
154   int len = str_len(s);
155   int err = pcre_exec(r->rx, r->extra, s, len, 0, 0, r->matches, r->match_array_size);
156   if (err >= 0)
157     {
158       r->real_matches = err;
159       /* need to check that the full string matches */
160       return !(r->matches[0] || s[r->matches[1]]);
161     }
162   else if (err == PCRE_ERROR_NOMATCH)
163     return 0;
164   else if (err == PCRE_ERROR_NOMEMORY)
165     die("Regex matching ran out of memory");
166   else
167     die("Regex matching failed with unknown error %d", err);
168 }
169
170 int
171 rx_subst(regex *r, const char *by, const char *src, char *dest, uns destlen)
172 {
173   char *end = dest + destlen - 1;
174
175   if (!rx_match(r, src))
176     return 0;
177
178   while (*by)
179     {
180       if (*by == '\\')
181         {
182           by++;
183           if (*by >= '0' && *by <= '9') /* \0 gets replaced by entire pattern */
184             {
185               uns j = *by++ - '0';
186               if (j < r->real_matches && r->matches[2*j] >= 0)
187                 {
188                   char *s = src + r->matches[2*j];
189                   uns i = r->matches[2*j+1] - r->matches[2*j];
190                   if (dest + i >= end)
191                     return -1;
192                   memcpy(dest, s, i);
193                   dest += i;
194                   continue;
195                 }
196             }
197         }
198       if (dest < end)
199         *dest++ = *by++;
200       else
201         return -1;
202     }
203   *dest = 0;
204   return 1;
205 }
206
207 #else
208
209 /* BSD regular expression library */
210
211 #include <regex.h>
212
213 #define INITIAL_MEM 1024                /* Initial space allocated for each pattern */
214 #define CHAR_SET_SIZE 256               /* How many characters in the character set.  */
215
216 struct regex {
217   struct re_pattern_buffer buf;
218   struct re_registers regs;             /* Must not change between re_match() calls */
219   int len_cache;
220 };
221
222 regex *
223 rx_compile(const char *p, int icase)
224 {
225   regex *r = xmalloc_zero(sizeof(regex));
226   const char *msg;
227
228   r->buf.buffer = xmalloc(INITIAL_MEM);
229   r->buf.allocated = INITIAL_MEM;
230   if (icase)
231     {
232       unsigned i;
233       r->buf.translate = xmalloc (CHAR_SET_SIZE);
234       /* Map uppercase characters to corresponding lowercase ones.  */
235       for (i = 0; i < CHAR_SET_SIZE; i++)
236         r->buf.translate[i] = Cupcase(i);
237     }
238   else
239     r->buf.translate = NULL;
240   re_set_syntax(RE_SYNTAX_POSIX_EXTENDED);
241   msg = re_compile_pattern(p, strlen(p), &r->buf);
242   if (!msg)
243     return r;
244   die("Error parsing pattern `%s': %s", p, msg);
245 }
246
247 void
248 rx_free(regex *r)
249 {
250   xfree(r->buf.buffer);
251   if (r->buf.translate)
252     xfree(r->buf.translate);
253   xfree(r);
254 }
255
256 int
257 rx_match(regex *r, const char *s)
258 {
259   int len = strlen(s);
260
261   r->len_cache = len;
262   if (re_match(&r->buf, s, len, 0, &r->regs) < 0)
263     return 0;
264   if (r->regs.start[0] || r->regs.end[0] != len) /* XXX: Why regex doesn't enforce implicit "^...$" ? */
265     return 0;
266   return 1;
267 }
268
269 int
270 rx_subst(regex *r, const char *by, const char *src, char *dest, uns destlen)
271 {
272   char *end = dest + destlen - 1;
273
274   if (!rx_match(r, src))
275     return 0;
276
277   while (*by)
278     {
279       if (*by == '\\')
280         {
281           by++;
282           if (*by >= '0' && *by <= '9') /* \0 gets replaced by entire pattern */
283             {
284               uns j = *by++ - '0';
285               if (j < r->regs.num_regs)
286                 {
287                   const char *s = src + r->regs.start[j];
288                   uns i = r->regs.end[j] - r->regs.start[j];
289                   if (r->regs.start[j] > r->len_cache || r->regs.end[j] > r->len_cache)
290                     return -1;
291                   if (dest + i >= end)
292                     return -1;
293                   memcpy(dest, s, i);
294                   dest += i;
295                   continue;
296                 }
297             }
298         }
299       if (dest < end)
300         *dest++ = *by++;
301       else
302         return -1;
303     }
304   *dest = 0;
305   return 1;
306 }
307
308 #endif
309
310 #ifdef TEST
311
312 int main(int argc, char **argv)
313 {
314   regex *r;
315   char buf1[4096], buf2[4096];
316   int opt_i = 0;
317
318   if (!strcmp(argv[1], "-i"))
319     {
320       opt_i = 1;
321       argv++;
322       argc--;
323     }
324   r = rx_compile(argv[1], opt_i);
325   while (fgets(buf1, sizeof(buf1), stdin))
326     {
327       char *p = strchr(buf1, '\n');
328       if (p)
329         *p = 0;
330       if (argc == 2)
331         {
332           if (rx_match(r, buf1))
333             puts("MATCH");
334           else
335             puts("NO MATCH");
336         }
337       else
338         {
339           int i = rx_subst(r, argv[2], buf1, buf2, sizeof(buf2));
340           if (i < 0)
341             puts("OVERFLOW");
342           else if (!i)
343             puts("NO MATCH");
344           else
345             puts(buf2);
346         }
347     }
348   rx_free(r);
349 }
350
351 #endif