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