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