Error output changed interface throughout code.
[cantaveria.git] / zip.c
blob270678640aa4968fc75c3a49251276d359bde1d3
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 256
21 #define TABLE_SIZE 128
22 #define EBUF_SIZE 256
24 struct record {
25 char* filename;
26 unsigned method;
27 unsigned offset;
28 unsigned clen;
29 unsigned ulen;
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 ptr;
47 int len;
48 byte inbuf[BUF_SIZE];
49 int eof;
50 z_stream strm;
53 struct zip_dir {
54 struct record* ptr;
58 char errbuf[EBUF_SIZE] = "";
61 /* internal routines */
63 static void set_error(char* msg){
64 strncpy(errbuf, msg, EBUF_SIZE);
65 errbuf[EBUF_SIZE-1] = '\0';
68 static void out_of_memory(){
69 set_error("OUT OF MEMORY");
72 /* http://www.cse.yorku.ca/~oz/hash.html */
73 static unsigned long hash(char* str){
74 unsigned char* ptr = (unsigned char*)str;
75 unsigned long hash = 5381;
76 int c;
77 while((c = *ptr++))
78 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
79 return hash;
82 static struct record* get_record(zip_archive* arc, char* filename){
83 int i = hash(filename) % TABLE_SIZE;
84 struct record* ptr = arc->table[i];
85 while(ptr){
86 if(strcmp(filename, ptr->filename)) return ptr;
87 ptr = ptr->next;
89 return NULL;
92 static void set_record(zip_archive* arc, struct record* r){
93 /* check if r is a dir or file */
95 /* if file, find dir. insert into dir. insert file */
96 /* if dir, find dir or create, insert into parent */
97 int i = hash(r->filename) % TABLE_SIZE;
98 if(arc->table[i] == NULL)
99 arc->table[i] = r;
100 else{
101 struct record* ptr = arc->table[i];
102 while(ptr->next){
103 ptr = ptr->next;
105 ptr->next = r;
110 static int skip(zip_archive* arc, int count){
111 return -1; /* FIXME */
114 static int read_bytes(zip_archive* arc, char* buf, int count){
115 return -1;
118 static int read_long(zip_archive* arc, unsigned* n){
119 unsigned char b[4];
120 int e = arc->read(arc->userdata, b, 4);
121 if(e < 0){return -1;}
122 *n = b[0] | (b[1]<<8) | (b[2]<<16) | (b[3]<<24);
123 return 0;
126 static int read_short(zip_archive* arc, unsigned* n){
127 unsigned char b[2];
128 int e = arc->read(arc->userdata, b, 2);
129 if(e < 0){return -1;}
130 *n = b[0] | (b[1]<<8);
131 return 0;
135 static int parse_local_header(zip_archive* arc, struct record** result){
136 unsigned L1, L2;
137 struct record* r;
138 unsigned n;
139 unsigned ddesc;
141 set_error("refused to execute code before review");
142 return -1;
143 r = malloc(sizeof(struct record));
144 if(r == NULL){
145 out_of_memory();
146 return -1;
150 skip(arc, 4) ||
151 read_short(arc, &r->method) ||
152 skip(arc, 8) ||
153 read_long(arc, &n) ||
154 read_long(arc, &n) ||
155 read_short(arc, &L1) ||
156 read_short(arc, &L2)
158 free(r);
159 return -1;
162 char* filename = malloc(L1+1);
164 read_bytes(arc, filename, L1) ||
165 skip(arc, L2) ||
166 skip(arc, r->clen)
168 free(filename);
169 free(r);
170 return -1;
174 r->filename = filename;
175 r->offset = arc->ptr + 30 + L1 + L2;
176 arc->ptr += 26 + L1 + L2 + r->clen + ddesc;
178 *result = r;
180 return 0;
183 static int build_directory(zip_archive* arc){
184 while(1){
185 struct record* r;
186 if(parse_local_header(arc, &r) < 0){
187 return -1;
190 if(r == NULL){ /* end of file chunks */
191 break;
194 set_record(arc, r);
197 return 0;
201 static void free_directory_r(struct record* r){
202 if(r->contents) free_directory_r(r->contents);
203 if(r->next) free_directory_r(r->next);
204 free(r);
207 static void free_directory(zip_archive* arc){
208 int i;
209 for(i=0; i<TABLE_SIZE; i++){
210 if(arc->table[i]) free_directory_r(arc->table[i]);
215 static int fill_inbuf(zip_file* f){
216 int n = f->arc->read(f->arc->userdata, f->inbuf, BUF_SIZE);
217 if(n<0){
218 set_error("archive i/o error");
219 return -1;
221 f->strm.next_in = f->inbuf;
222 f->strm.avail_in = n;
223 return n;
226 static int inflate_chunk(zip_file* f, byte buf[], int count){
227 f->strm.next_out = buf;
228 f->strm.avail_out = count;
229 int e = inflate(&f->strm, Z_SYNC_FLUSH);
230 switch(e){
231 case Z_OK:
232 return count - f->strm.avail_out;
233 case Z_STREAM_END:
234 f->eof = 1;
235 return count = f->strm.avail_out;
236 case Z_NEED_DICT:
237 set_error("inflate needs a preset dictionary at this point");
238 return -1;
239 case Z_DATA_ERROR:
240 set_error("inflate data error (input corrupted or in wrong format)");
241 return -1;
242 case Z_STREAM_ERROR:
243 set_error("inflate stream error (inconsistent stream structure)");
244 return -1;
245 case Z_BUF_ERROR:
246 set_error("inflate buffer error (probably not enough input data)");
247 return -1;
248 case Z_MEM_ERROR:
249 set_error("inflate out of memory");
250 return -1;
251 default:
252 set_error("inflate error (unknown)");
253 return -1;
258 /* these are callbacks for the default archive reader, filesystem i/o */
259 static int file_read(void* f, byte buf[], int count){
260 return fread(buf, 1, count, f);
263 static int file_seek(void* f, int offset, int whence){
264 return fseek(f, offset, whence);
267 static void file_close(void* f){
268 fclose(f);
277 /* public methods */
279 zip_archive* zip_aropenf(char* filename){
280 FILE* f = fopen(filename, "r");
281 if(f == NULL){
282 set_error("i/o error");
283 return NULL;
285 zip_reader rd = {file_read, file_seek, file_close, f};
286 return zip_aropen(&rd);
289 zip_archive* zip_aropen(zip_reader* rd){
290 zip_archive* arc = malloc(sizeof(zip_archive));
291 if(arc == NULL){
292 out_of_memory();
293 return NULL;
295 arc->read = rd->read;
296 arc->seek = rd->seek;
297 arc->close = rd->close;
298 arc->userdata = rd->userdata;
300 if(build_directory(arc) < 0){
301 free_directory(arc);
302 free(arc);
303 return NULL;
306 return arc;
309 void zip_arclose(zip_archive* arc){
310 arc->close(arc->userdata);
311 free_directory(arc);
312 free(arc);
317 zip_file* zip_fopen(zip_archive* arc, char* path){
318 zip_file* f = malloc(sizeof(zip_file));
319 if(f == NULL){
320 out_of_memory();
321 return NULL;
324 f->eof = 0;
325 f->arc = arc;
326 f->strm.zalloc = NULL;
327 f->strm.zfree = NULL;
328 f->strm.opaque = NULL;
329 f->strm.next_in = f->inbuf;
330 f->strm.avail_in = 0;
331 f->strm.next_out = NULL;
332 f->strm.avail_out = 0;
334 /*TODO: get file offset and location in arc */
335 f->len = 0;
336 f->ptr = 0;
338 return f;
341 void zip_fclose(zip_file* f){
342 free(f);
345 int zip_fread(zip_file* f, byte buf[], int count){
346 int total = 0;
347 int n;
349 while(count > 0 && !f->eof){
350 if(fill_inbuf(f) < 0){
351 return -1;
354 n = inflate_chunk(f, buf, count);
355 if(n < 0){
356 return -1;
358 count -= n;
359 total += n;
362 return total;
365 int zip_feof(zip_file* f){
366 return f->eof;
372 zip_dir* zip_opendir(zip_archive* arc, char* path){
373 zip_dir* dir = malloc(sizeof(zip_dir));
374 if(dir == NULL){
375 out_of_memory();
376 return NULL;
379 struct record* r = get_record(arc, path);
380 if(r == NULL){
381 set_error("file not found");
382 return NULL;
385 dir->ptr = r->contents;
386 return dir;
389 char* zip_readdir(zip_dir* dir){
390 if(dir->ptr == NULL){
391 /* no more entries, not an error */
392 return NULL;
394 else{
395 char* filename = dir->ptr->filename;
396 dir->ptr = dir->ptr->next;
397 return filename;
401 void zip_closedir(zip_dir* dir){
402 free(dir);
406 char* zip_geterror(){
407 return errbuf;