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