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