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