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
16
#if ! defined (LUA_VERSION_NUM) || LUA_VERSION_NUM < 501
17
#include "compat-5.1.h"
20
#if (LUA_VERSION_NUM == 502)
23
#define luaL_register(L,n,f) \
24
{ if ((n) == NULL) luaL_setfuncs(L,f,0); else luaL_newlib(L,f); }
32
XPSpre, /* parser just initialized */
33
XPSok, /* state while parsing */
34
XPSfinished, /* state after finished parsing */
36
XPSstring /* state while reading a string */
41
XML_Parser parser; /* associated expat parser */
42
int tableref; /* table with callbacks for this parser */
44
luaL_Buffer *b; /* to concatenate sequences of cdata pieces */
47
typedef struct lxp_userdata lxp_userdata;
50
static int reporterror (lxp_userdata *xpu) {
51
lua_State *L = xpu->L;
52
XML_Parser p = xpu->parser;
54
lua_pushstring(L, XML_ErrorString(XML_GetErrorCode(p)));
55
lua_pushnumber(L, XML_GetCurrentLineNumber(p));
56
lua_pushnumber(L, XML_GetCurrentColumnNumber(p) + 1);
57
lua_pushnumber(L, XML_GetCurrentByteIndex(p) + 1);
62
static lxp_userdata *createlxp (lua_State *L) {
63
lxp_userdata *xpu = (lxp_userdata *)lua_newuserdata(L, sizeof(lxp_userdata));
64
xpu->tableref = LUA_REFNIL; /* in case of errors... */
68
luaL_getmetatable(L, ParserType);
69
lua_setmetatable(L, -2);
74
static void lxpclose (lua_State *L, lxp_userdata *xpu) {
75
luaL_unref(L, LUA_REGISTRYINDEX, xpu->tableref);
76
xpu->tableref = LUA_REFNIL;
78
XML_ParserFree(xpu->parser);
86
** Auxiliary function to call a Lua handle
88
static void docall (lxp_userdata *xpu, int nargs, int nres) {
89
lua_State *L = xpu->L;
90
assert(xpu->state == XPSok);
91
if (lua_pcall(L, nargs + 1, nres, 0) != 0) {
92
xpu->state = XPSerror;
93
luaL_unref(L, LUA_REGISTRYINDEX, xpu->tableref);
94
xpu->tableref = luaL_ref(L, LUA_REGISTRYINDEX); /* error message */
100
** Check whether there is pending Cdata, and call its handle if necessary
102
static void dischargestring (lxp_userdata *xpu) {
103
assert(xpu->state == XPSstring);
105
luaL_pushresult(xpu->b);
111
** Check whether there is a Lua handle for a given event: If so,
112
** put it on the stack (to be called later), and also push `self'
114
static int getHandle (lxp_userdata *xpu, const char *handle) {
115
lua_State *L = xpu->L;
116
if (xpu->state == XPSstring) dischargestring(xpu);
117
if (xpu->state == XPSerror)
118
return 0; /* some error happened before; skip all handles */
119
lua_pushstring(L, handle);
121
if (lua_toboolean(L, -1) == 0) {
125
if (!lua_isfunction(L, -1)) {
126
luaL_error(L, "lxp `%s' callback is not a function", handle);
128
lua_pushvalue(L, 1); /* first argument in every call (self) */
135
** {======================================================
137
** =======================================================
141
static void f_StartCdata (void *ud) {
142
lxp_userdata *xpu = (lxp_userdata *)ud;
143
if (getHandle(xpu, StartCdataKey) == 0) return; /* no handle */
148
static void f_EndCdataKey (void *ud) {
149
lxp_userdata *xpu = (lxp_userdata *)ud;
150
if (getHandle(xpu, EndCdataKey) == 0) return; /* no handle */
155
static void f_CharData (void *ud, const char *s, int len) {
156
lxp_userdata *xpu = (lxp_userdata *)ud;
157
if (xpu->state == XPSok) {
158
if (getHandle(xpu, CharDataKey) == 0) return; /* no handle */
159
xpu->state = XPSstring;
160
luaL_buffinit(xpu->L, xpu->b);
162
if (xpu->state == XPSstring)
163
luaL_addlstring(xpu->b, s, len);
167
static void f_Comment (void *ud, const char *data) {
168
lxp_userdata *xpu = (lxp_userdata *)ud;
169
if (getHandle(xpu, CommentKey) == 0) return; /* no handle */
170
lua_pushstring(xpu->L, data);
175
static void f_Default (void *ud, const char *data, int len) {
176
lxp_userdata *xpu = (lxp_userdata *)ud;
177
if (getHandle(xpu, DefaultKey) == 0) return; /* no handle */
178
lua_pushlstring(xpu->L, data, len);
183
static void f_DefaultExpand (void *ud, const char *data, int len) {
184
lxp_userdata *xpu = (lxp_userdata *)ud;
185
if (getHandle(xpu, DefaultExpandKey) == 0) return; /* no handle */
186
lua_pushlstring(xpu->L, data, len);
191
static void f_StartElement (void *ud, const char *name, const char **attrs) {
192
lxp_userdata *xpu = (lxp_userdata *)ud;
193
lua_State *L = xpu->L;
194
int lastspec = XML_GetSpecifiedAttributeCount(xpu->parser) / 2;
196
if (getHandle(xpu, StartElementKey) == 0) return; /* no handle */
197
lua_pushstring(L, name);
201
lua_pushnumber(L, i++);
202
lua_pushstring(L, *attrs);
205
lua_pushstring(L, *attrs++);
206
lua_pushstring(L, *attrs++);
209
docall(xpu, 2, 0); /* call function with self, name, and attributes */
213
static void f_EndElement (void *ud, const char *name) {
214
lxp_userdata *xpu = (lxp_userdata *)ud;
215
if (getHandle(xpu, EndElementKey) == 0) return; /* no handle */
216
lua_pushstring(xpu->L, name);
221
static int f_ExternaEntity (XML_Parser p, const char *context,
223
const char *systemId,
224
const char *publicId) {
225
lxp_userdata *xpu = (lxp_userdata *)XML_GetUserData(p);
226
lua_State *L = xpu->L;
229
if (getHandle(xpu, ExternalEntityKey) == 0) return 1; /* no handle */
230
child = createlxp(L);
231
child->parser = XML_ExternalEntityParserCreate(p, context, NULL);
233
luaL_error(L, "XML_ParserCreate failed");
234
lua_rawgeti(L, LUA_REGISTRYINDEX, xpu->tableref); /* child uses the same table of its father */
235
child->tableref = luaL_ref(L, LUA_REGISTRYINDEX);
236
lua_pushstring(L, base);
237
lua_pushstring(L, systemId);
238
lua_pushstring(L, publicId);
240
status = lua_toboolean(L, -1);
247
static void f_StartNamespaceDecl (void *ud, const char *prefix,
249
lxp_userdata *xpu = (lxp_userdata *)ud;
250
lua_State *L = xpu->L;
251
if (getHandle(xpu, StartNamespaceDeclKey) == 0) return; /* no handle */
252
lua_pushstring(L, prefix);
253
lua_pushstring(L, uri);
258
static void f_EndNamespaceDecl (void *ud, const char *prefix) {
259
lxp_userdata *xpu = (lxp_userdata *)ud;
260
if (getHandle(xpu, EndNamespaceDeclKey) == 0) return; /* no handle */
261
lua_pushstring(xpu->L, prefix);
266
static void f_NotationDecl (void *ud, const char *notationName,
268
const char *systemId,
269
const char *publicId) {
270
lxp_userdata *xpu = (lxp_userdata *)ud;
271
lua_State *L = xpu->L;
272
if (getHandle(xpu, NotationDeclKey) == 0) return; /* no handle */
273
lua_pushstring(L, notationName);
274
lua_pushstring(L, base);
275
lua_pushstring(L, systemId);
276
lua_pushstring(L, publicId);
281
static int f_NotStandalone (void *ud) {
283
lxp_userdata *xpu = (lxp_userdata *)ud;
284
lua_State *L = xpu->L;
285
if (getHandle(xpu, NotStandaloneKey) == 0) return 1; /* no handle */
287
status = lua_toboolean(L, -1);
293
static void f_ProcessingInstruction (void *ud, const char *target,
295
lxp_userdata *xpu = (lxp_userdata *)ud;
296
lua_State *L = xpu->L;
297
if (getHandle(xpu, ProcessingInstructionKey) == 0) return; /* no handle */
298
lua_pushstring(L, target);
299
lua_pushstring(L, data);
304
static void f_UnparsedEntityDecl (void *ud, const char *entityName,
306
const char *systemId,
307
const char *publicId,
308
const char *notationName) {
309
lxp_userdata *xpu = (lxp_userdata *)ud;
310
lua_State *L = xpu->L;
311
if (getHandle(xpu, UnparsedEntityDeclKey) == 0) return; /* no handle */
312
lua_pushstring(L, entityName);
313
lua_pushstring(L, base);
314
lua_pushstring(L, systemId);
315
lua_pushstring(L, publicId);
316
lua_pushstring(L, notationName);
320
static void f_StartDoctypeDecl (void *ud, const XML_Char *doctypeName,
321
const XML_Char *sysid,
322
const XML_Char *pubid,
323
int has_internal_subset) {
324
lxp_userdata *xpu = (lxp_userdata *)ud;
325
if (getHandle(xpu, StartDoctypeDeclKey) == 0) return; /* no handle */
326
lua_pushstring(xpu->L, doctypeName);
327
lua_pushstring(xpu->L, sysid);
328
lua_pushstring(xpu->L, pubid);
329
lua_pushboolean(xpu->L, has_internal_subset);
333
/* }====================================================== */
337
static int hasfield (lua_State *L, const char *fname) {
339
lua_pushstring(L, fname);
341
res = !lua_isnil(L, -1);
347
static void checkcallbacks (lua_State *L) {
348
static const char *const validkeys[] = {
349
"StartCdataSection", "EndCdataSection", "CharacterData", "Comment",
350
"Default", "DefaultExpand", "StartElement", "EndElement",
351
"ExternalEntityRef", "StartNamespaceDecl", "EndNamespaceDecl",
352
"NotationDecl", "NotStandalone", "ProcessingInstruction",
353
"UnparsedEntityDecl", "StartDoctypeDecl", NULL};
354
if (hasfield(L, "_nonstrict")) return;
356
while (lua_next(L, 1)) {
357
lua_pop(L, 1); /* remove value */
358
#if ! defined (LUA_VERSION_NUM) || LUA_VERSION_NUM < 501
359
if (lua_type(L, -1) != LUA_TSTRING ||
360
luaL_findstring(lua_tostring(L, -1), validkeys) < 0)
361
luaL_error(L, "invalid key `%s' in callback table", lua_tostring(L, -1));
363
luaL_checkoption(L, -1, NULL, validkeys);
369
static int lxp_make_parser (lua_State *L) {
371
char sep = *luaL_optstring(L, 2, "");
372
lxp_userdata *xpu = createlxp(L);
373
p = xpu->parser = (sep == '\0') ? XML_ParserCreate(NULL) :
374
XML_ParserCreateNS(NULL, sep);
376
luaL_error(L, "XML_ParserCreate failed");
377
luaL_checktype(L, 1, LUA_TTABLE);
380
xpu->tableref = luaL_ref(L, LUA_REGISTRYINDEX);
381
XML_SetUserData(p, xpu);
382
if (hasfield(L, StartCdataKey) || hasfield(L, EndCdataKey))
383
XML_SetCdataSectionHandler(p, f_StartCdata, f_EndCdataKey);
384
if (hasfield(L, CharDataKey))
385
XML_SetCharacterDataHandler(p, f_CharData);
386
if (hasfield(L, CommentKey))
387
XML_SetCommentHandler(p, f_Comment);
388
if (hasfield(L, DefaultKey))
389
XML_SetDefaultHandler(p, f_Default);
390
if (hasfield(L, DefaultExpandKey))
391
XML_SetDefaultHandlerExpand(p, f_DefaultExpand);
392
if (hasfield(L, StartElementKey) || hasfield(L, EndElementKey))
393
XML_SetElementHandler(p, f_StartElement, f_EndElement);
394
if (hasfield(L, ExternalEntityKey))
395
XML_SetExternalEntityRefHandler(p, f_ExternaEntity);
396
if (hasfield(L, StartNamespaceDeclKey) || hasfield(L, EndNamespaceDeclKey))
397
XML_SetNamespaceDeclHandler(p, f_StartNamespaceDecl, f_EndNamespaceDecl);
398
if (hasfield(L, NotationDeclKey))
399
XML_SetNotationDeclHandler(p, f_NotationDecl);
400
if (hasfield(L, NotStandaloneKey))
401
XML_SetNotStandaloneHandler(p, f_NotStandalone);
402
if (hasfield(L, ProcessingInstructionKey))
403
XML_SetProcessingInstructionHandler(p, f_ProcessingInstruction);
404
if (hasfield(L, UnparsedEntityDeclKey))
405
XML_SetUnparsedEntityDeclHandler(p, f_UnparsedEntityDecl);
406
if (hasfield(L, StartDoctypeDeclKey))
407
XML_SetStartDoctypeDeclHandler(p, f_StartDoctypeDecl);
412
static lxp_userdata *checkparser (lua_State *L, int idx) {
413
lxp_userdata *xpu = (lxp_userdata *)luaL_checkudata(L, idx, ParserType);
414
luaL_argcheck(L, xpu, idx, "expat parser expected");
415
luaL_argcheck(L, xpu->parser, idx, "parser is closed");
420
static int parser_gc (lua_State *L) {
421
lxp_userdata *xpu = (lxp_userdata *)luaL_checkudata(L, 1, ParserType);
422
luaL_argcheck(L, xpu, 1, "expat parser expected");
428
static int setbase (lua_State *L) {
429
lxp_userdata *xpu = checkparser(L, 1);
430
if (XML_SetBase(xpu->parser, luaL_checkstring(L, 2)) == 0)
431
luaL_error(L, "no memory to store base");
436
static int getbase (lua_State *L) {
437
lxp_userdata *xpu = checkparser(L, 1);
438
lua_pushstring(L, XML_GetBase(xpu->parser));
443
static int getcallbacks (lua_State *L) {
444
lxp_userdata *xpu = checkparser(L, 1);
445
lua_rawgeti(L, LUA_REGISTRYINDEX, xpu->tableref);
450
static int parse_aux (lua_State *L, lxp_userdata *xpu, const char *s,
458
lua_rawgeti(L, LUA_REGISTRYINDEX, xpu->tableref); /* to be used by handlers */
459
status = XML_Parse(xpu->parser, s, (int)len, s == NULL);
460
if (xpu->state == XPSstring) dischargestring(xpu);
461
if (xpu->state == XPSerror) { /* callback error? */
462
lua_rawgeti(L, LUA_REGISTRYINDEX, xpu->tableref); /* get original msg. */
465
if (s == NULL) xpu->state = XPSfinished;
467
lua_pushboolean(L, 1);
471
return reporterror(xpu);
476
static int lxp_parse (lua_State *L) {
477
lxp_userdata *xpu = checkparser(L, 1);
479
const char *s = luaL_optlstring(L, 2, NULL, &len);
480
if (xpu->state == XPSfinished && s != NULL) {
482
lua_pushliteral(L, "cannot parse - document is finished");
485
return parse_aux(L, xpu, s, len);
489
static int lxp_close (lua_State *L) {
491
lxp_userdata *xpu = (lxp_userdata *)luaL_checkudata(L, 1, ParserType);
492
luaL_argcheck(L, xpu, 1, "expat parser expected");
493
if (xpu->state != XPSfinished)
494
status = parse_aux(L, xpu, NULL, 0);
496
if (status > 1) luaL_error(L, "error closing parser: %s",
497
lua_tostring(L, -status+1));
502
static int lxp_pos (lua_State *L) {
503
lxp_userdata *xpu = checkparser(L, 1);
504
XML_Parser p = xpu->parser;
505
lua_pushnumber(L, XML_GetCurrentLineNumber(p));
506
lua_pushnumber(L, XML_GetCurrentColumnNumber(p) + 1);
507
lua_pushnumber(L, XML_GetCurrentByteIndex(p) + 1);
512
static int lxp_setencoding (lua_State *L) {
513
lxp_userdata *xpu = checkparser(L, 1);
514
const char *encoding = luaL_checkstring(L, 2);
515
luaL_argcheck(L, xpu->state == XPSpre, 1, "invalid parser state");
516
XML_SetEncoding(xpu->parser, encoding);
520
static int lxp_stop (lua_State *L) {
521
lxp_userdata *xpu = checkparser(L, 1);
522
lua_pushboolean(L, XML_StopParser(xpu->parser, XML_FALSE) == XML_STATUS_OK);
526
static int lxp_getcurrentbytecount (lua_State* L) {
527
lxp_userdata *xpu = checkparser(L, 1);
528
lua_pushinteger(L, XML_GetCurrentByteCount(xpu->parser));
532
static const struct luaL_Reg lxp_meths[] = {
533
{"parse", lxp_parse},
534
{"close", lxp_close},
537
{"getcurrentbytecount", lxp_getcurrentbytecount},
538
{"setencoding", lxp_setencoding},
539
{"getcallbacks", getcallbacks},
540
{"getbase", getbase},
541
{"setbase", setbase},
546
static const struct luaL_Reg lxp_funcs[] = {
547
{"new", lxp_make_parser},
553
** Assumes the table is on top of the stack.
555
static void set_info (lua_State *L) {
556
lua_pushliteral (L, "_COPYRIGHT");
557
lua_pushliteral (L, "Copyright (C) 2003-2007 Kepler Project");
558
lua_settable (L, -3);
559
lua_pushliteral (L, "_DESCRIPTION");
560
lua_pushliteral (L, "LuaExpat is a SAX XML parser based on the Expat library");
561
lua_settable (L, -3);
562
lua_pushliteral (L, "_VERSION");
563
lua_pushliteral (L, "LuaExpat 1.2.0");
564
lua_settable (L, -3);
568
int luaopen_lxp (lua_State *L) {
569
luaL_newmetatable(L, ParserType);
570
lua_pushliteral(L, "__index");
571
lua_pushvalue(L, -2);
573
luaL_register (L, NULL, lxp_meths);
574
luaL_register (L, "lxp", lxp_funcs);