diff options
Diffstat (limited to 'src/LuaEngine/LuaEngine.c')
| -rw-r--r-- | src/LuaEngine/LuaEngine.c | 124 |
1 files changed, 123 insertions, 1 deletions
diff --git a/src/LuaEngine/LuaEngine.c b/src/LuaEngine/LuaEngine.c index c34fd68..af30403 100644 --- a/src/LuaEngine/LuaEngine.c +++ b/src/LuaEngine/LuaEngine.c @@ -143,6 +143,121 @@ static char *make_chunk_name(const char *input_path, unsigned long line) { return chunk_name; } +typedef struct { + htmlDocPtr source; + EmitList *emits; +} EmitContext; + +static int grow_emit_list(EmitList *emits) { + size_t larger_capacity; + EmittedPage *larger_items; + + if (emits->capacity > SIZE_MAX / (2 * sizeof(*emits->items))) { + return -1; + } + + larger_capacity = emits->capacity == 0 ? 4 : emits->capacity * 2; + larger_items = + realloc(emits->items, larger_capacity * sizeof(*emits->items)); + if (larger_items == NULL) { + return -1; + } + + emits->items = larger_items; + emits->capacity = larger_capacity; + return 0; +} + +static int append_emit(EmitList *emits, const char *path, + htmlDocPtr document) { + EmittedPage *emitted; + + if (emits->length == emits->capacity && grow_emit_list(emits) != 0) { + return -1; + } + + emitted = &emits->items[emits->length]; + emitted->path = duplicate_string(path); + if (emitted->path == NULL) { + return -1; + } + + emitted->document = document; + ++emits->length; + return 0; +} + +void emit_list_free(EmitList *emits) { + size_t index; + + for (index = 0; index < emits->length; ++index) { + free(emits->items[index].path); + xmlFreeDoc(emits->items[index].document); + } + free(emits->items); + emits->items = NULL; + emits->length = 0; + emits->capacity = 0; +} + +static htmlDocPtr snapshot_document(htmlDocPtr source, lua_State *lua) { + htmlDocPtr copy; + xmlDtdPtr dtd; + xmlNodePtr root; + + lua_getglobal(lua, "page"); + root = lua_istable(lua, -1) ? dom_page_from_lua(lua, lua_gettop(lua)) : NULL; + lua_pop(lua, 1); + if (root == NULL) { + return NULL; + } + + copy = xmlNewDoc(BAD_CAST "1.0"); + if (copy == NULL) { + xmlFreeNode(root); + return NULL; + } + if (source->encoding != NULL) { + copy->encoding = xmlStrdup(source->encoding); + } + + dtd = xmlGetIntSubset(source); + if (dtd != NULL) { + dtd = xmlCopyDtd(dtd); + if (dtd != NULL) { + xmlAddChild((xmlNodePtr)copy, (xmlNodePtr)dtd); + copy->intSubset = dtd; + } + } + + xmlAddChild((xmlNodePtr)copy, root); + return copy; +} + +static int page_emit(lua_State *lua) { + EmitContext *context; + const char *path; + htmlDocPtr document; + + luaL_checktype(lua, 1, LUA_TTABLE); + path = luaL_checkstring(lua, 2); + if (path[0] == '\0') { + return luaL_error(lua, "emit path cannot be empty"); + } + context = lua_touserdata(lua, lua_upvalueindex(1)); + + document = snapshot_document(context->source, lua); + if (document == NULL) { + return luaL_error(lua, "unable to snapshot the current page"); + } + + if (append_emit(context->emits, path, document) != 0) { + xmlFreeDoc(document); + return luaL_error(lua, "unable to record emitted page"); + } + return 0; +} + static int run_script(lua_State *lua, const LuaScript *script, const char *input_path) { char *source_path = NULL; @@ -228,7 +343,9 @@ static int replace_document_root(lua_State *lua, htmlDocPtr document, return 0; } -int process_html_document(htmlDocPtr document, const char *input_path) { +int process_html_document(htmlDocPtr document, const char *input_path, + EmitList *emits) { + EmitContext context = {document, emits}; xmlNodePtr root = xmlDocGetRootElement(document); ScriptList scripts = {0}; lua_State *lua; @@ -262,6 +379,11 @@ int process_html_document(htmlDocPtr document, const char *input_path) { directory_walker_register(lua, input_path); dom_push_page(lua, root); lua_setglobal(lua, "page"); + lua_getglobal(lua, "page"); + lua_pushlightuserdata(lua, &context); + lua_pushcclosure(lua, page_emit, 1); + lua_setfield(lua, -2, "emit"); + lua_pop(lua, 1); result = run_scripts(lua, &scripts, input_path); if (result == 0) { |
