aboutsummaryrefslogtreecommitdiffstats
path: root/zipio.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--zipio.c816
1 files changed, 816 insertions, 0 deletions
diff --git a/zipio.c b/zipio.c
new file mode 100644
index 0000000..6ed67e0
--- /dev/null
+++ b/zipio.c
@@ -0,0 +1,816 @@
+/*
+ * zipio.c - stdio emulation library for reading zip files
+ *
+ * Version 1.1.1
+ */
+
+/*
+ * Copyright (c) 1995, Edward B. Hamrick
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that
+ *
+ * (i) the above copyright notice and the text in this "C" comment block
+ * appear in all copies of the software and related documentation, and
+ *
+ * (ii) any modifications to this source file must be sent, via e-mail
+ * to the copyright owner (currently hamrick@primenet.com) within
+ * 30 days of such modification.
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL EDWARD B. HAMRICK BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
+ * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Changes from 1.1 to 1.1.1:
+ * Changed "z*" functions to "Z*" to avoid namespace pollution.
+ * Added "zungetc" macro.
+ * Added definitions of SEEK_SET, SEEK_CUR, SEEK_END for the Posixly challenged
+ * John Cowan <cowan@ccil.org>
+ */
+
+/*
+ * Refer to zipio.h for a description of this package.
+ */
+
+/*
+ * The .zip file header is described below. It consists of
+ * 30 fixed bytes, followed by two variable length fields
+ * whose length is contained in the first 30 bytes. After this
+ * header, the data is stored (in deflate format if the compression
+ * method is 8).
+ *
+ * The crc-32 field is the crc on the uncompressed data.
+ *
+ * .zip file header:
+ *
+ * local file header signature 4 bytes (0x04034b50)
+ * version needed to extract 2 bytes
+ * general purpose bit flag 2 bytes
+ * compression method 2 bytes
+ * last mod file time 2 bytes
+ * last mod file date 2 bytes
+ * crc-32 4 bytes
+ * compressed size 4 bytes
+ * uncompressed size 4 bytes
+ * filename length 2 bytes
+ * extra field length 2 bytes
+ *
+ * filename (variable size)
+ * extra field (variable size)
+ *
+ * These fields are described in more detail in appnote.txt
+ * in the pkzip 1.93 distribution.
+ */
+
+#include <stdlib.h>
+#ifdef MEMCPY
+#include <mem.h>
+#endif
+
+#include "zipio.h"
+#include "inflate.h"
+#include "crc.h"
+
+/*
+ * Macros for constants
+ */
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef ZIPSIGNATURE
+#define ZIPSIGNATURE 0x04034b50L
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+
+/*
+ * Buffer size macros
+ *
+ * The following constants are optimized for large-model
+ * (but not flat model) Windows with virtual memory. It
+ * will work fine on unix and flat model Windows as well.
+ *
+ * The constant BUFFERTHRESHOLD determines when memory
+ * buffering changes to file buffering.
+ *
+ * Assumptions:
+ *
+ * 1) INPBUFSIZE + OUTBUFSIZE + sizeof(void *) * PTRBUFSIZE + delta < 64K
+ *
+ * 2) OUTBUFSIZE = 32K * N (related to inflate's 32K window size)
+ *
+ * 2) Max in-memory file size is OUTBUFSIZE * PTRBUFSIZE
+ * which is 64 MBytes by default (32K * 2K).
+ *
+ */
+
+#ifndef BUFFERTHRESHOLD
+#define BUFFERTHRESHOLD (256 * 1024L)
+#endif
+
+#ifndef INPBUFSIZE
+#define INPBUFSIZE ( 8 * 1024 )
+#endif
+
+#ifndef PTRBUFSIZE
+#define PTRBUFSIZE ( 2 * 1024 )
+#endif
+
+#ifndef OUTBUFSIZE
+#define OUTBUFSIZE ((unsigned int) ( 32 * 1024L))
+#endif
+
+#define MAXFILESIZE (OUTBUFSIZE * (long) PTRBUFSIZE)
+
+/*
+ * Macro for short-hand reference to ZipioState (from ZFILE *)
+ */
+
+#define ZS ((struct ZipioState *) stream)
+
+/*
+ * Macro to manipulate Zgetc() cache
+ */
+
+#define CACHEINIT \
+ zs->ptr = NULL; \
+ zs->len = 0;
+
+#define CACHEUPDATE \
+ if (ZS->ptr) \
+ { \
+ ZS->fileposition &= ~((long) (OUTBUFSIZE-1)); \
+ ZS->fileposition += ZS->ptr - ZS->getbuf; \
+ ZS->ptr = NULL; \
+ } \
+ ZS->len = 0;
+
+/*
+ * Macros for run-time type identification
+ */
+
+#ifndef RUNTIMEENABLE
+#define RUNTIMEENABLE 0
+#endif
+
+#if RUNTIMEENABLE
+#define ZIPIOSTATETYPE 0x0110f00fL
+#define RUNTIMEINIT \
+ zs->runtimetypeid1 = ZIPIOSTATETYPE; \
+ zs->runtimetypeid2 = ZIPIOSTATETYPE;
+
+#define RUNTIMECHECK \
+ if (!ZS || (ZS->runtimetypeid1 != ZIPIOSTATETYPE) \
+ || (ZS->runtimetypeid2 != ZIPIOSTATETYPE)) return -1;
+#else
+#define RUNTIMEINIT
+#define RUNTIMECHECK
+#endif
+
+/*
+ * Macros for converting bytes to unsigned integers
+ */
+
+#define GETUINT4(ptr, i4) \
+ { \
+ i4 = (((unsigned long) *(((unsigned char *) (ptr)) + 0)) ) | \
+ (((unsigned long) *(((unsigned char *) (ptr)) + 1)) << 8) | \
+ (((unsigned long) *(((unsigned char *) (ptr)) + 2)) << 16) | \
+ (((unsigned long) *(((unsigned char *) (ptr)) + 3)) << 24) ; \
+ }
+
+#define GETUINT2(ptr, i2) \
+ { \
+ i2 = (((unsigned int) *(((unsigned char *) (ptr)) + 0)) ) | \
+ (((unsigned int) *(((unsigned char *) (ptr)) + 1)) << 8) ; \
+ }
+
+/* Structure to hold state for decoding zip files */
+struct ZipioState {
+
+ /* Fields overlaid with ZFILE structure */
+ int len; /* length of Zgetc cache */
+ unsigned char *ptr; /* pointer to Zgetc cache */
+
+ /* Fields invisible to users of ZFILE structure */
+
+ unsigned long runtimetypeid1; /* to detect run-time errors */
+ int errorencountered; /* error encountered flag */
+
+ /* Buffering state */
+ unsigned char inpbuf[INPBUFSIZE]; /* inp buffer from zip file */
+ unsigned char *ptrbuf[PTRBUFSIZE]; /* pointers to in-memory bufs */
+
+ unsigned char getbuf[OUTBUFSIZE]; /* buffer for use by Zgetc */
+ long getoff; /* starting offset of getbuf */
+
+ FILE *tmpfil; /* file ptr to temp file */
+
+ /* Amount of input data inflated */
+ unsigned long inpinf;
+ unsigned long outinf;
+
+ /* Zip file header */
+ unsigned long sign; /* local file header signature (0x04034b50) */
+ unsigned int vers; /* version needed to extract 2 bytes */
+ unsigned int flag; /* general purpose bit flag 2 bytes */
+ unsigned int comp; /* compression method 2 bytes */
+ unsigned int mtim; /* last mod file time 2 bytes */
+ unsigned int mdat; /* last mod file date 2 bytes */
+ unsigned long crc3; /* crc-32 4 bytes */
+ unsigned long csiz; /* compressed size 4 bytes */
+ unsigned long usiz; /* uncompressed size 4 bytes */
+ unsigned int flen; /* filename length 2 bytes */
+ unsigned int elen; /* extra field length 2 bytes */
+
+ /* Application state */
+ FILE *OpenFile; /* currently open file */
+
+ void *inflatestate; /* current state for inflate */
+
+ unsigned long fileposition; /* current file position */
+
+ unsigned long filecrc; /* current crc */
+
+ unsigned long runtimetypeid2; /* to detect run-time errors */
+};
+
+/*
+ * Utility routines to handle uncompressed file buffers
+ */
+
+/* Initialize buffering */
+static void BufferInitialize(
+ struct ZipioState *zs,
+ int doinflate
+)
+{
+ zs->getoff = -1;
+ zs->tmpfil = NULL;
+
+ /*
+ * If not inflating, use the input file
+ */
+
+ if (!doinflate)
+ {
+ zs->tmpfil = zs->OpenFile;
+
+ /* Get the uncompressed file size */
+ fseek(zs->tmpfil, 0, SEEK_END);
+ zs->usiz = ftell(zs->tmpfil);
+ zs->outinf = zs->usiz;
+
+ /* Start at the beginning */
+ fseek(zs->tmpfil, 0, SEEK_SET);
+ }
+
+ /* If there's no file open, see if it's big enough for temp file */
+ if (!zs->tmpfil)
+ {
+ if (zs->usiz >= BUFFERTHRESHOLD)
+ zs->tmpfil = tmpfile();
+ }
+
+ /* If there's no file open, then use memory buffering */
+ if (!zs->tmpfil)
+ {
+ int i;
+
+ for (i=0; i<PTRBUFSIZE; i++)
+ zs->ptrbuf[i] = NULL;
+ }
+}
+
+/* pump data till length bytes of file are inflated or error encountered */
+static int BufferPump(struct ZipioState *zs, long length)
+{
+ size_t inplen, ret;
+
+ /* Check to see if the length is valid */
+ if (length > zs->usiz) return TRUE;
+
+ /* Loop till enough data is pumped */
+ while (!zs->errorencountered && (zs->outinf < length))
+ {
+ /* Compute how much data to read */
+ if ((zs->csiz - zs->inpinf) < INPBUFSIZE)
+ inplen = (size_t) (zs->csiz - zs->inpinf);
+ else
+ inplen = INPBUFSIZE;
+
+ if (inplen <= 0) return TRUE;
+
+ /* Read some data from the file */
+ ret = fread(zs->inpbuf, 1, inplen, zs->OpenFile);
+ if (ret != inplen) return TRUE;
+
+ /* Update how much data has been read from the file */
+ zs->inpinf += inplen;
+
+ /* Pump this data into the decompressor */
+ if (InflatePutBuffer(zs->inflatestate, zs->inpbuf, inplen)) return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Read from the buffer */
+static int BufferRead(
+ struct ZipioState *zs,
+ long offset,
+ unsigned char *buffer,
+ long length
+)
+{
+ /*
+ * Make sure enough bytes have been inflated
+ * Note that the correction for reading past EOF has to
+ * be done before calling this routine
+ */
+
+ if (BufferPump(zs, offset+length)) return TRUE;
+
+ /* If using file buffering, just get the data from the file */
+ if (zs->tmpfil)
+ {
+ if (fseek(zs->tmpfil, offset, SEEK_SET)) return TRUE;
+ if (fread(buffer, 1, (size_t) length, zs->tmpfil) != length) return TRUE;
+ }
+ /* If no temp file, use memory buffering */
+ else
+ {
+ unsigned int i;
+ unsigned int off, len;
+ unsigned char *ptr;
+
+ long tmpoff;
+ unsigned char *tmpbuf;
+ long tmplen;
+
+ /* Save copies of offset, buffer and length for the loop */
+ tmpoff = offset;
+ tmpbuf = buffer;
+ tmplen = length;
+
+ /* Validate the transfer */
+ if (tmpoff+tmplen > MAXFILESIZE) return TRUE;
+
+ /* Loop till done */
+ while (tmplen)
+ {
+ /* Get a pointer to the next block */
+ i = (unsigned int) (tmpoff / OUTBUFSIZE);
+ ptr = zs->ptrbuf[i];
+ if (!ptr) return TRUE;
+
+ /* Get the offset,length for this block */
+ off = (unsigned int) (tmpoff & (OUTBUFSIZE-1));
+ len = OUTBUFSIZE - off;
+ if (len > tmplen) len = (unsigned int) tmplen;
+
+ /* Get the starting pointer for the transfer */
+ ptr += off;
+
+ /* Copy the data for this block */
+#ifdef MEMCPY
+ memcpy(tmpbuf, ptr, len);
+#else
+ for (i=0; i<len; i++)
+ tmpbuf[i] = ptr[i];
+#endif
+
+ /* Update the offset, buffer, and length */
+ tmpoff += len;
+ tmpbuf += len;
+ tmplen -= len;
+ }
+ }
+
+ /* return success */
+ return FALSE;
+}
+
+/* Append to the buffer */
+static int BufferAppend(
+ struct ZipioState *zs,
+ unsigned char *buffer,
+ long length
+)
+{
+ /* If using file buffering, just append the data from the file */
+ if (zs->tmpfil)
+ {
+ if (fseek(zs->tmpfil, zs->outinf, SEEK_SET)) return TRUE;
+ if (fwrite(buffer, 1, (size_t) length, zs->tmpfil) != length) return TRUE;
+ }
+ /* If no temp file, use memory buffering */
+ else
+ {
+ unsigned int i;
+ unsigned int off, len;
+ unsigned char *ptr;
+
+ long tmpoff;
+ unsigned char *tmpbuf;
+ long tmplen;
+
+ /* Save copies of outinf, buffer and length for the loop */
+ tmpoff = zs->outinf;
+ tmpbuf = buffer;
+ tmplen = length;
+
+ /* Validate the transfer */
+ if (tmpoff+tmplen > MAXFILESIZE) return TRUE;
+
+ /* Loop till done */
+ while (tmplen)
+ {
+ /* Get a pointer to the next block */
+ i = (unsigned int) (tmpoff / OUTBUFSIZE);
+ ptr = zs->ptrbuf[i];
+ if (!ptr)
+ {
+ ptr = (unsigned char *) malloc(OUTBUFSIZE);
+ if (!ptr) return TRUE;
+ zs->ptrbuf[i] = ptr;
+ }
+
+ /* Get the offset,length for this block */
+ off = (unsigned int) (tmpoff & (OUTBUFSIZE-1));
+ len = OUTBUFSIZE - off;
+ if (len > tmplen) len = (unsigned int) tmplen;
+
+ /* Get the starting pointer for the transfer */
+ ptr += off;
+
+ /* Copy the data for this block */
+#ifdef MEMCPY
+ memcpy(ptr, tmpbuf, len);
+#else
+ for (i=0; i<len; i++)
+ ptr[i] = tmpbuf[i];
+#endif
+
+ /* Update the offset, buffer, and length */
+ tmpoff += len;
+ tmpbuf += len;
+ tmplen -= len;
+ }
+ }
+
+ /* Update the output buffer length */
+ zs->outinf += length;
+
+ /* return success */
+ return FALSE;
+}
+
+/* Terminate buffering */
+static void BufferTerminate(
+ struct ZipioState *zs
+)
+{
+ /* If reading directly from the uncompressed file, just mark with NULL */
+ if (zs->tmpfil == zs->OpenFile)
+ {
+ zs->tmpfil = NULL;
+ }
+ /* If using the a temporary file, close it */
+ else if (zs->tmpfil)
+ {
+ fclose(zs->tmpfil);
+ zs->tmpfil = NULL;
+ }
+ /* If doing memory buffering, free the buffers */
+ else
+ {
+ int i;
+
+ for (i=0; i<PTRBUFSIZE; i++)
+ if (zs->ptrbuf[i]) free(zs->ptrbuf[i]);
+ }
+}
+
+/*
+ * callout routines for InflateInitialize
+ */
+
+static int inflate_putbuffer( /* returns 0 on success */
+ void *stream, /* opaque ptr from Initialize */
+ unsigned char *buffer, /* buffer to put */
+ long length /* length of buffer */
+)
+{
+ RUNTIMECHECK;
+
+ /* If the write will go past the end of file, return an error */
+ if (ZS->outinf + length > ZS->usiz) return TRUE;
+
+ /* Update the CRC */
+ ZS->filecrc = CrcUpdate(ZS->filecrc, buffer, length);
+
+ /* Append to the buffer */
+ if (BufferAppend(ZS, buffer, length)) return TRUE;
+
+ /* Return success */
+ return FALSE;
+}
+
+static void *inflate_malloc(long length)
+{
+ return malloc((size_t) length);
+}
+
+static void inflate_free(void *buffer)
+{
+ free(buffer);
+}
+
+ZFILE *Zopen(const char *path, const char *mode)
+{
+ struct ZipioState *zs;
+
+ long inplen;
+
+ /* Allocate the ZipioState memory area */
+ zs = (struct ZipioState *) malloc(sizeof(struct ZipioState));
+ if (!zs) return NULL;
+
+ /* Set up the initial values of the inflate state */
+
+ CACHEINIT;
+
+ RUNTIMEINIT;
+
+ zs->errorencountered = FALSE;
+
+ zs->inpinf = 0;
+ zs->outinf = 0;
+
+ zs->fileposition = 0;
+
+ zs->filecrc = 0xffffffffL;
+
+ /* Open the real file */
+ zs->OpenFile = fopen(path, mode);
+ if (!zs->OpenFile)
+ {
+ free(zs);
+ return NULL;
+ }
+
+ /* Read the first input buffer */
+ if ((inplen = (long) fread(zs->inpbuf, 1, INPBUFSIZE, zs->OpenFile)) >= 30)
+ {
+ GETUINT4(zs->inpbuf+ 0, zs->sign);
+ GETUINT2(zs->inpbuf+ 4, zs->vers);
+ GETUINT2(zs->inpbuf+ 6, zs->flag);
+ GETUINT2(zs->inpbuf+ 8, zs->comp);
+ GETUINT2(zs->inpbuf+10, zs->mtim);
+ GETUINT2(zs->inpbuf+12, zs->mdat);
+ GETUINT4(zs->inpbuf+14, zs->crc3);
+ GETUINT4(zs->inpbuf+18, zs->csiz);
+ GETUINT4(zs->inpbuf+22, zs->usiz);
+ GETUINT2(zs->inpbuf+26, zs->flen);
+ GETUINT2(zs->inpbuf+28, zs->elen);
+
+#ifdef PRINTZIPHEADER
+ fprintf(stderr, "local file header signature hex %8lx\n", zs->sign);
+ fprintf(stderr, "version needed to extract %8d\n" , zs->vers);
+ fprintf(stderr, "general purpose bit flag hex %8x\n" , zs->flag);
+ fprintf(stderr, "compression method %8d\n" , zs->comp);
+ fprintf(stderr, "last mod file time %8d\n" , zs->mtim);
+ fprintf(stderr, "last mod file date %8d\n" , zs->mdat);
+ fprintf(stderr, "crc-32 hex %8lx\n", zs->crc3);
+ fprintf(stderr, "compressed size %8ld\n", zs->csiz);
+ fprintf(stderr, "uncompressed size %8ld\n", zs->usiz);
+ fprintf(stderr, "filename length %8d\n" , zs->flen);
+ fprintf(stderr, "extra field length %8d\n" , zs->elen);
+#endif
+ }
+ else
+ {
+ zs->sign = 0;
+ }
+
+ /*
+ * If the file isn't a zip file, set up to read it normally
+ */
+ if ((zs->sign != ZIPSIGNATURE) ||
+ (zs->flag & 1) ||
+ (zs->comp != 8) ||
+ (inplen <= 30 + zs->flen + zs->elen) )
+ {
+ /* Initialize buffering */
+ BufferInitialize(zs, FALSE);
+
+ zs->inflatestate = NULL;
+ }
+ else
+ {
+ /* Initialize buffering */
+ BufferInitialize(zs, TRUE);
+
+ zs->inflatestate = InflateInitialize(
+ (void *) zs,
+ inflate_putbuffer,
+ inflate_malloc,
+ inflate_free
+ );
+
+ if (InflatePutBuffer(zs->inflatestate,
+ zs->inpbuf+30+zs->flen+zs->elen,
+ inplen-30-zs->flen-zs->elen
+ )
+ )
+ zs->errorencountered = TRUE;
+
+ zs->inpinf += inplen-30-zs->flen-zs->elen;
+ }
+
+ /* Return this state info to the caller */
+ return (ZFILE *) zs;
+}
+
+int _Zgetc(ZFILE *stream)
+{
+ long offset, length;
+
+ int off;
+
+ RUNTIMECHECK;
+
+ if (ZS->errorencountered) return -1;
+
+ CACHEUPDATE;
+
+ /* If already at EOF, return */
+ if (ZS->fileposition >= ZS->usiz) return -1;
+
+ /* If data isn't in current outbuf, get it */
+ offset = ZS->fileposition & ~((long) (OUTBUFSIZE-1));
+ if (ZS->getoff != offset)
+ {
+ length = ZS->usiz - offset;
+ if (length > OUTBUFSIZE) length = OUTBUFSIZE;
+
+ if (BufferRead(ZS, offset, ZS->getbuf, length)) return -1;
+
+ ZS->getoff = offset;
+ }
+
+ /* Set up the cache */
+ off = (int) (ZS->fileposition & (OUTBUFSIZE-1));
+ ZS->len = (int) (length - off);
+ ZS->ptr = ZS->getbuf + off;
+
+ /* Return the character */
+ ZS->len--;
+ return *(ZS->ptr++);
+}
+
+size_t Zread(void *ptr, size_t size, size_t n, ZFILE *stream)
+{
+ long length;
+
+ RUNTIMECHECK;
+
+ if (ZS->errorencountered) return 0;
+
+ CACHEUPDATE;
+
+ /* Compute the length requested */
+ length = size * (long) n;
+
+ /* Adjust the length to account for premature EOF */
+ if (ZS->fileposition+length > ZS->usiz)
+ length = ZS->usiz - ZS->fileposition;
+
+ /* If the length is zero, then just return an EOF error */
+ if (length <= 0) return 0;
+
+ /* Make the length a multiple of size */
+ length /= size;
+ length *= size;
+
+ /* If the length is zero, then just return an EOF error */
+ if (length <= 0) return 0;
+
+ /* Read from the buffer */
+ if (BufferRead(ZS, ZS->fileposition, (unsigned char *) ptr, length))
+ return 0;
+
+ /* Update the file position */
+ ZS->fileposition += length;
+
+ /* Return the number of items transferred */
+ return (size_t) (length / size);
+}
+
+int Zseek(ZFILE *stream, long offset, int whence)
+{
+ long newoffset;
+
+ RUNTIMECHECK;
+
+ if (ZS->errorencountered) return -1;
+
+ CACHEUPDATE;
+
+ if (whence == SEEK_SET)
+ {
+ newoffset = offset;
+ }
+ else if (whence == SEEK_CUR)
+ {
+ newoffset = ZS->fileposition + offset;
+ }
+ else if (whence == SEEK_END)
+ {
+ newoffset = ZS->fileposition + ZS->usiz;
+ }
+ else
+ {
+ return -1;
+ }
+
+ if ((newoffset < 0) || (newoffset > ZS->usiz)) return -1;
+
+ ZS->fileposition = newoffset;
+
+ return 0;
+}
+
+long Ztell(ZFILE *stream)
+{
+ RUNTIMECHECK;
+
+ if (ZS->errorencountered) return -1;
+
+ CACHEUPDATE;
+
+ return ZS->fileposition;
+}
+
+int Zclose(ZFILE *stream)
+{
+ int ret;
+
+ RUNTIMECHECK;
+
+ CACHEUPDATE;
+
+ /* terminate the inflate routines, and check for errors */
+ if (ZS->inflatestate)
+ {
+ if (InflateTerminate(ZS->inflatestate))
+ ZS->errorencountered = TRUE;
+
+ /* Check that the CRC is OK */
+ if (ZS->filecrc != (ZS->crc3 ^ 0xffffffffL))
+ ZS->errorencountered = TRUE;
+ }
+
+ /* save the final error status */
+ ret = ZS->errorencountered;
+
+ /* terminate the buffering */
+ BufferTerminate(ZS);
+
+ /* free the ZipioState structure */
+ free(ZS);
+
+ /* return the final error status */
+ return ret;
+}