Add to git.
[c2lib.git] / pre.c
1 /* String functions which allocate strings on the pool.
2  * By Richard W.M. Jones <rich@annexia.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  * $Id: pre.c,v 1.3 2003/01/22 14:29:37 rich Exp $
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #ifdef HAVE_ALLOCA_H
27 #include <alloca.h>
28 #endif
29
30 #ifdef HAVE_STRING_H
31 #include <string.h>
32 #endif
33
34 #include <pcre.h>
35
36 #include <pool.h>
37 #include <vector.h>
38 #include <pstring.h>
39 #include <pre.h>
40
41 /* These private functions are used to capture memory allocations made
42  * by the PCRE library.
43  */
44 static void *malloc_in_pool (size_t);
45 static void do_nothing (void *);
46 static pool malloc_pool = 0;
47
48 pcre *
49 precomp (pool pool, const char *pattern, int options)
50 {
51   const char *errptr;
52   int erroffset;
53   pcre *result;
54   void *(*old_malloc)(size_t);
55   void (*old_free) (void *);
56
57   /* Allocations to the pool. */
58   old_malloc = pcre_malloc;
59   old_free = pcre_free;
60   pcre_malloc = malloc_in_pool;
61   malloc_pool = pool;
62   pcre_free = do_nothing;
63
64   /* Compile the pattern. */
65   result = pcre_compile (pattern, options, &errptr, &erroffset, 0);
66   if (result == 0)
67     {
68       fprintf (stderr,
69                "pcre: internal error compiling regular expression:\n"
70                "pcre: %s\n"
71                "pcre: %s\n"
72                "pcre: %s^\n",
73                errptr,
74                pattern,
75                pchrs (pool, ' ', erroffset-1));
76       exit (1);
77     }
78
79   /* Restore memory allocation. */
80   pcre_malloc = old_malloc;
81   pcre_free = old_free;
82
83   return result;
84 }
85
86 vector
87 prematch (pool pool, const char *str, const pcre *pattern, int options)
88 {
89   int err, n, i, ovecsize;
90   int *ovector;
91   vector result;
92   void *(*old_malloc)(size_t);
93   void (*old_free) (void *);
94
95   /* Allocations to the pool. */
96   old_malloc = pcre_malloc;
97   old_free = pcre_free;
98   pcre_malloc = malloc_in_pool;
99   malloc_pool = pool;
100   pcre_free = do_nothing;
101
102   /* Get the number of capturing substrings in the pattern (n). */
103   if ((err = pcre_fullinfo (pattern, 0, PCRE_INFO_CAPTURECOUNT, &n)) != 0)
104     abort ();
105
106   /* Allocate a vector large enough to contain the resulting substrings. */
107   ovecsize = (n+1) * 3;
108   ovector = alloca (ovecsize * sizeof (int));
109
110   /* Do the match. n is the number of strings found. */
111   n = pcre_exec (pattern, 0, str, strlen (str), 0, options, ovector, ovecsize);
112
113   /* Restore memory allocation. */
114   pcre_malloc = old_malloc;
115   pcre_free = old_free;
116
117   if (n == PCRE_ERROR_NOMATCH) /* No match, return NULL. */
118     return 0;
119   else if (n <= 0)              /* Some other error. */
120     abort ();
121
122   /* Some matches. Construct the vector. */
123   result = new_vector (pool, char *);
124   for (i = 0; i < n; ++i)
125     {
126       char *s = 0;
127       int start = ovector[i*2];
128       int end = ovector[i*2+1];
129
130       if (start >= 0)
131         s = pstrndup (pool, str + start, end - start);
132       vector_push_back (result, s);
133     }
134
135   return result;
136 }
137
138 static int do_match_and_sub (pool pool, const char *str, char **newstrp,
139                              const pcre *pattern, const char *sub,
140                              int startoffset, int options, int cc,
141                              int *ovector, int ovecsize, int placeholders);
142
143 const char *
144 presubst (pool pool, const char *str,
145           const pcre *pattern, const char *sub,
146           int options)
147 {
148   char *newstr = pstrdup (pool, "");
149   int cc, err, n, ovecsize;
150   int *ovector;
151   void *(*old_malloc)(size_t);
152   void (*old_free) (void *);
153   int placeholders = (options & PRESUBST_NO_PLACEHOLDERS) ? 0 : 1;
154   int global = (options & PRESUBST_GLOBAL) ? 1 : 0;
155
156   options &= ~(PRESUBST_NO_PLACEHOLDERS | PRESUBST_GLOBAL);
157
158   /* Allocations to the pool. */
159   old_malloc = pcre_malloc;
160   old_free = pcre_free;
161   pcre_malloc = malloc_in_pool;
162   malloc_pool = pool;
163   pcre_free = do_nothing;
164
165   /* Get the number of capturing substrings in the pattern. */
166   if ((err = pcre_fullinfo (pattern, 0, PCRE_INFO_CAPTURECOUNT, &cc)) != 0)
167     abort ();
168
169   /* Allocate a vector large enough to contain the resulting substrings. */
170   ovecsize = (cc+1) * 3;
171   ovector = alloca (ovecsize * sizeof (int));
172
173   /* Find a match and substitute. */
174   n = do_match_and_sub (pool, str, &newstr, pattern, sub, 0, options, cc,
175                         ovector, ovecsize, placeholders);
176
177   if (global)
178     {
179       /* Do the remaining matches. */
180       while (n > 0)
181         {
182           n = do_match_and_sub (pool, str, &newstr, pattern, sub, n,
183                                 options, cc,
184                                 ovector, ovecsize, placeholders);
185         }
186     }
187   else if (n > 0)
188     {
189       /* Concatenate the remainder of the string. */
190       newstr = pstrcat (pool, newstr, str + n);
191     }
192
193   /* Restore memory allocation. */
194   pcre_malloc = old_malloc;
195   pcre_free = old_free;
196
197   return newstr;
198 }
199
200 static int
201 do_match_and_sub (pool pool, const char *str, char **newstrp,
202                   const pcre *pattern, const char *sub,
203                   int startoffset, int options, int cc,
204                   int *ovector, int ovecsize, int placeholders)
205 {
206   int so, eo, err;
207   char *newstr = *newstrp;
208
209   /* Find the next match. */
210   err = pcre_exec (pattern, 0, str, strlen (str), startoffset,
211                    options, ovector, ovecsize);
212   if (err == PCRE_ERROR_NOMATCH) /* No match. */
213     {
214       if (startoffset == 0)
215         /* Special case: we can just return the original string. */
216         *newstrp = (char *) str;
217       else
218         {
219           /* Concatenate the end of the string. */
220           newstr = pstrcat (pool, newstr, str + startoffset);
221           *newstrp = newstr;
222         }
223       return -1;
224     }
225   else if (err != cc+1)         /* Some other error. */
226     abort ();
227
228   /* Get position of the match. */
229   so = ovector[0];
230   eo = ovector[1];
231
232   /* Substitute for the match. */
233   newstr = pstrncat (pool, newstr, str + startoffset, so - startoffset);
234   if (placeholders)
235     {
236       int i;
237
238       /* Substitute $1, $2, ... placeholders with captured substrings. */
239       for (i = 0; i < strlen (sub); ++i)
240         {
241           if (sub[i] == '$' && (sub[i+1] >= '0' && sub[i+1] <= '9'))
242             {
243               int n = sub[i+1] - '0';
244
245               if (n > cc)
246                 newstr = pstrncat (pool, newstr, &sub[i], 2);
247               else
248                 {
249                   int nso = ovector[n*2];
250                   int neo = ovector[n*2+1];
251
252                   newstr = pstrncat (pool, newstr, str+nso, neo-nso);
253                 }
254
255               i++;
256             }
257           else
258             newstr = pstrncat (pool, newstr, &sub[i], 1);
259         }
260     }
261   else
262     newstr = pstrcat (pool, newstr, sub);
263
264   *newstrp = newstr;
265   return eo;
266 }
267
268 static void *
269 malloc_in_pool (size_t n)
270 {
271   return pmalloc (malloc_pool, n);
272 }
273
274 static void
275 do_nothing (void *p)
276 {
277   /* Yes, really, do nothing. */
278 }