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.
21 #define TABLE_SIZE 128
31 struct record
* contents
;
35 int (*read
)(void* userdata
, byte buf
[], int count
);
36 int (*seek
)(void* userdata
, int offset
, int whence
);
37 void (*close
)(void* userdata
);
40 struct record
* table
[TABLE_SIZE
];
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;
79 hash
= ((hash
<< 5) + hash
) + c
; /* hash * 33 + c */
84 static struct record
* make_record(char* filename
, int ulen
, int clen
, int offset
, int method
){
85 struct record
* r
= malloc(sizeof(struct record
));
89 r
->filename
= strdup(filename
);
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
];
107 if(strcmp(filename
, ptr
->filename
) == 0){
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
);
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';
144 static void contents_insert(struct record
* item
, struct record
* dir
){
148 static void print_hash(zip_archive
* arc
){
151 for(i
=0; i
<TABLE_SIZE
; i
++){
152 printf("table[%d]: ",i
);
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
){
173 case 0: return "uncompressed";
174 case 1: return "shrink";
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
){
195 snprintf(buf
, 64, "unrecognized compression method '%s'", method_str(method
));
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
);
224 set_error("read error");
228 set_error("not enough data");
234 static int skip(zip_archive
* arc
, int count
){
239 while(total
!= count
){
240 int diff
= count
- total
;
241 int chunk
= diff
< 256 ? diff
: 256;
242 n
= read_bytes(arc
, buf
, chunk
);
244 set_error("read error");
248 set_error("not enough data");
257 static int read_long(zip_archive
* arc
, unsigned* n
){
259 int e
= read_bytes(arc
, b
, 4);
261 set_error("read error");
265 set_error("not enough data");
268 *n
= b
[0] | (b
[1]<<8) | (b
[2]<<16) | (b
[3]<<24);
272 static int read_short(zip_archive
* arc
, unsigned* n
){
274 int e
= read_bytes(arc
, b
, 2);
276 set_error("read error");
280 set_error("not enough data");
283 *n
= b
[0] | (b
[1]<<8);
288 static int parse_local_header(zip_archive
* arc
){
298 read_short(arc
, &flags
) ||
299 read_short(arc
, &method
) ||
301 read_long(arc
, &clen
) ||
302 read_long(arc
, &ulen
) ||
303 read_short(arc
, &L1
) ||
309 filename
= malloc(L1
+1);
311 read_chunk(arc
, filename
, L1
) ||
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
);
330 static int build_directory(zip_archive
* arc
){
333 if(read_long(arc
, &sig
) < 0) return -1;
334 else if(sig
== 0x04034b50) parse_local_header(arc
);
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
);
349 static void free_directory(zip_archive
* arc
){
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
){
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");
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
);
379 set_error("archive read error");
384 f
->strm
.avail_in
+= 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
);
394 return count
- f
->strm
.avail_out
;
397 return count
- f
->strm
.avail_out
;
401 set_error("inflate needs a preset dictionary at this point");
404 set_error("inflate data error (input corrupted or in wrong format)");
407 set_error("inflate stream error (inconsistent stream structure)");
410 set_error("inflate out of memory");
413 set_error("inflate error (unknown)");
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
){
440 zip_archive
* zip_aropenf(char* filename
){
441 FILE* f
= fopen(filename
, "r");
443 set_error("i/o error");
446 zip_reader rd
= {file_read
, file_seek
, file_close
, f
};
447 return zip_aropen(&rd
);
450 zip_archive
* zip_aropen(zip_reader
* rd
){
452 zip_archive
* arc
= malloc(sizeof(zip_archive
));
457 arc
->read
= rd
->read
;
458 arc
->seek
= rd
->seek
;
459 arc
->close
= rd
->close
;
460 arc
->userdata
= rd
->userdata
;
463 for(i
=0; i
<TABLE_SIZE
; i
++){
464 arc
->table
[i
] = NULL
;
467 if(build_directory(arc
) < 0){
476 void zip_arclose(zip_archive
* arc
){
477 arc
->close(arc
->userdata
);
482 zip_file
* zip_fopen(zip_archive
* arc
, char* path
){
483 struct record
* r
= get_record(arc
, path
);
485 set_error("file not found");
489 if(r
->filename
[strlen(r
->filename
)-1] == '/'){
490 set_error("cannot open directory");
495 unrecognized_method(r
->method
);
499 zip_file
* f
= malloc(sizeof(zip_file
));
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;
518 f
->offset
= r
->offset
;
520 int e
= inflateInit2(&f
->strm
, -15);
523 case Z_MEM_ERROR
: set_error("memory"); break;
524 case Z_STREAM_ERROR
: set_error("stream"); break;
533 void zip_fclose(zip_file
* f
){
534 inflateEnd(&f
->strm
);
538 int zip_fread(zip_file
* f
, byte buf
[], int count
){
541 int sentry
= 0; /* annoying */
543 while(count
> 0 && !f
->eof
){
544 n
= inflate_chunk(f
, buf
+total
, count
);
548 /* need more input */
549 if(fill_inbuf(f
) < 0) return -1;
551 set_error("unable to satisfy buffer requirements");
563 if(f
->uptr
== f
->ulen
){
571 int zip_feof(zip_file
* f
){
578 zip_dir
* zip_opendir(zip_archive
* arc
, char* path
){
579 zip_dir
* dir
= malloc(sizeof(zip_dir
));
585 struct record
* r
= get_record(arc
, path
);
587 set_error("file not found");
591 dir
->ptr
= r
->contents
;
595 char* zip_readdir(zip_dir
* dir
){
596 if(dir
->ptr
== NULL
){
597 /* no more entries, not an error */
601 char* filename
= dir
->ptr
->filename
;
602 dir
->ptr
= dir
->ptr
->next
;
607 void zip_closedir(zip_dir
* dir
){
612 char* zip_geterror(){