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
){
145 item
->next
= dir
->contents
;
146 dir
->contents
= item
;
149 static void print_hash(zip_archive* arc){
152 for(i=0; i<TABLE_SIZE; i++){
153 printf("table[%d]: ",i);
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
){
174 case 0: return "uncompressed";
175 case 1: return "shrink";
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
){
196 snprintf(buf
, 64, "unrecognized compression method '%s'", method_str(method
));
201 static void set_record(zip_archive
* arc
, char* filename
, int ulen
, int clen
, int offset
, int method
){
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
);
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
);
226 set_error("read error");
230 set_error("not enough data");
236 static int skip(zip_archive
* arc
, int count
){
241 while(total
!= count
){
242 int diff
= count
- total
;
243 int chunk
= diff
< 256 ? diff
: 256;
244 n
= read_bytes(arc
, buf
, chunk
);
246 set_error("read error");
250 set_error("not enough data");
259 static int read_long(zip_archive
* arc
, unsigned* n
){
261 int e
= read_bytes(arc
, b
, 4);
263 set_error("read error");
267 set_error("not enough data");
270 *n
= b
[0] | (b
[1]<<8) | (b
[2]<<16) | (b
[3]<<24);
274 static int read_short(zip_archive
* arc
, unsigned* n
){
276 int e
= read_bytes(arc
, b
, 2);
278 set_error("read error");
282 set_error("not enough data");
285 *n
= b
[0] | (b
[1]<<8);
290 static int parse_local_header(zip_archive
* arc
){
300 read_short(arc
, &flags
) ||
301 read_short(arc
, &method
) ||
303 read_long(arc
, &clen
) ||
304 read_long(arc
, &ulen
) ||
305 read_short(arc
, &L1
) ||
311 filename
= malloc(L1
+1);
313 read_chunk(arc
, filename
, L1
) ||
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
);
332 static int build_directory(zip_archive
* arc
){
335 if(read_long(arc
, &sig
) < 0) return -1;
336 else if(sig
== 0x04034b50) parse_local_header(arc
);
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
);
351 static void free_directory(zip_archive
* arc
){
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
){
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");
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
);
381 set_error("archive read error");
386 f
->strm
.avail_in
+= 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
);
396 return count
- f
->strm
.avail_out
;
399 return count
- f
->strm
.avail_out
;
403 set_error("inflate needs a preset dictionary at this point");
406 set_error("inflate data error (input corrupted or in wrong format)");
409 set_error("inflate stream error (inconsistent stream structure)");
412 set_error("inflate out of memory");
415 set_error("inflate error (unknown)");
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
){
442 zip_archive
* zip_aropenf(char* filename
){
443 FILE* f
= fopen(filename
, "r");
445 set_error("i/o error");
448 zip_reader rd
= {file_read
, file_seek
, file_close
, f
};
449 return zip_aropen(&rd
);
452 zip_archive
* zip_aropen(zip_reader
* rd
){
454 zip_archive
* arc
= malloc(sizeof(zip_archive
));
459 arc
->read
= rd
->read
;
460 arc
->seek
= rd
->seek
;
461 arc
->close
= rd
->close
;
462 arc
->userdata
= rd
->userdata
;
465 for(i
=0; i
<TABLE_SIZE
; i
++){
466 arc
->table
[i
] = NULL
;
469 if(build_directory(arc
) < 0){
478 void zip_arclose(zip_archive
* arc
){
479 arc
->close(arc
->userdata
);
484 zip_file
* zip_fopen(zip_archive
* arc
, char* path
){
485 struct record
* r
= get_record(arc
, path
);
487 set_error("file not found");
491 if(r
->filename
[strlen(r
->filename
)-1] == '/'){
492 set_error("cannot open directory");
497 unrecognized_method(r
->method
);
501 zip_file
* f
= malloc(sizeof(zip_file
));
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;
520 f
->offset
= r
->offset
;
522 int e
= inflateInit2(&f
->strm
, -15);
525 case Z_MEM_ERROR
: set_error("memory"); break;
526 case Z_STREAM_ERROR
: set_error("stream"); break;
535 void zip_fclose(zip_file
* f
){
536 inflateEnd(&f
->strm
);
540 int zip_fread(zip_file
* f
, byte buf
[], int count
){
543 int sentry
= 0; /* annoying */
545 while(count
> 0 && !f
->eof
){
546 n
= inflate_chunk(f
, buf
+total
, count
);
550 /* need more input */
551 if(fill_inbuf(f
) < 0) return -1;
553 set_error("unable to satisfy buffer requirements");
565 if(f
->uptr
== f
->ulen
){
573 int zip_feof(zip_file
* f
){
580 zip_dir
* zip_opendir(zip_archive
* arc
, char* path
){
581 if(path
[strlen(path
)-1] != '/'){
582 set_error("path does not specify directory");
586 zip_dir
* dir
= malloc(sizeof(zip_dir
));
592 struct record
* r
= get_record(arc
, path
);
594 set_error("file not found");
599 dir
->ptr
= r
->contents
;
603 char* zip_readdir(zip_dir
* dir
){
604 if(dir
->ptr
== NULL
){
605 /* no more entries, not an error */
609 char* filename
= dir
->ptr
->filename
;
610 dir
->ptr
= dir
->ptr
->next
;
615 void zip_closedir(zip_dir
* dir
){
620 char* zip_geterror(){