diff options
| author | frosty <gabriel@bwaaa.monster> | 2026-09-16 16:32:06 -0400 |
|---|---|---|
| committer | frosty <gabriel@bwaaa.monster> | 2026-09-16 16:32:06 -0400 |
| commit | 4c8d764d171ec77ff626cec273c44ec7e7a995fc (patch) | |
| tree | dc36193e611ece53c8f7e74d1dea71f8d9e7004e /src/Dom | |
| download | yapssg-master.tar.gz | |
Diffstat (limited to 'src/Dom')
| -rw-r--r-- | src/Dom/Dom.c | 303 | ||||
| -rw-r--r-- | src/Dom/Dom.h | 10 |
2 files changed, 313 insertions, 0 deletions
diff --git a/src/Dom/Dom.c b/src/Dom/Dom.c new file mode 100644 index 0000000..ede7ddd --- /dev/null +++ b/src/Dom/Dom.c @@ -0,0 +1,303 @@ +#include "Dom/Dom.h" + +#include <lauxlib.h> +#include <limits.h> +#include <stddef.h> +#include <string.h> +#include <strings.h> + +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; +} diff --git a/src/Dom/Dom.h b/src/Dom/Dom.h new file mode 100644 index 0000000..06bd1ff --- /dev/null +++ b/src/Dom/Dom.h @@ -0,0 +1,10 @@ +#ifndef YAPSSG_DOM_H +#define YAPSSG_DOM_H + +#include <libxml/tree.h> +#include <lua.h> + +void dom_push_page(lua_State *lua, const xmlNode *root); +xmlNodePtr dom_page_from_lua(lua_State *lua, int table_index); + +#endif |
