home *** CD-ROM | disk | FTP | other *** search
- #define __MILETOS_ZIP_CPP__
-
- //
- // Libmiletos
- //
- // Copyright (C) Lauris Kaplinski 2007-2009
- //
-
- static const int debug = 0;
-
- #include <stdio.h>
-
- #include <libarikkei/token.h>
-
- #ifdef WIN32
- #include <zlib/zlib.h>
- #else
- #include <zlib.h>
- #endif
-
- #include "zip.h"
-
- namespace Miletos {
-
- namespace Zip {
-
- #ifdef WIN32
- inline char *strdup (const char *str) { return _strdup (str); }
- #endif
-
- // Gzip flags
- static const int FTEXT = 1;
- static const int FHCRC = 2;
- static const int FEXTRA = 4;
- static const int FNAME = 8;
- static const int FCOMMENT = 16;
-
- unsigned int
- isGzip (const unsigned char *cdata, size_t csize)
- {
- if (csize < 10) return 0;
- size_t cpos = 0;
- if ((cdata[0] != 0x1f) || (cdata[1] != 0x8b) || (cdata[2] != 0x8)) return 0;
- int flags = cdata[3];
- cpos += 10;
- if (flags & FEXTRA) {
- if ((cpos + 2) >= csize) return 0;
- int xlen = cdata[cpos] | (cdata[cpos + 1] << 8);
- cpos += 2;
- cpos += xlen;
- }
- if (flags & FNAME) {
- while ((cpos < csize) && cdata[cpos]) cpos += 1;
- if (cpos < csize) cpos += 1;
- }
- if (flags & FCOMMENT) {
- while ((cpos < csize) && cdata[cpos]) cpos += 1;
- if (cpos < csize) cpos += 1;
- }
- if (flags & FHCRC) {
- if ((cpos + 2) >= csize) return 0;
- cpos += 2;
- }
- return 1;
- }
-
- static int
- uncompress (unsigned char *dest, u32 *destLen, const unsigned char *source, u32 sourceLen)
- {
- z_stream stream;
- int err;
-
- stream.next_in = (Bytef*) source;
- stream.avail_in = (uInt) sourceLen;
- /* Check for source > 64K on 16-bit machine: */
- if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
-
- stream.next_out = dest;
- stream.avail_out = (uInt)*destLen;
- if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
-
- stream.zalloc = (alloc_func)0;
- stream.zfree = (free_func)0;
-
- err = inflateInit2 (&stream, 15 + 16);
- if (err != Z_OK) return err;
-
- err = inflate(&stream, Z_FINISH);
- if (err != Z_STREAM_END) {
- inflateEnd(&stream);
- if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
- return Z_DATA_ERROR;
- return err;
- }
- *destLen = stream.total_out;
-
- err = inflateEnd(&stream);
- return err;
- }
-
- unsigned char *
- gunzip (const unsigned char *cdata, size_t csize, size_t *zsize)
- {
- if (csize < 10) return NULL;
- size_t cpos = 0;
- if ((cdata[0] != 0x1f) || (cdata[1] != 0x8b) || (cdata[2] != 0x8)) return NULL;
- int flags = cdata[3];
- cpos += 10;
- if (flags & FEXTRA) {
- if ((cpos + 2) >= csize) return NULL;
- int xlen = cdata[cpos] | (cdata[cpos + 1] << 8);
- cpos += 2;
- cpos += xlen;
- }
- if (flags & FNAME) {
- while ((cpos < csize) && cdata[cpos]) cpos += 1;
- if (cpos < csize) cpos += 1;
- }
- if (flags & FCOMMENT) {
- while ((cpos < csize) && cdata[cpos]) cpos += 1;
- if (cpos < csize) cpos += 1;
- }
- if (flags & FHCRC) {
- if ((cpos + 2) >= csize) return NULL;
- cpos += 2;
- }
- u32 isize = cdata[csize - 4] | (cdata[csize - 3] << 8) | (cdata[csize - 2] << 16) | (cdata[csize - 1] << 24);
- unsigned char *zdata = (unsigned char *) malloc (isize);
- u32 dlen = isize + 10000000;
- int zresult = uncompress ((unsigned char *) zdata, &dlen, cdata /* + cpos */, csize /* - 8 - cpos */);
- if (zresult != Z_OK) {
- free (zdata);
- return NULL;
- }
- if (zsize) *zsize = dlen;
- return zdata;
- }
-
- // FileData
-
- #ifdef _MSC_VER
- # pragma pack( push, packing )
- # pragma pack( 1 )
- # define PACK_STRUCT
- #elif defined( __GNUC__ )
- # define PACK_STRUCT __attribute__((packed))
- #else
- # error compiler not supported
- #endif
-
- struct ZIPFileDataDescriptor {
- i32 CRC32;
- i32 CompressedSize;
- i32 UncompressedSize;
- } PACK_STRUCT;
-
- struct ZIPFileHeader {
- i32 Sig;
- i16 VersionToExtract;
- i16 GeneralBitFlag;
- i16 CompressionMethod;
- i16 LastModFileTime;
- i16 LastModFileDate;
- ZIPFileDataDescriptor DataDescriptor;
- i16 FilenameLength;
- i16 ExtraFieldLength;
- } PACK_STRUCT;
-
- FileData::FileData (const char *zipfilename, bool ignorecase)
- : cdata(NULL), csize(0), privmap(true)
- {
- cdata = FileSystem::mmapFile (zipfilename, &csize);
- if (cdata) loadEntries (ignorecase);
- }
-
- FileData::FileData (const unsigned char *zdata, size_t zsize, bool ignorecase)
- : cdata(zdata), csize(zsize), privmap(false)
- {
- if (cdata) loadEntries (ignorecase);
- }
-
- FileData::~FileData (void)
- {
- for (size_t i = 0; i < files.size (); i++) {
- if (files[i].cdata) free (files[i].cdata);
- }
- if (privmap && cdata) FileSystem::munmapFile (cdata);
- }
-
- void
- FileData::loadEntries (bool ignorecase)
- {
- size_t offset = 0;
- if ((offset + sizeof (ZIPFileDataDescriptor)) >= csize) return;
- const ZIPFileHeader *lh = (ZIPFileHeader *) (cdata + offset);
- while (((offset + sizeof (ZIPFileHeader)) < csize) && (lh->Sig == 0x04034b50)) {
- // Valid header
- if ((offset + sizeof (ZIPFileHeader) + lh->FilenameLength) >= csize) break;
- const char *fndata = (const char *) cdata + offset + sizeof (ZIPFileHeader);
- bool isdir = (fndata[lh->FilenameLength - 1] == '/') || (fndata[lh->FilenameLength - 1] == '\\');
- char *cname = FileSystem::canonizeName (fndata, lh->FilenameLength, isdir, ignorecase);
- FileEntry fdata;
- fdata.canonicalname = std::string("/") + cname;
- fdata.offset = offset + sizeof (ZIPFileHeader) + lh->FilenameLength + lh->ExtraFieldLength;
- fdata.compressedsize = lh->DataDescriptor.CompressedSize;
- fdata.uncompressedsize = lh->DataDescriptor.UncompressedSize;
- fdata.compressionmethod = lh->CompressionMethod;
- fdata.cdata = NULL;
- fdata.csize = 0;
- fdata.cref = 0;
- free (cname);
-
- if (debug) fprintf (stderr, "ZIPPED file: %s\n", fdata.canonicalname.c_str ());
- files.push_back (fdata);
-
- offset += sizeof (ZIPFileHeader) + lh->FilenameLength + lh->ExtraFieldLength + lh->DataDescriptor.CompressedSize;
- lh = (ZIPFileHeader *) (cdata + offset);
- }
- }
-
- bool
- FileData::getFileEntry (FileEntry& entry)
- {
- switch (entry.compressionmethod) {
- case 0:
- // Store
- entry.cdata = (unsigned char *) malloc (entry.uncompressedsize);
- if (!entry.cdata) return false;
- memcpy (entry.cdata, cdata + entry.offset, entry.uncompressedsize);
- entry.csize = entry.uncompressedsize;
- return true;
- break;
- case 8:
- // Deflate
- entry.cdata = (unsigned char *) malloc (entry.uncompressedsize);
- if (!entry.cdata) return false;
- // Setup the inflate stream.
- z_stream stream;
- i32 err;
- stream.next_in = (Bytef *) cdata + entry.offset;
- stream.avail_in = (uInt) entry.compressedsize;
- stream.next_out = (Bytef *) entry.cdata;
- stream.avail_out = entry.uncompressedsize;
- stream.zalloc = (alloc_func) 0;
- stream.zfree = (free_func) 0;
- // Perform inflation. wbits < 0 indicates no zlib header inside the data.
- err = inflateInit2 (&stream, -MAX_WBITS);
- if (err == Z_OK) {
- err = inflate (&stream, Z_FINISH);
- inflateEnd (&stream);
- if (err == Z_STREAM_END) err = Z_OK;
- err = Z_OK;
- inflateEnd (&stream);
- }
- if (err != Z_OK) {
- free (entry.cdata);
- entry.cdata = NULL;
- return false;
- }
- entry.csize = entry.uncompressedsize;
- return true;
- break;
- default:
- break;
- }
- return false;
- }
-
- // FileHandler
-
- FileHandler::FileHandler (const char *zipfilename)
- : HandlerFileList(zipfilename), data(zipfilename, true), dirloaded(false)
- {
- _valid = data.isValid ();
- ignorecase = true;
-
- if (_valid) {
- for (size_t i = 0; i < data.files.size (); i++) {
- Entry *e = new Entry();
- e->id = (int) i;
- e->name = strdup (data.files[i].canonicalname.c_str ());
- e->dataref = 0;
- e->csize = 0;
- e->cdata = 0;
- entries.push_back (e);
- namedict.insert (e->name, e);
- }
- }
- }
-
- bool
- FileHandler::hasDir (const char *name)
- {
- if (!name || !*name) return false;
- char *cname = getAbsoluteCanonicalName (name, true, true);
- size_t len = strlen (cname);
- for (size_t i = 0; i < data.files.size (); i++) {
- if (!data.files[i].canonicalname.compare (0, len, cname)) {
- free (cname);
- return true;
- }
- }
- free (cname);
- return false;
- }
-
- bool
- FileHandler::loadDir (const char *name)
- {
- dirloaded = false;
-
- if (!name || !*name) return false;
- // We have to create canonincal name before clearing subdirs
- char *cname = getAbsoluteCanonicalName (name, true, true);
-
- _lfiles.clear ();
- _lsizes.clear ();
- _lsubdirs.clear ();
-
- size_t clen = strlen (cname);
- for (size_t i = 0; i < data.files.size (); i++) {
- if (!data.files[i].canonicalname.compare (0, clen, cname)) {
- const char *relname = data.files[i].canonicalname.c_str () + clen;
- const char *subname = strchr (relname, '/');
- if (!subname) {
- // File
- size_t fnlen = data.files[i].canonicalname.length () - clen;
- if (fnlen) {
- _lfiles.push_back (relname);
- _lsizes.push_back (data.files[i].uncompressedsize);
- }
- } else {
- // Subdir or file in subdir
- if (!subname[1]) {
- _lsubdirs.push_back (data.files[i].canonicalname.substr (clen, subname - relname));
- }
- }
- }
- }
- if (!_lfiles.empty () || !_lsubdirs.empty ()) {
- dirloaded = true;
- currentdir = cname;
- }
- free (cname);
- return dirloaded;
- }
-
- int
- FileHandler::getNumFiles (void)
- {
- if (!dirloaded) return 0;
- return (int) _lfiles.size ();
- }
-
- const char *
- FileHandler::getFileName (int idx)
- {
- if (!dirloaded) return NULL;
- if ((idx < 0) || (idx >= (int) _lfiles.size ())) return NULL;
- return _lfiles[idx].c_str ();
- }
-
- size_t
- FileHandler::getFileSize (int idx)
- {
- if (!dirloaded) return 0;
- if ((idx < 0) || (idx >= (int) _lsizes.size ())) return 0;
- return _lsizes[idx];
- }
-
- int
- FileHandler::getNumSubDirs (void)
- {
- if (!dirloaded) return 0;
- return (int) _lsubdirs.size ();
- }
-
- const char *
- FileHandler::getSubDirName (int idx)
- {
- if (!dirloaded) return NULL;
- if ((idx < 0) || (idx >= (int) _lsubdirs.size ())) return NULL;
- return _lsubdirs[idx].c_str ();
- }
-
- void
- FileHandler::addFileMapping (Entry *entry)
- {
- // fixme: Clean this handler up (Lauris)
- }
-
- const unsigned char *
- FileHandler::mmapFile (const char *name, size_t *size)
- {
- if (!name) return false;
- char *cname = getAbsoluteCanonicalName (name, false, true);
- for (size_t i = 0; i < data.files.size (); i++) {
- if (data.files[i].canonicalname == cname) {
- free (cname);
- if (data.files[i].cref > 0) {
- // Already mapped
- data.files[i].cref += 1;
- *size = data.files[i].csize;
- return data.files[i].cdata;
- } else {
- // Try to map
- if (data.getFileEntry (data.files[i])) {
- data.files[i].cref += 1;
- *size = data.files[i].csize;
- return data.files[i].cdata;
- }
- return NULL;
- }
- }
- }
- free (cname);
- return NULL;
- }
-
- void
- FileHandler::munmapFile (const unsigned char *cdata)
- {
- if (!cdata) return;
- for (size_t i = 0; i < data.files.size (); i++) {
- if (data.files[i].cdata == cdata) {
- data.files[i].cref -= 1;
- if (data.files[i].cref < 1) {
- free (data.files[i].cdata);
- data.files[i].cdata = NULL;
- data.files[i].csize = 0;
- return;
- }
- }
- }
- }
-
- // URLHandler
-
- URLHandler::URLHandler (const char *location)
- : URI::URLHandler(location)
- {
- // syntax: zip:(file[;file...])/
- char *ploc = strdup (location + 5);
- ploc[strlen (ploc) - 2] = 0;
- Arikkei::TokenChar ltoken(ploc);
- Arikkei::TokenChar tokenz[16];
- int ntokenz = ltoken.tokenize (tokenz, 16, Arikkei::TokenChar(";"), false);
- for (int i = 0; i < ntokenz; i++) {
- FileHandler *handler = new FileHandler((const char *) tokenz[i]);
- handlers.push_back (handler);
- }
- free (ploc);
- }
-
- URLHandler::~URLHandler (void)
- {
- for (size_t i = 0; i < handlers.size (); i++) {
- handlers[i]->unRef ();
- }
- }
-
- URI::URLHandler *
- URLHandler::newURLHandler (const char *url)
- {
- // syntax: xfpk:(table,data;[table,data...])/[file]
- if (!url || !*url) return NULL;
- if (strncmp (url, "zip:(", 5)) return NULL;
- if (!strstr (url, ")/")) return NULL;
- char *purl = strdup (url);
- char *pq = strstr (purl, ")/");
- *(pq + 2) = 0;
- return new URLHandler(purl);
- }
-
- const unsigned char *
- URLHandler::mmapDataRelative (const char *name, size_t *size)
- {
- if (!name || !*name) return NULL;
- for (size_t i = 0; i < handlers.size (); i++) {
- const unsigned char *data = handlers[i]->mmapFile (name, size);
- if (data) return data;
- }
- return NULL;
- }
-
- void
- URLHandler::munmapData (const unsigned char *data)
- {
- // fixme: (Lauris)
- for (size_t i = 0; i < handlers.size (); i++) {
- handlers[i]->munmapFile (data);
- }
- }
-
- }; // Namespace Zip
-
- }; // Namespace Miletos
-
-