/*1* zipio.c - stdio emulation library for reading zip files2*3* Version 1.1.24*/56/*7* Copyright (C) 1995, Edward B. Hamrick8*9* Permission to use, copy, modify, and distribute this software and10* its documentation for any purpose and without fee is hereby granted,11* provided that the above copyright notice appear in all copies and12* that both that copyright notice and this permission notice appear in13* supporting documentation, and that the name of the copyright holders14* not be used in advertising or publicity pertaining to distribution of15* the software without specific, written prior permission. The copyright16* holders makes no representations about the suitability of this software17* for any purpose. It is provided "as is" without express or implied warranty.18*19* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS20* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,21* IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT22* OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF23* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER24* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE25* OF THIS SOFTWARE.26*/2728/*29* Changes from 1.1 to 1.1.1:30* Changed "z*" functions to "Z*" to avoid namespace pollution.31* Added "zungetc" macro.32* Added definitions of SEEK_SET, SEEK_CUR, SEEK_END for the Posixly challenged33* John Cowan <[email protected]>34*35* Changes from 1.1.1 to 1.1.2:36* Relicensed under the MIT license, with consent of the copyright holders.37* Avoid usage of unitialized "length" variable in _Zgetc38* Claudio Matsuoka (Jan 11 2011)39*/4041/*42* Refer to zipio.h for a description of this package.43*/4445/*46* The .zip file header is described below. It consists of47* 30 fixed bytes, followed by two variable length fields48* whose length is contained in the first 30 bytes. After this49* header, the data is stored (in deflate format if the compression50* method is 8).51*52* The crc-32 field is the crc on the uncompressed data.53*54* .zip file header:55*56* local file header signature 4 bytes (0x04034b50)57* version needed to extract 2 bytes58* general purpose bit flag 2 bytes59* compression method 2 bytes60* last mod file time 2 bytes61* last mod file date 2 bytes62* crc-32 4 bytes63* compressed size 4 bytes64* uncompressed size 4 bytes65* filename length 2 bytes66* extra field length 2 bytes67*68* filename (variable size)69* extra field (variable size)70*71* These fields are described in more detail in appnote.txt72* in the pkzip 1.93 distribution.73*/7475#include <stdlib.h>76#ifdef MEMCPY77#include <mem.h>78#endif7980#include "zipio.h"81#include "inflate.h"82#include "crc.h"8384/*85* Macros for constants86*/8788#ifndef NULL89#define NULL ((void *) 0)90#endif9192#ifndef TRUE93#define TRUE 194#endif9596#ifndef FALSE97#define FALSE 098#endif99100#ifndef ZIPSIGNATURE101#define ZIPSIGNATURE 0x04034b50L102#endif103104#ifndef SEEK_SET105#define SEEK_SET 0106#endif107108#ifndef SEEK_CUR109#define SEEK_CUR 1110#endif111112#ifndef SEEK_END113#define SEEK_END 2114#endif115116117/*118* Buffer size macros119*120* The following constants are optimized for large-model121* (but not flat model) Windows with virtual memory. It122* will work fine on unix and flat model Windows as well.123*124* The constant BUFFERTHRESHOLD determines when memory125* buffering changes to file buffering.126*127* Assumptions:128*129* 1) INPBUFSIZE + OUTBUFSIZE + sizeof(void *) * PTRBUFSIZE + delta < 64K130*131* 2) OUTBUFSIZE = 32K * N (related to inflate's 32K window size)132*133* 2) Max in-memory file size is OUTBUFSIZE * PTRBUFSIZE134* which is 64 MBytes by default (32K * 2K).135*136*/137138#ifndef BUFFERTHRESHOLD139#define BUFFERTHRESHOLD (256 * 1024L)140#endif141142#ifndef INPBUFSIZE143#define INPBUFSIZE ( 8 * 1024 )144#endif145146#ifndef PTRBUFSIZE147#define PTRBUFSIZE ( 2 * 1024 )148#endif149150#ifndef OUTBUFSIZE151#define OUTBUFSIZE ((unsigned int) ( 32 * 1024L))152#endif153154#define MAXFILESIZE (OUTBUFSIZE * (long) PTRBUFSIZE)155156/*157* Macro for short-hand reference to ZipioState (from ZFILE *)158*/159160#define ZS ((struct ZipioState *) stream)161162/*163* Macro to manipulate Zgetc() cache164*/165166#define CACHEINIT \167zs->ptr = NULL; \168zs->len = 0;169170#define CACHEUPDATE \171if (ZS->ptr) \172{ \173ZS->fileposition &= ~((long) (OUTBUFSIZE-1)); \174ZS->fileposition += ZS->ptr - ZS->getbuf; \175ZS->ptr = NULL; \176} \177ZS->len = 0;178179/*180* Macros for run-time type identification181*/182183#ifndef RUNTIMEENABLE184#define RUNTIMEENABLE 0185#endif186187#if RUNTIMEENABLE188#define ZIPIOSTATETYPE 0x0110f00fL189#define RUNTIMEINIT \190zs->runtimetypeid1 = ZIPIOSTATETYPE; \191zs->runtimetypeid2 = ZIPIOSTATETYPE;192193#define RUNTIMECHECK \194if (!ZS || (ZS->runtimetypeid1 != ZIPIOSTATETYPE) \195|| (ZS->runtimetypeid2 != ZIPIOSTATETYPE)) return -1;196#else197#define RUNTIMEINIT198#define RUNTIMECHECK199#endif200201/*202* Macros for converting bytes to unsigned integers203*/204205#define GETUINT4(ptr, i4) \206{ \207i4 = (((unsigned long) *(((unsigned char *) (ptr)) + 0)) ) | \208(((unsigned long) *(((unsigned char *) (ptr)) + 1)) << 8) | \209(((unsigned long) *(((unsigned char *) (ptr)) + 2)) << 16) | \210(((unsigned long) *(((unsigned char *) (ptr)) + 3)) << 24) ; \211}212213#define GETUINT2(ptr, i2) \214{ \215i2 = (((unsigned int) *(((unsigned char *) (ptr)) + 0)) ) | \216(((unsigned int) *(((unsigned char *) (ptr)) + 1)) << 8) ; \217}218219/* Structure to hold state for decoding zip files */220struct ZipioState {221222/* Fields overlaid with ZFILE structure */223int len; /* length of Zgetc cache */224unsigned char *ptr; /* pointer to Zgetc cache */225226/* Fields invisible to users of ZFILE structure */227228unsigned long runtimetypeid1; /* to detect run-time errors */229int errorencountered; /* error encountered flag */230231/* Buffering state */232unsigned char inpbuf[INPBUFSIZE]; /* inp buffer from zip file */233unsigned char *ptrbuf[PTRBUFSIZE]; /* pointers to in-memory bufs */234235unsigned char getbuf[OUTBUFSIZE]; /* buffer for use by Zgetc */236long getoff; /* starting offset of getbuf */237238FILE *tmpfil; /* file ptr to temp file */239240/* Amount of input data inflated */241unsigned long inpinf;242unsigned long outinf;243244/* Zip file header */245unsigned long sign; /* local file header signature (0x04034b50) */246unsigned int vers; /* version needed to extract 2 bytes */247unsigned int flag; /* general purpose bit flag 2 bytes */248unsigned int comp; /* compression method 2 bytes */249unsigned int mtim; /* last mod file time 2 bytes */250unsigned int mdat; /* last mod file date 2 bytes */251unsigned long crc3; /* crc-32 4 bytes */252unsigned long csiz; /* compressed size 4 bytes */253unsigned long usiz; /* uncompressed size 4 bytes */254unsigned int flen; /* filename length 2 bytes */255unsigned int elen; /* extra field length 2 bytes */256257/* Application state */258FILE *OpenFile; /* currently open file */259260void *inflatestate; /* current state for inflate */261262unsigned long fileposition; /* current file position */263264unsigned long filecrc; /* current crc */265266unsigned long runtimetypeid2; /* to detect run-time errors */267};268269/*270* Utility routines to handle uncompressed file buffers271*/272273/* Initialize buffering */274static void BufferInitialize(275struct ZipioState *zs,276int doinflate277)278{279zs->getoff = -1;280zs->tmpfil = NULL;281282/*283* If not inflating, use the input file284*/285286if (!doinflate)287{288zs->tmpfil = zs->OpenFile;289290/* Get the uncompressed file size */291fseek(zs->tmpfil, 0, SEEK_END);292zs->usiz = ftell(zs->tmpfil);293zs->outinf = zs->usiz;294295/* Start at the beginning */296fseek(zs->tmpfil, 0, SEEK_SET);297}298299/* If there's no file open, see if it's big enough for temp file */300if (!zs->tmpfil)301{302if (zs->usiz >= BUFFERTHRESHOLD)303zs->tmpfil = tmpfile();304}305306/* If there's no file open, then use memory buffering */307if (!zs->tmpfil)308{309int i;310311for (i=0; i<PTRBUFSIZE; i++)312zs->ptrbuf[i] = NULL;313}314}315316/* pump data till length bytes of file are inflated or error encountered */317static int BufferPump(struct ZipioState *zs, long length)318{319size_t inplen, ret;320321/* Check to see if the length is valid */322if (length > zs->usiz) return TRUE;323324/* Loop till enough data is pumped */325while (!zs->errorencountered && (zs->outinf < length))326{327/* Compute how much data to read */328if ((zs->csiz - zs->inpinf) < INPBUFSIZE)329inplen = (size_t) (zs->csiz - zs->inpinf);330else331inplen = INPBUFSIZE;332333if (inplen <= 0) return TRUE;334335/* Read some data from the file */336ret = fread(zs->inpbuf, 1, inplen, zs->OpenFile);337if (ret != inplen) return TRUE;338339/* Update how much data has been read from the file */340zs->inpinf += inplen;341342/* Pump this data into the decompressor */343if (InflatePutBuffer(zs->inflatestate, zs->inpbuf, inplen)) return TRUE;344}345346return FALSE;347}348349/* Read from the buffer */350static int BufferRead(351struct ZipioState *zs,352long offset,353unsigned char *buffer,354long length355)356{357/*358* Make sure enough bytes have been inflated359* Note that the correction for reading past EOF has to360* be done before calling this routine361*/362363if (BufferPump(zs, offset+length)) return TRUE;364365/* If using file buffering, just get the data from the file */366if (zs->tmpfil)367{368if (fseek(zs->tmpfil, offset, SEEK_SET)) return TRUE;369if (fread(buffer, 1, (size_t) length, zs->tmpfil) != length) return TRUE;370}371/* If no temp file, use memory buffering */372else373{374unsigned int i;375unsigned int off, len;376unsigned char *ptr;377378long tmpoff;379unsigned char *tmpbuf;380long tmplen;381382/* Save copies of offset, buffer and length for the loop */383tmpoff = offset;384tmpbuf = buffer;385tmplen = length;386387/* Validate the transfer */388if (tmpoff+tmplen > MAXFILESIZE) return TRUE;389390/* Loop till done */391while (tmplen)392{393/* Get a pointer to the next block */394i = (unsigned int) (tmpoff / OUTBUFSIZE);395ptr = zs->ptrbuf[i];396if (!ptr) return TRUE;397398/* Get the offset,length for this block */399off = (unsigned int) (tmpoff & (OUTBUFSIZE-1));400len = OUTBUFSIZE - off;401if (len > tmplen) len = (unsigned int) tmplen;402403/* Get the starting pointer for the transfer */404ptr += off;405406/* Copy the data for this block */407#ifdef MEMCPY408memcpy(tmpbuf, ptr, len);409#else410for (i=0; i<len; i++)411tmpbuf[i] = ptr[i];412#endif413414/* Update the offset, buffer, and length */415tmpoff += len;416tmpbuf += len;417tmplen -= len;418}419}420421/* return success */422return FALSE;423}424425/* Append to the buffer */426static int BufferAppend(427struct ZipioState *zs,428unsigned char *buffer,429long length430)431{432/* If using file buffering, just append the data from the file */433if (zs->tmpfil)434{435if (fseek(zs->tmpfil, zs->outinf, SEEK_SET)) return TRUE;436if (fwrite(buffer, 1, (size_t) length, zs->tmpfil) != length) return TRUE;437}438/* If no temp file, use memory buffering */439else440{441unsigned int i;442unsigned int off, len;443unsigned char *ptr;444445long tmpoff;446unsigned char *tmpbuf;447long tmplen;448449/* Save copies of outinf, buffer and length for the loop */450tmpoff = zs->outinf;451tmpbuf = buffer;452tmplen = length;453454/* Validate the transfer */455if (tmpoff+tmplen > MAXFILESIZE) return TRUE;456457/* Loop till done */458while (tmplen)459{460/* Get a pointer to the next block */461i = (unsigned int) (tmpoff / OUTBUFSIZE);462ptr = zs->ptrbuf[i];463if (!ptr)464{465ptr = (unsigned char *) malloc(OUTBUFSIZE);466if (!ptr) return TRUE;467zs->ptrbuf[i] = ptr;468}469470/* Get the offset,length for this block */471off = (unsigned int) (tmpoff & (OUTBUFSIZE-1));472len = OUTBUFSIZE - off;473if (len > tmplen) len = (unsigned int) tmplen;474475/* Get the starting pointer for the transfer */476ptr += off;477478/* Copy the data for this block */479#ifdef MEMCPY480memcpy(ptr, tmpbuf, len);481#else482for (i=0; i<len; i++)483ptr[i] = tmpbuf[i];484#endif485486/* Update the offset, buffer, and length */487tmpoff += len;488tmpbuf += len;489tmplen -= len;490}491}492493/* Update the output buffer length */494zs->outinf += length;495496/* return success */497return FALSE;498}499500/* Terminate buffering */501static void BufferTerminate(502struct ZipioState *zs503)504{505/* If reading directly from the uncompressed file, just mark with NULL */506if (zs->tmpfil == zs->OpenFile)507{508zs->tmpfil = NULL;509}510/* If using the a temporary file, close it */511else if (zs->tmpfil)512{513fclose(zs->tmpfil);514zs->tmpfil = NULL;515}516/* If doing memory buffering, free the buffers */517else518{519int i;520521for (i=0; i<PTRBUFSIZE; i++)522if (zs->ptrbuf[i]) free(zs->ptrbuf[i]);523}524}525526/*527* callout routines for InflateInitialize528*/529530static int inflate_putbuffer( /* returns 0 on success */531void *stream, /* opaque ptr from Initialize */532unsigned char *buffer, /* buffer to put */533long length /* length of buffer */534)535{536RUNTIMECHECK;537538/* If the write will go past the end of file, return an error */539if (ZS->outinf + length > ZS->usiz) return TRUE;540541/* Update the CRC */542ZS->filecrc = CrcUpdate(ZS->filecrc, buffer, length);543544/* Append to the buffer */545if (BufferAppend(ZS, buffer, length)) return TRUE;546547/* Return success */548return FALSE;549}550551static void *inflate_malloc(long length)552{553return malloc((size_t) length);554}555556static void inflate_free(void *buffer)557{558free(buffer);559}560561ZFILE *Zopen(const char *path, const char *mode)562{563struct ZipioState *zs;564565long inplen;566567/* Allocate the ZipioState memory area */568zs = (struct ZipioState *) malloc(sizeof(struct ZipioState));569if (!zs) return NULL;570571/* Set up the initial values of the inflate state */572573CACHEINIT;574575RUNTIMEINIT;576577zs->errorencountered = FALSE;578579zs->inpinf = 0;580zs->outinf = 0;581582zs->fileposition = 0;583584zs->filecrc = 0xffffffffL;585586/* Open the real file */587zs->OpenFile = fopen(path, mode);588if (!zs->OpenFile)589{590free(zs);591return NULL;592}593594/* Read the first input buffer */595if ((inplen = (long) fread(zs->inpbuf, 1, INPBUFSIZE, zs->OpenFile)) >= 30)596{597GETUINT4(zs->inpbuf+ 0, zs->sign);598GETUINT2(zs->inpbuf+ 4, zs->vers);599GETUINT2(zs->inpbuf+ 6, zs->flag);600GETUINT2(zs->inpbuf+ 8, zs->comp);601GETUINT2(zs->inpbuf+10, zs->mtim);602GETUINT2(zs->inpbuf+12, zs->mdat);603GETUINT4(zs->inpbuf+14, zs->crc3);604GETUINT4(zs->inpbuf+18, zs->csiz);605GETUINT4(zs->inpbuf+22, zs->usiz);606GETUINT2(zs->inpbuf+26, zs->flen);607GETUINT2(zs->inpbuf+28, zs->elen);608609#ifdef PRINTZIPHEADER610fprintf(stderr, "local file header signature hex %8lx\n", zs->sign);611fprintf(stderr, "version needed to extract %8d\n" , zs->vers);612fprintf(stderr, "general purpose bit flag hex %8x\n" , zs->flag);613fprintf(stderr, "compression method %8d\n" , zs->comp);614fprintf(stderr, "last mod file time %8d\n" , zs->mtim);615fprintf(stderr, "last mod file date %8d\n" , zs->mdat);616fprintf(stderr, "crc-32 hex %8lx\n", zs->crc3);617fprintf(stderr, "compressed size %8ld\n", zs->csiz);618fprintf(stderr, "uncompressed size %8ld\n", zs->usiz);619fprintf(stderr, "filename length %8d\n" , zs->flen);620fprintf(stderr, "extra field length %8d\n" , zs->elen);621#endif622}623else624{625zs->sign = 0;626}627628/*629* If the file isn't a zip file, set up to read it normally630*/631if ((zs->sign != ZIPSIGNATURE) ||632(zs->flag & 1) ||633(zs->comp != 8) ||634(inplen <= 30 + zs->flen + zs->elen) )635{636/* Initialize buffering */637BufferInitialize(zs, FALSE);638639zs->inflatestate = NULL;640}641else642{643/* Initialize buffering */644BufferInitialize(zs, TRUE);645646zs->inflatestate = InflateInitialize(647(void *) zs,648inflate_putbuffer,649inflate_malloc,650inflate_free651);652653if (InflatePutBuffer(zs->inflatestate,654zs->inpbuf+30+zs->flen+zs->elen,655inplen-30-zs->flen-zs->elen656)657)658zs->errorencountered = TRUE;659660zs->inpinf += inplen-30-zs->flen-zs->elen;661}662663/* Return this state info to the caller */664return (ZFILE *) zs;665}666667int _Zgetc(ZFILE *stream)668{669long offset, length;670671int off;672673RUNTIMECHECK;674675if (ZS->errorencountered) return -1;676677CACHEUPDATE;678679/* If already at EOF, return */680if (ZS->fileposition >= ZS->usiz) return -1;681682/* If data isn't in current outbuf, get it */683offset = ZS->fileposition & ~((long) (OUTBUFSIZE-1));684length = ZS->usiz - offset;685if (length > OUTBUFSIZE) length = OUTBUFSIZE;686687if (ZS->getoff != offset)688{689if (BufferRead(ZS, offset, ZS->getbuf, length)) return -1;690691ZS->getoff = offset;692}693694/* Set up the cache */695off = (int) (ZS->fileposition & (OUTBUFSIZE-1));696ZS->len = (int) (length - off);697ZS->ptr = ZS->getbuf + off;698699/* Return the character */700ZS->len--;701return *(ZS->ptr++);702}703704size_t Zread(void *ptr, size_t size, size_t n, ZFILE *stream)705{706long length;707708RUNTIMECHECK;709710if (ZS->errorencountered) return 0;711712CACHEUPDATE;713714/* Compute the length requested */715length = size * (long) n;716717/* Adjust the length to account for premature EOF */718if (ZS->fileposition+length > ZS->usiz)719length = ZS->usiz - ZS->fileposition;720721/* If the length is zero, then just return an EOF error */722if (length <= 0) return 0;723724/* Make the length a multiple of size */725length /= size;726length *= size;727728/* If the length is zero, then just return an EOF error */729if (length <= 0) return 0;730731/* Read from the buffer */732if (BufferRead(ZS, ZS->fileposition, (unsigned char *) ptr, length))733return 0;734735/* Update the file position */736ZS->fileposition += length;737738/* Return the number of items transferred */739return (size_t) (length / size);740}741742int Zseek(ZFILE *stream, long offset, int whence)743{744long newoffset;745746RUNTIMECHECK;747748if (ZS->errorencountered) return -1;749750CACHEUPDATE;751752if (whence == SEEK_SET)753{754newoffset = offset;755}756else if (whence == SEEK_CUR)757{758newoffset = ZS->fileposition + offset;759}760else if (whence == SEEK_END)761{762newoffset = ZS->fileposition + ZS->usiz;763}764else765{766return -1;767}768769if ((newoffset < 0) || (newoffset > ZS->usiz)) return -1;770771ZS->fileposition = newoffset;772773return 0;774}775776long Ztell(ZFILE *stream)777{778RUNTIMECHECK;779780if (ZS->errorencountered) return -1;781782CACHEUPDATE;783784return ZS->fileposition;785}786787int Zclose(ZFILE *stream)788{789int ret;790791RUNTIMECHECK;792793CACHEUPDATE;794795/* terminate the inflate routines, and check for errors */796if (ZS->inflatestate)797{798if (InflateTerminate(ZS->inflatestate))799ZS->errorencountered = TRUE;800801/* Check that the CRC is OK */802if (ZS->filecrc != (ZS->crc3 ^ 0xffffffffL))803ZS->errorencountered = TRUE;804}805806/* save the final error status */807ret = ZS->errorencountered;808809/* terminate the buffering */810BufferTerminate(ZS);811812/* free the ZipioState structure */813free(ZS);814815/* return the final error status */816return ret;817}818819820