- initial import of revision 374 from cnc
[apt.git] / lua / local / lrexlib.c
1 /* lrexlib.c - POSIX & PCRE regular expression library */
2 /* POSIX regexs can use Spencer extensions for matching NULs if available
3    (REG_BASIC) */
4 /* Reuben Thomas   nov00-09jan04 */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include "lua.h"
11 #include "lauxlib.h"
12
13 /* Sanity check */
14 #if !defined(WITH_POSIX) && !defined(WITH_PCRE)
15 #error Define WITH_POSIX or WITH_PCRE, otherwise this library is useless!
16 #endif
17
18
19 /* POSIX regex methods */
20
21 #ifdef WITH_POSIX
22
23 #include <regex.h>
24
25 static int posix_comp(lua_State *L) {
26   size_t l;
27   const char *pattern;
28   int res;
29   regex_t *pr = (regex_t *)lua_newuserdata(L, sizeof(regex_t));
30   pattern = luaL_checklstring(L, 1, &l);
31 #ifdef REG_BASIC
32   pr->re_endp = pattern + lua_strlen(L, 1);
33   res = regcomp(pr, pattern, REG_EXTENDED | REG_PEND);
34 #else
35   res = regcomp(pr, pattern, REG_EXTENDED);
36 #endif
37   if (res) {
38     size_t sz = regerror(res, pr, NULL, 0);
39     char errbuf[sz];
40     regerror(res, pr, errbuf, sz);
41     lua_pushstring(L, errbuf);
42     lua_error(L);
43   }
44   luaL_getmetatable(L, "regex_t");
45   lua_setmetatable(L, -2);
46   return 1;
47 }
48
49 static void posix_getargs(lua_State *L,
50 #ifdef REG_BASIC
51                           size_t *len,
52 #endif
53                           size_t *ncapt, const char **text, regex_t **pr,
54                           regmatch_t **match) {
55   luaL_checkany(L, 1);
56   *pr = (regex_t *)lua_touserdata(L, 1);
57 #ifdef REG_BASIC
58   *text = luaL_checklstring(L, 2, len);
59 #else
60   *text = luaL_checklstring(L, 2, NULL);
61 #endif
62   *ncapt = (*pr)->re_nsub;
63   luaL_checkstack(L, *ncapt + 2, "too many captures");
64   *match = malloc((*ncapt + 1) * sizeof(regmatch_t));
65 }
66
67 static void posix_push_matches(lua_State *L, const char *text, regmatch_t *match,
68                              size_t ncapt) {
69   size_t i;
70   lua_newtable(L);
71   for (i = 1; i <= ncapt; i++) {
72     if (match[i].rm_so >= 0) {
73       lua_pushlstring(L, text + match[i].rm_so,
74                       match[i].rm_eo - match[i].rm_so);
75       lua_rawseti(L, -2, i);
76     }
77   }
78 }
79
80 static int posix_match(lua_State *L) {
81   int res;
82 #ifdef REG_BASIC
83   size_t len;
84 #endif
85   size_t ncapt;
86   const char *text;
87   regex_t *pr;
88   regmatch_t *match;
89   posix_getargs(L,
90 #ifdef REG_BASIC
91           &len,
92 #endif
93           &ncapt, &text, &pr, &match);
94 #ifdef REG_BASIC
95   match[0].rm_so = 0;
96   match[0].rm_eo = len;
97   res = regexec(pr, text, ncapt + 1, match, REG_STARTEND);
98 #else
99   res = regexec(pr, text, ncapt + 1, match, 0);
100 #endif
101   if (res == 0) {
102     lua_pushnumber(L, match[0].rm_so + 1);
103     lua_pushnumber(L, match[0].rm_eo);
104     posix_push_matches(L, text, match, ncapt);
105     lua_pushstring(L, "n");
106     lua_pushnumber(L, ncapt);
107     lua_rawset(L, -3);
108     return 3;
109   } else
110     return 0;
111 }
112
113 static int posix_gmatch(lua_State *L) {
114   int res;
115 #ifdef REG_BASIC
116   size_t len;
117 #endif
118   size_t ncapt, nmatch = 0, maxmatch = 0, limit = 0;
119   const char *text;
120   regex_t *pr;
121   regmatch_t *match;
122   posix_getargs(L,
123 #ifdef REG_BASIC
124           &len,
125 #endif
126           &ncapt, &text, &pr, &match);
127   luaL_checktype(L, 3, LUA_TFUNCTION);
128   if (lua_gettop(L) > 3) {
129     maxmatch = (size_t)luaL_checknumber(L, 4);
130     limit = 1;
131   }
132   while (!limit || nmatch < maxmatch) {
133 #ifdef REG_BASIC
134     match[0].rm_so = 0;
135     match[0].rm_eo = len;
136     res = regexec(pr, text, ncapt + 1, match, REG_STARTEND);
137 #else
138     res = regexec(pr, text, ncapt + 1, match, 0);
139 #endif
140     if (res == 0) {
141       lua_pushvalue(L, 3);
142       lua_pushlstring(L, text + match[0].rm_so, match[0].rm_eo - match[0].rm_so);
143       posix_push_matches(L, text, match, ncapt);
144       lua_call(L, 2, 0);
145       text += match[0].rm_eo;
146 #ifdef REG_BASIC
147       len -= match[0].rm_eo;
148 #endif
149       nmatch++;
150     } else
151       break;
152   }
153   lua_pushnumber(L, nmatch);
154   return 1;
155 }
156
157 static int posix_gc (lua_State *L) {
158   regex_t *r = (regex_t *)luaL_checkudata(L, 1, "regex_t");
159   if (r)
160     regfree(r);
161   return 0;
162 }
163
164 static const luaL_reg posixmeta[] = {
165   {"match",   posix_match},
166   {"gmatch",  posix_gmatch},
167   {"__gc",    posix_gc},
168   {NULL, NULL}
169 };
170
171 #endif /* WITH_POSIX */
172
173
174 /* PCRE methods */
175
176 #ifdef WITH_PCRE
177
178 #include <pcre/pcre.h>
179
180 static int pcre_comp(lua_State *L)
181 {
182   size_t l;
183   const char *pattern;
184   const char *error;
185   int erroffset;
186   pcre **ppr = (pcre **)lua_newuserdata(L, sizeof(pcre **));
187   pcre *pr;
188   pattern = luaL_checklstring(L, 1, &l);
189   pr = pcre_compile(pattern, 0, &error, &erroffset, NULL);
190   if (!pr) {
191     lua_pushstring(L, error);
192     lua_error(L);
193   }
194   *ppr = pr;
195   luaL_getmetatable(L, "pcre");
196   lua_setmetatable(L, -2);
197   return 1;
198 }
199
200 static void pcre_getargs(lua_State *L, size_t *len, int *ncapt, const char **text,
201                         pcre ***ppr, int **match)
202 {
203   luaL_checkany(L, 1);
204   *ppr = (pcre **)lua_touserdata(L, 1);
205   *text = luaL_checklstring(L, 2, len);
206   pcre_fullinfo(**ppr, NULL, PCRE_INFO_CAPTURECOUNT, ncapt);
207   luaL_checkstack(L, *ncapt + 2, "too many captures");
208   /* need (2 ints per capture, plus one for substring match) * 3/2 */
209   *match = malloc((*ncapt + 1) * 3 * sizeof(int));
210 }
211
212 static void pcre_push_matches(lua_State *L, const char *text, int *match,
213                              int ncapt)
214 {
215   int i;
216   lua_newtable(L);
217   for (i = 1; i <= ncapt; i++) {
218     if (match[i * 2] >= 0) {
219       lua_pushlstring(L, text + match[i * 2],
220                       match[i * 2 + 1] - match[i * 2]);
221       lua_rawseti(L, -2, i);
222     }
223   }
224 }
225
226 static int pcre_match(lua_State *L)
227 {
228   int res;
229   const char *text;
230   pcre **ppr;
231   int *match;
232   int ncapt;
233   size_t len;
234   pcre_getargs(L, &len, &ncapt, &text, &ppr, &match);
235   res = pcre_exec(*ppr, NULL, text, (int)len, 0, 0, match, (ncapt + 1) * 3);
236   if (res >= 0) {
237     lua_pushnumber(L, match[0] + 1);
238     lua_pushnumber(L, match[1]);
239     pcre_push_matches(L, text, match, ncapt);
240     lua_pushstring(L, "n");
241     lua_pushnumber(L, ncapt);
242     lua_rawset(L, -3);
243     return 3;
244   } else
245     return 0;
246 }
247
248 static int pcre_gmatch(lua_State *L)
249 {
250   int res;
251   size_t len;
252   int ncapt, nmatch = 0, maxmatch = 0, limit = 0;
253   const char *text;
254   pcre **ppr;
255   int *match;
256   pcre_getargs(L, &len, &ncapt, &text, &ppr, &match);
257   luaL_checktype(L, 3, LUA_TFUNCTION);
258   if (lua_gettop(L) > 3) {
259     maxmatch = (int)luaL_checknumber(L, 4);
260     limit = 1;
261   }
262   while (!limit || nmatch < maxmatch) {
263     res = pcre_exec(*ppr, NULL, text, (int)len, 0, 0, match,
264                     (ncapt + 1) * 3);
265     if (res >= 0) {
266       lua_pushvalue(L, 3);
267       lua_pushlstring(L, text + match[0], match[1] - match[0]);
268       pcre_push_matches(L, text, match, ncapt);
269       lua_call(L, 2, 0);
270       text += match[1];
271       len -= match[1];
272       nmatch++;
273     } else
274       break;
275   }
276   lua_pushnumber(L, nmatch);
277   return 1;
278 }
279
280 static int pcre_gc (lua_State *L)
281 {
282   pcre **ppr = (pcre **)luaL_checkudata(L, 1, "pcre");
283   if (ppr)
284     pcre_free(*ppr);
285   return 0;
286 }
287
288 static const luaL_reg pcremeta[] = {
289   {"match",  pcre_match},
290   {"gmatch", pcre_gmatch},
291   {"__gc",   pcre_gc},
292   {NULL, NULL}
293 };
294
295 #endif /* defined(WITH_PCRE) */
296
297
298 /* Open the library */
299
300 static const luaL_reg rexlib[] = {
301 #ifdef WITH_POSIX
302   {"newPOSIX", posix_comp},
303 #endif
304 #ifdef WITH_PCRE
305   {"newPCRE", pcre_comp},
306 #endif
307   {NULL, NULL}
308 };
309
310 static void createmeta(lua_State *L, const char *name)
311 {
312   luaL_newmetatable(L, name);   /* create new metatable */
313   lua_pushliteral(L, "__index");
314   lua_pushvalue(L, -2);         /* push metatable */
315   lua_rawset(L, -3);            /* metatable.__index = metatable */
316 }
317
318 LUALIB_API int luaopen_rex(lua_State *L)
319 {
320 #ifdef WITH_POSIX
321   createmeta(L, "regex_t");
322   luaL_openlib(L, NULL, posixmeta, 0);
323   lua_pop(L, 1);
324 #endif
325 #ifdef WITH_PCRE
326   createmeta(L, "pcre");
327   luaL_openlib(L, NULL, pcremeta, 0);
328   lua_pop(L, 1);
329 #endif
330   luaL_openlib(L, "rex", rexlib, 0);
331   return 1;
332 }