3 * Guido Draheim <guidod@gmx.de>
4 * Tomi Ollila <Tomi.Ollila@iki.fi>
6 * Copyright (c) 1999,2000,2001,2002,2003 Guido Draheim
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
14 #include <zzip/lib.h> /* exported...*/
15 #include <zzip/file.h>
23 #include <zzip/format.h>
24 #include <zzip/fetch.h>
25 #include <zzip/__debug.h>
28 # if defined ZZIP_HAVE_IO_H
29 # include <io.h> /* tell */
31 # define tell(fd) lseek(fd,0,SEEK_CUR)
34 #define tells(fd) seeks(fd,0,SEEK_CUR)
38 * the direct function of => zzip_close(fp). it will cleanup the
39 * inflate-portion of => zlib and free the structure given.
41 * it is called quite from the error-cleanup parts
42 * of the various => _open functions.
44 * the .refcount is decreased and if zero the fp->dir is closed just as well.
47 zzip_file_close(ZZIP_FILE * fp)
49 ZZIP_DIR * dir = fp->dir;
52 inflateEnd(&fp->d_stream); /* inflateEnd() can be called many times */
56 if (dir->cache.buf32k == NULL) dir->cache.buf32k = fp->buf32k;
57 else free(fp->buf32k);
60 if (dir->currentfp == fp)
61 dir->currentfp = NULL;
64 /* ease to notice possible dangling reference errors */
65 memset(fp, 0, sizeof(*fp));
67 if (dir->cache.fp == NULL) dir->cache.fp = fp;
70 if (! dir->refcount) return zzip_dir_close(dir); else return 0;
75 zzip_file_saveoffset(ZZIP_FILE * fp)
80 zzip_off_t off = fp->io->fd.seeks(fd, 0, SEEK_CUR);
89 # ifndef ZZIP_CHECK_BACKSLASH_DIRSEPARATOR /* NOTE: also default */
90 # define ZZIP_CHECK_BACKSLASH_DIRSEPARATOR 0 /* to "NO" on win32 ! */
93 # if !defined strcasecmp && !defined ZZIP_HAVE_STRCASECMP
94 # define ZZIP_CHECK_BACKSLASH_DIRSEPARATOR 1
97 #if ! ZZIP_CHECK_BACKSLASH_DIRSEPARATOR+0
98 #define dirsep_strrchr(N,C) strrchr(N,C)
99 #define dirsep_casecmp strcasecmp
101 #define dirsep_strrchr(N,C) _dirsep_strrchr(N)
102 #define dirsep_casecmp _dirsep_casecmp
104 _dirsep_strrchr (zzip_char_t* name)
106 char* n = strrchr (name, '/');
107 char* m = strrchr (name, '\\');
108 if (m && n && m > n) n = m;
112 _dirsep_casecmp (zzip_char_t* s1, zzip_char_t* s2)
114 /* ASCII tolower - including mapping of backslash in normal slash */
115 static const char mapping[] = "@abcdefghijklmnopqrstuvwxyz[/]^_";
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];
128 return (((int)(unsigned char) *s1) - ((int)(unsigned char) *s2));
132 static int zzip_inflate_init(ZZIP_FILE *, struct zzip_dir_hdr *);
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.
139 * Note: the zlib supports 2..15 bit windowsize, hence we provide a 32k
140 * memchunk here... just to be safe.
143 zzip_file_open(ZZIP_DIR * dir, zzip_char_t* name, int o_mode)
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*);
150 cmp = (o_mode & ZZIP_CASELESS)? dirsep_casecmp: strcmp;
152 if (! dir || !dir->fd || dir->fd == -1 ) return 0;
154 if (o_mode & ZZIP_NOPATHS)
156 register zzip_char_t* n = dirsep_strrchr(name, '/');
163 register zzip_char_t* hdr_name = hdr->d_name;
164 if (o_mode & ZZIP_NOPATHS)
166 register zzip_char_t* n = dirsep_strrchr(hdr_name, '/');
167 if (n) hdr_name = n + 1;
170 HINT4("name='%s', compr=%d, size=%d\n",
171 hdr->d_name, hdr->d_compr, hdr->d_usize);
173 if (!cmp(hdr_name, name))
175 switch (hdr->d_compr)
178 case 8: /* inflate */
181 { err = ZZIP_UNSUPP_COMPR; goto error; }
186 fp = dir->cache.fp; dir->cache.fp = NULL;
187 /* memset(zfp, 0, sizeof *fp); cleared in zzip_file_close() */
190 if (! (fp = (ZZIP_FILE *)calloc(1, sizeof(*fp))))
191 { err = ZZIP_OUTOFMEM; goto error; }
198 if (dir->cache.buf32k)
199 { fp->buf32k = dir->cache.buf32k; dir->cache.buf32k = NULL; }
202 if (! (fp->buf32k = (char *)malloc(ZZIP_32K)))
203 { err = ZZIP_OUTOFMEM; goto error; }
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
212 if (zzip_file_saveoffset(dir->currentfp) < 0)
213 { err = ZZIP_DIR_SEEK; goto error; }
215 fp->offset = hdr->d_off;
218 if (dir->io->fd.seeks(dir->fd, hdr->d_off, SEEK_SET) < 0)
219 { err = ZZIP_DIR_SEEK; goto error; }
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;
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; }
232 dataoff = zzip_file_header_sizeof_tail(p);
234 if (dir->io->fd.seeks(dir->fd, dataoff, SEEK_CUR) < 0)
235 { err = ZZIP_DIR_SEEK; goto error; }
237 fp->dataoffset = dir->io->fd.tells(dir->fd);
238 fp->usize = hdr->d_usize;
239 fp->csize = hdr->d_csize;
242 err = zzip_inflate_init (fp, hdr);
243 if (err) { goto error; }
248 if (hdr->d_reclen == 0)
250 hdr = (struct zzip_dir_hdr *)((char *)hdr + hdr->d_reclen);
253 dir->errcode = ZZIP_ENOENT; zzip_errno(ZZIP_ENOENT);
256 if (fp) zzip_file_close(fp);
257 dir->errcode = err; zzip_errno(err);
262 * call => inflateInit and setup fp's iterator variables,
263 * used by lowlevel => _open functions.
266 zzip_inflate_init(ZZIP_FILE * fp, struct zzip_dir_hdr* hdr)
269 fp->method = hdr->d_compr;
270 fp->restlen = hdr->d_usize;
274 memset(&fp->d_stream, 0, sizeof(fp->d_stream));
276 err = inflateInit2(&fp->d_stream, -MAX_WBITS);
277 if (err != Z_OK) { goto error; }
279 fp->crestlen = hdr->d_csize;
283 if (fp) zzip_file_close(fp);
288 * This function closes the given ZZIP_FILE handle.
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.
294 zzip_fclose(ZZIP_FILE * fp)
298 { int r = fp->io->fd.close(fp->fd); free(fp); return r; } /* stat fd */
299 else return zzip_file_close(fp);
305 zzip_close(ZZIP_FILE* fp)
307 return zzip_fclose (fp);
311 * This functions read data from zip-contained file.
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.
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.
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
325 zzip_file_read(ZZIP_FILE * fp, char * buf, zzip_size_t len)
331 if (! fp || ! fp->dir) return 0;
334 l = fp->restlen > len ? len : fp->restlen;
335 if (fp->restlen == 0)
339 * If this is other handle than previous, save current seek pointer
340 * and read the file position of `this' handle.
342 if (dir->currentfp != fp)
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; }
348 { dir->currentfp = fp; }
351 /* if more methods is to be supported, change this to `switch ()' */
352 if (fp->method) /* method != 0 == 8, inflate */
354 fp->d_stream.avail_out = l;
355 fp->d_stream.next_out = (unsigned char *)buf;
359 zzip_size_t startlen;
361 if (fp->crestlen > 0 && fp->d_stream.avail_in == 0)
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; */
367 zzip_ssize_t i = fp->io->fd.read(dir->fd, fp->buf32k, cl);
370 dir->errcode = ZZIP_DIR_READ; /* or ZZIP_DIR_READ_EOF ? */
374 fp->d_stream.avail_in = i;
375 fp->d_stream.next_in = (unsigned char *)fp->buf32k;
378 startlen = fp->d_stream.total_out;
379 err = inflate(&fp->d_stream, Z_NO_FLUSH);
381 if (err == Z_STREAM_END)
385 { fp->restlen -= (fp->d_stream.total_out - startlen); }
387 { dir->errcode = err; return -1; }
388 } while (fp->restlen && fp->d_stream.avail_out);
390 return l - fp->d_stream.avail_out;
392 { /* method == 0 -- unstore */
393 rv = fp->io->fd.read(dir->fd, buf, l);
395 { fp->restlen-= rv; }
398 { dir->errcode = ZZIP_DIR_READ; }
404 * This function will read(2) data from a real/zipped file.
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.
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).
415 zzip_read(ZZIP_FILE * fp, char * buf, zzip_size_t len)
419 { return fp->io->fd.read(fp->fd, buf, len); } /* stat fd */
421 { register zzip_ssize_t v;
422 v = zzip_file_read(fp, buf, len);
423 if (v == -1) { errno = zzip_errno(fp->dir->errcode); }
431 zzip_fread(void *ptr, zzip_size_t size, zzip_size_t nmemb, ZZIP_FILE *file)
434 return zzip_read (file, ptr, size*nmemb)/size;
438 #define ZZIP_WRONLY O_WRONLY
439 #define ZZIP_EXCL O_EXCL
442 #define ZZIP_SYNC O_SYNC
447 #if defined O_NONBLOCK
448 #define ZZIP_NONBLOCK O_NONBLOCK
449 #elif defined O_NDELAY
450 #define ZZIP_NOCTTY O_NDELAY
452 #define ZZIP_NOCTTY 0
455 /* ------------------------------------------------------------------- */
458 * This function will => fopen(3) a real/zipped file.
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.
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).
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.
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.
502 zzip_fopen(zzip_char_t* filename, zzip_char_t* mode)
504 return zzip_freopen (filename, mode, 0);
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
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.
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.
527 zzip_freopen(zzip_char_t* filename, zzip_char_t* mode, ZZIP_FILE* stream)
531 if (!mode) mode = "rb";
543 # define O_NONBLOCK 0
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 */
577 zzip_open_shared_io (stream, filename, o_flags, o_modes, 0, 0);
579 if (! o_modes&ZZIP_FACTORY && stream)
580 zzip_file_close (stream);
587 * This function will => open(2) a real/zipped file
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.
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).
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.
611 * compare with => open(2) and => zzip_fopen
614 zzip_open(zzip_char_t* filename, int o_flags)
616 /* backward compatibility */
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);
625 /* ZZIP_ONLYZIP won't work on platforms with sizeof(int) == 16bit */
626 #if ZZIP_SIZEOF_INT+0 == 2
632 * This function uses explicit ext and io instead of the internal
633 * defaults, setting them to zero is equivalent to => zzip_open
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.
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)
645 return zzip_open_shared_io (0, filename, o_flags, o_modes, ext, io);
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.
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.
663 * Note that this function is also used by => zzip_freopen that
664 * will unshare the old handle, thereby possibly closing the handle.
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)
671 if (stream && stream->dir)
673 if (! ext) ext = stream->dir->fileext;
674 if (! io) io = stream->dir->io;
676 if (! io) io = zzip_get_default_io ();
678 if (o_modes & (ZZIP_PREFERZIP|ZZIP_ONLYZIP)) goto try_zzip;
680 /* prefer an existing real file */
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 */
687 ZZIP_FILE* fp = calloc (1, sizeof(ZZIP_FILE));
688 if (!fp) { os->fd.close(fd); return 0; } /* io->fd.close */
694 if (o_modes & ZZIP_PREFERZIP) return 0;
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; }
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; } */
708 /* see if we can open a file that is a zip file */
709 { char basename[PATH_MAX];
711 strcpy (basename, filename);
713 /* see if we can share the same zip directory */
714 if (stream && stream->dir && stream->dir->realname)
716 zzip_size_t len = strlen (stream->dir->realname);
717 if (! memcmp (filename, stream->dir->realname, len) &&
718 filename[len] == '/' && filename[len+1])
721 zzip_file_open (stream->dir, filename+len+1, o_modes);
722 if (! fp) { errno = zzip_errno (stream->dir->errcode); }
727 /* per each slash in filename, check if it there is a zzip around */
728 while ((p = strrchr (basename, '/')))
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; }
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; }
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); }
749 /* note: since (fp) is attached that (dir) will survive */
750 /* but (dir) is implicitly closed on next zzip_close(fp) */
755 if (o_modes & ZZIP_PREFERZIP) goto try_real;
756 errno = ENOENT; return 0;
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 */
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)
770 if (! io) return zzip_open_shared_io64 (stream, name, o_flags, o_modes,
772 errno = EOVERFLOW; return NULL;
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)
779 if (! io) return zzip_open_ext_io64 (name, o_flags, o_modes, ext, io);
780 errno = EOVERFLOW; return NULL;
784 ZZIP_DIR * zzip_opendir_ext_io(zzip_char_t* name, int o_modes,
785 zzip_strings_t* ext, zzip_plugin_io_t io)
787 if (! io) return zzip_opendir_ext_io64 (name, o_modes, ext, io);
788 errno = EOVERFLOW; return NULL;
791 #endif /* ZZIP_LARGEFILE_RENAME && EOVERFLOW && PIC */
793 /* ------------------------------------------------------------------- */
796 * This function will rewind a real/zipped file.
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.
802 zzip_rewind(ZZIP_FILE *fp)
812 fp->io->fd.seeks(fp->fd,0,SEEK_SET);
818 * If this is other handle than previous, save current seek pointer
820 if (dir->currentfp != fp)
822 if (zzip_file_saveoffset(dir->currentfp) < 0)
823 { dir->errcode = ZZIP_DIR_SEEK; return -1; }
825 { dir->currentfp = fp; }
828 /* seek to beginning of this file */
829 if (fp->io->fd.seeks(dir->fd, fp->dataoffset, SEEK_SET) < 0)
832 /* reset the inflate init stuff */
833 fp->restlen = fp->usize;
834 fp->offset = fp->dataoffset;
837 { /* method == 8, deflate */
838 err = inflateReset(&fp->d_stream);
839 if (err != Z_OK) { goto error; }
841 /* start over at next inflate with a fresh read() */
842 fp->d_stream.avail_in = 0;
843 fp->crestlen = fp->csize;
849 if (fp) zzip_file_close(fp);
854 * This function will perform a => lseek(2) operation on a real/zipped file
856 * It will try to seek to the offset specified by offset, relative to whence,
857 * which is one of SEEK_SET, SEEK_CUR or SEEK_END.
859 * If the file-handle is wrapping a stat'able file then it will actually just
860 * perform a normal => lseek(2)-call. Otherwise the relative offset
861 * is calculated, negative offsets are transformed into positive ones
862 * by rewinding the file, and then data is read until the offset is
863 * reached. This can make the function terribly slow, but this is
864 * how gzio implements it, so I'm not sure there is a better way
865 * without using the internals of the algorithm.
868 zzip_seek(ZZIP_FILE * fp, zzip_off_t offset, int whence)
870 zzip_off_t cur_pos, rel_ofs, read_size, ofs;
878 return fp->io->fd.seeks(fp->fd, offset, whence);
881 cur_pos = zzip_tell(fp);
883 /* calculate relative offset */
886 case SEEK_SET: /* from beginning */
887 rel_ofs = offset - cur_pos;
889 case SEEK_CUR: /* from current */
892 case SEEK_END: /* from end */
893 rel_ofs = fp->usize + offset - cur_pos;
895 default: /* something wrong */
900 return cur_pos; /* don't have to move */
903 { /* convert backward into forward */
904 if (zzip_rewind(fp) == -1)
907 read_size = cur_pos + rel_ofs;
910 { /* amount to read is positive relative offset */
914 if (read_size < 0) /* bad offset, before beginning of file */
917 if (read_size + cur_pos > (zzip_off_t)fp->usize) /* bad offset, past EOF */
920 if (read_size == 0) /* nothing to read */
925 * If this is other handle than previous, save current seek pointer
926 * and read the file position of `this' handle.
928 if (dir->currentfp != fp)
930 if (zzip_file_saveoffset(dir->currentfp) < 0
931 || dir->currentfp->io->fd.seeks(dir->fd, fp->offset, SEEK_SET) < 0)
932 { dir->errcode = ZZIP_DIR_SEEK; return -1; }
934 { dir->currentfp = fp; }
938 { /* unstore, just lseek relatively */
939 ofs = fp->io->fd.tells(dir->fd);
940 ofs = fp->io->fd.seeks(dir->fd,read_size,SEEK_CUR);
942 { /* readjust from beginning of file */
943 ofs -= fp->dataoffset;
944 fp->restlen = fp->usize - ofs;
948 { /* method == 8, inflate */
950 /*FIXME: use a static buffer! */
951 buf = (char *)malloc(ZZIP_32K);
952 if (! buf) return -1;
954 while (read_size > 0)
956 zzip_off_t size = ZZIP_32K;
957 if (read_size < size/*32K*/) size = read_size;
959 size = zzip_file_read(fp, buf, (zzip_size_t)size);
960 if (size <= 0) { free(buf); return -1; }
968 return zzip_tell(fp);
972 * This function will => tell(2) the current position in a real/zipped file
974 * It will return the current offset within the real/zipped file,
975 * measured in uncompressed bytes for the zipped-file case.
977 * If the file-handle is wrapping a stat'able file then it will actually just
978 * perform a normal => tell(2)-call, otherwise the offset is
979 * calculated from the amount of data left and the total uncompressed
983 zzip_tell(ZZIP_FILE * fp)
988 if (! fp->dir) /* stat fd */
989 return fp->io->fd.tells(fp->fd);
991 /* current uncompressed offset is uncompressed size - data left */
992 return (fp->usize - fp->restlen);
997 * c-file-style: "stroustrup"