#include "Dom/Dom.h" #include #include #include #include #include static int absolute_index(lua_State *lua, int index) { return index < 0 ? lua_gettop(lua) + index + 1 : index; } static int is_lua_element(const xmlNode *node) { return node->type == XML_ELEMENT_NODE && strcasecmp((const char *)node->name, "lua") == 0; } static void set_string_field(lua_State *lua, int table_index, const char *name, const char *value) { lua_pushstring(lua, value); lua_setfield(lua, table_index, name); } static int has_structured_children(const xmlNode *node) { const xmlNode *child; for (child = node->children; child != NULL; child = child->next) { if (is_lua_element(child)) { continue; } if (child->type != XML_TEXT_NODE && child->type != XML_CDATA_SECTION_NODE) { return 1; } } return 0; } static void push_element(lua_State *lua, const xmlNode *node); static void push_special_node(lua_State *lua, const xmlNode *node) { lua_newtable(lua); if (node->type == XML_COMMENT_NODE) { set_string_field(lua, -2, "kind", "comment"); } else { set_string_field(lua, -2, "kind", "processing_instruction"); set_string_field(lua, -2, "target", (const char *)node->name); } set_string_field(lua, -2, "text", node->content == NULL ? "" : (const char *)node->content); } static void add_child_alias(lua_State *lua, int element_index, const xmlNode *child, int child_index) { const char *name = (const char *)child->name; lua_getfield(lua, element_index, name); if (lua_isnil(lua, -1)) { lua_pop(lua, 1); lua_pushvalue(lua, child_index); lua_setfield(lua, element_index, name); return; } lua_pop(lua, 1); } static void push_children(lua_State *lua, const xmlNode *node, int element_index, int include_text) { const xmlNode *child; int children_index; int array_index = 1; lua_newtable(lua); children_index = lua_gettop(lua); for (child = node->children; child != NULL; child = child->next) { if (is_lua_element(child)) { continue; } if (child->type == XML_ELEMENT_NODE) { int child_index; push_element(lua, child); child_index = lua_gettop(lua); lua_pushvalue(lua, child_index); lua_rawseti(lua, children_index, array_index++); add_child_alias(lua, element_index, child, child_index); lua_pop(lua, 1); } else if (include_text && (child->type == XML_TEXT_NODE || child->type == XML_CDATA_SECTION_NODE)) { lua_pushstring( lua, child->content == NULL ? "" : (const char *)child->content); lua_rawseti(lua, children_index, array_index++); } else if (child->type == XML_COMMENT_NODE || child->type == XML_PI_NODE) { push_special_node(lua, child); lua_rawseti(lua, children_index, array_index++); } } lua_setfield(lua, element_index, "children"); } static void push_attributes(lua_State *lua, const xmlNode *node, int element_index) { const xmlAttr *attribute; lua_newtable(lua); for (attribute = node->properties; attribute != NULL; attribute = attribute->next) { xmlChar *value = xmlNodeListGetString(node->doc, attribute->children, 1); lua_pushstring(lua, value == NULL ? "" : (const char *)value); lua_setfield(lua, -2, (const char *)attribute->name); xmlFree(value); } lua_setfield(lua, element_index, "attributes"); } static void push_text(lua_State *lua, const xmlNode *node, int element_index) { const xmlNode *child; luaL_Buffer buffer; luaL_buffinit(lua, &buffer); for (child = node->children; child != NULL; child = child->next) { if (child->type == XML_TEXT_NODE || child->type == XML_CDATA_SECTION_NODE) { luaL_addstring( &buffer, child->content == NULL ? "" : (const char *)child->content); } } luaL_pushresult(&buffer); lua_setfield(lua, element_index, "text"); } static void push_element(lua_State *lua, const xmlNode *node) { int element_index; int structured_children = has_structured_children(node); lua_newtable(lua); element_index = lua_gettop(lua); set_string_field(lua, element_index, "tag", (const char *)node->name); push_attributes(lua, node, element_index); push_children(lua, node, element_index, structured_children); if (!structured_children) { push_text(lua, node, element_index); } } void dom_push_page(lua_State *lua, const xmlNode *root) { push_element(lua, root); } static xmlNodePtr node_from_table(lua_State *lua, int table_index, int *valid); static int add_attributes(lua_State *lua, int table_index, xmlNodePtr node) { int attributes_index; int valid = 1; lua_getfield(lua, table_index, "attributes"); attributes_index = lua_gettop(lua); if (lua_istable(lua, -1)) { lua_pushnil(lua); while (valid && lua_next(lua, -2) != 0) { if (lua_type(lua, -2) != LUA_TSTRING || lua_type(lua, -1) != LUA_TSTRING || xmlNewProp(node, BAD_CAST lua_tostring(lua, -2), BAD_CAST lua_tostring(lua, -1)) == NULL) { valid = 0; } lua_pop(lua, 1); } } lua_settop(lua, attributes_index - 1); return valid ? 0 : -1; } static int append_lua_child(lua_State *lua, xmlNodePtr parent, int child_index) { xmlNodePtr child = NULL; int valid = 1; if (lua_type(lua, child_index) == LUA_TSTRING) { child = xmlNewText(BAD_CAST lua_tostring(lua, child_index)); } else if (lua_istable(lua, child_index)) { child = node_from_table(lua, child_index, &valid); } else { valid = 0; } if (!valid || child == NULL) { xmlFreeNode(child); return -1; } if (xmlAddChild(parent, child) == NULL) { xmlFreeNode(child); return -1; } return 0; } static int add_children(lua_State *lua, int table_index, xmlNodePtr node) { size_t index; size_t length; int result = 0; lua_getfield(lua, table_index, "children"); length = lua_istable(lua, -1) ? lua_objlen(lua, -1) : 0; if (length > INT_MAX) { lua_pop(lua, 1); return -1; } for (index = 1; result == 0 && index <= length; ++index) { lua_rawgeti(lua, -1, (int)index); result = append_lua_child(lua, node, lua_gettop(lua)); lua_pop(lua, 1); } lua_pop(lua, 1); if (result == 0 && length == 0) { lua_getfield(lua, table_index, "text"); if (lua_type(lua, -1) == LUA_TSTRING) { xmlNodePtr text = xmlNewText(BAD_CAST lua_tostring(lua, -1)); if (text == NULL || xmlAddChild(node, text) == NULL) { xmlFreeNode(text); result = -1; } } lua_pop(lua, 1); } return result; } static xmlNodePtr special_node_from_table(lua_State *lua, int table_index, const char *kind, int *valid) { xmlNodePtr node; const char *text; lua_getfield(lua, table_index, "text"); text = lua_tostring(lua, -1); if (strcmp(kind, "comment") == 0) { node = xmlNewComment(BAD_CAST(text == NULL ? "" : text)); } else if (strcmp(kind, "processing_instruction") == 0) { const char *target; lua_getfield(lua, table_index, "target"); target = lua_tostring(lua, -1); node = target == NULL || target[0] == '\0' ? NULL : xmlNewPI(BAD_CAST target, BAD_CAST(text == NULL ? "" : text)); lua_pop(lua, 1); } else { node = NULL; } lua_pop(lua, 1); if (node == NULL) { *valid = 0; } return node; } static xmlNodePtr node_from_table(lua_State *lua, int table_index, int *valid) { const char *kind; const char *tag; xmlNodePtr node; table_index = absolute_index(lua, table_index); lua_getfield(lua, table_index, "kind"); kind = lua_tostring(lua, -1); if (kind != NULL) { node = special_node_from_table(lua, table_index, kind, valid); lua_pop(lua, 1); return node; } lua_pop(lua, 1); lua_getfield(lua, table_index, "tag"); tag = lua_tostring(lua, -1); node = tag == NULL || tag[0] == '\0' ? NULL : xmlNewNode(NULL, BAD_CAST tag); lua_pop(lua, 1); if (node == NULL || add_attributes(lua, table_index, node) != 0 || add_children(lua, table_index, node) != 0) { xmlFreeNode(node); *valid = 0; return NULL; } return node; } xmlNodePtr dom_page_from_lua(lua_State *lua, int table_index) { int valid = 1; xmlNodePtr root = node_from_table(lua, table_index, &valid); if (!valid) { xmlFreeNode(root); return NULL; } return root; }