PageRenderTime 88ms CodeModel.GetById 25ms app.highlight 53ms RepoModel.GetById 1ms app.codeStats 1ms

/src/zziplib/zzip/file.c

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