From c2b02eb36944f042034973d4fe3afa0a74937cb7 Mon Sep 17 00:00:00 2001 From: Alexandr Smolnikov Date: Tue, 15 Oct 2019 12:57:44 +0500 Subject: [PATCH 1/3] Add new routine open_memory to create a zip in memory --- README.txt | 17 +++++++ lua_zip.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 149 insertions(+), 2 deletions(-) diff --git a/README.txt b/README.txt index 3e73afa..5726922 100644 --- a/README.txt +++ b/README.txt @@ -94,6 +94,23 @@ local zip_arc = zip.open(filename [, flags]) If an error occurs, returns nil plus an error message. +local zip_arc = zip.open_memory() +local zip_arc = zip.open_memory("file", filename [, flags]) +local zip_arc = zip.open_memory("string", str [, flags]) + Make zip in memory. + Fill it up with a zip file or a string content (which must be a zip) + Optionally specify a bitwise flags (see zip.open) + + zip_arc:close() will return zip content. + --- + local arc = zip.open_memory("file", "template.odt") + local icon = arc:name_locate"content.xml" + arc:replace(icon, "string", newcontent) + local zdata = arc:close() + send_over_network(zdata) + --- + + zip_arc:close() If any files within were changed, those changes are written to diff --git a/lua_zip.c b/lua_zip.c index 6570595..80583d9 100644 --- a/lua_zip.c +++ b/lua_zip.c @@ -87,6 +87,103 @@ static int S_push_error(lua_State* L, int zip_error, int sys_error) { return zip_error; } +//open_memory("file", filename[, flags]) +//open_memory("string", str[, flags]) +//open_memory([flags]) +static int S_archive_open_memory(lua_State* L) { + const char* path = 0; + struct zip** ar = 0; + zip_source_t *zsmem = 0; + zip_error_t error; + int flags = 0; + const char * buf = 0; + size_t buf_sz = 0; + const char* errmsg = 0; + + size_t ln; + path = lua_tolstring(L, 1, &ln); + if (path && ln==4 && path[0]=='f' && path[1]=='i' && path[2]=='l' && path[3]=='e'){ + path = lua_tostring(L, 2); + flags = lua_tointeger(L, 3); + }else if (path && ln==6 && path[0]=='s' && path[1]=='t' && path[2]=='r' && path[3]=='i'){ + buf = lua_tolstring(L, 2, &buf_sz); + path = 0; + if (!buf || !buf_sz) errmsg = "invalid argument"; + flags = lua_tointeger(L, 3); + }else{ + flags = lua_tointeger(L, 1); + } + + if (!errmsg && path) { + FILE *fp = fopen(path, "rb"); + if (!fp) errmsg = strerror(errno); //FIXME: strerror() is not thread-safe + else{ + fseek(fp, 0, SEEK_END); + buf_sz = ftell(fp); + fseek(fp, 0, SEEK_SET); + //char *data = (char*)malloc(buf_sz); + char *data = (char*)lua_newuserdata(L, buf_sz); + size_t was_read = fread(data, 1, buf_sz, fp); + buf = (const char*) data; + fclose(fp); + } + }else if (!buf) { + flags |= ZIP_TRUNCATE; + } + + if (!errmsg && !(zsmem = zip_source_buffer_create(0, 0, 0, &error))) + errmsg = zip_error_strerror(&error); + + if (!errmsg && buf){ + zip_source_begin_write(zsmem); + zip_source_write(zsmem, buf, buf_sz); + zip_source_commit_write(zsmem); + } + + if (!errmsg){ + assert(zsmem); + zip_source_keep(zsmem); //zip_close frees source, so add ref + ar = (struct zip**)lua_newuserdata(L, sizeof(struct zip*)); + *ar = zip_open_from_source(zsmem, flags, &error); + if (!*ar) errmsg = zip_error_strerror(&error); + } + + if (errmsg){ + lua_pushnil(L); + lua_pushstring(L, errmsg); + return 2; + } + + assert(lua_isuserdata(L, -1)); + lua_newtable(L); + + lua_pushlightuserdata(L, zsmem); + lua_setfield(L, -2, "__source_memory"); + + luaL_getmetatable(L, ARCHIVE_MT); + assert(!lua_isnil(L, -1)/* ARCHIVE_MT found? */); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, S_archive_gc); + lua_setfield(L, -2, "__gc"); + + lua_pushcfunction(L, S_archive_get_num_files); + lua_setfield(L, -2, "__len"); + + /* Each archive has a weak table of objects that are invalidated + * when the archive is closed or garbage collected. + */ + lua_newtable(L); + luaL_getmetatable(L, WEAK_MT); + assert(!lua_isnil(L, -1)/* WEAK_MT found? */); + lua_setmetatable(L, -2); + lua_setfield(L, -2, "refs"); + + lua_setmetatable(L, -2); + + return 1; +} + static int S_archive_open(lua_State* L) { const char* path = luaL_checkstring(L, 1); int flags = (lua_gettop(L) < 2) ? 0 : luaL_checkint(L, 2); @@ -201,6 +298,11 @@ static int S_archive_close(lua_State* L) { if ( ! ar ) return 0; + lua_getmetatable(L, 1); + lua_getfield(L, -1, "__source_memory"); + zip_source_t *zsmem = (zip_source_t*)lua_touserdata(L, -1); + if (!lua_islightuserdata(L, -1)) zsmem = 0; + S_archive_gc_refs(L, 1); err = zip_close(ar); @@ -209,6 +311,27 @@ static int S_archive_close(lua_State* L) { lua_error(L); } + if (zsmem){ + int rc = zip_source_open(zsmem); + if (rc){ + zip_error_t* err = zip_source_error(zsmem); + lua_pushstring(L, zip_error_strerror(err)); + return 1; + } + zip_source_seek(zsmem, 0, SEEK_END); + zip_int64_t sz = zip_source_tell(zsmem); + zip_source_seek(zsmem, 0, SEEK_SET); + + char *ptr = (char*)lua_newuserdata(L, sz); + zip_source_read(zsmem, ptr, sz); + + zip_source_close(zsmem); + + zip_source_free(zsmem); + + lua_pushlstring(L, ptr, sz); + return 1; + } return 0; } @@ -220,11 +343,17 @@ static int S_archive_gc(lua_State* L) { if ( ! ar ) return 0; + lua_getmetatable(L, 1); + lua_getfield(L, -1, "__source_memory"); + zip_source_t *zsmem = (zip_source_t*)lua_touserdata(L, -1); + if (!lua_islightuserdata(L, -1)) zsmem = 0; + S_archive_gc_refs(L, 1); zip_unchange_all(ar); zip_close(ar); + if (zsmem) zip_source_free(zsmem); return 0; } @@ -790,8 +919,9 @@ static int S_OR(lua_State* L) { LUALIB_API int luaopen_brimworks_zip(lua_State* L) { static luaL_Reg fns[] = { - { "open", S_archive_open }, - { "OR", S_OR }, + { "open_memory", S_archive_open_memory }, + { "open", S_archive_open }, + { "OR", S_OR }, { NULL, NULL } }; From 2d84ca5e86a97c1a3531087fbe1f2e83d7c64505 Mon Sep 17 00:00:00 2001 From: Alexandr Smolnikov Date: Tue, 15 Oct 2019 13:02:59 +0500 Subject: [PATCH 2/3] Add note to README --- README.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/README.txt b/README.txt index 5726922..88274af 100644 --- a/README.txt +++ b/README.txt @@ -110,6 +110,7 @@ local zip_arc = zip.open_memory("string", str [, flags]) send_over_network(zdata) --- + If an error occurs, returns nil plus an error message. zip_arc:close() From 538137b7c29f2480e93c23965d688aa2c19d48b9 Mon Sep 17 00:00:00 2001 From: Alexandr Smolnikov Date: Tue, 15 Oct 2019 15:11:47 +0500 Subject: [PATCH 3/3] Clean up; using thread-safe strerror_r instead of strerror --- lua_zip.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lua_zip.c b/lua_zip.c index 80583d9..15c18df 100644 --- a/lua_zip.c +++ b/lua_zip.c @@ -91,14 +91,14 @@ static int S_push_error(lua_State* L, int zip_error, int sys_error) { //open_memory("string", str[, flags]) //open_memory([flags]) static int S_archive_open_memory(lua_State* L) { - const char* path = 0; - struct zip** ar = 0; - zip_source_t *zsmem = 0; + const char* path = NULL; + struct zip** ar = NULL; + zip_source_t *zsmem = NULL; zip_error_t error; int flags = 0; - const char * buf = 0; - size_t buf_sz = 0; - const char* errmsg = 0; + const char * buf = NULL; + size_t buf_sz = 0; + const char* errmsg = NULL; size_t ln; path = lua_tolstring(L, 1, &ln); @@ -116,12 +116,16 @@ static int S_archive_open_memory(lua_State* L) { if (!errmsg && path) { FILE *fp = fopen(path, "rb"); - if (!fp) errmsg = strerror(errno); //FIXME: strerror() is not thread-safe + if (!fp) { + //Lua will clean up stack on return + char *ptr = lua_newuserdata(L, 1024); + strerror_r(errno, ptr, 1024); + errmsg = (const char*)ptr; + } else{ fseek(fp, 0, SEEK_END); buf_sz = ftell(fp); fseek(fp, 0, SEEK_SET); - //char *data = (char*)malloc(buf_sz); char *data = (char*)lua_newuserdata(L, buf_sz); size_t was_read = fread(data, 1, buf_sz, fp); buf = (const char*) data; @@ -301,7 +305,7 @@ static int S_archive_close(lua_State* L) { lua_getmetatable(L, 1); lua_getfield(L, -1, "__source_memory"); zip_source_t *zsmem = (zip_source_t*)lua_touserdata(L, -1); - if (!lua_islightuserdata(L, -1)) zsmem = 0; + if (!lua_islightuserdata(L, -1)) zsmem = NULL; S_archive_gc_refs(L, 1); @@ -346,7 +350,7 @@ static int S_archive_gc(lua_State* L) { lua_getmetatable(L, 1); lua_getfield(L, -1, "__source_memory"); zip_source_t *zsmem = (zip_source_t*)lua_touserdata(L, -1); - if (!lua_islightuserdata(L, -1)) zsmem = 0; + if (!lua_islightuserdata(L, -1)) zsmem = NULL; S_archive_gc_refs(L, 1);