src/lxplib.c

changeset 0
24d141cb2d1e
child 1
304b5a6f85e4
equal deleted inserted replaced
-1:000000000000 0:24d141cb2d1e
1 /*
2 ** $Id: lxplib.c,v 1.16 2007/06/05 20:03:12 carregal Exp $
3 ** LuaExpat: Lua bind for Expat library
4 ** See Copyright Notice in license.html
5 */
6
7
8 #include <assert.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include "expat.h"
13
14 #include "lua.h"
15 #include "lauxlib.h"
16 #if ! defined (LUA_VERSION_NUM) || LUA_VERSION_NUM < 501
17 #include "compat-5.1.h"
18 #endif
19
20
21 #include "lxplib.h"
22
23
24
25 enum XPState {
26 XPSpre, /* parser just initialized */
27 XPSok, /* state while parsing */
28 XPSfinished, /* state after finished parsing */
29 XPSerror,
30 XPSstring /* state while reading a string */
31 };
32
33 struct lxp_userdata {
34 lua_State *L;
35 XML_Parser parser; /* associated expat parser */
36 int tableref; /* table with callbacks for this parser */
37 enum XPState state;
38 luaL_Buffer *b; /* to concatenate sequences of cdata pieces */
39 };
40
41 typedef struct lxp_userdata lxp_userdata;
42
43
44 static int reporterror (lxp_userdata *xpu) {
45 lua_State *L = xpu->L;
46 XML_Parser p = xpu->parser;
47 lua_pushnil(L);
48 lua_pushstring(L, XML_ErrorString(XML_GetErrorCode(p)));
49 lua_pushnumber(L, XML_GetCurrentLineNumber(p));
50 lua_pushnumber(L, XML_GetCurrentColumnNumber(p) + 1);
51 lua_pushnumber(L, XML_GetCurrentByteIndex(p) + 1);
52 return 5;
53 }
54
55
56 static lxp_userdata *createlxp (lua_State *L) {
57 lxp_userdata *xpu = (lxp_userdata *)lua_newuserdata(L, sizeof(lxp_userdata));
58 xpu->tableref = LUA_REFNIL; /* in case of errors... */
59 xpu->parser = NULL;
60 xpu->L = NULL;
61 xpu->state = XPSpre;
62 luaL_getmetatable(L, ParserType);
63 lua_setmetatable(L, -2);
64 return xpu;
65 }
66
67
68 static void lxpclose (lua_State *L, lxp_userdata *xpu) {
69 lua_unref(L, xpu->tableref);
70 xpu->tableref = LUA_REFNIL;
71 if (xpu->parser)
72 XML_ParserFree(xpu->parser);
73 xpu->parser = NULL;
74 }
75
76
77
78
79 /*
80 ** Auxiliary function to call a Lua handle
81 */
82 static void docall (lxp_userdata *xpu, int nargs, int nres) {
83 lua_State *L = xpu->L;
84 assert(xpu->state == XPSok);
85 if (lua_pcall(L, nargs + 1, nres, 0) != 0) {
86 xpu->state = XPSerror;
87 luaL_unref(L, LUA_REGISTRYINDEX, xpu->tableref);
88 xpu->tableref = luaL_ref(L, LUA_REGISTRYINDEX); /* error message */
89 }
90 }
91
92
93 /*
94 ** Check whether there is pending Cdata, and call its handle if necessary
95 */
96 static void dischargestring (lxp_userdata *xpu) {
97 assert(xpu->state == XPSstring);
98 xpu->state = XPSok;
99 luaL_pushresult(xpu->b);
100 docall(xpu, 1, 0);
101 }
102
103
104 /*
105 ** Check whether there is a Lua handle for a given event: If so,
106 ** put it on the stack (to be called later), and also push `self'
107 */
108 static int getHandle (lxp_userdata *xpu, const char *handle) {
109 lua_State *L = xpu->L;
110 if (xpu->state == XPSstring) dischargestring(xpu);
111 if (xpu->state == XPSerror)
112 return 0; /* some error happened before; skip all handles */
113 lua_pushstring(L, handle);
114 lua_gettable(L, 3);
115 if (lua_toboolean(L, -1) == 0) {
116 lua_pop(L, 1);
117 return 0;
118 }
119 if (!lua_isfunction(L, -1)) {
120 luaL_error(L, "lxp `%s' callback is not a function", handle);
121 }
122 lua_pushvalue(L, 1); /* first argument in every call (self) */
123 return 1;
124 }
125
126
127
128 /*
129 ** {======================================================
130 ** Handles
131 ** =======================================================
132 */
133
134
135 static void f_StartCdata (void *ud) {
136 lxp_userdata *xpu = (lxp_userdata *)ud;
137 if (getHandle(xpu, StartCdataKey) == 0) return; /* no handle */
138 docall(xpu, 0, 0);
139 }
140
141
142 static void f_EndCdataKey (void *ud) {
143 lxp_userdata *xpu = (lxp_userdata *)ud;
144 if (getHandle(xpu, EndCdataKey) == 0) return; /* no handle */
145 docall(xpu, 0, 0);
146 }
147
148
149 static void f_CharData (void *ud, const char *s, int len) {
150 lxp_userdata *xpu = (lxp_userdata *)ud;
151 if (xpu->state == XPSok) {
152 if (getHandle(xpu, CharDataKey) == 0) return; /* no handle */
153 xpu->state = XPSstring;
154 luaL_buffinit(xpu->L, xpu->b);
155 }
156 if (xpu->state == XPSstring)
157 luaL_addlstring(xpu->b, s, len);
158 }
159
160
161 static void f_Comment (void *ud, const char *data) {
162 lxp_userdata *xpu = (lxp_userdata *)ud;
163 if (getHandle(xpu, CommentKey) == 0) return; /* no handle */
164 lua_pushstring(xpu->L, data);
165 docall(xpu, 1, 0);
166 }
167
168
169 static void f_Default (void *ud, const char *data, int len) {
170 lxp_userdata *xpu = (lxp_userdata *)ud;
171 if (getHandle(xpu, DefaultKey) == 0) return; /* no handle */
172 lua_pushlstring(xpu->L, data, len);
173 docall(xpu, 1, 0);
174 }
175
176
177 static void f_DefaultExpand (void *ud, const char *data, int len) {
178 lxp_userdata *xpu = (lxp_userdata *)ud;
179 if (getHandle(xpu, DefaultExpandKey) == 0) return; /* no handle */
180 lua_pushlstring(xpu->L, data, len);
181 docall(xpu, 1, 0);
182 }
183
184
185 static void f_StartElement (void *ud, const char *name, const char **attrs) {
186 lxp_userdata *xpu = (lxp_userdata *)ud;
187 lua_State *L = xpu->L;
188 int lastspec = XML_GetSpecifiedAttributeCount(xpu->parser) / 2;
189 int i = 1;
190 if (getHandle(xpu, StartElementKey) == 0) return; /* no handle */
191 lua_pushstring(L, name);
192 lua_newtable(L);
193 while (*attrs) {
194 if (i <= lastspec) {
195 lua_pushnumber(L, i++);
196 lua_pushstring(L, *attrs);
197 lua_settable(L, -3);
198 }
199 lua_pushstring(L, *attrs++);
200 lua_pushstring(L, *attrs++);
201 lua_settable(L, -3);
202 }
203 docall(xpu, 2, 0); /* call function with self, name, and attributes */
204 }
205
206
207 static void f_EndElement (void *ud, const char *name) {
208 lxp_userdata *xpu = (lxp_userdata *)ud;
209 if (getHandle(xpu, EndElementKey) == 0) return; /* no handle */
210 lua_pushstring(xpu->L, name);
211 docall(xpu, 1, 0);
212 }
213
214
215 static int f_ExternaEntity (XML_Parser p, const char *context,
216 const char *base,
217 const char *systemId,
218 const char *publicId) {
219 lxp_userdata *xpu = (lxp_userdata *)XML_GetUserData(p);
220 lua_State *L = xpu->L;
221 lxp_userdata *child;
222 int status;
223 if (getHandle(xpu, ExternalEntityKey) == 0) return 1; /* no handle */
224 child = createlxp(L);
225 child->parser = XML_ExternalEntityParserCreate(p, context, NULL);
226 if (!child->parser)
227 luaL_error(L, "XML_ParserCreate failed");
228 lua_getref(L, xpu->tableref); /* child uses the same table of its father */
229 child->tableref = luaL_ref(L, LUA_REGISTRYINDEX);
230 lua_pushstring(L, base);
231 lua_pushstring(L, systemId);
232 lua_pushstring(L, publicId);
233 docall(xpu, 4, 1);
234 status = lua_toboolean(L, -1);
235 lua_pop(L, 1);
236 lxpclose(L, child);
237 return status;
238 }
239
240
241 static void f_StartNamespaceDecl (void *ud, const char *prefix,
242 const char *uri) {
243 lxp_userdata *xpu = (lxp_userdata *)ud;
244 lua_State *L = xpu->L;
245 if (getHandle(xpu, StartNamespaceDeclKey) == 0) return; /* no handle */
246 lua_pushstring(L, prefix);
247 lua_pushstring(L, uri);
248 docall(xpu, 2, 0);
249 }
250
251
252 static void f_EndNamespaceDecl (void *ud, const char *prefix) {
253 lxp_userdata *xpu = (lxp_userdata *)ud;
254 if (getHandle(xpu, EndNamespaceDeclKey) == 0) return; /* no handle */
255 lua_pushstring(xpu->L, prefix);
256 docall(xpu, 1, 0);
257 }
258
259
260 static void f_NotationDecl (void *ud, const char *notationName,
261 const char *base,
262 const char *systemId,
263 const char *publicId) {
264 lxp_userdata *xpu = (lxp_userdata *)ud;
265 lua_State *L = xpu->L;
266 if (getHandle(xpu, NotationDeclKey) == 0) return; /* no handle */
267 lua_pushstring(L, notationName);
268 lua_pushstring(L, base);
269 lua_pushstring(L, systemId);
270 lua_pushstring(L, publicId);
271 docall(xpu, 4, 0);
272 }
273
274
275 static int f_NotStandalone (void *ud) {
276 int status;
277 lxp_userdata *xpu = (lxp_userdata *)ud;
278 lua_State *L = xpu->L;
279 if (getHandle(xpu, NotStandaloneKey) == 0) return 1; /* no handle */
280 docall(xpu, 0, 1);
281 status = lua_toboolean(L, -1);
282 lua_pop(L, 1);
283 return status;
284 }
285
286
287 static void f_ProcessingInstruction (void *ud, const char *target,
288 const char *data) {
289 lxp_userdata *xpu = (lxp_userdata *)ud;
290 lua_State *L = xpu->L;
291 if (getHandle(xpu, ProcessingInstructionKey) == 0) return; /* no handle */
292 lua_pushstring(L, target);
293 lua_pushstring(L, data);
294 docall(xpu, 2, 0);
295 }
296
297
298 static void f_UnparsedEntityDecl (void *ud, const char *entityName,
299 const char *base,
300 const char *systemId,
301 const char *publicId,
302 const char *notationName) {
303 lxp_userdata *xpu = (lxp_userdata *)ud;
304 lua_State *L = xpu->L;
305 if (getHandle(xpu, UnparsedEntityDeclKey) == 0) return; /* no handle */
306 lua_pushstring(L, entityName);
307 lua_pushstring(L, base);
308 lua_pushstring(L, systemId);
309 lua_pushstring(L, publicId);
310 lua_pushstring(L, notationName);
311 docall(xpu, 5, 0);
312 }
313
314 /* }====================================================== */
315
316
317
318 static int hasfield (lua_State *L, const char *fname) {
319 int res;
320 lua_pushstring(L, fname);
321 lua_gettable(L, 1);
322 res = !lua_isnil(L, -1);
323 lua_pop(L, 1);
324 return res;
325 }
326
327
328 static void checkcallbacks (lua_State *L) {
329 static const char *const validkeys[] = {
330 "StartCdataSection", "EndCdataSection", "CharacterData", "Comment",
331 "Default", "DefaultExpand", "StartElement", "EndElement",
332 "ExternalEntityRef", "StartNamespaceDecl", "EndNamespaceDecl",
333 "NotationDecl", "NotStandalone", "ProcessingInstruction",
334 "UnparsedEntityDecl", NULL};
335 if (hasfield(L, "_nonstrict")) return;
336 lua_pushnil(L);
337 while (lua_next(L, 1)) {
338 lua_pop(L, 1); /* remove value */
339 #if ! defined (LUA_VERSION_NUM) || LUA_VERSION_NUM < 501
340 if (lua_type(L, -1) != LUA_TSTRING ||
341 luaL_findstring(lua_tostring(L, -1), validkeys) < 0)
342 luaL_error(L, "invalid key `%s' in callback table", lua_tostring(L, -1));
343 #else
344 luaL_checkoption(L, -1, NULL, validkeys);
345 #endif
346 }
347 }
348
349
350 static int lxp_make_parser (lua_State *L) {
351 XML_Parser p;
352 char sep = *luaL_optstring(L, 2, "");
353 lxp_userdata *xpu = createlxp(L);
354 p = xpu->parser = (sep == '\0') ? XML_ParserCreate(NULL) :
355 XML_ParserCreateNS(NULL, sep);
356 if (!p)
357 luaL_error(L, "XML_ParserCreate failed");
358 luaL_checktype(L, 1, LUA_TTABLE);
359 checkcallbacks(L);
360 lua_pushvalue(L, 1);
361 xpu->tableref = luaL_ref(L, LUA_REGISTRYINDEX);
362 XML_SetUserData(p, xpu);
363 if (hasfield(L, StartCdataKey) || hasfield(L, EndCdataKey))
364 XML_SetCdataSectionHandler(p, f_StartCdata, f_EndCdataKey);
365 if (hasfield(L, CharDataKey))
366 XML_SetCharacterDataHandler(p, f_CharData);
367 if (hasfield(L, CommentKey))
368 XML_SetCommentHandler(p, f_Comment);
369 if (hasfield(L, DefaultKey))
370 XML_SetDefaultHandler(p, f_Default);
371 if (hasfield(L, DefaultExpandKey))
372 XML_SetDefaultHandlerExpand(p, f_DefaultExpand);
373 if (hasfield(L, StartElementKey) || hasfield(L, EndElementKey))
374 XML_SetElementHandler(p, f_StartElement, f_EndElement);
375 if (hasfield(L, ExternalEntityKey))
376 XML_SetExternalEntityRefHandler(p, f_ExternaEntity);
377 if (hasfield(L, StartNamespaceDeclKey) || hasfield(L, EndNamespaceDeclKey))
378 XML_SetNamespaceDeclHandler(p, f_StartNamespaceDecl, f_EndNamespaceDecl);
379 if (hasfield(L, NotationDeclKey))
380 XML_SetNotationDeclHandler(p, f_NotationDecl);
381 if (hasfield(L, NotStandaloneKey))
382 XML_SetNotStandaloneHandler(p, f_NotStandalone);
383 if (hasfield(L, ProcessingInstructionKey))
384 XML_SetProcessingInstructionHandler(p, f_ProcessingInstruction);
385 if (hasfield(L, UnparsedEntityDeclKey))
386 XML_SetUnparsedEntityDeclHandler(p, f_UnparsedEntityDecl);
387 return 1;
388 }
389
390
391 static lxp_userdata *checkparser (lua_State *L, int idx) {
392 lxp_userdata *xpu = (lxp_userdata *)luaL_checkudata(L, idx, ParserType);
393 luaL_argcheck(L, xpu, idx, "expat parser expected");
394 luaL_argcheck(L, xpu->parser, idx, "parser is closed");
395 return xpu;
396 }
397
398
399 static int parser_gc (lua_State *L) {
400 lxp_userdata *xpu = (lxp_userdata *)luaL_checkudata(L, 1, ParserType);
401 luaL_argcheck(L, xpu, 1, "expat parser expected");
402 lxpclose(L, xpu);
403 return 0;
404 }
405
406
407 static int setbase (lua_State *L) {
408 lxp_userdata *xpu = checkparser(L, 1);
409 if (XML_SetBase(xpu->parser, luaL_checkstring(L, 2)) == 0)
410 luaL_error(L, "no memory to store base");
411 return 0;
412 }
413
414
415 static int getbase (lua_State *L) {
416 lxp_userdata *xpu = checkparser(L, 1);
417 lua_pushstring(L, XML_GetBase(xpu->parser));
418 return 1;
419 }
420
421
422 static int getcallbacks (lua_State *L) {
423 lxp_userdata *xpu = checkparser(L, 1);
424 lua_rawgeti(L, LUA_REGISTRYINDEX, xpu->tableref);
425 return 1;
426 }
427
428
429 static int parse_aux (lua_State *L, lxp_userdata *xpu, const char *s,
430 size_t len) {
431 luaL_Buffer b;
432 int status;
433 xpu->L = L;
434 xpu->state = XPSok;
435 xpu->b = &b;
436 lua_settop(L, 2);
437 lua_getref(L, xpu->tableref); /* to be used by handlers */
438 status = XML_Parse(xpu->parser, s, (int)len, s == NULL);
439 if (xpu->state == XPSstring) dischargestring(xpu);
440 if (xpu->state == XPSerror) { /* callback error? */
441 lua_rawgeti(L, LUA_REGISTRYINDEX, xpu->tableref); /* get original msg. */
442 lua_error(L);
443 }
444 if (s == NULL) xpu->state = XPSfinished;
445 if (status) {
446 lua_pushboolean(L, 1);
447 return 1;
448 }
449 else { /* error */
450 return reporterror(xpu);
451 }
452 }
453
454
455 static int lxp_parse (lua_State *L) {
456 lxp_userdata *xpu = checkparser(L, 1);
457 size_t len;
458 const char *s = luaL_optlstring(L, 2, NULL, &len);
459 if (xpu->state == XPSfinished && s != NULL) {
460 lua_pushnil(L);
461 lua_pushliteral(L, "cannot parse - document is finished");
462 return 2;
463 }
464 return parse_aux(L, xpu, s, len);
465 }
466
467
468 static int lxp_close (lua_State *L) {
469 int status = 1;
470 lxp_userdata *xpu = (lxp_userdata *)luaL_checkudata(L, 1, ParserType);
471 luaL_argcheck(L, xpu, 1, "expat parser expected");
472 if (xpu->state != XPSfinished)
473 status = parse_aux(L, xpu, NULL, 0);
474 lxpclose(L, xpu);
475 if (status > 1) luaL_error(L, "error closing parser: %s",
476 lua_tostring(L, -status+1));
477 return 0;
478 }
479
480
481 static int lxp_pos (lua_State *L) {
482 lxp_userdata *xpu = checkparser(L, 1);
483 XML_Parser p = xpu->parser;
484 lua_pushnumber(L, XML_GetCurrentLineNumber(p));
485 lua_pushnumber(L, XML_GetCurrentColumnNumber(p) + 1);
486 lua_pushnumber(L, XML_GetCurrentByteIndex(p) + 1);
487 return 3;
488 }
489
490
491 static int lxp_setencoding (lua_State *L) {
492 lxp_userdata *xpu = checkparser(L, 1);
493 const char *encoding = luaL_checkstring(L, 2);
494 luaL_argcheck(L, xpu->state == XPSpre, 1, "invalid parser state");
495 XML_SetEncoding(xpu->parser, encoding);
496 return 0;
497 }
498
499
500 static const struct luaL_reg lxp_meths[] = {
501 {"parse", lxp_parse},
502 {"close", lxp_close},
503 {"__gc", parser_gc},
504 {"pos", lxp_pos},
505 {"setencoding", lxp_setencoding},
506 {"getcallbacks", getcallbacks},
507 {"getbase", getbase},
508 {"setbase", setbase},
509 {NULL, NULL}
510 };
511
512 static const struct luaL_reg lxp_funcs[] = {
513 {"new", lxp_make_parser},
514 {NULL, NULL}
515 };
516
517
518 /*
519 ** Assumes the table is on top of the stack.
520 */
521 static void set_info (lua_State *L) {
522 lua_pushliteral (L, "_COPYRIGHT");
523 lua_pushliteral (L, "Copyright (C) 2003-2007 Kepler Project");
524 lua_settable (L, -3);
525 lua_pushliteral (L, "_DESCRIPTION");
526 lua_pushliteral (L, "LuaExpat is a SAX XML parser based on the Expat library");
527 lua_settable (L, -3);
528 lua_pushliteral (L, "_VERSION");
529 lua_pushliteral (L, "LuaExpat 1.1.0");
530 lua_settable (L, -3);
531 }
532
533
534 int luaopen_lxp (lua_State *L) {
535 luaL_newmetatable(L, ParserType);
536 lua_pushliteral(L, "__index");
537 lua_pushvalue(L, -2);
538 lua_rawset(L, -3);
539 luaL_openlib (L, NULL, lxp_meths, 0);
540 luaL_openlib (L, "lxp", lxp_funcs, 0);
541 set_info (L);
542
543 return 1;
544 }

mercurial