]> granicus.if.org Git - zziplib/blob - zzip/zip.c
fixing some issues about variable declarations in the middle of a block
[zziplib] / zzip / zip.c
1 /*
2  * Author: 
3  *      Guido Draheim <guidod@gmx.de>
4  *      Tomi Ollila <too@iki.fi>
5  *
6  * Copyright (c) 1999,2000,2001,2002,2003 Guido Draheim
7  *          All rights reserved,
8  *          use under the restrictions of the 
9  *          Lesser GNU General Public License
10  *          or alternatively the restrictions 
11  *          of the Mozilla Public License 1.1
12  */
13
14 #include <zzip/lib.h>                                  /* archive handling */
15 #include <zzip/file.h>
16 #include <zzip/format.h>
17 #include <zzip/fetch.h>
18
19 #include <ctype.h>
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <fcntl.h>
24 #ifdef ZZIP_HAVE_SYS_STAT_H
25 #include <sys/stat.h>
26 #endif
27
28 #include <zzip/__mmap.h>
29 #include <zzip/__debug.h>
30
31 #define __sizeof(X) ((zzip_ssize_t)(sizeof(X)))
32
33 /* per default, we use a little hack to correct bad z_rootseek parts */
34 #define ZZIP_CORRECT_ROOTSEEK 1
35
36
37 /* ---------------------------  internals  -------------------------------- */
38 /* internal functions of zziplib, avoid at all cost, changes w/o warning.
39  * we do export them for debugging purpose and special external tools
40  * which know what they do and which can adapt from version to version
41  */
42
43 int __zzip_find_disk_trailer( int fd, zzip_off_t filesize, 
44                               struct zzip_disk_trailer * trailer,
45                               zzip_plugin_io_t io);
46 int __zzip_parse_root_directory( int fd, 
47                                  struct zzip_disk_trailer * trailer, 
48                                  struct zzip_dir_hdr ** hdr_return,
49                                  zzip_plugin_io_t io);
50
51 _zzip_inline char* __zzip_aligned4(char* p);
52
53 /* ------------------------  harden routines ------------------------------ */
54
55 #ifdef ZZIP_HARDEN
56 /*
57  * check for inconsistent values in trailer and prefer lower seek value
58  * - we fix values assuming the root directory was written at the end
59  * and it is just before the zip trailer. Therefore, ...
60  */
61 _zzip_inline static void __fixup_rootseek(
62     zzip_off_t offset_of_trailer,
63     struct zzip_disk_trailer* trailer)
64 {
65     if (                    zzip_disk_trailer_rootseek (trailer) >
66         offset_of_trailer - zzip_disk_trailer_rootsize (trailer) &&
67         offset_of_trailer > zzip_disk_trailer_rootsize (trailer))
68     {
69         register zzip_off_t offset;
70         offset = offset_of_trailer - zzip_disk_trailer_rootsize (trailer);
71         zzip_disk_trailer_set_rootseek (trailer, offset);
72         HINT2("new rootseek=%li", (long) zzip_disk_trailer_rootseek (trailer));
73     }
74 }
75 #define __correct_rootseek(A,B,C)
76
77 #elif defined ZZIP_CORRECT_ROOTSEEK
78 /* store the seekvalue of the trailer into the "z_magic" field and with 
79  * a 64bit off_t we overwrite z_disk/z_finaldisk as well. If you change
80  * anything in zziplib or dump the trailer structure then watch out that
81  * these are still unused, so that this code may still (ab)use those. */
82 #define __fixup_rootseek(_offset_of_trailer, _trailer)          \
83                       *(zzip_off_t*)_trailer = _offset_of_trailer;
84 #define __correct_rootseek( _u_rootseek, _u_rootsize, _trailer) \
85     if (_u_rootseek > *(zzip_off_t*)_trailer - _u_rootsize)     \
86         _u_rootseek = *(zzip_off_t*)_trailer - _u_rootsize;
87 #else
88 #define __fixup_rootseek(A,B) 
89 #define __correct_rootseek(A,B,C)
90 #endif
91
92
93 #ifdef DEBUG
94 _zzip_inline static void __debug_dir_hdr (struct zzip_dir_hdr* hdr)
95 {
96     if (sizeof(struct zzip_dir_hdr) > sizeof(struct zzip_disk_entry))
97     { WARN1("internal sizeof-mismatch may break wreakage"); }
98     /*  the internal directory structure is never bigger than the
99      *  external zip central directory space had been beforehand
100      *  (as long as the following assertion holds...) 
101      */
102
103     if (((unsigned)hdr)&3)
104     { NOTE1("this machine's malloc(3) returns sth. not u32-aligned"); }
105     /* we assume that if this machine's malloc has returned a non-aligned 
106      * memory block, then it is actually safe to access misaligned data, and 
107      * since it does only affect the first hdr it should not even bring about
108      * too much of that cpu's speed penalty
109      */
110 }
111 #else
112 #define __debug_dir_hdr(X)
113 #endif
114
115 /* -------------------------- low-level interface -------------------------- */
116
117 #if defined BUFSIZ 
118 #if BUFSIZ == 1024 || BUFSIZ == 512 || BUFSIZ == 256
119 #define ZZIP_BUFSIZ BUFSIZ
120 #endif
121 #endif
122
123 #ifndef ZZIP_BUFSIZ
124 #define ZZIP_BUFSIZ 512
125 /* #define ZZIP_BUFSIZ 64 */ /* for testing */
126 #endif
127
128 /**
129  * This function is used by => zzip_file_open. It tries to find
130  * the zip's central directory info that is usually a few
131  * bytes off the end of the file.
132  */
133 int 
134 __zzip_find_disk_trailer(int fd, zzip_off_t filesize, 
135                          struct zzip_disk_trailer * trailer,
136                          zzip_plugin_io_t io)
137 {
138 #ifdef DEBUG
139 #define return(val) { e=val; HINT2("%s", zzip_strerror(e)); goto cleanup; }
140 #else
141 #define return(val) { e=val; goto cleanup; }
142 #endif
143     register int e;
144     
145 #ifndef _LOWSTK
146     auto char buffer[2*ZZIP_BUFSIZ];
147     char* buf = buffer;
148 #else
149     char* buf = malloc(2*ZZIP_BUFSIZ);
150 #endif
151     zzip_off_t offset = 0;
152     zzip_ssize_t maplen = 0; /* mmap(),read(),getpagesize() use size_t !! */
153     char* fd_map = 0;
154
155     if (!trailer)
156         { return(EINVAL); }
157   
158     if (filesize < __sizeof(struct zzip_disk_trailer))
159         { return(ZZIP_DIR_TOO_SHORT); }
160           
161     if (!buf)
162         { return(ZZIP_OUTOFMEM); }
163
164     offset = filesize; /* a.k.a. old offset */
165     while(1) /* outer loop */
166     {
167         register unsigned char* mapped;
168
169          if (offset <= 0) { return(ZZIP_DIR_EDH_MISSING); }
170
171          /* trailer cannot be farther away than 64K from fileend */
172          if (filesize-offset > 64*1024) 
173              { return(ZZIP_DIR_EDH_MISSING); }
174
175         /* the new offset shall overlap with the area after the old offset! */
176         if (USE_MMAP && io->fd.sys)
177         {
178             zzip_off_t mapoff = offset;
179             { 
180                 zzip_ssize_t pagesize = _zzip_getpagesize (io->fd.sys);
181                 if (pagesize < ZZIP_BUFSIZ) goto non_mmap; /* an error? */
182                 if (mapoff == filesize && filesize > pagesize) 
183                     mapoff -= pagesize;
184                 if (mapoff < pagesize) {
185                     maplen = (zzip_ssize_t)mapoff + pagesize; mapoff = 0;
186                 } else {               
187                     mapoff -= pagesize; maplen = 2*pagesize; 
188                     if ((zzip_ssize_t)mapoff & (pagesize-1)) { /*only 1. run */
189                         pagesize -= (zzip_ssize_t)mapoff & (pagesize-1);
190                         mapoff += pagesize;
191                         maplen -= pagesize;
192                     }   
193                 }
194                 if (mapoff + maplen > filesize) maplen = filesize - mapoff;
195             }
196
197             fd_map = _zzip_mmap(io->fd.sys, fd, mapoff, (zzip_size_t)maplen);
198             if (fd_map == MAP_FAILED) goto non_mmap;
199             mapped = (unsigned char*) fd_map; offset = mapoff; /* success */
200             HINT3("mapped *%p len=%li", fd_map, (long) maplen);
201         } else {
202         non_mmap:
203             fd_map = 0; /* have no mmap */
204             {
205                 zzip_off_t pagesize = ZZIP_BUFSIZ;
206                 if (offset == filesize && filesize > pagesize)
207                     offset -= pagesize;
208                 if (offset < pagesize) {
209                     maplen = (zzip_ssize_t)offset + pagesize; offset = 0;
210                 } else {
211                     offset -= pagesize; maplen = 2*pagesize;
212                     if ((zzip_ssize_t)offset & (pagesize-1)) { /*on 1st run*/
213                         pagesize -= (zzip_ssize_t)offset & (pagesize-1);
214                         offset += pagesize;
215                         maplen -= pagesize; 
216                     }    
217                 }
218                 if (offset + maplen > filesize) maplen = filesize - offset;
219             }
220             
221             if (io->fd.seeks(fd, offset, SEEK_SET) < 0)
222                 { return(ZZIP_DIR_SEEK); }
223             if (io->fd.read(fd, buf, (zzip_size_t)maplen) < maplen)
224                 { return(ZZIP_DIR_READ); }
225             mapped = (unsigned char*) buf; /* success */
226             HINT5("offs=$%lx len=%li filesize=%li pagesize=%i", 
227                   (long)offset, (long)maplen, (long)filesize, ZZIP_BUFSIZ);
228         }
229
230         {/* now, check for the trailer-magic, hopefully near the end of file */
231             register unsigned char* end = mapped + maplen;
232             register unsigned char* tail;
233             for (tail = end-1; (tail >= mapped); tail--)
234             {
235                 if ((*tail == 'P') && /* quick pre-check for trailer magic */
236                     end-tail >= __sizeof(*trailer)-2 &&
237                     ZZIP_DISK_TRAILER_CHECKMAGIC(tail))
238                 {
239                     /* if the file-comment is not present, it happens
240                        that the z_comment field often isn't either */
241                     if (end-tail >= __sizeof(*trailer))
242                     {
243                         memcpy (trailer, tail, sizeof(*trailer)); 
244                     }else{
245                         memcpy (trailer, tail, sizeof(*trailer)-2);
246                         trailer->z_comment[0] = 0; 
247                         trailer->z_comment[1] = 0;
248                     }
249
250                     __fixup_rootseek (offset + tail-mapped, trailer);
251                     { return(0); }
252                 }
253             }
254         }
255         
256          if (USE_MMAP && fd_map) 
257          { 
258              HINT3("unmap *%p len=%li",  fd_map, (long) maplen);
259              _zzip_munmap(io->fd.sys, fd_map, (zzip_size_t)maplen); 
260              fd_map = 0; 
261          }
262     } /*outer loop*/
263                
264  cleanup:
265     if (USE_MMAP && fd_map)
266     { 
267         HINT3("unmap *%p len=%li",  fd_map, (long) maplen);
268         _zzip_munmap(io->fd.sys, fd_map, (zzip_size_t)maplen); 
269     }
270 #   ifdef _LOWSTK
271     free(buf);
272 #   endif
273 #   undef return
274     return e; 
275 }
276
277 /*
278  * making pointer alignments to values that can be handled as structures
279  * is tricky. We assume here that an align(4) is sufficient even for
280  * 64 bit machines. Note that binary operations are not usually allowed
281  * to pointer types but we do need only the lower bits in this implementation,
282  * so we can just cast the value to a long value.
283  */
284 _zzip_inline char* __zzip_aligned4(char* p)
285 {
286 #define aligned4   __zzip_aligned4
287     p += ((long)p)&1;            /* warnings about truncation of a "pointer" */
288     p += ((long)p)&2;            /* to a "long int" may be safely ignored :) */
289     return p;
290 }
291
292 /**
293  * This function is used by => zzip_file_open, it is usually called after
294  * => __zzip_find_disk_trailer. It will parse the zip's central directory
295  * information and create a zziplib private directory table in
296  * memory.
297  */
298 int 
299 __zzip_parse_root_directory(int fd, 
300     struct zzip_disk_trailer * trailer, 
301     struct zzip_dir_hdr ** hdr_return,
302     zzip_plugin_io_t io)
303 {
304     auto struct zzip_disk_entry dirent;
305     struct zzip_dir_hdr * hdr;
306     struct zzip_dir_hdr * hdr0;
307     uint16_t * p_reclen = 0;
308     short entries; 
309     long offset;          /* offset from start of root directory */
310     char* fd_map = 0; 
311     int32_t  fd_gap = 0;
312     uint16_t u_entries  = zzip_disk_trailer_get_entries (trailer);   
313     uint32_t u_rootsize = zzip_disk_trailer_get_rootsize (trailer);  
314     uint32_t u_rootseek = zzip_disk_trailer_get_rootseek (trailer);
315     __correct_rootseek (u_rootseek, u_rootsize, trailer);
316
317     hdr0 = (struct zzip_dir_hdr*) malloc(u_rootsize);
318     if (!hdr0) 
319         return ZZIP_DIRSIZE;
320     hdr = hdr0;                  __debug_dir_hdr (hdr);
321
322     if (USE_MMAP && io->fd.sys)
323     {
324         fd_gap = u_rootseek & (_zzip_getpagesize(io->fd.sys)-1) ;
325         HINT4(" mapseek=0x%x, maplen=%d, fd_gap=%d", 
326               u_rootseek-fd_gap, u_rootsize+fd_gap, fd_gap);
327         fd_map = _zzip_mmap(io->fd.sys, fd,
328                             u_rootseek-fd_gap, u_rootsize+fd_gap);
329         /* if mmap failed we will fallback to seek/read mode */
330         if (fd_map == MAP_FAILED) { 
331             NOTE2("map failed: %s",strerror(errno)); 
332             fd_map=0; 
333         }else{
334             HINT3("mapped *%p len=%i", fd_map, u_rootsize+fd_gap);
335         }
336     }
337
338     for (entries=u_entries, offset=0; entries > 0; entries--)
339     {
340         register struct zzip_disk_entry * d;
341         uint16_t u_extras, u_comment, u_namlen;
342
343         if (fd_map) 
344         { d = (void*)(fd_map+fd_gap+offset); } /* fd_map+fd_gap==u_rootseek */
345         else
346         {
347             if (io->fd.seeks(fd, u_rootseek+offset, SEEK_SET) < 0)
348                 return ZZIP_DIR_SEEK;
349             if (io->fd.read(fd, &dirent, sizeof(dirent)) < __sizeof(dirent))
350                 return ZZIP_DIR_READ;
351             d = &dirent;
352         }
353
354         if (offset+sizeof(*d) > u_rootsize)
355         { FAIL2("%i's entry stretches beyond root directory", entries); break;}
356
357 #       if 0 && defined DEBUG
358         zzip_debug_xbuf ((unsigned char*) d, sizeof(*d) + 8);
359 #       endif        
360         
361         u_extras  = zzip_disk_entry_get_extras (d);
362         u_comment = zzip_disk_entry_get_comment (d); 
363         u_namlen  = zzip_disk_entry_get_namlen (d); 
364         HINT5("offset=0x%lx, size %ld, dirent *%p, hdr %p\n",
365               offset+u_rootseek, (long)u_rootsize, d, hdr);
366
367         /* writes over the read buffer, Since the structure where data is
368            copied is smaller than the data in buffer this can be done.
369            It is important that the order of setting the fields is considered
370            when filling the structure, so that some data is not trashed in
371            first structure read.
372            at the end the whole copied list of structures  is copied into
373            newly allocated buffer */
374         hdr->d_crc32 = zzip_disk_entry_get_crc32 (d);
375         hdr->d_csize = zzip_disk_entry_get_csize (d); 
376         hdr->d_usize = zzip_disk_entry_get_usize (d);
377         hdr->d_off   = zzip_disk_entry_get_offset (d);
378         hdr->d_compr = zzip_disk_entry_get_compr (d);
379         if (hdr->d_compr > 255) hdr->d_compr = 255;
380
381         if (offset+sizeof(*d) + u_namlen > u_rootsize)
382         { FAIL2("%i's name stretches beyond root directory", entries); break;}
383
384         if (fd_map) 
385         {  memcpy(hdr->d_name, fd_map+fd_gap + offset+sizeof(*d), u_namlen); }
386         else { io->fd.read(fd, hdr->d_name, u_namlen); }
387         hdr->d_name[u_namlen] = '\0'; 
388         hdr->d_namlen = u_namlen;
389     
390         /* update offset by the total length of this entry -> next entry */
391         offset += sizeof(*d) + u_namlen + u_extras + u_comment;
392     
393         if (offset > (long)u_rootsize)
394         { FAIL2("%i's end beyond root directory", entries); entries--; break;}
395
396         HINT5("file %d { compr=%d crc32=$%x offset=%d", 
397               entries,  hdr->d_compr, hdr->d_crc32, hdr->d_off);
398         HINT5("csize=%d usize=%d namlen=%d extras=%d", 
399               hdr->d_csize, hdr->d_usize, u_namlen, u_extras);
400         HINT5("comment=%d name='%s' %s <sizeof %d> } ", 
401               u_comment, hdr->d_name, "",(int) sizeof(*d));
402   
403         p_reclen = &hdr->d_reclen;
404     
405         {   register char* p = (char*) hdr; 
406             register char* q = aligned4 (p + sizeof(*hdr) + u_namlen + 1);
407             *p_reclen = (uint16_t)(q - p);
408             hdr = (struct zzip_dir_hdr*) q;
409         }
410     }/*for*/
411     
412     if (USE_MMAP && fd_map) 
413     {
414         HINT3("unmap *%p len=%i",   fd_map, u_rootsize+fd_gap);
415         _zzip_munmap(io->fd.sys, fd_map, u_rootsize+fd_gap);
416     }
417     
418     if (p_reclen)
419     {
420         *p_reclen = 0; /* mark end of list */
421     
422         if (hdr_return) 
423             *hdr_return = hdr0;
424     } /* else zero (sane) entries */
425     return (entries ?  ZZIP_CORRUPTED : 0);
426 }
427
428 /* ------------------------- high-level interface ------------------------- */
429
430 #ifndef O_BINARY
431 #define O_BINARY 0
432 #endif
433
434 static zzip_strings_t* zzip_get_default_ext(void)
435 {
436     static zzip_strings_t ext [] =
437     {
438        ".zip", ".ZIP", /* common extension */
439 #  ifdef ZZIP_USE_ZIPLIKES
440        ".pk3", ".PK3", /* ID Software's Quake3 zipfiles */
441        ".jar", ".JAR", /* Java zipfiles */ 
442 #  endif
443        0
444     };
445
446     return ext;
447 }
448
449 /**
450  * allocate a new ZZIP_DIR handle and do basic 
451  * initializations before usage by => zzip_dir_fdopen
452  * => zzip_dir_open => zzip_file_open or through
453  * => zzip_open
454  * (ext==null flags uses { ".zip" , ".ZIP" } )
455  * (io ==null flags use of posix io defaults)
456  */
457 ZZIP_DIR*
458 zzip_dir_alloc_ext_io (zzip_strings_t* ext, const zzip_plugin_io_t io)
459 {
460     ZZIP_DIR* dir;
461     if ((dir = (ZZIP_DIR *)calloc(1, sizeof(*dir))) == NULL)
462         return 0; 
463
464     /* dir->fileext is currently unused - so what, still initialize it */
465     dir->fileext = ext ? ext : zzip_get_default_ext();
466     dir->io = io ? io : zzip_get_default_io ();
467     return dir;
468 }
469
470 /** => zzip_dir_alloc_ext_io
471  * this function is obsolete - it was generally used for implementation
472  * and exported to let other code build on it. It is now advised to
473  * use => zzip_dir_alloc_ext_io now on explicitly, just set that second
474  * argument to zero to achieve the same functionality as the old style.
475  */
476 ZZIP_DIR*
477 zzip_dir_alloc (zzip_strings_t* fileext)
478 {
479     return zzip_dir_alloc_ext_io (fileext, 0);
480 }
481
482 /**
483  * will free the zzip_dir handle unless there are still 
484  * zzip_files attached (that may use its cache buffer).
485  * This is the inverse of => zzip_dir_alloc , and both
486  * are helper functions used implicitly in other zzipcalls
487  * e.g. => zzip_dir_close = zzip_close 
488  *
489  * returns zero on sucess
490  * returns the refcount when files are attached.
491  */
492 int 
493 zzip_dir_free(ZZIP_DIR * dir)
494 {
495     if (dir->refcount)
496         return (dir->refcount); /* still open files attached */
497
498     if (dir->fd >= 0)      dir->io->fd.close(dir->fd);
499     if (dir->hdr0)         free(dir->hdr0);
500     if (dir->cache.fp)     free(dir->cache.fp);
501     if (dir->cache.buf32k) free(dir->cache.buf32k);
502     if (dir->realname)     free(dir->realname);
503     free(dir);
504     return 0;
505 }
506
507 /** => zzip_dir_free
508  * It will also => free(2) the => ZZIP_DIR-handle given. 
509  * the counterpart for => zzip_dir_open
510  * see also => zzip_dir_free
511  */
512 int 
513 zzip_dir_close(ZZIP_DIR * dir)
514 {
515     dir->refcount &=~ 0x10000000; /* explicit dir close */
516     return zzip_dir_free(dir);
517 }
518
519 /** 
520  * used by the => zzip_dir_open and zzip_opendir(2) call. Opens the
521  * zip-archive as specified with the fd which points to an
522  * already openend file. This function then search and parse
523  * the zip's central directory.
524  *  
525  * NOTE: refcount is zero, so an _open/_close pair will also delete 
526  *       this _dirhandle 
527  */
528 ZZIP_DIR * 
529 zzip_dir_fdopen(int fd, zzip_error_t * errcode_p)
530 {
531     return zzip_dir_fdopen_ext_io(fd, errcode_p, 0, 0);
532 }
533
534 static zzip_error_t __zzip_dir_parse (ZZIP_DIR* dir); /* forward */
535
536 /** => zzip_dir_fdopen
537  * this function uses explicit ext and io instead of the internal 
538  * defaults, setting these to zero is equivalent to => zzip_dir_fdopen
539  */
540 ZZIP_DIR * 
541 zzip_dir_fdopen_ext_io(int fd, zzip_error_t * errcode_p,
542                        zzip_strings_t* ext, const zzip_plugin_io_t io)
543 {
544     zzip_error_t rv;
545     ZZIP_DIR * dir;
546
547     if ((dir = zzip_dir_alloc_ext_io (ext, io)) == NULL)
548         { rv = ZZIP_OUTOFMEM; goto error; }
549
550     dir->fd = fd;
551     if ((rv = __zzip_dir_parse (dir)))
552         goto error;
553
554     dir->hdr = dir->hdr0;
555     dir->refcount |= 0x10000000; 
556   
557     if (errcode_p) *errcode_p = rv;
558     return dir;
559 error:
560     if (dir) zzip_dir_free(dir);
561     if (errcode_p) *errcode_p = rv;
562     return NULL;
563 }
564
565 static zzip_error_t
566 __zzip_dir_parse (ZZIP_DIR* dir)
567 {
568     zzip_error_t rv;
569     zzip_off_t filesize;
570     struct zzip_disk_trailer trailer;
571     /* if (! dir || dir->fd < 0) 
572      *     { rv = EINVAL; goto error; } 
573      */
574
575     HINT2("------------------ fd=%i", (int) dir->fd);
576     if ((filesize = dir->io->fd.filesize(dir->fd)) < 0)
577         { rv = ZZIP_DIR_STAT; goto error; }
578
579     HINT2("------------------ filesize=%ld", (long) filesize);
580     if ((rv = __zzip_find_disk_trailer(dir->fd, filesize, &trailer, 
581                                        dir->io)) != 0)
582         { goto error; }
583                 
584     HINT5("directory = { entries= %ld/%ld, size= %ld, seek= %ld } ", 
585           (long) zzip_disk_trailer_localentries (&trailer),
586           (long) zzip_disk_trailer_finalentries (&trailer),
587           (long) zzip_disk_trailer_rootsize (&trailer),
588           (long) zzip_disk_trailer_rootseek (&trailer));
589     
590     if ( (rv = __zzip_parse_root_directory(dir->fd, &trailer, &dir->hdr0, 
591                                            dir->io)) != 0)
592         { goto error; }
593  error:
594     return rv;
595 }
596
597 /**
598  * will attach a .zip extension and tries to open it
599  * the with => open(2). This is a helper function for
600  * => zzip_dir_open, => zzip_opendir and => zzip_open.
601  */
602 int
603 __zzip_try_open(zzip_char_t* filename, int filemode, 
604                 zzip_strings_t* ext, zzip_plugin_io_t io)
605 {
606     auto char file[PATH_MAX];
607     int fd;
608     zzip_size_t len = strlen (filename);
609     
610     if (len+4 >= PATH_MAX) return -1;
611     memcpy(file, filename, len+1);
612
613     if (!io) io = zzip_get_default_io();
614     if (!ext) ext = zzip_get_default_ext();
615
616     for ( ; *ext ; ++ext)
617     {
618         strcpy (file+len, *ext);
619         fd = io->fd.open(file, filemode);
620         if (fd != -1) return fd;
621     }
622     return -1;
623 }    
624
625 /**
626  * Opens the zip-archive (if available).
627  * the two ext_io arguments will default to use posix io and 
628  * a set of default fileext that can atleast add .zip ext itself.
629  */
630 ZZIP_DIR* 
631 zzip_dir_open(zzip_char_t* filename, zzip_error_t* e)
632 {
633     return zzip_dir_open_ext_io (filename, e, 0, 0);
634 }
635
636 /** => zzip_dir_open
637  * this function uses explicit ext and io instead of the internal 
638  * defaults. Setting these to zero is equivalent to => zzip_dir_open
639  */
640 ZZIP_DIR* 
641 zzip_dir_open_ext_io(zzip_char_t* filename, zzip_error_t* e,
642                      zzip_strings_t* ext, zzip_plugin_io_t io)
643 {
644     int fd;
645
646     if (!io) io = zzip_get_default_io();
647     if (!ext) ext = zzip_get_default_ext();
648
649     fd = io->fd.open(filename, O_RDONLY|O_BINARY);
650     if (fd != -1) 
651       { return zzip_dir_fdopen_ext_io(fd, e, ext, io); }
652     else
653     {
654         fd = __zzip_try_open(filename, O_RDONLY|O_BINARY, ext, io);
655         if (fd != -1) 
656           { return zzip_dir_fdopen_ext_io(fd, e, ext, io); }
657         else
658         {
659             if (e) { *e = ZZIP_DIR_OPEN; } 
660             return 0; 
661         }
662     }
663 }
664
665 /** => zzip_dir_open
666  * fills the dirent-argument with the values and 
667  * increments the read-pointer of the dir-argument.
668  * 
669  * returns 0 if there no entry (anymore).
670  */
671 int
672 zzip_dir_read(ZZIP_DIR * dir, ZZIP_DIRENT * d )
673 {
674     if (! dir || ! dir->hdr || ! d) return 0;
675
676     d->d_compr = dir->hdr->d_compr;
677     d->d_csize = dir->hdr->d_csize;
678     d->st_size = dir->hdr->d_usize;
679     d->d_name  = dir->hdr->d_name;
680
681     if (! dir->hdr->d_reclen) 
682     { dir->hdr = 0; }
683     else  
684     { dir->hdr = (struct zzip_dir_hdr *)((char *)dir->hdr + dir->hdr->d_reclen); }
685   
686     return 1;
687 }
688
689 /* 
690  * Local variables:
691  * c-file-style: "stroustrup"
692  * End:
693  */