1 /*
2  LuaZip - Reading files inside zip files.
3  http://www.keplerproject.org/luazip/
4 
5  Author: Danilo Tuler
6  Copyright (c) 2003-2006 Kepler Project
7 
8  $Id: luazip.c,v 1.9 2006/03/23 20:44:53 carregal Exp $
9 */
10 
11 #include <string.h>
12 #include <stdlib.h>
13 #include "zzip/zzip.h"
14 #include "luazip.h"
15 #include "lauxlib.h"
16 
17 #define ZIPFILEHANDLE    "lzip.File"
18 #define ZIPINTERNALFILEHANDLE  "lzip.InternalFile"
19 #define LUAZIP_MAX_EXTENSIONS 32
20 
pushresult(lua_State * L,int i,const char * filename)21 static int pushresult (lua_State *L, int i, const char *filename) {
22   if (i) {
23     lua_pushboolean(L, 1);
24     return 1;
25   }
26   else {
27     lua_pushnil(L);
28     if (filename)
29       lua_pushfstring(L, "%s: %s", filename, zzip_strerror(zzip_errno(errno)));
30     else
31       lua_pushfstring(L, "%s", zzip_strerror(zzip_errno(errno)));
32     lua_pushnumber(L, zzip_errno(errno));
33     return 3;
34   }
35 }
36 
topfile(lua_State * L,int findex)37 static ZZIP_DIR** topfile (lua_State *L, int findex) {
38   ZZIP_DIR** f = (ZZIP_DIR**)luaL_checkudata(L, findex, ZIPFILEHANDLE);
39   if (f == NULL) luaL_argerror(L, findex, "bad zip file");
40   return f;
41 }
42 
tofile(lua_State * L,int findex)43 static ZZIP_DIR* tofile (lua_State *L, int findex) {
44   ZZIP_DIR** f = topfile(L, findex);
45   if (*f == NULL)
46     luaL_error(L, "attempt to use a closed zip file");
47   return *f;
48 }
49 
topinternalfile(lua_State * L,int findex)50 static ZZIP_FILE** topinternalfile (lua_State *L, int findex) {
51   ZZIP_FILE** f = (ZZIP_FILE**)luaL_checkudata(L, findex, ZIPINTERNALFILEHANDLE);
52   if (f == NULL) luaL_argerror(L, findex, "bad zip file");
53   return f;
54 }
55 
tointernalfile(lua_State * L,int findex)56 static ZZIP_FILE* tointernalfile (lua_State *L, int findex) {
57   ZZIP_FILE** f = topinternalfile(L, findex);
58   if (*f == NULL)
59     luaL_error(L, "attempt to use a closed zip file");
60   return *f;
61 }
62 
63 /*
64 ** When creating file handles, always creates a `closed' file handle
65 ** before opening the actual file; so, if there is a memory error, the
66 ** file is not left opened.
67 */
newfile(lua_State * L)68 static ZZIP_DIR** newfile (lua_State *L) {
69   ZZIP_DIR** pf = (ZZIP_DIR**)lua_newuserdata(L, sizeof(ZZIP_DIR*));
70   *pf = NULL;  /* file handle is currently `closed' */
71   luaL_getmetatable(L, ZIPFILEHANDLE);
72   lua_setmetatable(L, -2);
73   return pf;
74 }
75 
newinternalfile(lua_State * L)76 static ZZIP_FILE** newinternalfile (lua_State *L) {
77   ZZIP_FILE** pf = (ZZIP_FILE**)lua_newuserdata(L, sizeof(ZZIP_FILE*));
78   *pf = NULL;  /* file handle is currently `closed' */
79   luaL_getmetatable(L, ZIPINTERNALFILEHANDLE);
80   lua_setmetatable(L, -2);
81   return pf;
82 }
83 
84 
zip_open(lua_State * L)85 static int zip_open (lua_State *L) {
86   const char *zipfilename = luaL_checkstring(L, 1);
87   /*const char *mode = luaL_optstring(L, 2, "r");*/
88 
89   ZZIP_DIR** pf = newfile(L);
90   *pf = zzip_dir_open(zipfilename, 0);
91   if (*pf == NULL)
92   {
93     lua_pushnil(L);
94     lua_pushfstring(L, "could not open file `%s'", zipfilename);
95     return 2;
96   }
97   return 1;
98 }
99 
100 
zip_close(lua_State * L)101 static int zip_close (lua_State *L) {
102   ZZIP_DIR* f = tofile(L, 1);
103   if ( zzip_closedir(f) == 0 )
104     {
105       *(ZZIP_DIR**)lua_touserdata(L, 1) = NULL; /* mark file as close */
106       lua_pushboolean(L, 1);
107     }
108     else {
109       lua_pushboolean(L, 0);
110     }
111   return 1;
112 }
113 
f_open(lua_State * L)114 static int f_open (lua_State *L) {
115   ZZIP_DIR* uf = tofile(L, 1);
116   const char *filename = luaL_checkstring(L, 2);
117   /*const char *mode = luaL_optstring(L, 3, "r");*/
118   ZZIP_FILE** inf = newinternalfile(L);
119 
120   *inf = zzip_file_open(uf, filename, 0);
121   if (*inf)
122     return 1;
123 
124   lua_pushnil(L);
125   lua_pushfstring(L, "could not open file `%s'", filename);
126   return 2;
127 }
128 
129 /*
130 
131 */
zip_openfile(lua_State * L)132 static int zip_openfile (lua_State *L) {
133   ZZIP_FILE** inf;
134 
135   const char * ext2[LUAZIP_MAX_EXTENSIONS+1];
136   zzip_strings_t *ext = ext2;
137 
138   const char *filename = luaL_checkstring(L, 1);
139   /*const char *mode = luaL_optstring(L, 2, "r");*/
140 
141   inf = newinternalfile(L);
142 
143   if (lua_isstring(L, 2))
144   {
145     /* creates a table with the string as the first and only (numerical) element */
146     lua_newtable(L);
147     lua_pushvalue(L, 2);
148     lua_rawseti(L, -2, 1);
149 
150     /* replaces the string by the table with the string inside */
151     lua_replace(L, 2);
152   }
153 
154   if (lua_istable(L, 2))
155   {
156     int i, m, n;
157 
158     /* how many extension were specified? */
159     n = lua_rawlen(L, 2);
160 
161     if (n > LUAZIP_MAX_EXTENSIONS)
162     {
163       luaL_error(L, "too many extensions specified");
164     }
165 
166     for (i = 0, m = 0; i < n; i++)
167     {
168       lua_rawgeti(L, 2, i+1);
169       if (lua_isstring(L, -1))
170       {
171         /* luazip specifies "zip" as the extension, but zziplib expects ".zip" */
172         lua_pushstring(L, ".");
173         lua_insert(L, -2);
174         lua_concat(L, 2);
175 
176         ext2[m] = lua_tostring(L, -1);
177         m++;
178       }
179       lua_pop(L, 1);
180     }
181     ext2[m] = 0;
182 
183     *inf = zzip_open_ext_io(filename, 0, 0664, ext, 0);
184   }
185   else
186   {
187     *inf = zzip_open(filename, 0);
188   }
189 
190   if (*inf)
191     return 1;
192 
193   lua_pushnil(L);
194   lua_pushfstring(L, "could not open file `%s'", filename);
195   return 2;
196 }
197 
zip_type(lua_State * L)198 static int zip_type (lua_State *L) {
199   ZZIP_DIR** f = (ZZIP_DIR**)luaL_checkudata(L, 1, ZIPFILEHANDLE);
200   if (f == NULL) lua_pushnil(L);
201   else if (*f == NULL)
202     lua_pushliteral(L, "closed zip file");
203   else
204     lua_pushliteral(L, "zip file");
205   return 1;
206 }
207 
zip_tostring(lua_State * L)208 static int zip_tostring (lua_State *L) {
209   char buff[32];
210   ZZIP_DIR** f = topfile(L, 1);
211   if (*f == NULL)
212     strcpy(buff, "closed");
213   else
214     sprintf(buff, "%p", lua_touserdata(L, 1));
215   lua_pushfstring(L, "zip file (%s)", buff);
216   return 1;
217 }
218 
ff_tostring(lua_State * L)219 static int ff_tostring (lua_State *L) {
220   char buff[32];
221   ZZIP_FILE** f = topinternalfile(L, 1);
222   if (*f == NULL)
223     strcpy(buff, "closed");
224   else
225     sprintf(buff, "%p", lua_touserdata(L, 1));
226   lua_pushfstring(L, "file in zip file (%s)", buff);
227   return 1;
228 }
229 
zip_gc(lua_State * L)230 static int zip_gc (lua_State *L) {
231   ZZIP_DIR**f = topfile(L, 1);
232   if (*f != NULL)  /* ignore closed files */
233     zip_close(L);
234   return 0;
235 }
236 
zip_readfile(lua_State * L)237 static int zip_readfile (lua_State *L) {
238   ZZIP_DIRENT* ent = NULL;
239   ZZIP_DIR* uf = NULL;
240 
241   uf = *(ZZIP_DIR**)lua_touserdata(L, lua_upvalueindex(1));
242   if (uf == NULL)  /* file is already closed? */
243     luaL_error(L, "file is already closed");
244 
245   ent = zzip_readdir(uf);
246 
247   if (ent == NULL)
248     return 0;
249 
250   lua_newtable(L);
251   lua_pushstring(L, "compressed_size"); lua_pushnumber(L, ent->d_csize); lua_settable(L, -3);
252   lua_pushstring(L, "compression_method"); lua_pushnumber(L, ent->d_compr); lua_settable(L, -3);
253   lua_pushstring(L, "uncompressed_size"); lua_pushnumber(L, ent->st_size); lua_settable(L, -3);
254   lua_pushstring(L, "filename"); lua_pushstring(L, ent->d_name); lua_settable(L, -3);
255 
256   return 1;
257 }
258 
f_files(lua_State * L)259 static int f_files (lua_State *L) {
260   ZZIP_DIR *f = tofile(L, 1);
261   zzip_rewinddir(f);
262   lua_pushliteral(L, ZIPFILEHANDLE);
263   lua_rawget(L, LUA_REGISTRYINDEX);
264   lua_pushcclosure(L, zip_readfile, 2);
265   return 1;
266 }
267 
aux_close(lua_State * L)268 static int aux_close (lua_State *L) {
269   ZZIP_FILE *f = tointernalfile(L, 1);
270   int ok = (zzip_fclose(f) == 0);
271   if (ok)
272     *(ZZIP_FILE **)lua_touserdata(L, 1) = NULL;  /* mark file as closed */
273   return ok;
274 }
275 
ff_close(lua_State * L)276 static int ff_close (lua_State *L) {
277   return pushresult(L, aux_close(L), NULL);
278 }
279 
ff_gc(lua_State * L)280 static int ff_gc (lua_State *L) {
281   ZZIP_FILE**f = topinternalfile(L, 1);
282   if (*f != NULL)  /* ignore closed files */
283     aux_close(L);
284   return 0;
285 }
286 
zzip_getc(ZZIP_FILE * f)287 static int zzip_getc (ZZIP_FILE *f)
288 {
289   char c;
290   return (zzip_fread(&c, sizeof(char), 1, f) == 0) ? EOF : (int)c;
291 }
292 
zzip_fgets(char * str,int size,ZZIP_FILE * stream)293 static char* zzip_fgets(char *str, int size, ZZIP_FILE *stream)
294 {
295   int c, i;
296 
297 	for (i = 0; i < size-1; i++)
298 	{
299     c = zzip_getc(stream);
300 		if (EOF == c)
301 			return NULL;
302 		str[i]=c;
303 		if (('\n' == c)/* || ('\r' == c)*/)
304 		{
305       str[i++]='\n';
306 			break;
307 		}
308 	}
309 	str[i] = '\0';
310 
311 	return str;
312 }
313 
314 /* no support to read numbers
315 static int zzip_fscanf (ZZIP_FILE *f, const char *format, ...)
316 {
317   // TODO
318   return 0;
319 }
320 
321 static int read_number (lua_State *L, ZZIP_FILE *f) {
322   lua_Number d;
323   if (zzip_fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
324     lua_pushnumber(L, d);
325     return 1;
326   }
327   else return 0;  // read fails
328 }
329 */
330 
test_eof(lua_State * L,ZZIP_FILE * f)331 static int test_eof (lua_State *L, ZZIP_FILE *f) {
332   /* TODO */
333 	(void) L;
334 	(void) f;
335   return 1;
336 }
337 
read_line(lua_State * L,ZZIP_FILE * f)338 static int read_line (lua_State *L, ZZIP_FILE *f) {
339   luaL_Buffer b;
340   luaL_buffinit(L, &b);
341   for (;;) {
342     size_t l;
343     char *p = luaL_prepbuffer(&b);
344     if (zzip_fgets(p, LUAL_BUFFERSIZE, f) == NULL) {  /* eof? */
345       luaL_pushresult(&b);  /* close buffer */
346       return (lua_rawlen(L, -1) > 0);  /* check whether read something */
347     }
348     l = strlen(p);
349     if (p[l-1] != '\n')
350       luaL_addsize(&b, l);
351     else {
352       luaL_addsize(&b, l - 1);  /* do not include `eol' */
353       luaL_pushresult(&b);  /* close buffer */
354       return 1;  /* read at least an `eol' */
355     }
356   }
357 }
358 
read_chars(lua_State * L,ZZIP_FILE * f,size_t n)359 static int read_chars (lua_State *L, ZZIP_FILE *f, size_t n) {
360   size_t rlen;  /* how much to read */
361   size_t nr;  /* number of chars actually read */
362   luaL_Buffer b;
363   luaL_buffinit(L, &b);
364   rlen = LUAL_BUFFERSIZE;  /* try to read that much each time */
365   do {
366     char *p = luaL_prepbuffer(&b);
367     if (rlen > n) rlen = n;  /* cannot read more than asked */
368     nr = zzip_fread(p, sizeof(char), rlen, f);
369     luaL_addsize(&b, nr);
370     n -= nr;  /* still have to read `n' chars */
371   } while (n > 0 && nr == rlen);  /* until end of count or eof */
372   luaL_pushresult(&b);  /* close buffer */
373   return (n == 0 || lua_rawlen(L, -1) > 0);
374 }
375 
g_read(lua_State * L,ZZIP_FILE * f,int first)376 static int g_read (lua_State *L, ZZIP_FILE *f, int first) {
377   int nargs = lua_gettop(L) - 1;
378   int success;
379   int n;
380   if (nargs == 0) {  /* no arguments? */
381     success = read_line(L, f);
382     n = first+1;  /* to return 1 result */
383   }
384   else {  /* ensure stack space for all results and for auxlib's buffer */
385     luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
386     success = 1;
387     for (n = first; nargs-- && success; n++) {
388       if (lua_type(L, n) == LUA_TNUMBER) {
389         size_t l = (size_t)lua_tonumber(L, n);
390         success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
391       }
392       else {
393         const char *p = lua_tostring(L, n);
394         luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
395         switch (p[1]) {
396           case 'l':  /* line */
397             success = read_line(L, f);
398             break;
399           case 'a':  /* file */
400             read_chars(L, f, ~((size_t)0));  /* read MAX_SIZE_T chars */
401             success = 1; /* always success */
402             break;
403           default:
404             return luaL_argerror(L, n, "invalid format");
405         }
406       }
407     }
408   }
409   if (!success) {
410     lua_pop(L, 1);  /* remove last result */
411     lua_pushnil(L);  /* push nil instead */
412   }
413   return n - first;
414 }
415 
ff_read(lua_State * L)416 static int ff_read (lua_State *L) {
417   return g_read(L, tointernalfile(L, 1), 2);
418 }
419 
420 static int zip_readline (lua_State *L);
421 
aux_lines(lua_State * L,int idx,int close)422 static void aux_lines (lua_State *L, int idx, int close) {
423   lua_pushliteral(L, ZIPINTERNALFILEHANDLE);
424   lua_rawget(L, LUA_REGISTRYINDEX);
425   lua_pushvalue(L, idx);
426   lua_pushboolean(L, close);  /* close/not close file when finished */
427   lua_pushcclosure(L, zip_readline, 3);
428 }
429 
ff_lines(lua_State * L)430 static int ff_lines (lua_State *L) {
431   tointernalfile(L, 1);  /* check that it's a valid file handle */
432   aux_lines(L, 1, 0);
433   return 1;
434 }
435 
zip_readline(lua_State * L)436 static int zip_readline (lua_State *L) {
437   ZZIP_FILE *f = *(ZZIP_FILE **)lua_touserdata(L, lua_upvalueindex(2));
438   if (f == NULL)  /* file is already closed? */
439     luaL_error(L, "file is already closed");
440   if (read_line(L, f)) return 1;
441   else {  /* EOF */
442     if (lua_toboolean(L, lua_upvalueindex(3))) {  /* generator created file? */
443       lua_settop(L, 0);
444       lua_pushvalue(L, lua_upvalueindex(2));
445       aux_close(L);  /* close it */
446     }
447     return 0;
448   }
449 }
450 
ff_seek(lua_State * L)451 static int ff_seek (lua_State *L) {
452   static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
453   static const char *const modenames[] = {"set", "cur", "end", NULL};
454   ZZIP_FILE *f = tointernalfile(L, 1);
455   int op = luaL_checkoption(L, 2, "cur", modenames);
456   long offset = luaL_optlong(L, 3, 0);
457   luaL_argcheck(L, op != -1, 2, "invalid mode");
458   op = zzip_seek(f, offset, mode[op]);
459   if (op < 0)
460     return pushresult(L, 0, NULL);  /* error */
461   else {
462     lua_pushnumber(L, zzip_tell(f));
463     return 1;
464   }
465 }
466 
467 static const luaL_Reg ziplib[] = {
468   {"open", zip_open},
469   {"close", zip_close},
470   {"type", zip_type},
471   /* {"files", io_files},*/
472   {"openfile", zip_openfile},
473   {NULL, NULL}
474 };
475 
476 static const luaL_Reg flib[] = {
477   {"open", f_open},
478   {"close", zip_close},
479   {"files", f_files},
480   {"__gc", zip_gc},
481   {"__tostring", zip_tostring},
482   {NULL, NULL}
483 };
484 
485 static const luaL_Reg fflib[] = {
486   {"read", ff_read},
487   {"close", ff_close},
488   {"seek", ff_seek},
489   {"lines", ff_lines},
490   {"__gc", ff_gc},
491   {"__tostring", ff_tostring},
492 /*  {"flush", ff_flush},
493   {"write", ff_write},*/
494   {NULL, NULL}
495 };
496 
497 
498 /*
499 ** Assumes the table is on top of the stack.
500 */
set_info(lua_State * L)501 static void set_info (lua_State *L) {
502 	lua_pushliteral (L, "_COPYRIGHT");
503 	lua_pushliteral (L, "Copyright (C) 2003-2006 Kepler Project");
504 	lua_settable (L, -3);
505 	lua_pushliteral (L, "_DESCRIPTION");
506 	lua_pushliteral (L, "Reading files inside zip files");
507 	lua_settable (L, -3);
508 	lua_pushliteral (L, "_VERSION");
509 	lua_pushliteral (L, "LuaZip 1.2.2");
510 	lua_settable (L, -3);
511 }
512 
createmeta(lua_State * L)513 static void createmeta (lua_State *L) {
514   luaL_newmetatable(L, ZIPFILEHANDLE);  /* create new metatable for file handles */
515   /* file methods */
516   lua_pushliteral(L, "__index");
517   lua_pushvalue(L, -2);  /* push metatable */
518   lua_rawset(L, -3);  /* metatable.__index = metatable */
519   luaL_openlib(L, NULL, flib, 0);
520 
521   luaL_newmetatable(L, ZIPINTERNALFILEHANDLE); /* create new metatable for internal file handles */
522   /* internal file methods */
523   lua_pushliteral(L, "__index");
524   lua_pushvalue(L, -2);  /* push metatable */
525   lua_rawset(L, -3);  /* metatable.__index = metatable */
526   luaL_openlib(L, NULL, fflib, 0);
527 }
528 
luaopen_zip(lua_State * L)529 LUAZIP_API int luaopen_zip (lua_State *L) {
530   createmeta(L);
531   lua_pushvalue(L, -1);
532   luaL_openlib(L, LUA_ZIPLIBNAME, ziplib, 1);
533   set_info(L);
534   return 1;
535 }
536