Merge branch 'io'
[cantaveria.git] / zip.c
blobdff41ce305cae914f6e76dab41d8db1f6a23a7f1
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){
145 item->next = dir->contents;
146 dir->contents = item;
149 static void print_hash(zip_archive* arc){
150 int i;
151 struct record* rec;
152 for(i=0; i<TABLE_SIZE; i++){
153 printf("table[%d]: ",i);
154 rec = arc->table[i];
155 while(rec){
156 printf("%p ", rec);
157 rec = rec->next;
159 printf("\n");
163 static void print_zipfile(zip_file* f){
164 printf("(+0x%x, %u/%uB, cptr +0x%x, uptr %u)\n",
165 f->offset, f->clen, f->ulen, f->cptr, f->uptr);
168 static void print_record(struct record* rec){
169 printf("(%s, %uB, %uB, +0x%x, method %u)\n", rec->filename, rec->clen, rec->ulen, rec->offset, rec->method);
172 static char* method_str(unsigned method){
173 switch(method){
174 case 0: return "uncompressed";
175 case 1: return "shrink";
176 case 2:
177 case 3:
178 case 4:
179 case 5: return "reduce";
180 case 6: return "implode";
181 case 8: return "deflate";
182 case 9: return "deflate64";
183 case 10: return "IBM TERSE (old)";
184 case 12: return "bzip2";
185 case 14: return "lzma";
186 case 18: return "IBM TERSE (new)";
187 case 19: return "IBM LZ77 z";
188 case 97: return "WavPack";
189 case 98: return "PPMd";
190 default: return "unknown";
194 static void unrecognized_method(unsigned method){
195 char buf[64];
196 snprintf(buf, 64, "unrecognized compression method '%s'", method_str(method));
197 buf[63] = '\0';
198 set_error(buf);
201 static void set_record(zip_archive* arc, char* filename, int ulen, int clen, int offset, int method){
202 struct record* rec;
203 struct record* dir;
204 char* dirname;
205 if(get_record(arc, filename)) return;
206 rec = make_record(filename, ulen, clen, offset, method);
207 hash_insert(arc, rec);
208 dirname = baseof(filename);
209 if(dirname == NULL) return;
210 if(get_record(arc, dirname) == NULL)
211 set_record(arc, dirname, 0, 0, 0, 0);
212 dir = get_record(arc, dirname);
213 contents_insert(copy_record(rec), dir);
214 free(dirname);
219 static int read_bytes(zip_archive* arc, void* buf, int count){
220 return arc->read(arc->userdata, buf, count);
223 static int read_chunk(zip_archive* arc, void* buf, int count){
224 int n = read_bytes(arc, buf, count);
225 if(n < 0){
226 set_error("read error");
227 return -1;
229 if(n < count){
230 set_error("not enough data");
231 return -1;
233 return 0;
236 static int skip(zip_archive* arc, int count){
237 char buf[256];
238 int n;
239 int total = 0;
241 while(total != count){
242 int diff = count - total;
243 int chunk = diff < 256 ? diff : 256;
244 n = read_bytes(arc, buf, chunk);
245 if(n < 0){
246 set_error("read error");
247 return -1;
249 if(n < chunk){
250 set_error("not enough data");
251 return -1;
253 total += n;
256 return 0;
259 static int read_long(zip_archive* arc, unsigned* n){
260 unsigned char b[4];
261 int e = read_bytes(arc, b, 4);
262 if(e < 0){
263 set_error("read error");
264 return -1;
266 if(e < 4){
267 set_error("not enough data");
268 return -1;
270 *n = b[0] | (b[1]<<8) | (b[2]<<16) | (b[3]<<24);
271 return 0;
274 static int read_short(zip_archive* arc, unsigned* n){
275 unsigned char b[2];
276 int e = read_bytes(arc, b, 2);
277 if(e < 0){
278 set_error("read error");
279 return -1;
281 if(e < 2){
282 set_error("not enough data");
283 return -1;
285 *n = b[0] | (b[1]<<8);
286 return 0;
290 static int parse_local_header(zip_archive* arc){
291 unsigned L1, L2;
292 char* filename;
293 unsigned method;
294 unsigned clen, ulen;
295 unsigned offset;
296 unsigned flags;
299 skip(arc, 2) ||
300 read_short(arc, &flags) ||
301 read_short(arc, &method) ||
302 skip(arc, 8) ||
303 read_long(arc, &clen) ||
304 read_long(arc, &ulen) ||
305 read_short(arc, &L1) ||
306 read_short(arc, &L2)
308 return -1;
311 filename = malloc(L1+1);
313 read_chunk(arc, filename, L1) ||
314 skip(arc, L2) ||
315 skip(arc, clen)
317 free(filename);
318 return -1;
321 filename[L1] = '\0';
322 offset = arc->ptr + 30 + L1 + L2;
323 arc->ptr = offset + clen + (flags&(1<<3) ? 12 : 0);
325 set_record(arc, filename, ulen, clen, offset, method);
327 free(filename);
329 return 0;
332 static int build_directory(zip_archive* arc){
333 while(1){
334 unsigned sig;
335 if(read_long(arc, &sig) < 0) return -1;
336 else if(sig == 0x04034b50) parse_local_header(arc);
337 else break;
340 return 0;
344 static void free_directory_r(struct record* r){
345 if(r->contents) free_directory_r(r->contents);
346 if(r->next) free_directory_r(r->next);
347 free(r->filename);
348 free(r);
351 static void free_directory(zip_archive* arc){
352 int i;
353 for(i=0; i<TABLE_SIZE; i++){
354 if(arc->table[i]) free_directory_r(arc->table[i]);
359 static int fill_inbuf(zip_file* f){
360 int nread, nwant;
361 int nleftstream, nleftbuffer;
363 /* shift everything left */
364 memmove(f->inbuf, f->strm.next_in, f->strm.avail_in);
365 f->strm.next_in = f->inbuf;
367 /* fill the buffer */
368 if(f->arc->seek(f->arc->userdata, f->offset+f->cptr, SEEK_SET) < 0){
369 set_error("archive seek error");
370 return -1;
373 /* you want the minimum of
374 a) bytes needed to fill the buffer and
375 b) bytes left in the compressed stream */
376 nleftstream = f->clen - f->cptr;
377 nleftbuffer = BUF_SIZE - f->strm.avail_in;
378 nwant = nleftstream < nleftbuffer ? nleftstream : nleftbuffer;
379 nread = f->arc->read(f->arc->userdata, f->inbuf+f->strm.avail_in, nwant);
380 if(nread < 0){
381 set_error("archive read error");
382 return -1;
385 f->cptr += nread;
386 f->strm.avail_in += nread;
387 return nread;
390 static int inflate_chunk(zip_file* f, byte buf[], int count){
391 f->strm.next_out = buf;
392 f->strm.avail_out = count;
393 int e = inflate(&f->strm, Z_SYNC_FLUSH);
394 switch(e){
395 case Z_OK:
396 return count - f->strm.avail_out;
397 case Z_STREAM_END:
398 f->eof = 1;
399 return count - f->strm.avail_out;
400 case Z_BUF_ERROR:
401 return 0;
402 case Z_NEED_DICT:
403 set_error("inflate needs a preset dictionary at this point");
404 return -1;
405 case Z_DATA_ERROR:
406 set_error("inflate data error (input corrupted or in wrong format)");
407 return -1;
408 case Z_STREAM_ERROR:
409 set_error("inflate stream error (inconsistent stream structure)");
410 return -1;
411 case Z_MEM_ERROR:
412 set_error("inflate out of memory");
413 return -1;
414 default:
415 set_error("inflate error (unknown)");
416 return -1;
421 /* these are callbacks for the default archive reader, filesystem i/o */
422 static int file_read(void* f, byte buf[], int count){
423 return fread(buf, 1, count, f);
426 static int file_seek(void* f, int offset, int whence){
427 return fseek(f, offset, whence);
430 static void file_close(void* f){
431 fclose(f);
440 /* public methods */
442 zip_archive* zip_aropenf(char* filename){
443 FILE* f = fopen(filename, "r");
444 if(f == NULL){
445 set_error("i/o error");
446 return NULL;
448 zip_reader rd = {file_read, file_seek, file_close, f};
449 return zip_aropen(&rd);
452 zip_archive* zip_aropen(zip_reader* rd){
453 int i;
454 zip_archive* arc = malloc(sizeof(zip_archive));
455 if(arc == NULL){
456 out_of_memory();
457 return NULL;
459 arc->read = rd->read;
460 arc->seek = rd->seek;
461 arc->close = rd->close;
462 arc->userdata = rd->userdata;
463 arc->ptr = 0;
465 for(i=0; i<TABLE_SIZE; i++){
466 arc->table[i] = NULL;
469 if(build_directory(arc) < 0){
470 free_directory(arc);
471 free(arc);
472 return NULL;
475 return arc;
478 void zip_arclose(zip_archive* arc){
479 arc->close(arc->userdata);
480 free_directory(arc);
481 free(arc);
484 zip_file* zip_fopen(zip_archive* arc, char* path){
485 struct record* r = get_record(arc, path);
486 if(r == NULL){
487 set_error("file not found");
488 return NULL;
491 if(r->filename[strlen(r->filename)-1] == '/'){
492 set_error("cannot open directory");
493 return NULL;
496 if(r->method != 8){
497 unrecognized_method(r->method);
498 return NULL;
501 zip_file* f = malloc(sizeof(zip_file));
502 if(f == NULL){
503 out_of_memory();
504 return NULL;
507 f->eof = 0;
508 f->arc = arc;
509 f->strm.zalloc = NULL;
510 f->strm.zfree = NULL;
511 f->strm.opaque = NULL;
512 f->strm.next_in = f->inbuf;
513 f->strm.avail_in = 0;
514 f->strm.next_out = NULL;
515 f->strm.avail_out = 0;
516 f->clen = r->clen;
517 f->cptr = 0;
518 f->ulen = r->ulen;
519 f->uptr = 0;
520 f->offset = r->offset;
522 int e = inflateInit2(&f->strm, -15);
523 if(e != Z_OK){
524 switch(e){
525 case Z_MEM_ERROR: set_error("memory"); break;
526 case Z_STREAM_ERROR: set_error("stream"); break;
528 free(f);
529 return NULL;
532 return f;
535 void zip_fclose(zip_file* f){
536 inflateEnd(&f->strm);
537 free(f);
540 int zip_fread(zip_file* f, byte buf[], int count){
541 int total = 0;
542 int n;
543 int sentry = 0; /* annoying */
545 while(count > 0 && !f->eof){
546 n = inflate_chunk(f, buf+total, count);
548 if(n < 0) return -1;
549 if(n == 0){
550 /* need more input */
551 if(fill_inbuf(f) < 0) return -1;
552 if(sentry == 1){
553 set_error("unable to satisfy buffer requirements");
554 return -1;
556 sentry = 1;
557 continue;
560 sentry = 0;
561 count -= n;
562 total += n;
564 f->uptr += n;
565 if(f->uptr == f->ulen){
566 f->eof = 1;
570 return total;
573 int zip_feof(zip_file* f){
574 return f->eof;
580 zip_dir* zip_opendir(zip_archive* arc, char* path){
581 if(path[strlen(path)-1] != '/'){
582 set_error("path does not specify directory");
583 return NULL;
586 zip_dir* dir = malloc(sizeof(zip_dir));
587 if(dir == NULL){
588 out_of_memory();
589 return NULL;
592 struct record* r = get_record(arc, path);
593 if(r == NULL){
594 set_error("file not found");
595 free(dir);
596 return NULL;
599 dir->ptr = r->contents;
600 return dir;
603 char* zip_readdir(zip_dir* dir){
604 if(dir->ptr == NULL){
605 /* no more entries, not an error */
606 return NULL;
608 else{
609 char* filename = dir->ptr->filename;
610 dir->ptr = dir->ptr->next;
611 return filename;
615 void zip_closedir(zip_dir* dir){
616 free(dir);
620 char* zip_geterror(){
621 return errbuf;