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