Fixed memory leak in zip.c, started on dir contents listing.
[cantaveria.git] / zip.c
blob7a64b1c222711c0944f7de35faa305c904c3ce7c
1 /*
2 zip
3 read from a zip file
5 Copyright (C) 2009 Evan Rinehart
7 This software comes with no warranty.
8 1. This software can be used for any purpose, good or evil.
9 2. Modifications must retain this license, at least in spirit.
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdlib.h>
16 #include <zlib.h>
18 #include <zip.h>
20 #define BUF_SIZE 1024
21 #define TABLE_SIZE 128
22 #define EBUF_SIZE 256
24 struct record {
25 char* filename;
26 unsigned offset;
27 unsigned clen;
28 unsigned ulen;
29 unsigned method;
30 struct record* next;
31 struct record* contents;
34 struct zip_archive {
35 int (*read)(void* userdata, byte buf[], int count);
36 int (*seek)(void* userdata, int offset, int whence);
37 void (*close)(void* userdata);
38 void* userdata;
40 struct record* table[TABLE_SIZE];
41 int ptr;
44 struct zip_file {
45 zip_archive* arc;
46 int cptr, clen;
47 int uptr, ulen;
48 int offset;
49 byte inbuf[BUF_SIZE];
50 int eof;
51 z_stream strm;
54 struct zip_dir {
55 struct record* ptr;
59 static char errbuf[EBUF_SIZE] = "";
62 /* internal routines */
64 static void set_error(char* msg){
65 strncpy(errbuf, msg, EBUF_SIZE);
66 errbuf[EBUF_SIZE-1] = '\0';
69 static void out_of_memory(){
70 set_error("OUT OF MEMORY");
73 /* http://www.cse.yorku.ca/~oz/hash.html */
74 static unsigned long hash(char* str){
75 unsigned char* ptr = (unsigned char*)str;
76 unsigned long hash = 5381;
77 int c;
78 while((c = *ptr++))
79 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
80 return hash;
84 static struct record* make_record(char* filename, int ulen, int clen, int offset, int method){
85 struct record* r = malloc(sizeof(struct record));
86 if(r == NULL){
87 return NULL;
89 r->filename = strdup(filename);
90 r->clen = clen;
91 r->ulen = ulen;
92 r->offset = offset;
93 r->method = method;
94 r->next = NULL;
95 r->contents = NULL;
96 return r;
99 static struct record* copy_record(struct record* rec){
100 return make_record(rec->filename, rec->ulen, rec->clen, rec->offset, rec->method);
103 static struct record* get_record(zip_archive* arc, char* filename){
104 int i = hash(filename) % TABLE_SIZE;
105 struct record* ptr = arc->table[i];
106 while(ptr){
107 if(strcmp(filename, ptr->filename) == 0){
108 return ptr;
110 ptr = ptr->next;
112 return NULL;
115 static void hash_insert(zip_archive* arc, struct record* rec){
116 int i = hash(rec->filename) % TABLE_SIZE;
117 struct record* ptr = arc->table[i];
119 if(get_record(arc, rec->filename)){
120 printf("cant insert %s twice\n", rec->filename);
121 return;
124 if(ptr == NULL){
125 arc->table[i] = rec;
126 return;
129 rec->next = ptr;
130 arc->table[i] = rec;
133 static char* baseof(char* path){
134 if(strlen(path) < 2) return NULL;
135 int ptr = strlen(path) - 2;
136 while(ptr > 0 && path[ptr] != '/') ptr--;
137 if(ptr == 0) return NULL;
138 char* result = malloc(ptr+5);
139 memcpy(result, path, ptr+1);
140 result[ptr+1] = '\0';
141 return result;
144 static void contents_insert(struct record* item, struct record* dir){
148 static void print_hash(zip_archive* arc){
149 int i;
150 struct record* rec;
151 for(i=0; i<TABLE_SIZE; i++){
152 printf("table[%d]: ",i);
153 rec = arc->table[i];
154 while(rec){
155 printf("%p ", rec);
156 rec = rec->next;
158 printf("\n");
162 static void print_zipfile(zip_file* f){
163 printf("(+0x%x, %u/%uB, cptr +0x%x, uptr %u)\n",
164 f->offset, f->clen, f->ulen, f->cptr, f->uptr);
167 static void print_record(struct record* rec){
168 printf("(%s, %uB, %uB, +0x%x, method %u)\n", rec->filename, rec->clen, rec->ulen, rec->offset, rec->method);
171 static char* method_str(unsigned method){
172 switch(method){
173 case 0: return "uncompressed";
174 case 1: return "shrink";
175 case 2:
176 case 3:
177 case 4:
178 case 5: return "reduce";
179 case 6: return "implode";
180 case 8: return "deflate";
181 case 9: return "deflate64";
182 case 10: return "IBM TERSE (old)";
183 case 12: return "bzip2";
184 case 14: return "lzma";
185 case 18: return "IBM TERSE (new)";
186 case 19: return "IBM LZ77 z";
187 case 97: return "WavPack";
188 case 98: return "PPMd";
189 default: return "unknown";
193 static void unrecognized_method(unsigned method){
194 char buf[64];
195 snprintf(buf, 64, "unrecognized compression method '%s'", method_str(method));
196 buf[63] = '\0';
197 set_error(buf);
200 static void set_record(zip_archive* arc, char* filename, int ulen, int clen, int offset, int method){
201 if(get_record(arc, filename)) return;
203 struct record* rec = make_record(filename, ulen, clen, offset, method);
204 hash_insert(arc, rec);
206 char* dirname = baseof(filename);
207 if(!get_record(arc, dirname)){
208 set_record(arc, dirname, 0, 0, 0, 0);
210 struct record* dir = get_record(arc, dirname);
211 contents_insert(dir, copy_record(rec));
217 static int read_bytes(zip_archive* arc, void* buf, int count){
218 return arc->read(arc->userdata, buf, count);
221 static int read_chunk(zip_archive* arc, void* buf, int count){
222 int n = read_bytes(arc, buf, count);
223 if(n < 0){
224 set_error("read error");
225 return -1;
227 if(n < count){
228 set_error("not enough data");
229 return -1;
231 return 0;
234 static int skip(zip_archive* arc, int count){
235 char buf[256];
236 int n;
237 int total = 0;
239 while(total != count){
240 int diff = count - total;
241 int chunk = diff < 256 ? diff : 256;
242 n = read_bytes(arc, buf, chunk);
243 if(n < 0){
244 set_error("read error");
245 return -1;
247 if(n < chunk){
248 set_error("not enough data");
249 return -1;
251 total += n;
254 return 0;
257 static int read_long(zip_archive* arc, unsigned* n){
258 unsigned char b[4];
259 int e = read_bytes(arc, b, 4);
260 if(e < 0){
261 set_error("read error");
262 return -1;
264 if(e < 4){
265 set_error("not enough data");
266 return -1;
268 *n = b[0] | (b[1]<<8) | (b[2]<<16) | (b[3]<<24);
269 return 0;
272 static int read_short(zip_archive* arc, unsigned* n){
273 unsigned char b[2];
274 int e = read_bytes(arc, b, 2);
275 if(e < 0){
276 set_error("read error");
277 return -1;
279 if(e < 2){
280 set_error("not enough data");
281 return -1;
283 *n = b[0] | (b[1]<<8);
284 return 0;
288 static int parse_local_header(zip_archive* arc){
289 unsigned L1, L2;
290 char* filename;
291 unsigned method;
292 unsigned clen, ulen;
293 unsigned offset;
294 unsigned flags;
297 skip(arc, 2) ||
298 read_short(arc, &flags) ||
299 read_short(arc, &method) ||
300 skip(arc, 8) ||
301 read_long(arc, &clen) ||
302 read_long(arc, &ulen) ||
303 read_short(arc, &L1) ||
304 read_short(arc, &L2)
306 return -1;
309 filename = malloc(L1+1);
311 read_chunk(arc, filename, L1) ||
312 skip(arc, L2) ||
313 skip(arc, clen)
315 free(filename);
316 return -1;
319 filename[L1] = '\0';
320 offset = arc->ptr + 30 + L1 + L2;
321 arc->ptr = offset + clen + (flags&(1<<3) ? 12 : 0);
323 set_record(arc, filename, ulen, clen, offset, method);
325 free(filename);
327 return 0;
330 static int build_directory(zip_archive* arc){
331 while(1){
332 unsigned sig;
333 if(read_long(arc, &sig) < 0) return -1;
334 else if(sig == 0x04034b50) parse_local_header(arc);
335 else break;
338 return 0;
342 static void free_directory_r(struct record* r){
343 if(r->contents) free_directory_r(r->contents);
344 if(r->next) free_directory_r(r->next);
345 free(r->filename);
346 free(r);
349 static void free_directory(zip_archive* arc){
350 int i;
351 for(i=0; i<TABLE_SIZE; i++){
352 if(arc->table[i]) free_directory_r(arc->table[i]);
357 static int fill_inbuf(zip_file* f){
358 int nread, nwant;
359 int nleftstream, nleftbuffer;
361 /* shift everything left */
362 memmove(f->inbuf, f->strm.next_in, f->strm.avail_in);
363 f->strm.next_in = f->inbuf;
365 /* fill the buffer */
366 if(f->arc->seek(f->arc->userdata, f->offset+f->cptr, SEEK_SET) < 0){
367 set_error("archive seek error");
368 return -1;
371 /* you want the minimum of
372 a) bytes needed to fill the buffer and
373 b) bytes left in the compressed stream */
374 nleftstream = f->clen - f->cptr;
375 nleftbuffer = BUF_SIZE - f->strm.avail_in;
376 nwant = nleftstream < nleftbuffer ? nleftstream : nleftbuffer;
377 nread = f->arc->read(f->arc->userdata, f->inbuf+f->strm.avail_in, nwant);
378 if(nread < 0){
379 set_error("archive read error");
380 return -1;
383 f->cptr += nread;
384 f->strm.avail_in += nread;
385 return nread;
388 static int inflate_chunk(zip_file* f, byte buf[], int count){
389 f->strm.next_out = buf;
390 f->strm.avail_out = count;
391 int e = inflate(&f->strm, Z_SYNC_FLUSH);
392 switch(e){
393 case Z_OK:
394 return count - f->strm.avail_out;
395 case Z_STREAM_END:
396 f->eof = 1;
397 return count - f->strm.avail_out;
398 case Z_BUF_ERROR:
399 return 0;
400 case Z_NEED_DICT:
401 set_error("inflate needs a preset dictionary at this point");
402 return -1;
403 case Z_DATA_ERROR:
404 set_error("inflate data error (input corrupted or in wrong format)");
405 return -1;
406 case Z_STREAM_ERROR:
407 set_error("inflate stream error (inconsistent stream structure)");
408 return -1;
409 case Z_MEM_ERROR:
410 set_error("inflate out of memory");
411 return -1;
412 default:
413 set_error("inflate error (unknown)");
414 return -1;
419 /* these are callbacks for the default archive reader, filesystem i/o */
420 static int file_read(void* f, byte buf[], int count){
421 return fread(buf, 1, count, f);
424 static int file_seek(void* f, int offset, int whence){
425 return fseek(f, offset, whence);
428 static void file_close(void* f){
429 fclose(f);
438 /* public methods */
440 zip_archive* zip_aropenf(char* filename){
441 FILE* f = fopen(filename, "r");
442 if(f == NULL){
443 set_error("i/o error");
444 return NULL;
446 zip_reader rd = {file_read, file_seek, file_close, f};
447 return zip_aropen(&rd);
450 zip_archive* zip_aropen(zip_reader* rd){
451 int i;
452 zip_archive* arc = malloc(sizeof(zip_archive));
453 if(arc == NULL){
454 out_of_memory();
455 return NULL;
457 arc->read = rd->read;
458 arc->seek = rd->seek;
459 arc->close = rd->close;
460 arc->userdata = rd->userdata;
461 arc->ptr = 0;
463 for(i=0; i<TABLE_SIZE; i++){
464 arc->table[i] = NULL;
467 if(build_directory(arc) < 0){
468 free_directory(arc);
469 free(arc);
470 return NULL;
473 return arc;
476 void zip_arclose(zip_archive* arc){
477 arc->close(arc->userdata);
478 free_directory(arc);
479 free(arc);
482 zip_file* zip_fopen(zip_archive* arc, char* path){
483 struct record* r = get_record(arc, path);
484 if(r == NULL){
485 set_error("file not found");
486 return NULL;
489 if(r->filename[strlen(r->filename)-1] == '/'){
490 set_error("cannot open directory");
491 return NULL;
494 if(r->method != 8){
495 unrecognized_method(r->method);
496 return NULL;
499 zip_file* f = malloc(sizeof(zip_file));
500 if(f == NULL){
501 out_of_memory();
502 return NULL;
505 f->eof = 0;
506 f->arc = arc;
507 f->strm.zalloc = NULL;
508 f->strm.zfree = NULL;
509 f->strm.opaque = NULL;
510 f->strm.next_in = f->inbuf;
511 f->strm.avail_in = 0;
512 f->strm.next_out = NULL;
513 f->strm.avail_out = 0;
514 f->clen = r->clen;
515 f->cptr = 0;
516 f->ulen = r->ulen;
517 f->uptr = 0;
518 f->offset = r->offset;
520 int e = inflateInit2(&f->strm, -15);
521 if(e != Z_OK){
522 switch(e){
523 case Z_MEM_ERROR: set_error("memory"); break;
524 case Z_STREAM_ERROR: set_error("stream"); break;
526 free(f);
527 return NULL;
530 return f;
533 void zip_fclose(zip_file* f){
534 inflateEnd(&f->strm);
535 free(f);
538 int zip_fread(zip_file* f, byte buf[], int count){
539 int total = 0;
540 int n;
541 int sentry = 0; /* annoying */
543 while(count > 0 && !f->eof){
544 n = inflate_chunk(f, buf+total, count);
546 if(n < 0) return -1;
547 if(n == 0){
548 /* need more input */
549 if(fill_inbuf(f) < 0) return -1;
550 if(sentry == 1){
551 set_error("unable to satisfy buffer requirements");
552 return -1;
554 sentry = 1;
555 continue;
558 sentry = 0;
559 count -= n;
560 total += n;
562 f->uptr += n;
563 if(f->uptr == f->ulen){
564 f->eof = 1;
568 return total;
571 int zip_feof(zip_file* f){
572 return f->eof;
578 zip_dir* zip_opendir(zip_archive* arc, char* path){
579 zip_dir* dir = malloc(sizeof(zip_dir));
580 if(dir == NULL){
581 out_of_memory();
582 return NULL;
585 struct record* r = get_record(arc, path);
586 if(r == NULL){
587 set_error("file not found");
588 return NULL;
591 dir->ptr = r->contents;
592 return dir;
595 char* zip_readdir(zip_dir* dir){
596 if(dir->ptr == NULL){
597 /* no more entries, not an error */
598 return NULL;
600 else{
601 char* filename = dir->ptr->filename;
602 dir->ptr = dir->ptr->next;
603 return filename;
607 void zip_closedir(zip_dir* dir){
608 free(dir);
612 char* zip_geterror(){
613 return errbuf;