]> granicus.if.org Git - zziplib/blob - zzip/file.c
indentation
[zziplib] / zzip / file.c
1
2 /*
3  * Author: 
4  *      Guido Draheim <guidod@gmx.de>
5  *      Tomi Ollila <Tomi.Ollila@iki.fi>
6  *
7  * Copyright (c) 1999,2000,2001,2002,2003 Guido Draheim
8  *          All rights reserved,
9  *          use under the restrictions of the
10  *          Lesser GNU General Public License
11  *          or alternatively the restrictions 
12  *          of the Mozilla Public License 1.1
13  */
14
15 #include <zzip/lib.h>           /* exported... */
16 #include <zzip/file.h>
17
18 #include <string.h>
19 #include <sys/stat.h>
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <ctype.h>
23
24 #include <zzip/format.h>
25 #include <zzip/fetch.h>
26 #include <zzip/__debug.h>
27
28 #if 0
29 # if defined ZZIP_HAVE_IO_H
30 # include <io.h>                /* tell */
31 # else
32 # define tell(fd) lseek(fd,0,SEEK_CUR)
33 # endif
34 #else
35 #define tells(fd) seeks(fd,0,SEEK_CUR)
36 #endif
37
38 /**
39  * the direct function of => zzip_close(fp). it will cleanup the
40  * inflate-portion of => zlib and free the structure given.
41  * 
42  * it is called quite from the error-cleanup parts
43  * of the various => _open functions. 
44  * 
45  * the .refcount is decreased and if zero the fp->dir is closed just as well.
46  */
47 int
48 zzip_file_close(ZZIP_FILE * fp)
49 {
50     auto int self;
51     ZZIP_DIR *dir = fp->dir;
52
53     if (fp->method)
54         inflateEnd(&fp->d_stream);      /* inflateEnd() can be called many times */
55
56     if (dir->cache.locked == NULL)
57         dir->cache.locked = &self;
58
59     if (fp->buf32k)
60     {
61         if (dir->cache.locked == &self && dir->cache.buf32k == NULL)
62             dir->cache.buf32k = fp->buf32k;
63         else
64             free(fp->buf32k);
65     }
66
67     if (dir->currentfp == fp)
68         dir->currentfp = NULL;
69
70     dir->refcount--;
71     /* ease to notice possible dangling reference errors */
72     memset(fp, 0, sizeof(*fp));
73
74     if (dir->cache.locked == &self && dir->cache.fp == NULL)
75         dir->cache.fp = fp;
76     else
77         free(fp);
78
79     if (dir->cache.locked == &self)
80         dir->cache.locked = NULL;
81
82     if (! dir->refcount)
83         return zzip_dir_close(dir);
84     else
85         return 0;
86 }
87
88
89 static int
90 zzip_file_saveoffset(ZZIP_FILE * fp)
91 {
92     if (fp)
93     {
94         int fd = fp->dir->fd;
95         zzip_off_t off = fp->io->fd.seeks(fd, 0, SEEK_CUR);
96
97         if (off < 0)
98             return -1;
99
100         fp->offset = off;
101     }
102     return 0;
103 }
104
105 # ifndef ZZIP_CHECK_BACKSLASH_DIRSEPARATOR      /* NOTE: also default */
106 # define ZZIP_CHECK_BACKSLASH_DIRSEPARATOR 0    /* to "NO" on win32 ! */
107 # endif
108
109 # if ! defined strcasecmp && ! defined ZZIP_HAVE_STRCASECMP
110 # define ZZIP_CHECK_BACKSLASH_DIRSEPARATOR 1
111 # endif
112
113 #if ! ZZIP_CHECK_BACKSLASH_DIRSEPARATOR+0
114 #define dirsep_strrchr(N,C) strrchr(N,C)
115 #define dirsep_casecmp strcasecmp
116 #else
117 #define dirsep_strrchr(N,C) _dirsep_strrchr(N)
118 #define dirsep_casecmp _dirsep_casecmp
119 static zzip_char_t *
120 _dirsep_strrchr(zzip_char_t * name)
121 {
122     char *n = strrchr(name, '/');
123     char *m = strrchr(name, '\\');
124
125     if (m && n && m > n)
126         n = m;
127     return n;
128 }
129 static int
130 _dirsep_casecmp(zzip_char_t * s1, zzip_char_t * s2)
131 {
132     /* ASCII tolower - including mapping of backslash in normal slash */
133     static const char mapping[] = "@abcdefghijklmnopqrstuvwxyz[/]^_";
134     int c1, c2;
135
136     while (*s1 && *s2)
137     {
138         c1 = (int) (unsigned char) *s1;
139         c2 = (int) (unsigned char) *s2;
140         if ((c1 & 0xE0) == 0x40)
141             c1 = mapping[c1 & 0x1f];
142         if ((c2 & 0xE0) == 0x40)
143             c2 = mapping[c2 & 0x1f];
144         if (c1 != c2)
145             return (c1 - c2);
146         s1++;
147         s2++;
148     }
149
150     return (((int) (unsigned char) *s1) - ((int) (unsigned char) *s2));
151 }
152 #endif
153
154 static int zzip_inflate_init(ZZIP_FILE *, struct zzip_dir_hdr *);
155
156 /**
157  * open an => ZZIP_FILE from an already open => ZZIP_DIR handle. Since
158  * we have a chance to reuse a cached => buf32k and => ZZIP_FILE memchunk
159  * this is the best choice to unpack multiple files.
160  * 
161  * Note: the zlib supports 2..15 bit windowsize, hence we provide a 32k
162  *       memchunk here... just to be safe.
163  *
164  * On error it returns null and sets errcode in the ZZIP_DIR.
165  */
166 ZZIP_FILE *
167 zzip_file_open(ZZIP_DIR * dir, zzip_char_t * name, int o_mode)
168 {
169     auto int self;
170     zzip_error_t err = 0;
171     struct zzip_file *fp = 0;
172     struct zzip_dir_hdr *hdr = dir->hdr0;
173     int (*cmp) (zzip_char_t *, zzip_char_t *);
174
175     cmp = (o_mode & ZZIP_CASELESS) ? dirsep_casecmp : strcmp;
176
177     if (! dir)
178         return NULL;
179     if (! dir->fd || dir->fd == -1)
180         { dir->errcode = EBADF; return NULL; }
181     if (! hdr)
182         { dir->errcode = ENOENT; return NULL; }
183
184     if (o_mode & ZZIP_NOPATHS)
185     {
186         register zzip_char_t *n = dirsep_strrchr(name, '/');
187
188         if (n)
189             name = n + 1;
190     }
191
192     while (1)
193     {
194         register zzip_char_t *hdr_name = hdr->d_name;
195
196         if (o_mode & ZZIP_NOPATHS)
197         {
198             register zzip_char_t *n = dirsep_strrchr(hdr_name, '/');
199
200             if (n)
201                 hdr_name = n + 1;
202         }
203
204         HINT4("name='%s', compr=%d, size=%d\n",
205               hdr->d_name, hdr->d_compr, hdr->d_usize);
206
207         if (! cmp(hdr_name, name))
208         {
209             switch (hdr->d_compr)
210             {
211             case 0:            /* store */
212             case 8:            /* inflate */
213                 break;
214             default:
215                 { err = ZZIP_UNSUPP_COMPR; goto error; }
216             }
217
218             if (dir->cache.locked == NULL)
219                 dir->cache.locked = &self;
220
221             if (dir->cache.locked == &self && dir->cache.fp)
222             {
223                 fp = dir->cache.fp;
224                 dir->cache.fp = NULL;
225                 /* memset(zfp, 0, sizeof *fp); cleared in zzip_file_close() */
226             } else
227             {
228                 if (! (fp = (ZZIP_FILE *) calloc(1, sizeof(*fp))))
229                     { err =  ZZIP_OUTOFMEM; goto error; }
230             }
231
232             fp->dir = dir;
233             fp->io = dir->io;
234             dir->refcount++;
235
236             if (dir->cache.locked == &self && dir->cache.buf32k)
237             {
238                 fp->buf32k = dir->cache.buf32k;
239                 dir->cache.buf32k = NULL;
240             } else
241             {
242                 if (! (fp->buf32k = (char *) malloc(ZZIP_32K)))
243                     { err = ZZIP_OUTOFMEM; goto error; }
244             }
245
246             if (dir->cache.locked == &self)
247                 dir->cache.locked = NULL;
248             /*
249              * In order to support simultaneous open files in one zip archive
250              * we'll fix the fd offset when opening new file/changing which
251              * file to read...
252              */
253
254             if (zzip_file_saveoffset(dir->currentfp) < 0)
255                 { err = ZZIP_DIR_SEEK; goto error; }
256
257             fp->offset = hdr->d_off;
258             dir->currentfp = fp;
259
260             if (dir->io->fd.seeks(dir->fd, hdr->d_off, SEEK_SET) < 0)
261                 { err = ZZIP_DIR_SEEK; goto error; }
262
263             {                   /* skip local header - should test tons of other info, 
264                                  * but trust that those are correct */
265                 zzip_ssize_t dataoff;
266                 struct zzip_file_header *p = (void *) fp->buf32k;
267
268                 dataoff = dir->io->fd.read(dir->fd, (void *) p, sizeof(*p));
269                 if (dataoff < (zzip_ssize_t) sizeof(*p))
270                     { err = ZZIP_DIR_READ;  goto error; }
271                 if (! zzip_file_header_check_magic(p))   /* PK\3\4 */
272                     { err = ZZIP_CORRUPTED; goto error; }
273
274                 dataoff = zzip_file_header_sizeof_tail(p);
275
276                 if (dir->io->fd.seeks(dir->fd, dataoff, SEEK_CUR) < 0)
277                     { err = ZZIP_DIR_SEEK; goto error; }
278
279                 fp->dataoffset = dir->io->fd.tells(dir->fd);
280                 fp->usize = hdr->d_usize;
281                 fp->csize = hdr->d_csize;
282             }
283
284             err = zzip_inflate_init(fp, hdr);
285             if (err)
286                 goto error;
287
288             return fp;
289         } else
290         {
291             if (hdr->d_reclen == 0)
292                 break;
293             hdr = (struct zzip_dir_hdr *) ((char *) hdr + hdr->d_reclen);
294         }                       /*cmp name */
295     }                           /*forever */
296     dir->errcode = ZZIP_ENOENT;
297     return NULL;
298   error:
299     if (fp)
300         zzip_file_close(fp);
301     dir->errcode = err;
302     return NULL;
303 }
304
305 /**
306  *  call => inflateInit and setup fp's iterator variables, 
307  *  used by lowlevel => _open functions.
308  */
309 static int
310 zzip_inflate_init(ZZIP_FILE * fp, struct zzip_dir_hdr *hdr)
311 {
312     int err;
313
314     fp->method = hdr->d_compr;
315     fp->restlen = hdr->d_usize;
316
317     if (fp->method)
318     {
319         memset(&fp->d_stream, 0, sizeof(fp->d_stream));
320
321         err = inflateInit2(&fp->d_stream, -MAX_WBITS);
322         if (err != Z_OK)
323             goto error;
324
325         fp->crestlen = hdr->d_csize;
326     }
327     return 0;
328   error:
329     if (fp)
330         zzip_file_close(fp);
331     return err;
332 }
333
334 /**                                                             
335  * This function closes the given ZZIP_FILE handle. 
336  *
337  * If the ZZIP_FILE wraps a normal stat'fd then it is just that int'fd 
338  * that is being closed and the otherwise empty ZZIP_FILE gets freed.
339  */
340 int
341 zzip_fclose(ZZIP_FILE * fp)
342 {
343     if (! fp)
344         return 0;
345     if (! fp->dir)
346         { int r = fp->io->fd.close(fp->fd); free(fp); return r; }        /* stat fd */
347     else
348         return zzip_file_close(fp);
349 }
350
351 /** => zzip_fclose
352  */
353 int
354 zzip_close(ZZIP_FILE * fp)
355 {
356     return zzip_fclose(fp);
357 }
358
359 /**                                                              
360  * This functions read data from zip-contained file.
361  *
362  * It works like => read(2) and will fill the given buffer with bytes from
363  * the opened file. It will return the number of bytes read, so if the => EOF
364  * is encountered you will be prompted with the number of bytes actually read.
365  * 
366  * This is the routines that needs the => buf32k buffer, and it would have
367  * need for much more polishing but it does already work quite well.
368  * 
369  * Note: the 32K buffer is rather big. The original inflate-algorithm
370  *       required just that but the latest zlib would work just fine with
371  *       a smaller buffer.
372  */
373 zzip_ssize_t
374 zzip_file_read(ZZIP_FILE * fp, void *buf, zzip_size_t len)
375 {
376     ZZIP_DIR *dir;
377     zzip_size_t l;
378     zzip_ssize_t rv;
379
380     if (! fp || ! fp->dir)
381         return 0;
382
383     dir = fp->dir;
384     l = fp->restlen > len ? len : fp->restlen;
385     if (fp->restlen == 0)
386         return 0;
387
388     /*
389      * If this is other handle than previous, save current seek pointer
390      * and read the file position of `this' handle.
391      */
392     if (dir->currentfp != fp)
393     {
394         if (zzip_file_saveoffset(dir->currentfp) < 0
395             || fp->io->fd.seeks(dir->fd, fp->offset, SEEK_SET) < 0)
396             { dir->errcode = ZZIP_DIR_SEEK; return -1; }
397         else
398             { dir->currentfp = fp; }
399     }
400
401     /* if more methods is to be supported, change this to `switch ()' */
402     if (fp->method)             /* method != 0   == 8, inflate */
403     {
404         fp->d_stream.avail_out = l;
405         fp->d_stream.next_out = (unsigned char *) buf;
406
407         do
408         {
409             int err;
410             zzip_size_t startlen;
411
412             if (fp->crestlen > 0 && fp->d_stream.avail_in == 0)
413             {
414                 zzip_size_t cl = (fp->crestlen < ZZIP_32K ?
415                                   fp->crestlen : ZZIP_32K);
416                 /*  zzip_size_t cl = fp->crestlen > 128 ? 128 : fp->crestlen; */
417
418                 zzip_ssize_t i = fp->io->fd.read(dir->fd, fp->buf32k, cl);
419
420                 if (i <= 0)
421                 {
422                     dir->errcode = ZZIP_DIR_READ;       /* or ZZIP_DIR_READ_EOF ? */
423                     return -1;
424                 }
425                 fp->crestlen -= i;
426                 fp->d_stream.avail_in = i;
427                 fp->d_stream.next_in = (unsigned char *) fp->buf32k;
428             }
429
430             startlen = fp->d_stream.total_out;
431             err = inflate(&fp->d_stream, Z_NO_FLUSH);
432
433             if (err == Z_STREAM_END)
434                 { fp->restlen = 0; }
435             else if (err == Z_OK)
436                 { fp->restlen -= (fp->d_stream.total_out - startlen); }
437             else
438                 { dir->errcode = err; return -1; }
439         }
440         while (fp->restlen && fp->d_stream.avail_out);
441
442         return l - fp->d_stream.avail_out;
443     } else
444     {                           /* method == 0 -- unstore */
445         rv = fp->io->fd.read(dir->fd, buf, l);
446         if (rv > 0)
447             { fp->restlen-= rv; }
448         else if (rv < 0)
449             { dir->errcode = ZZIP_DIR_READ; }
450         return rv;
451     }
452 }
453
454 /**                                                               
455  * This function will read(2) data from a real/zipped file.
456  *
457  * the replacement for => read(2) will fill the given buffer with bytes from
458  * the opened file. It will return the number of bytes read, so if the EOF
459  * is encountered you will be prompted with the number of bytes actually read.
460  * 
461  * If the file-handle is wrapping a stat'able file then it will actually just
462  * perform a normal => read(2)-call, otherwise => zzip_file_read is called
463  * to decompress the data stream and any error is mapped to => errno(3).
464  */
465 zzip_ssize_t
466 zzip_read(ZZIP_FILE * fp, void *buf, zzip_size_t len)
467 {
468     if (! fp)
469         return 0;
470     if (! fp->dir)
471         { return fp->io->fd.read(fp->fd, buf, len); }    /* stat fd */
472     else
473     {
474         register zzip_ssize_t v;
475
476         v = zzip_file_read(fp, buf, len);
477         if (v == -1)
478             { errno = zzip_errno(fp->dir->errcode); }
479         return v;
480     }
481 }
482
483 /** => zzip_read
484  */
485 zzip_size_t
486 zzip_fread(void *ptr, zzip_size_t size, zzip_size_t nmemb, ZZIP_FILE * file)
487 {
488     if (! size)
489         size = 1;
490     return zzip_read(file, ptr, size * nmemb) / size;
491 }
492
493
494 #define ZZIP_WRONLY             O_WRONLY
495 #define ZZIP_EXCL               O_EXCL
496
497 #if     defined                 O_SYNC
498 #define ZZIP_SYNC               O_SYNC
499 #else
500 #define ZZIP_SYNC               0
501 #endif
502
503 #if     defined                 O_NONBLOCK
504 #define ZZIP_NONBLOCK           O_NONBLOCK
505 #elif   defined                 O_NDELAY
506 #define ZZIP_NOCTTY             O_NDELAY
507 #else
508 #define ZZIP_NOCTTY             0
509 #endif
510
511 /* ------------------------------------------------------------------- */
512
513 /**                                                          also: fopen(2)  
514  * This function will => fopen(3) a real/zipped file.
515  * 
516  * It has some magic functionality builtin - it will first try to open
517  * the given <em>filename</em> as a normal file. If it does not
518  * exist, the given path to the filename (if any) is split into
519  * its directory-part and the file-part. A ".zip" extension is
520  * then added to the directory-part to create the name of a
521  * zip-archive. That zip-archive (if it exists) is being searched
522  * for the file-part, and if found a zzip-handle is returned. 
523  * 
524  * Note that if the file is found in the normal fs-directory the
525  * returned structure is mostly empty and the => zzip_read call will
526  * use the libc => read to obtain data. Otherwise a => zzip_file_open 
527  * is performed and any error mapped to => errno(3).
528  * 
529  * unlike the posix-wrapper => zzip_open the mode-argument is
530  * a string which allows for more freedom to support the extra
531  * zzip modes called ZZIP_CASEINSENSITIVE and ZZIP_IGNOREPATH.
532  * Currently, this => zzip_fopen call will convert the following
533  * characters in the mode-string into their corrsponding mode-bits: 
534  * * <code> "r" : O_RDONLY : </code> read-only
535  * * <code> "b" : O_BINARY : </code> binary (win32 specific)
536  * * <code> "f" : O_NOCTTY : </code> no char device (unix)
537  * * <code> "i" : ZZIP_CASELESS : </code> inside zip file
538  * * <code> "*" : ZZIP_NOPATHS : </code> inside zip file only
539  * all other modes will be ignored for zip-contained entries
540  * but they are transferred for compatibility and portability,
541  * including these extra sugar bits:
542  * * <code> "x" : O_EXCL :</code> fail if file did exist
543  * * <code> "s" : O_SYNC :</code> synchronized access
544  * * <code> "n" : O_NONBLOCK :</code> nonblocking access
545  * * <code> "z#" : compression level :</code> for zlib
546  * * <code> "g#" : group access :</code> unix access bits
547  * * <code> "u#" : owner access :</code> unix access bits
548  * * <code> "o#" : world access :</code> unix access bits
549  * ... the access bits are in traditional unix bit format
550  * with 7 = read/write/execute, 6 = read/write, 4 = read-only.
551  *
552  * The default access mode is 0664, and the compression level
553  * is ignored since the lib can not yet write zip files, otherwise
554  * it would be the initialisation value for the zlib deflateInit
555  * where 0 = no-compression, 1 = best-speed, 9 = best-compression.
556  *
557  * This function returns a new zzip-handle (use => zzip_close to return
558  * it). On error this function will return null setting => errno(3).
559  */
560 ZZIP_FILE *
561 zzip_fopen(zzip_char_t * filename, zzip_char_t * mode)
562 {
563     return zzip_freopen(filename, mode, 0);
564 }
565
566 /** => zzip_fopen
567  *
568  * This function receives an additional argument pointing to
569  * a ZZIP_FILE* being already in use. If this extra argument is
570  * null then this function is identical with calling => zzip_fopen
571  *
572  * Per default, the old file stream is closed and only the internal
573  * structures associated with it are kept. These internal structures
574  * may be reused for the return value, and this is a lot quicker when
575  * the filename matches a zipped file that is incidently in the very
576  * same zip arch as the old filename wrapped in the stream struct.
577  *
578  * That's simply because the zip arch's central directory does not 
579  * need to be read again. As an extension for this function, if the 
580  * mode-string contains a "q" then the old stream is not closed but
581  * left untouched, instead it is only given as a hint that a new
582  * file handle may share/copy the zip arch structures of the old file
583  * handle if that is possible, i.e when they are in the same zip arch.
584  *
585  * This function returns a new zzip-handle (use => zzip_close to return
586  * it). On error this function will return null setting => errno(3).
587  */
588 ZZIP_FILE *
589 zzip_freopen(zzip_char_t * filename, zzip_char_t * mode, ZZIP_FILE * stream)
590 {
591     int o_flags = 0;
592     int o_modes = 0664;
593
594     if (! mode)
595         mode = "rb";
596
597 #   ifndef O_BINARY
598 #   define O_BINARY 0
599 #   endif
600 #   ifndef O_NOCTTY
601 #   define O_NOCTTY 0
602 #   endif
603 #   ifndef O_SYNC
604 #   define O_SYNC 0
605 #   endif
606 #   ifndef O_NONBLOCK
607 #   define O_NONBLOCK 0
608 #   endif
609
610     for (; *mode; mode++)
611     {
612         switch (*mode)
613         {
614             /* *INDENT-OFF* */
615         case '0': case '1': case '2': case '3': case '4': 
616         case '5': case '6': case '7': case '8': case '9':
617             continue; /* ignore if not attached to other info */
618         case 'r': o_flags |= mode[1] == '+' ? O_RDWR : O_RDONLY; break;
619         case 'w': o_flags |= mode[1] == '+' ? O_RDWR : O_WRONLY; 
620                   o_flags |= O_TRUNC; break;
621         case 'b': o_flags |= O_BINARY; break;
622         case 'f': o_flags |= O_NOCTTY; break;
623         case 'i': o_modes |= ZZIP_CASELESS; break;
624         case '*': o_modes |= ZZIP_NOPATHS; break;
625         case 'x': o_flags |= O_EXCL; break;
626         case 's': o_flags |= O_SYNC; break;
627         case 'n': o_flags |= O_NONBLOCK; break;
628         case 'o': o_modes &=~ 07; 
629                   o_modes |= ((mode[1] - '0')) & 07; continue;
630         case 'g': o_modes &=~ 070; 
631                   o_modes |= ((mode[1] - '0') << 3) & 070; continue;
632         case 'u': o_modes &=~ 0700; 
633                   o_modes |= ((mode[1] - '0') << 6) & 0700; continue;
634         case 'q': o_modes |= ZZIP_FACTORY; break;
635         case 'z': /* compression level */
636             continue; /* currently ignored, just for write mode */
637             /* *INDENT-ON* */
638         }
639     }
640
641     {
642         ZZIP_FILE *fp =
643             zzip_open_shared_io(stream, filename, o_flags, o_modes, 0, 0);
644
645         if (! (o_modes & ZZIP_FACTORY) && stream)
646             zzip_file_close(stream);
647
648         return fp;
649     }
650 }
651
652 /**                                                        
653  * This function will => open(2) a real/zipped file
654  *
655  * It has some magic functionality builtin - it will first try to open
656  * the given <em>filename</em> as a normal file. If it does not
657  * exist, the given path to the filename (if any) is split into
658  * its directory-part and the file-part. A ".zip" extension is
659  * then added to the directory-part to create the name of a
660  * zip-archive. That zip-archive (if it exists) is being searched
661  * for the file-part, and if found a zzip-handle is returned. 
662  * 
663  * Note that if the file is found in the normal fs-directory the
664  * returned structure is mostly empty and the => zzip_read call will
665  * use the libc => read to obtain data. Otherwise a => zzip_file_open 
666  * is performed and any error mapped to => errno(3).
667  * 
668  * There was a possibility to transfer zziplib-specific openmodes
669  * through o_flags but you should please not use them anymore and
670  * look into => zzip_open_ext_io to submit them down. This function
671  * is shallow in that it just extracts the zzipflags and calls 
672  * * <code>zzip_open_ext_io(filename, o_flags, zzipflags|0664, 0, 0) </code>
673  * you must stop using this extra functionality (not well known anyway) 
674  * since zzip_open might be later usable to open files for writing 
675  * in which case the _EXTRAFLAGS will get in conflict.
676  *
677  * compare with  => open(2) and => zzip_fopen
678  */
679 ZZIP_FILE *
680 zzip_open(zzip_char_t * filename, int o_flags)
681 {
682     /* backward compatibility */
683     int o_modes = 0664;
684
685     if (o_flags & ZZIP_CASEINSENSITIVE)
686         {  o_flags ^= ZZIP_CASEINSENSITIVE; o_modes |= ZZIP_CASELESS; }
687     if (o_flags & ZZIP_IGNOREPATH)
688         {  o_flags ^= ZZIP_IGNOREPATH;      o_modes |= ZZIP_NOPATHS; }
689     return zzip_open_ext_io(filename, o_flags, o_modes, 0, 0);
690 }
691
692 /* ZZIP_ONLYZIP won't work on platforms with sizeof(int) == 16bit */
693 #if ZZIP_SIZEOF_INT+0 == 2
694 #undef ZZIP_ONLYZIP
695 #endif
696
697 /** => zzip_open
698  *
699  * This function uses explicit ext and io instead of the internal 
700  * defaults, setting them to zero is equivalent to => zzip_open
701  * 
702  * note that the two flag types have been split into an o_flags
703  * (for fcntl-like openflags) and o_modes where the latter shall
704  * carry the zzip_flags and possibly accessmodes for unix filesystems.
705  * Since this version of zziplib can not write zipfiles, it is not
706  * yet used for anything else than zzip-specific modeflags.
707  *
708  * This function returns a new zzip-handle (use => zzip_close to return
709  * it). On error this function will return null setting => errno(3).
710  */
711 ZZIP_FILE *
712 zzip_open_ext_io(zzip_char_t * filename, int o_flags, int o_modes,
713                  zzip_strings_t * ext, zzip_plugin_io_t io)
714 {
715     return zzip_open_shared_io(0, filename, o_flags, o_modes, ext, io);
716 }
717
718 /** => zzip_open
719  * 
720  * This function takes an extra stream argument - if a handle has been
721  * then ext/io can be left null and the new stream handle will pick up 
722  * the ext/io. This should be used only in specific environment however 
723  * since => zzip_file_real does not store any ext-sequence.
724  *
725  * The benefit for this function comes in when the old file handle
726  * was openened from a file within a zip archive. When the new file
727  * is in the same zip archive then the internal zzip_dir structures
728  * will be shared. It is even quicker, as no check needs to be done
729  * anymore trying to guess the zip archive place in the filesystem,
730  * here we just check whether the zip archive's filepath is a prefix
731  * part of the filename to be opened. 
732  *
733  * Note that this function is also used by => zzip_freopen that
734  * will unshare the old handle, thereby possibly closing the handle.
735  *
736  * This function returns a new zzip-handle (use => zzip_close to return
737  * it). On error this function will return null setting => errno(3).
738  */
739 ZZIP_FILE *
740 zzip_open_shared_io(ZZIP_FILE * stream,
741                     zzip_char_t * filename, int o_flags, int o_modes,
742                     zzip_strings_t * ext, zzip_plugin_io_t io)
743 {
744     if (stream && stream->dir)
745     {
746         if (! ext)
747             ext = stream->dir->fileext;
748         if (! io)
749             io = stream->dir->io;
750     }
751     if (! io)
752         io = zzip_get_default_io();
753
754     if (o_modes & (ZZIP_PREFERZIP | ZZIP_ONLYZIP))
755         goto try_zzip;
756   try_real:
757     /* prefer an existing real file */
758     {
759         zzip_plugin_io_t os = (o_modes & ZZIP_ALLOWREAL)
760             ? zzip_get_default_io() : io;
761         int fd = os->fd.open(filename, o_flags);        /* io->fd.open */
762
763         if (fd != -1)
764         {
765             ZZIP_FILE *fp = calloc(1, sizeof(ZZIP_FILE));
766
767             if (! fp)
768                 { os->fd.close(fd); return 0; }  /* io->fd.close */
769
770             fp->fd = fd;
771             fp->io = os;
772             return fp;
773         }
774         if (o_modes & ZZIP_PREFERZIP)
775             return 0;
776     }
777   try_zzip:
778
779     /* if the user had it in place of a normal xopen, then
780      * we better defend this lib against illegal usage */
781     if (o_flags & (O_CREAT | O_WRONLY))
782         { errno = EINVAL; return 0; }
783     if (o_flags & (O_RDWR))
784         { o_flags ^= O_RDWR; o_flags |= O_RDONLY; }
785
786     /* this is just for backward compatibility -and strictly needed to
787      * prepare ourselves for more options and more options later on... */
788     /*# if (o_modes & ZZIP_CASELESS) { o_flags |= ZZIP_CASEINSENSITIVE; } */
789     /*# if (o_modes & ZZIP_NOPATHS)  { o_flags |= ZZIP_IGNOREPATH; } */
790
791     /* see if we can open a file that is a zip file */
792     {
793         char basename[PATH_MAX];
794         char *p;
795         int filename_len = strlen(filename);
796
797         if (filename_len >= PATH_MAX)
798             { errno = ENAMETOOLONG; return 0; }
799         memcpy(basename, filename, filename_len + 1);
800
801         /* see if we can share the same zip directory */
802         if (stream && stream->dir && stream->dir->realname)
803         {
804             zzip_size_t len = strlen(stream->dir->realname);
805
806             if (! memcmp(filename, stream->dir->realname, len) &&
807                 filename[len] == '/' && filename[len + 1])
808             {
809                 ZZIP_FILE *fp =
810                     zzip_file_open(stream->dir, filename + len + 1, o_modes);
811                 if (! fp)
812                     { errno = zzip_errno (stream->dir->errcode); }
813                 return fp;
814             }
815         }
816
817         /* per each slash in filename, check if it there is a zzip around */
818         while ((p = strrchr(basename, '/')))
819         {
820             zzip_error_t e = 0;
821             ZZIP_DIR *dir;
822             ZZIP_FILE *fp;
823             int fd;
824
825             *p = '\0';          /* cut at path separator == possible zipfile basename */
826             fd = __zzip_try_open(basename, o_flags | O_RDONLY | O_BINARY, ext,
827                                  io);
828             if (fd == -1)
829                 { continue; }
830
831 /*    found: */
832             /* found zip-file, now try to parse it */
833             dir = zzip_dir_fdopen_ext_io(fd, &e, ext, io);
834             if (e)
835                 { errno = zzip_errno(e); io->fd.close(fd); return 0; }
836
837             /* (p - basename) is the lenghtof zzip_dir part of the filename */
838             fp = zzip_file_open(dir, filename + (p - basename) + 1, o_modes);
839             if (! fp)
840                 { errno = zzip_errno(dir->errcode); }
841             else
842                 { if (! dir->realname) dir->realname = strdup (basename); }
843
844             zzip_dir_close(dir);
845             /* note: since (fp) is attached that (dir) will survive */
846             /* but (dir) is implicitly closed on next zzip_close(fp) */
847
848             return fp;
849         }                       /*again */
850
851         if (o_modes & ZZIP_PREFERZIP)
852             goto try_real;
853         else
854             { errno = ENOENT; return 0; }
855     }
856 }
857
858 #if defined ZZIP_LARGEFILE_RENAME && defined EOVERFLOW && defined PIC
859 #undef zzip_open_shared_io      /* zzip_open_shared_io64 */
860 #undef zzip_open_ext_io         /* zzip_open_ext_io64 */
861 #undef zzip_opendir_ext_io      /* zzip_opendir_ext_io64 */
862
863 _zzip_export
864     ZZIP_FILE * zzip_open_shared_io(ZZIP_FILE * stream,
865                                     zzip_char_t * name, int o_flags,
866                                     int o_modes, zzip_strings_t * ext,
867                                     zzip_plugin_io_t io);
868 _zzip_export ZZIP_FILE *zzip_open_ext_io(zzip_char_t * name, int o_flags,
869                                          int o_modes, zzip_strings_t * ext,
870                                          zzip_plugin_io_t io);
871 _zzip_export ZZIP_DIR *zzip_opendir_ext_io(zzip_char_t * name, int o_modes,
872                                            zzip_strings_t * ext,
873                                            zzip_plugin_io_t io);
874
875 /* DLL compatibility layer - so that 32bit code can link with this lib too */
876
877 _zzip_export
878     ZZIP_FILE * zzip_open_shared_io(ZZIP_FILE * stream,
879                                     zzip_char_t * name, int o_flags,
880                                     int o_modes, zzip_strings_t * ext,
881                                     zzip_plugin_io_t io)
882 {
883     if (! io)
884         return zzip_open_shared_io64(stream, name, o_flags, o_modes, ext, io);
885     errno = EOVERFLOW;
886     return NULL;
887 }
888
889 _zzip_export
890     ZZIP_FILE * zzip_open_ext_io(zzip_char_t * name, int o_flags, int o_modes,
891                                  zzip_strings_t * ext, zzip_plugin_io_t io)
892 {
893     if (! io)
894         return zzip_open_ext_io64(name, o_flags, o_modes, ext, io);
895     errno = EOVERFLOW;
896     return NULL;
897 }
898
899 _zzip_export
900     ZZIP_DIR * zzip_opendir_ext_io(zzip_char_t * name, int o_modes,
901                                    zzip_strings_t * ext, zzip_plugin_io_t io)
902 {
903     if (! io)
904         return zzip_opendir_ext_io64(name, o_modes, ext, io);
905     else
906         { errno = EOVERFLOW; return NULL; }
907 }
908
909 #endif /* ZZIP_LARGEFILE_RENAME && EOVERFLOW && PIC */
910
911 /* ------------------------------------------------------------------- */
912
913 /**                                                                
914  * This function will rewind a real/zipped file. 
915  *
916  * It seeks to the beginning of this file's data in the zip, 
917  * or the beginning of the file for a stat'fd.
918  */
919 int
920 zzip_rewind(ZZIP_FILE * fp)
921 {
922     ZZIP_DIR *dir;
923     int err;
924
925     if (! fp)
926         return -1;
927
928     if (! fp->dir)
929     {                           /* stat fd */
930         fp->io->fd.seeks(fp->fd, 0, SEEK_SET);
931         return 0;
932     }
933
934     dir = fp->dir;
935     /*
936      * If this is other handle than previous, save current seek pointer
937      */
938     if (dir->currentfp != fp)
939     {
940         if (zzip_file_saveoffset(dir->currentfp) < 0)
941             { dir->errcode = ZZIP_DIR_SEEK; return -1; }
942         else
943             { dir->currentfp = fp; }
944     }
945
946     /* seek to beginning of this file */
947     if (fp->io->fd.seeks(dir->fd, fp->dataoffset, SEEK_SET) < 0)
948         return -1;
949
950     /* reset the inflate init stuff */
951     fp->restlen = fp->usize;
952     fp->offset = fp->dataoffset;
953
954     if (fp->method)
955     {                           /* method == 8, deflate */
956         err = inflateReset(&fp->d_stream);
957         if (err != Z_OK)
958             goto error;
959
960         /* start over at next inflate with a fresh read() */
961         fp->d_stream.avail_in = 0;
962         fp->crestlen = fp->csize;
963     }
964
965     return 0;
966
967   error:
968     if (fp)
969         zzip_file_close(fp);
970     return err;
971 }
972
973 /**  
974  * This function will perform a => lseek(2) operation on a real/zipped file
975  *
976  * It will try to seek to the offset specified by offset, relative to whence, 
977  * which is one of SEEK_SET, SEEK_CUR or SEEK_END.
978  * 
979  * If the file-handle is wrapping a stat'able file then it will actually just
980  * perform a normal => lseek(2)-call. Otherwise the relative offset
981  * is calculated, negative offsets are transformed into positive ones
982  * by rewinding the file, and then data is read until the offset is
983  * reached.  This can make the function terribly slow, but this is
984  * how gzio implements it, so I'm not sure there is a better way
985  * without using the internals of the algorithm.
986  */
987 zzip_off_t
988 zzip_seek(ZZIP_FILE * fp, zzip_off_t offset, int whence)
989 {
990     zzip_off_t cur_pos, rel_ofs, read_size, ofs;
991     ZZIP_DIR *dir;
992
993     if (! fp)
994         return -1;
995
996     if (! fp->dir)
997     {                           /* stat fd */
998         return fp->io->fd.seeks(fp->fd, offset, whence);
999     }
1000
1001     cur_pos = zzip_tell(fp);
1002
1003     /* calculate relative offset */
1004     switch (whence)
1005     {
1006     case SEEK_SET:             /* from beginning */
1007         rel_ofs = offset - cur_pos;
1008         break;
1009     case SEEK_CUR:             /* from current */
1010         rel_ofs = offset;
1011         break;
1012     case SEEK_END:             /* from end */
1013         rel_ofs = fp->usize + offset - cur_pos;
1014         break;
1015     default:                   /* something wrong */
1016         return -1;
1017     }
1018
1019     if (rel_ofs == 0)
1020         return cur_pos;         /* don't have to move */
1021
1022     if (rel_ofs < 0)
1023     {                           /* convert backward into forward */
1024         if (zzip_rewind(fp) == -1)
1025             return -1;
1026
1027         read_size = cur_pos + rel_ofs;
1028         cur_pos = 0;
1029     } else
1030     {                           /* amount to read is positive relative offset */
1031         read_size = rel_ofs;
1032     }
1033
1034     if (read_size < 0)          /* bad offset, before beginning of file */
1035         return -1;
1036
1037     if (read_size + cur_pos > (zzip_off_t) fp->usize)   /* bad offset, past EOF */
1038         return -1;
1039
1040     if (read_size == 0)         /* nothing to read */
1041         return cur_pos;
1042
1043     dir = fp->dir;
1044     /*
1045      * If this is other handle than previous, save current seek pointer
1046      * and read the file position of `this' handle.
1047      */
1048     if (dir->currentfp != fp)
1049     {
1050         if (zzip_file_saveoffset(dir->currentfp) < 0
1051             || dir->currentfp->io->fd.seeks(dir->fd, fp->offset, SEEK_SET) < 0)
1052             { dir->errcode = ZZIP_DIR_SEEK; return -1; }
1053         else
1054             { dir->currentfp = fp; }
1055     }
1056
1057     if (fp->method == 0)
1058     {                           /* unstore, just lseek relatively */
1059         ofs = fp->io->fd.tells(dir->fd);
1060         ofs = fp->io->fd.seeks(dir->fd, read_size, SEEK_CUR);
1061         if (ofs > 0)
1062         {                       /* readjust from beginning of file */
1063             ofs -= fp->dataoffset;
1064             fp->restlen = fp->usize - ofs;
1065         }
1066         return ofs;
1067     } else
1068     {                           /* method == 8, inflate */
1069         char *buf;
1070
1071         /*FIXME: use a static buffer! */
1072         buf = (char *) malloc(ZZIP_32K);
1073         if (! buf)
1074             return -1;
1075
1076         while (read_size > 0)
1077         {
1078             zzip_off_t size = ZZIP_32K;
1079
1080             if (read_size < size /*32K */ )
1081                 size = read_size;
1082
1083             size = zzip_file_read(fp, buf, (zzip_size_t) size);
1084             if (size <= 0)
1085                 { free(buf); return -1; }
1086
1087             read_size -= size;
1088         }
1089
1090         free(buf);
1091     }
1092
1093     return zzip_tell(fp);
1094 }
1095
1096 /**                                                                  
1097  * This function will => tell(2) the current position in a real/zipped file
1098  *
1099  * It will return the current offset within the real/zipped file, 
1100  * measured in uncompressed bytes for the zipped-file case.
1101  *
1102  * If the file-handle is wrapping a stat'able file then it will actually just
1103  * perform a normal => tell(2)-call, otherwise the offset is
1104  * calculated from the amount of data left and the total uncompressed
1105  * size;
1106  */
1107 zzip_off_t
1108 zzip_tell(ZZIP_FILE * fp)
1109 {
1110     if (! fp)
1111         return -1;
1112
1113     if (! fp->dir)               /* stat fd */
1114         return fp->io->fd.tells(fp->fd);
1115
1116     /* current uncompressed offset is uncompressed size - data left */
1117     return (fp->usize - fp->restlen);
1118 }
1119
1120 /* 
1121  * Local variables:
1122  * c-file-style: "stroustrup"
1123  * End:
1124  */