1 /* © 2013 David Given.
2 * WordGrinder is licensed under the MIT open source license. See the COPYING
3 * file in this distribution for the full text.
4 */
5
6 #include "globals.h"
7 #include <zlib.h>
8 #include "unzip.h"
9 #include "zip.h"
10
11 static const int STACKSIZE = 64;
12
decompress_cb(lua_State * L)13 static int decompress_cb(lua_State* L)
14 {
15 size_t srcsize;
16 const char* srcbuffer = luaL_checklstring(L, 1, &srcsize);
17
18 int outputchunks = 0;
19 uint8_t outputbuffer[64*1024];
20
21 z_stream zs = {0};
22 int i = inflateInit(&zs);
23 if (i != Z_OK)
24 return 0;
25
26 zs.avail_in = srcsize;
27 zs.next_in = (uint8_t*) srcbuffer;
28
29 luaL_checkstack(L, STACKSIZE, "out of memory");
30 do
31 {
32 zs.avail_out = sizeof(outputbuffer);
33 zs.next_out = outputbuffer;
34
35 i = inflate(&zs, Z_NO_FLUSH);
36 switch (i)
37 {
38 case Z_NEED_DICT:
39 case Z_DATA_ERROR:
40 case Z_MEM_ERROR:
41 (void)inflateEnd(&zs);
42 return 0;
43 }
44
45 int have = sizeof(outputbuffer) - zs.avail_out;
46 lua_pushlstring(L, (char*) outputbuffer, have);
47 outputchunks++;
48
49 if (outputchunks == (STACKSIZE-1))
50 {
51 /* Stack full! Concatenate what we've got, to empty the stack, and
52 * keep going. This will only happen on very large input files. */
53 lua_concat(L, outputchunks);
54 outputchunks = 1;
55 }
56 }
57 while (i != Z_STREAM_END);
58
59 (void)inflateEnd(&zs);
60
61 lua_concat(L, outputchunks);
62 return 1;
63 }
64
compress_cb(lua_State * L)65 static int compress_cb(lua_State* L)
66 {
67 size_t srcsize;
68 const char* srcbuffer = luaL_checklstring(L, 1, &srcsize);
69
70 int outputchunks = 0;
71 uint8_t outputbuffer[64*1024];
72
73 z_stream zs = {0};
74 int i = deflateInit(&zs, 1);
75 if (i != Z_OK)
76 return 0;
77
78 zs.avail_in = srcsize;
79 zs.next_in = (uint8_t*) srcbuffer;
80
81 luaL_checkstack(L, STACKSIZE, "out of memory");
82 do
83 {
84 zs.avail_out = sizeof(outputbuffer);
85 zs.next_out = outputbuffer;
86
87 i = deflate(&zs, Z_FINISH);
88
89 int have = sizeof(outputbuffer) - zs.avail_out;
90 lua_pushlstring(L, (char*) outputbuffer, have);
91 outputchunks++;
92
93 if (outputchunks == (STACKSIZE-1))
94 {
95 /* Stack full! Concatenate what we've got, to empty the stack, and
96 * keep going. This will only happen on very large input files. */
97 lua_concat(L, outputchunks);
98 outputchunks = 1;
99 }
100 }
101 while (i != Z_STREAM_END);
102
103 (void)deflateEnd(&zs);
104
105 lua_concat(L, outputchunks);
106 return 1;
107 }
108
readfromzip_cb(lua_State * L)109 static int readfromzip_cb(lua_State* L)
110 {
111 const char* zipname = luaL_checkstring(L, 1);
112 const char* subname = luaL_checkstring(L, 2);
113 int result = 0;
114
115 unzFile zf = unzOpen(zipname);
116 if (zf)
117 {
118 int i = unzLocateFile(zf, subname, 0);
119 if (i == UNZ_OK)
120 {
121 unz_file_info fi;
122 unzGetCurrentFileInfo(zf, &fi,
123 NULL, 0, NULL, 0, NULL, 0);
124
125 char* buffer = malloc(fi.uncompressed_size);
126 if (buffer)
127 {
128 unzOpenCurrentFile(zf);
129 i = unzReadCurrentFile(zf, buffer, fi.uncompressed_size);
130 if (i == fi.uncompressed_size)
131 {
132 lua_pushlstring(L, buffer, fi.uncompressed_size);
133 result = 1;
134 }
135 free(buffer);
136 }
137 }
138
139 unzClose(zf);
140 }
141
142 return result;
143 }
144
writezip_cb(lua_State * L)145 static int writezip_cb(lua_State* L)
146 {
147 const char* zipname = luaL_checkstring(L, 1);
148 luaL_checktype(L, 2, LUA_TTABLE);
149 int result = 0;
150
151 zipFile zf = zipOpen(zipname, APPEND_STATUS_CREATE);
152 if (zf)
153 {
154 result = 1;
155
156 lua_pushnil(L);
157 while (lua_next(L, 2) != 0)
158 {
159 const char* key = lua_tostring(L, -2);
160 size_t valuelen;
161 const char* value = lua_tolstring(L, -1, &valuelen);
162
163 int i = zipOpenNewFileInZip(zf, key, NULL,
164 NULL, 0,
165 NULL, 0,
166 NULL,
167 Z_DEFLATED,
168 Z_DEFAULT_COMPRESSION);
169 if (i != ZIP_OK)
170 {
171 result = 0;
172 break;
173 }
174
175 i = zipWriteInFileInZip(zf, value, valuelen);
176 if (i != ZIP_OK)
177 {
178 result = 0;
179 break;
180 }
181
182 i = zipCloseFileInZip(zf);
183 if (i != ZIP_OK)
184 {
185 result = 0;
186 break;
187 }
188
189 lua_pop(L, 1); /* leave key on stack */
190 }
191
192 zipClose(zf, NULL);
193 }
194
195 if (!result)
196 return 0;
197 lua_pushboolean(L, true);
198 return 1;
199 }
200
zip_init(void)201 void zip_init(void)
202 {
203 const static luaL_Reg funcs[] =
204 {
205 { "compress", compress_cb },
206 { "decompress", decompress_cb },
207 { "readfromzip", readfromzip_cb },
208 { "writezip", writezip_cb },
209 { NULL, NULL }
210 };
211
212 lua_getglobal(L, "wg");
213 luaL_setfuncs(L, funcs, 0);
214 }
215