new beta-0.90.0
[luatex.git] / source / libs / zziplib / zziplib-src / zzip / file.c
blob5cda62e72ced2e39be94f1f220bb36dbd6c2b615
2 /*
3 * Author:
4 * Guido Draheim <guidod@gmx.de>
5 * Tomi Ollila <Tomi.Ollila@iki.fi>
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
15 #include <zzip/lib.h> /* exported... */
16 #include <zzip/file.h>
18 #include <string.h>
19 #include <sys/stat.h>
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <ctype.h>
24 #include <zzip/format.h>
25 #include <zzip/fetch.h>
26 #include <zzip/__debug.h>
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
38 /**
39 * the direct function of => zzip_close(fp). it will cleanup the
40 * inflate-portion of => zlib and free the structure given.
42 * it is called quite from the error-cleanup parts
43 * of the various => _open functions.
45 * the .refcount is decreased and if zero the fp->dir is closed just as well.
47 int
48 zzip_file_close(ZZIP_FILE * fp)
50 auto int self;
51 ZZIP_DIR *dir = fp->dir;
53 if (fp->method)
54 inflateEnd(&fp->d_stream); /* inflateEnd() can be called many times */
56 if (dir->cache.locked == NULL)
57 dir->cache.locked = &self;
59 if (fp->buf32k)
61 if (dir->cache.locked == &self && dir->cache.buf32k == NULL)
62 dir->cache.buf32k = fp->buf32k;
63 else
64 free(fp->buf32k);
67 if (dir->currentfp == fp)
68 dir->currentfp = NULL;
70 dir->refcount--;
71 /* ease to notice possible dangling reference errors */
72 memset(fp, 0, sizeof(*fp));
74 if (dir->cache.locked == &self && dir->cache.fp == NULL)
75 dir->cache.fp = fp;
76 else
77 free(fp);
79 if (dir->cache.locked == &self)
80 dir->cache.locked = NULL;
82 if (! dir->refcount)
83 return zzip_dir_close(dir);
84 else
85 return 0;
89 static int
90 zzip_file_saveoffset(ZZIP_FILE * fp)
92 if (fp)
94 int fd = fp->dir->fd;
95 zzip_off_t off = fp->io->fd.seeks(fd, 0, SEEK_CUR);
97 if (off < 0)
98 return -1;
100 fp->offset = off;
102 return 0;
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
117 static zzip_char_t*
118 strrchr_basename(zzip_char_t* name)
120 register zzip_char_t *n = strrchr(name, '/');
121 if (n) return n + 1;
122 return name;
125 static zzip_char_t*
126 dirsep_basename(zzip_char_t* name)
128 register zzip_char_t *n = strrchr(name, '/');
130 if (ZZIP_BACKSLASH_DIRSEP)
132 register zzip_char_t *m = strrchr(name, '\\');
133 if (!n || (m && n < m))
134 n = m;
137 if (n) return n + 1;
138 return name;
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)
147 /* ASCII tolower - including mapping of backslash in normal slash */
148 static const char mapping[] = "@abcdefghijklmnopqrstuvwxyz[/]^_";
149 int c1, c2;
151 while (*s1 && *s2)
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++;
165 return (((int) (unsigned char) *s1) - ((int) (unsigned char) *s2));
167 #endif
169 static int zzip_inflate_init(ZZIP_FILE *, struct zzip_dir_hdr *);
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.
176 * Note: the zlib supports 2..15 bit windowsize, hence we provide a 32k
177 * memchunk here... just to be safe.
179 * On error it returns null and sets errcode in the ZZIP_DIR.
181 ZZIP_FILE *
182 zzip_file_open(ZZIP_DIR * dir, zzip_char_t * name, int o_mode)
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*);
191 filename_strcmp = (o_mode & ZZIP_CASELESS) ? dirsep_strcasecmp : strcmp;
192 filename_basename = (o_mode & ZZIP_CASELESS) ? dirsep_basename : strrchr_basename;
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; }
201 if (o_mode & ZZIP_NOPATHS)
202 name = filename_basename(name);
204 while (1)
206 register zzip_char_t *hdr_name = hdr->d_name;
208 if (o_mode & ZZIP_NOPATHS)
209 hdr_name = filename_basename(hdr_name);
211 HINT4("name='%s', compr=%d, size=%d\n",
212 hdr->d_name, hdr->d_compr, hdr->d_usize);
214 if (! filename_strcmp(hdr_name, name))
216 switch (hdr->d_compr)
218 case 0: /* store */
219 case 8: /* inflate */
220 break;
221 default:
222 { err = ZZIP_UNSUPP_COMPR; goto error; }
225 if (dir->cache.locked == NULL)
226 dir->cache.locked = &self;
228 if (dir->cache.locked == &self && dir->cache.fp)
230 fp = dir->cache.fp;
231 dir->cache.fp = NULL;
232 /* memset(zfp, 0, sizeof *fp); cleared in zzip_file_close() */
233 } else
235 if (! (fp = (ZZIP_FILE *) calloc(1, sizeof(*fp))))
236 { err = ZZIP_OUTOFMEM; goto error; }
239 fp->dir = dir;
240 fp->io = dir->io;
241 dir->refcount++;
243 if (dir->cache.locked == &self && dir->cache.buf32k)
245 fp->buf32k = dir->cache.buf32k;
246 dir->cache.buf32k = NULL;
247 } else
249 if (! (fp->buf32k = (char *) malloc(ZZIP_32K)))
250 { err = ZZIP_OUTOFMEM; goto error; }
253 if (dir->cache.locked == &self)
254 dir->cache.locked = NULL;
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...
261 if (zzip_file_saveoffset(dir->currentfp) < 0)
262 { err = ZZIP_DIR_SEEK; goto error; }
264 fp->offset = hdr->d_off;
265 dir->currentfp = fp;
267 if (dir->io->fd.seeks(dir->fd, hdr->d_off, SEEK_SET) < 0)
268 { err = ZZIP_DIR_SEEK; goto error; }
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;
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; }
282 dataoff = zzip_file_header_sizeof_tail(p);
284 if (dir->io->fd.seeks(dir->fd, dataoff, SEEK_CUR) < 0)
285 { err = ZZIP_DIR_SEEK; goto error; }
287 fp->dataoffset = dir->io->fd.tells(dir->fd);
288 fp->usize = hdr->d_usize;
289 fp->csize = hdr->d_csize;
292 err = zzip_inflate_init(fp, hdr);
293 if (err)
294 goto error;
296 return fp;
297 } else
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;
314 * call => inflateInit and setup fp's iterator variables,
315 * used by lowlevel => _open functions.
317 static int
318 zzip_inflate_init(ZZIP_FILE * fp, struct zzip_dir_hdr *hdr)
320 int err;
322 fp->method = hdr->d_compr;
323 fp->restlen = hdr->d_usize;
325 if (fp->method)
327 memset(&fp->d_stream, 0, sizeof(fp->d_stream));
329 err = inflateInit2(&fp->d_stream, -MAX_WBITS);
330 if (err != Z_OK)
331 goto error;
333 fp->crestlen = hdr->d_csize;
335 return 0;
336 error:
337 if (fp)
338 zzip_file_close(fp);
339 return err;
343 * This function closes the given ZZIP_FILE handle.
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.
349 zzip_fclose(ZZIP_FILE * fp)
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);
359 /** => zzip_fclose
362 zzip_close(ZZIP_FILE * fp)
364 return zzip_fclose(fp);
368 * This functions read data from zip-contained file.
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.
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.
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.
381 zzip_ssize_t
382 zzip_file_read(ZZIP_FILE * fp, void *buf, zzip_size_t len)
384 ZZIP_DIR *dir;
385 zzip_size_t l;
386 zzip_ssize_t rv;
388 if (! fp || ! fp->dir)
389 return 0;
391 dir = fp->dir;
392 l = fp->restlen > len ? len : fp->restlen;
393 if (fp->restlen == 0)
394 return 0;
397 * If this is other handle than previous, save current seek pointer
398 * and read the file position of `this' handle.
400 if (dir->currentfp != fp)
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; }
409 /* if more methods is to be supported, change this to `switch ()' */
410 if (fp->method) /* method != 0 == 8, inflate */
412 fp->d_stream.avail_out = l;
413 fp->d_stream.next_out = (unsigned char *) buf;
417 int err;
418 zzip_size_t startlen;
420 if (fp->crestlen > 0 && fp->d_stream.avail_in == 0)
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;
427 zzip_ssize_t i = fp->io->fd.read(dir->fd, fp->buf32k, cl);
429 if (i <= 0)
431 dir->errcode = ZZIP_DIR_READ;
432 /* or ZZIP_DIR_READ_EOF ? */
433 return -1;
435 fp->crestlen -= i;
436 fp->d_stream.avail_in = i;
437 fp->d_stream.next_in = (unsigned char *) fp->buf32k;
440 startlen = fp->d_stream.total_out;
441 err = inflate(&fp->d_stream, Z_NO_FLUSH);
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; }
450 while (fp->restlen && fp->d_stream.avail_out);
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;
465 * This function will read(2) data from a real/zipped file.
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.
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).
475 zzip_ssize_t
476 zzip_read(ZZIP_FILE * fp, void *buf, zzip_size_t len)
478 if (! fp)
479 return 0;
480 if (! fp->dir)
481 { return fp->io->fd.read(fp->fd, buf, len); } /* stat fd */
482 else
484 register zzip_ssize_t v;
486 v = zzip_file_read(fp, buf, len);
487 if (v == -1)
488 { errno = zzip_errno(fp->dir->errcode); }
489 return v;
493 /** => zzip_read
495 zzip_size_t
496 zzip_fread(void *ptr, zzip_size_t size, zzip_size_t nmemb, ZZIP_FILE * file)
498 if (! size)
499 size = 1;
500 return zzip_read(file, ptr, size * nmemb) / size;
504 #define ZZIP_WRONLY O_WRONLY
505 #define ZZIP_EXCL O_EXCL
507 #if defined O_SYNC
508 #define ZZIP_SYNC O_SYNC
509 #else
510 #define ZZIP_SYNC 0
511 #endif
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
521 /* ------------------------------------------------------------------- */
523 /** also: fopen(2)
524 * This function will => fopen(3) a real/zipped file.
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.
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).
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.
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.
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).
570 ZZIP_FILE *
571 zzip_fopen(zzip_char_t * filename, zzip_char_t * mode)
573 return zzip_freopen(filename, mode, 0);
576 /** => zzip_fopen
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
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.
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.
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).
598 ZZIP_FILE *
599 zzip_freopen(zzip_char_t * filename, zzip_char_t * mode, ZZIP_FILE * stream)
601 int o_flags = 0;
602 int o_modes = 0664;
604 if (! mode)
605 mode = "rb";
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
620 for (; *mode; mode++)
622 switch (*mode)
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* */
652 ZZIP_FILE *fp =
653 zzip_open_shared_io(stream, filename, o_flags, o_modes, 0, 0);
655 if (! (o_modes & ZZIP_FACTORY) && stream)
656 zzip_file_close(stream);
658 return fp;
663 * This function will => open(2) a real/zipped file
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.
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).
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.
687 * compare with => open(2) and => zzip_fopen
689 ZZIP_FILE *
690 zzip_open(zzip_char_t * filename, int o_flags)
692 /* backward compatibility */
693 int o_modes = 0664;
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);
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
707 /** => zzip_open
709 * This function uses explicit ext and io instead of the internal
710 * defaults, setting them to zero is equivalent to => zzip_open
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.
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).
721 * If any ext_io handlers were used then the referenced structure
722 * should be static as the allocated ZZIP_FILE does not copy them.
724 ZZIP_FILE *
725 zzip_open_ext_io(zzip_char_t * filename, int o_flags, int o_modes,
726 zzip_strings_t * ext, zzip_plugin_io_t io)
728 return zzip_open_shared_io(0, filename, o_flags, o_modes, ext, io);
731 /** => zzip_open
733 * This function takes an extra stream argument - if a handle has been
734 * then ext/io can be left null and the new stream handle will pick up
735 * the ext/io. This should be used only in specific environment however
736 * since => zzip_file_real does not store any ext-sequence.
738 * The benefit for this function comes in when the old file handle
739 * was openened from a file within a zip archive. When the new file
740 * is in the same zip archive then the internal zzip_dir structures
741 * will be shared. It is even quicker, as no check needs to be done
742 * anymore trying to guess the zip archive place in the filesystem,
743 * here we just check whether the zip archive's filepath is a prefix
744 * part of the filename to be opened.
746 * Note that this function is also used by => zzip_freopen that
747 * will unshare the old handle, thereby possibly closing the handle.
749 * This function returns a new zzip-handle (use => zzip_close to return
750 * it). On error this function will return null setting => errno(3).
752 ZZIP_FILE *
753 zzip_open_shared_io(ZZIP_FILE * stream,
754 zzip_char_t * filename, int o_flags, int o_modes,
755 zzip_strings_t * ext, zzip_plugin_io_t io)
757 if (stream && stream->dir)
759 if (! ext)
760 ext = stream->dir->fileext;
761 if (! io)
762 io = stream->dir->io;
764 if (! io)
765 io = zzip_get_default_io();
767 if (o_modes & (ZZIP_PREFERZIP | ZZIP_ONLYZIP))
768 goto try_zzip;
769 try_real:
770 /* prefer an existing real file */
772 zzip_plugin_io_t os = (o_modes & ZZIP_ALLOWREAL)
773 ? zzip_get_default_io() : io;
774 int fd = (os->fd.open)(filename, o_flags); /* io->fd.open */
776 if (fd != -1)
778 ZZIP_FILE *fp = calloc(1, sizeof(ZZIP_FILE));
780 if (! fp)
781 { os->fd.close(fd); return 0; } /* io->fd.close */
783 fp->fd = fd;
784 fp->io = os;
785 return fp;
787 if (o_modes & ZZIP_PREFERZIP)
788 return 0;
790 try_zzip:
792 /* if the user had it in place of a normal xopen, then
793 * we better defend this lib against illegal usage */
794 if (o_flags & (O_CREAT | O_WRONLY))
795 { errno = EINVAL; return 0; }
796 if (o_flags & (O_RDWR))
797 { o_flags ^= O_RDWR; o_flags |= O_RDONLY; }
799 /* this is just for backward compatibility -and strictly needed to
800 * prepare ourselves for more options and more options later on... */
801 /*# if (o_modes & ZZIP_CASELESS) { o_flags |= ZZIP_CASEINSENSITIVE; } */
802 /*# if (o_modes & ZZIP_NOPATHS) { o_flags |= ZZIP_IGNOREPATH; } */
804 /* see if we can open a file that is a zip file */
806 char basename[PATH_MAX];
807 char *p;
808 int filename_len = strlen(filename);
810 if (filename_len >= PATH_MAX)
811 { errno = ENAMETOOLONG; return 0; }
812 memcpy(basename, filename, filename_len + 1);
814 /* see if we can share the same zip directory */
815 if (stream && stream->dir && stream->dir->realname)
817 zzip_size_t len = strlen(stream->dir->realname);
819 if (! memcmp(filename, stream->dir->realname, len) &&
820 filename[len] == '/' && filename[len + 1])
822 ZZIP_FILE *fp =
823 zzip_file_open(stream->dir, filename + len + 1, o_modes);
824 if (! fp)
825 { errno = zzip_errno (stream->dir->errcode); }
826 return fp;
830 /* per each slash in filename, check if it there is a zzip around */
831 while ((p = strrchr(basename, '/')))
833 zzip_error_t e = 0;
834 ZZIP_DIR *dir;
835 ZZIP_FILE *fp;
836 int fd;
838 *p = '\0';
839 /* i.e. cut at path separator == possible zipfile basename */
840 fd = __zzip_try_open(basename, o_flags | O_RDONLY | O_BINARY,
841 ext, io);
842 if (fd == -1)
843 { continue; }
845 /* found zip-file .... now try to parse it */
846 dir = zzip_dir_fdopen_ext_io(fd, &e, ext, io);
847 if (e)
848 { errno = zzip_errno(e); io->fd.close(fd); return 0; }
850 /* (p - basename) is the lenghtof zzip_dir part of the filename */
851 fp = zzip_file_open(dir, filename + (p - basename) + 1, o_modes);
852 if (! fp)
853 { errno = zzip_errno(dir->errcode); }
854 else
855 { if (! dir->realname) dir->realname = strdup (basename); }
857 zzip_dir_close(dir);
858 /* note: since (fp) is attached that (dir) will survive */
859 /* but (dir) is implicitly closed on next zzip_close(fp) */
861 return fp;
864 if (o_modes & ZZIP_PREFERZIP)
865 goto try_real;
866 else
867 { errno = ENOENT; return 0; }
871 #if defined ZZIP_LARGEFILE_RENAME && defined EOVERFLOW && defined PIC
872 #undef zzip_open_shared_io /* zzip_open_shared_io64 */
873 #undef zzip_open_ext_io /* zzip_open_ext_io64 */
874 #undef zzip_opendir_ext_io /* zzip_opendir_ext_io64 */
876 ZZIP_FILE *zzip_open_shared_io(ZZIP_FILE * stream,
877 zzip_char_t * name, int o_flags,
878 int o_modes, zzip_strings_t * ext,
879 zzip_plugin_io_t io);
880 ZZIP_FILE *zzip_open_ext_io(zzip_char_t * name, int o_flags,
881 int o_modes, zzip_strings_t * ext,
882 zzip_plugin_io_t io);
883 ZZIP_DIR *zzip_opendir_ext_io(zzip_char_t * name, int o_modes,
884 zzip_strings_t * ext, zzip_plugin_io_t io);
886 /* DLL compatibility layer - so that 32bit code can link with this lib too */
888 ZZIP_FILE *
889 zzip_open_shared_io(ZZIP_FILE * stream,
890 zzip_char_t * name, int o_flags,
891 int o_modes, zzip_strings_t * ext, zzip_plugin_io_t io)
893 if (! io)
894 return zzip_open_shared_io64(stream, name, o_flags, o_modes, ext, io);
895 errno = EOVERFLOW;
896 return NULL;
899 ZZIP_FILE *
900 zzip_open_ext_io(zzip_char_t * name, int o_flags, int o_modes,
901 zzip_strings_t * ext, zzip_plugin_io_t io)
903 if (! io)
904 return zzip_open_ext_io64(name, o_flags, o_modes, ext, io);
905 errno = EOVERFLOW;
906 return NULL;
909 ZZIP_DIR *
910 zzip_opendir_ext_io(zzip_char_t * name, int o_modes,
911 zzip_strings_t * ext, zzip_plugin_io_t io)
913 if (! io)
914 return zzip_opendir_ext_io64(name, o_modes, ext, io);
915 else
916 { errno = EOVERFLOW; return NULL; }
919 #endif /* ZZIP_LARGEFILE_RENAME && EOVERFLOW && PIC */
921 /* ------------------------------------------------------------------- */
924 * This function will rewind a real/zipped file.
926 * It seeks to the beginning of this file's data in the zip,
927 * or the beginning of the file for a stat'fd.
930 zzip_rewind(ZZIP_FILE * fp)
932 ZZIP_DIR *dir;
933 int err;
935 if (! fp)
936 return -1;
938 if (! fp->dir)
939 { /* stat fd */
940 fp->io->fd.seeks(fp->fd, 0, SEEK_SET);
941 return 0;
944 dir = fp->dir;
946 * If this is other handle than previous, save current seek pointer
948 if (dir->currentfp != fp)
950 if (zzip_file_saveoffset(dir->currentfp) < 0)
951 { dir->errcode = ZZIP_DIR_SEEK; return -1; }
952 else
953 { dir->currentfp = fp; }
956 /* seek to beginning of this file */
957 if (fp->io->fd.seeks(dir->fd, fp->dataoffset, SEEK_SET) < 0)
958 return -1;
960 /* reset the inflate init stuff */
961 fp->restlen = fp->usize;
962 fp->offset = fp->dataoffset;
964 if (fp->method)
965 { /* method == 8, deflate */
966 err = inflateReset(&fp->d_stream);
967 if (err != Z_OK)
968 goto error;
970 /* start over at next inflate with a fresh read() */
971 fp->d_stream.avail_in = 0;
972 fp->crestlen = fp->csize;
975 return 0;
977 error:
978 if (fp)
979 zzip_file_close(fp);
980 return err;
984 * This function will perform a => lseek(2) operation on a real/zipped file
986 * It will try to seek to the offset specified by offset, relative to whence,
987 * which is one of SEEK_SET, SEEK_CUR or SEEK_END.
989 * If the file-handle is wrapping a stat'able file then it will actually just
990 * perform a normal => lseek(2)-call. Otherwise the relative offset
991 * is calculated, negative offsets are transformed into positive ones
992 * by rewinding the file, and then data is read until the offset is
993 * reached. This can make the function terribly slow, but this is
994 * how gzio implements it, so I'm not sure there is a better way
995 * without using the internals of the algorithm.
997 zzip_off_t
998 zzip_seek(ZZIP_FILE * fp, zzip_off_t offset, int whence)
1000 zzip_off_t cur_pos, rel_ofs, read_size, ofs;
1001 ZZIP_DIR *dir;
1003 if (! fp)
1004 return -1;
1006 if (! fp->dir)
1007 { /* stat fd */
1008 return fp->io->fd.seeks(fp->fd, offset, whence);
1011 cur_pos = zzip_tell(fp);
1013 /* calculate relative offset */
1014 switch (whence)
1016 case SEEK_SET: /* from beginning */
1017 rel_ofs = offset - cur_pos;
1018 break;
1019 case SEEK_CUR: /* from current */
1020 rel_ofs = offset;
1021 break;
1022 case SEEK_END: /* from end */
1023 rel_ofs = fp->usize + offset - cur_pos;
1024 break;
1025 default: /* something wrong */
1026 return -1;
1029 if (rel_ofs == 0)
1030 return cur_pos; /* don't have to move */
1032 if (rel_ofs < 0)
1033 { /* convert backward into forward */
1034 if (zzip_rewind(fp) == -1)
1035 return -1;
1037 read_size = cur_pos + rel_ofs;
1038 cur_pos = 0;
1039 } else
1040 { /* amount to read is positive relative offset */
1041 read_size = rel_ofs;
1044 if (read_size < 0) /* bad offset, before beginning of file */
1045 return -1;
1047 if (read_size + cur_pos > (zzip_off_t) fp->usize) /* bad offset, past EOF */
1048 return -1;
1050 if (read_size == 0) /* nothing to read */
1051 return cur_pos;
1053 dir = fp->dir;
1055 * If this is other handle than previous, save current seek pointer
1056 * and read the file position of `this' handle.
1058 if (dir->currentfp != fp)
1060 if (zzip_file_saveoffset(dir->currentfp) < 0
1061 || fp->io->fd.seeks(dir->fd, fp->offset, SEEK_SET) < 0)
1062 { dir->errcode = ZZIP_DIR_SEEK; return -1; }
1063 else
1064 { dir->currentfp = fp; }
1067 if (fp->method == 0)
1068 { /* unstore, just lseek relatively */
1069 ofs = fp->io->fd.tells(dir->fd);
1070 ofs = fp->io->fd.seeks(dir->fd, read_size, SEEK_CUR);
1071 if (ofs > 0)
1072 { /* readjust from beginning of file */
1073 ofs -= fp->dataoffset;
1074 fp->restlen = fp->usize - ofs;
1076 return ofs;
1077 } else
1078 { /* method == 8, inflate */
1079 char *buf;
1081 /*FIXME: use a static buffer! */
1082 buf = (char *) malloc(ZZIP_32K);
1083 if (! buf)
1084 return -1;
1086 while (read_size > 0)
1088 zzip_off_t size = ZZIP_32K;
1090 if (read_size < size /*32K */ )
1091 size = read_size;
1093 size = zzip_file_read(fp, buf, (zzip_size_t) size);
1094 if (size <= 0)
1095 { free(buf); return -1; }
1097 read_size -= size;
1100 free(buf);
1103 return zzip_tell(fp);
1107 * This function will => tell(2) the current position in a real/zipped file
1109 * It will return the current offset within the real/zipped file,
1110 * measured in uncompressed bytes for the zipped-file case.
1112 * If the file-handle is wrapping a stat'able file then it will actually just
1113 * perform a normal => tell(2)-call, otherwise the offset is
1114 * calculated from the amount of data left and the total uncompressed
1115 * size;
1117 zzip_off_t
1118 zzip_tell(ZZIP_FILE * fp)
1120 if (! fp)
1121 return -1;
1123 if (! fp->dir) /* stat fd */
1124 return fp->io->fd.tells(fp->fd);
1126 /* current uncompressed offset is uncompressed size - data left */
1127 return (fp->usize - fp->restlen);
1130 #ifndef EOVERFLOW
1131 #define EOVERFLOW EFBIG
1132 #endif
1134 /** => zzip_tell
1135 * This function is provided for users who can not use any largefile-mode.
1137 long
1138 zzip_tell32(ZZIP_FILE * fp)
1140 if (sizeof(zzip_off_t) == sizeof(long))
1142 return zzip_tell(fp);
1143 } else
1145 off_t off = zzip_tell(fp);
1146 if (off >= 0) {
1147 register long off32 = off;
1148 if (off32 == off) return off32;
1149 errno = EOVERFLOW;
1151 return -1;
1155 /** => zzip_seek
1156 * This function is provided for users who can not use any largefile-mode.
1158 long
1159 zzip_seek32(ZZIP_FILE * fp, long offset, int whence)
1161 if (sizeof(zzip_off_t) == sizeof(long))
1163 return zzip_seek(fp, offset, whence);
1164 } else
1166 off_t off = zzip_seek(fp, offset, whence);
1167 if (off >= 0) {
1168 register long off32 = off;
1169 if (off32 == off) return off32;
1170 errno = EOVERFLOW;
1172 return -1;
1177 * Local variables:
1178 * c-file-style: "stroustrup"
1179 * End: