home *** CD-ROM | disk | FTP | other *** search
/ Xentax forum attachments archive / xentax.7z / 5164 / miletos.7z / zip.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2011-04-02  |  11.8 KB  |  499 lines

  1. #define __MILETOS_ZIP_CPP__
  2.  
  3. //
  4. // Libmiletos
  5. //
  6. // Copyright (C) Lauris Kaplinski 2007-2009
  7. //
  8.  
  9. static const int debug = 0;
  10.  
  11. #include <stdio.h>
  12.  
  13. #include <libarikkei/token.h>
  14.  
  15. #ifdef WIN32
  16. #include <zlib/zlib.h>
  17. #else
  18. #include <zlib.h>
  19. #endif
  20.  
  21. #include "zip.h"
  22.  
  23. namespace Miletos {
  24.  
  25. namespace Zip {
  26.  
  27. #ifdef WIN32
  28. inline char *strdup (const char *str) { return _strdup (str); }
  29. #endif
  30.  
  31. // Gzip flags
  32. static const int FTEXT = 1;
  33. static const int FHCRC = 2;
  34. static const int FEXTRA = 4;
  35. static const int FNAME = 8;
  36. static const int FCOMMENT = 16;
  37.  
  38. unsigned int
  39. isGzip (const unsigned char *cdata, size_t csize)
  40. {
  41.     if (csize < 10) return 0;
  42.     size_t cpos = 0;
  43.     if ((cdata[0] != 0x1f) || (cdata[1] != 0x8b) || (cdata[2] != 0x8)) return 0;
  44.     int flags = cdata[3];
  45.     cpos += 10;
  46.     if (flags & FEXTRA) {
  47.         if ((cpos + 2) >= csize) return 0;
  48.         int xlen = cdata[cpos] | (cdata[cpos + 1] << 8);
  49.         cpos += 2;
  50.         cpos += xlen;
  51.     }
  52.     if (flags & FNAME) {
  53.         while ((cpos < csize) && cdata[cpos]) cpos += 1;
  54.         if (cpos < csize) cpos += 1;
  55.     }
  56.     if (flags & FCOMMENT) {
  57.         while ((cpos < csize) && cdata[cpos]) cpos += 1;
  58.         if (cpos < csize) cpos += 1;
  59.     }
  60.     if (flags & FHCRC) {
  61.         if ((cpos + 2) >= csize) return 0;
  62.         cpos += 2;
  63.     }
  64.     return 1;
  65. }
  66.  
  67. static int
  68. uncompress (unsigned char *dest, u32 *destLen, const unsigned char *source, u32 sourceLen)
  69. {
  70.     z_stream stream;
  71.     int err;
  72.  
  73.     stream.next_in = (Bytef*) source;
  74.     stream.avail_in = (uInt) sourceLen;
  75.     /* Check for source > 64K on 16-bit machine: */
  76.     if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
  77.  
  78.     stream.next_out = dest;
  79.     stream.avail_out = (uInt)*destLen;
  80.     if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
  81.  
  82.     stream.zalloc = (alloc_func)0;
  83.     stream.zfree = (free_func)0;
  84.  
  85.     err = inflateInit2 (&stream, 15 + 16);
  86.     if (err != Z_OK) return err;
  87.  
  88.     err = inflate(&stream, Z_FINISH);
  89.     if (err != Z_STREAM_END) {
  90.         inflateEnd(&stream);
  91.         if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
  92.             return Z_DATA_ERROR;
  93.         return err;
  94.     }
  95.     *destLen = stream.total_out;
  96.  
  97.     err = inflateEnd(&stream);
  98.     return err;
  99. }
  100.  
  101. unsigned char *
  102. gunzip (const unsigned char *cdata, size_t csize, size_t *zsize)
  103. {
  104.     if (csize < 10) return NULL;
  105.     size_t cpos = 0;
  106.     if ((cdata[0] != 0x1f) || (cdata[1] != 0x8b) || (cdata[2] != 0x8)) return NULL;
  107.     int flags = cdata[3];
  108.     cpos += 10;
  109.     if (flags & FEXTRA) {
  110.         if ((cpos + 2) >= csize) return NULL;
  111.         int xlen = cdata[cpos] | (cdata[cpos + 1] << 8);
  112.         cpos += 2;
  113.         cpos += xlen;
  114.     }
  115.     if (flags & FNAME) {
  116.         while ((cpos < csize) && cdata[cpos]) cpos += 1;
  117.         if (cpos < csize) cpos += 1;
  118.     }
  119.     if (flags & FCOMMENT) {
  120.         while ((cpos < csize) && cdata[cpos]) cpos += 1;
  121.         if (cpos < csize) cpos += 1;
  122.     }
  123.     if (flags & FHCRC) {
  124.         if ((cpos + 2) >= csize) return NULL;
  125.         cpos += 2;
  126.     }
  127.     u32 isize = cdata[csize - 4] | (cdata[csize - 3] << 8) | (cdata[csize - 2] << 16) | (cdata[csize - 1] << 24);
  128.     unsigned char *zdata = (unsigned char *) malloc (isize);
  129.     u32 dlen = isize + 10000000;
  130.     int zresult = uncompress ((unsigned char *) zdata, &dlen, cdata /* + cpos */, csize /* - 8 - cpos */);
  131.     if (zresult != Z_OK) {
  132.         free (zdata);
  133.         return NULL;
  134.     }
  135.     if (zsize) *zsize = dlen;
  136.     return zdata;
  137. }
  138.  
  139. // FileData
  140.  
  141. #ifdef _MSC_VER
  142. #    pragma pack( push, packing )
  143. #    pragma pack( 1 )
  144. #    define PACK_STRUCT
  145. #elif defined( __GNUC__ )
  146. #    define PACK_STRUCT    __attribute__((packed))
  147. #else
  148. #    error compiler not supported
  149. #endif
  150.  
  151. struct ZIPFileDataDescriptor {
  152.     i32 CRC32;
  153.     i32 CompressedSize;
  154.     i32 UncompressedSize;
  155. } PACK_STRUCT;
  156.  
  157. struct ZIPFileHeader {
  158.     i32 Sig;
  159.     i16 VersionToExtract;
  160.     i16 GeneralBitFlag;
  161.     i16 CompressionMethod;
  162.     i16 LastModFileTime;
  163.     i16 LastModFileDate;
  164.     ZIPFileDataDescriptor DataDescriptor;
  165.     i16 FilenameLength;
  166.     i16 ExtraFieldLength;
  167. } PACK_STRUCT;
  168.  
  169. FileData::FileData (const char *zipfilename, bool ignorecase)
  170. : cdata(NULL), csize(0), privmap(true)
  171. {
  172.     cdata = FileSystem::mmapFile (zipfilename, &csize);
  173.     if (cdata) loadEntries (ignorecase);
  174. }
  175.  
  176. FileData::FileData (const unsigned char *zdata, size_t zsize, bool ignorecase)
  177. : cdata(zdata), csize(zsize), privmap(false)
  178. {
  179.     if (cdata) loadEntries (ignorecase);
  180. }
  181.  
  182. FileData::~FileData (void)
  183. {
  184.     for (size_t i = 0; i < files.size (); i++) {
  185.         if (files[i].cdata) free (files[i].cdata);
  186.     }
  187.     if (privmap && cdata) FileSystem::munmapFile (cdata);
  188. }
  189.  
  190. void
  191. FileData::loadEntries (bool ignorecase)
  192. {
  193.     size_t offset = 0;
  194.     if ((offset + sizeof (ZIPFileDataDescriptor)) >= csize) return;
  195.     const ZIPFileHeader *lh = (ZIPFileHeader *) (cdata + offset);
  196.     while (((offset + sizeof (ZIPFileHeader)) < csize) && (lh->Sig == 0x04034b50)) {
  197.         // Valid header
  198.         if ((offset + sizeof (ZIPFileHeader) + lh->FilenameLength) >= csize) break;
  199.         const char *fndata = (const char *) cdata + offset + sizeof (ZIPFileHeader);
  200.         bool isdir = (fndata[lh->FilenameLength - 1] == '/') || (fndata[lh->FilenameLength - 1] == '\\');
  201.         char *cname = FileSystem::canonizeName (fndata, lh->FilenameLength, isdir, ignorecase);
  202.         FileEntry fdata;
  203.         fdata.canonicalname = std::string("/") + cname;
  204.         fdata.offset = offset + sizeof (ZIPFileHeader) + lh->FilenameLength + lh->ExtraFieldLength;
  205.         fdata.compressedsize = lh->DataDescriptor.CompressedSize;
  206.         fdata.uncompressedsize = lh->DataDescriptor.UncompressedSize;
  207.         fdata.compressionmethod = lh->CompressionMethod;
  208.         fdata.cdata = NULL;
  209.         fdata.csize = 0;
  210.         fdata.cref = 0;
  211.         free (cname);
  212.  
  213.         if (debug) fprintf (stderr, "ZIPPED file: %s\n", fdata.canonicalname.c_str ());
  214.         files.push_back (fdata);
  215.  
  216.         offset += sizeof (ZIPFileHeader) + lh->FilenameLength + lh->ExtraFieldLength + lh->DataDescriptor.CompressedSize;
  217.         lh = (ZIPFileHeader *) (cdata + offset);
  218.     }
  219. }
  220.  
  221. bool
  222. FileData::getFileEntry (FileEntry& entry)
  223. {
  224.     switch (entry.compressionmethod) {
  225.     case 0:
  226.         // Store
  227.         entry.cdata = (unsigned char *) malloc (entry.uncompressedsize);
  228.         if (!entry.cdata) return false;
  229.         memcpy (entry.cdata, cdata + entry.offset, entry.uncompressedsize);
  230.         entry.csize = entry.uncompressedsize;
  231.         return true;
  232.         break;
  233.     case 8:
  234.         // Deflate
  235.         entry.cdata = (unsigned char *) malloc (entry.uncompressedsize);
  236.         if (!entry.cdata) return false;
  237.         // Setup the inflate stream.
  238.         z_stream stream;
  239.         i32 err;
  240.         stream.next_in = (Bytef *) cdata + entry.offset;
  241.         stream.avail_in = (uInt) entry.compressedsize;
  242.         stream.next_out = (Bytef *) entry.cdata;
  243.         stream.avail_out = entry.uncompressedsize;
  244.         stream.zalloc = (alloc_func) 0;
  245.         stream.zfree = (free_func) 0;
  246.         // Perform inflation. wbits < 0 indicates no zlib header inside the data.
  247.         err = inflateInit2 (&stream, -MAX_WBITS);
  248.         if (err == Z_OK) {
  249.             err = inflate (&stream, Z_FINISH);
  250.             inflateEnd (&stream);
  251.             if (err == Z_STREAM_END) err = Z_OK;
  252.             err = Z_OK;
  253.             inflateEnd (&stream);
  254.         }
  255.         if (err != Z_OK) {
  256.             free (entry.cdata);
  257.             entry.cdata = NULL;
  258.             return false;
  259.         }
  260.         entry.csize = entry.uncompressedsize;
  261.         return true;
  262.         break;
  263.     default:
  264.         break;
  265.     }
  266.     return false;
  267. }
  268.  
  269. // FileHandler
  270.  
  271. FileHandler::FileHandler (const char *zipfilename)
  272. : HandlerFileList(zipfilename), data(zipfilename, true), dirloaded(false)
  273. {
  274.     _valid = data.isValid ();
  275.     ignorecase = true;
  276.  
  277.     if (_valid) {
  278.         for (size_t i = 0; i < data.files.size (); i++) {
  279.             Entry *e = new Entry();
  280.             e->id = (int) i;
  281.             e->name = strdup (data.files[i].canonicalname.c_str ());
  282.             e->dataref = 0;
  283.             e->csize = 0;
  284.             e->cdata = 0;
  285.             entries.push_back (e);
  286.             namedict.insert (e->name, e);
  287.         }
  288.     }
  289. }
  290.  
  291. bool
  292. FileHandler::hasDir (const char *name)
  293. {
  294.     if (!name || !*name) return false;
  295.     char *cname = getAbsoluteCanonicalName (name, true, true);
  296.     size_t len = strlen (cname);
  297.     for (size_t i = 0; i < data.files.size (); i++) {
  298.         if (!data.files[i].canonicalname.compare (0, len, cname)) {
  299.             free (cname);
  300.             return true;
  301.         }
  302.     }
  303.     free (cname);
  304.     return false;
  305. }
  306.  
  307. bool
  308. FileHandler::loadDir (const char *name)
  309. {
  310.     dirloaded = false;
  311.  
  312.     if (!name || !*name) return false;
  313.     // We have to create canonincal name before clearing subdirs
  314.     char *cname = getAbsoluteCanonicalName (name, true, true);
  315.  
  316.     _lfiles.clear ();
  317.     _lsizes.clear ();
  318.     _lsubdirs.clear ();
  319.  
  320.     size_t clen = strlen (cname);
  321.     for (size_t i = 0; i < data.files.size (); i++) {
  322.         if (!data.files[i].canonicalname.compare (0, clen, cname)) {
  323.             const char *relname = data.files[i].canonicalname.c_str () + clen;
  324.             const char *subname = strchr (relname, '/');
  325.             if (!subname) {
  326.                 // File
  327.                 size_t fnlen = data.files[i].canonicalname.length () - clen;
  328.                 if (fnlen) {
  329.                     _lfiles.push_back (relname);
  330.                     _lsizes.push_back (data.files[i].uncompressedsize);
  331.                 }
  332.             } else {
  333.                 // Subdir or file in subdir
  334.                 if (!subname[1]) {
  335.                     _lsubdirs.push_back (data.files[i].canonicalname.substr (clen, subname - relname));
  336.                 }
  337.             }
  338.         }
  339.     }
  340.     if (!_lfiles.empty () || !_lsubdirs.empty ()) {
  341.         dirloaded = true;
  342.         currentdir = cname;
  343.     }
  344.     free (cname);
  345.     return dirloaded;
  346. }
  347.  
  348. int
  349. FileHandler::getNumFiles (void)
  350. {
  351.     if (!dirloaded) return 0;
  352.     return (int) _lfiles.size ();
  353. }
  354.  
  355. const char *
  356. FileHandler::getFileName (int idx)
  357. {
  358.     if (!dirloaded) return NULL;
  359.     if ((idx < 0) || (idx >= (int) _lfiles.size ())) return NULL;
  360.     return _lfiles[idx].c_str ();
  361. }
  362.  
  363. size_t
  364. FileHandler::getFileSize (int idx)
  365. {
  366.     if (!dirloaded) return 0;
  367.     if ((idx < 0) || (idx >= (int) _lsizes.size ())) return 0;
  368.     return _lsizes[idx];
  369. }
  370.  
  371. int
  372. FileHandler::getNumSubDirs (void)
  373. {
  374.     if (!dirloaded) return 0;
  375.     return (int) _lsubdirs.size ();
  376. }
  377.  
  378. const char *
  379. FileHandler::getSubDirName (int idx)
  380. {
  381.     if (!dirloaded) return NULL;
  382.     if ((idx < 0) || (idx >= (int) _lsubdirs.size ())) return NULL;
  383.     return _lsubdirs[idx].c_str ();
  384. }
  385.  
  386. void
  387. FileHandler::addFileMapping (Entry *entry)
  388. {
  389.     // fixme: Clean this handler up (Lauris)
  390. }
  391.  
  392. const unsigned char *
  393. FileHandler::mmapFile (const char *name, size_t *size)
  394. {
  395.     if (!name) return false;
  396.     char *cname = getAbsoluteCanonicalName (name, false, true);
  397.     for (size_t i = 0; i < data.files.size (); i++) {
  398.         if (data.files[i].canonicalname == cname) {
  399.             free (cname);
  400.             if (data.files[i].cref > 0) {
  401.                 // Already mapped
  402.                 data.files[i].cref += 1;
  403.                 *size = data.files[i].csize;
  404.                 return data.files[i].cdata;
  405.             } else {
  406.                 // Try to map
  407.                 if (data.getFileEntry (data.files[i])) {
  408.                     data.files[i].cref += 1;
  409.                     *size = data.files[i].csize;
  410.                     return data.files[i].cdata;
  411.                 }
  412.                 return NULL;
  413.             }
  414.         }
  415.     }
  416.     free (cname);
  417.     return NULL;
  418. }
  419.  
  420. void
  421. FileHandler::munmapFile (const unsigned char *cdata)
  422. {
  423.     if (!cdata) return;
  424.     for (size_t i = 0; i < data.files.size (); i++) {
  425.         if (data.files[i].cdata == cdata) {
  426.             data.files[i].cref -= 1;
  427.             if (data.files[i].cref < 1) {
  428.                 free (data.files[i].cdata);
  429.                 data.files[i].cdata = NULL;
  430.                 data.files[i].csize = 0;
  431.                 return;
  432.             }
  433.         }
  434.     }
  435. }
  436.  
  437. // URLHandler
  438.  
  439. URLHandler::URLHandler (const char *location)
  440. : URI::URLHandler(location)
  441. {
  442.     // syntax: zip:(file[;file...])/
  443.     char *ploc = strdup (location + 5);
  444.     ploc[strlen (ploc) - 2] = 0;
  445.     Arikkei::TokenChar ltoken(ploc);
  446.     Arikkei::TokenChar tokenz[16];
  447.     int ntokenz = ltoken.tokenize (tokenz, 16, Arikkei::TokenChar(";"), false);
  448.     for (int i = 0; i < ntokenz; i++) {
  449.         FileHandler *handler = new FileHandler((const char *) tokenz[i]);
  450.         handlers.push_back (handler);
  451.     }
  452.     free (ploc);
  453. }
  454.  
  455. URLHandler::~URLHandler (void)
  456. {
  457.     for (size_t i = 0; i < handlers.size (); i++) {
  458.         handlers[i]->unRef ();
  459.     }
  460. }
  461.  
  462. URI::URLHandler *
  463. URLHandler::newURLHandler (const char *url)
  464. {
  465.     // syntax: xfpk:(table,data;[table,data...])/[file]
  466.     if (!url || !*url) return NULL;
  467.     if (strncmp (url, "zip:(", 5)) return NULL;
  468.     if (!strstr (url, ")/")) return NULL;
  469.     char *purl = strdup (url);
  470.     char *pq = strstr (purl, ")/");
  471.     *(pq + 2) = 0;
  472.     return new URLHandler(purl);
  473. }
  474.  
  475. const unsigned char *
  476. URLHandler::mmapDataRelative (const char *name, size_t *size)
  477. {
  478.     if (!name || !*name) return NULL;
  479.     for (size_t i = 0; i < handlers.size (); i++) {
  480.         const unsigned char *data = handlers[i]->mmapFile (name, size);
  481.         if (data) return data;
  482.     }
  483.     return NULL;
  484. }
  485.  
  486. void
  487. URLHandler::munmapData (const unsigned char *data)
  488. {
  489.     // fixme: (Lauris)
  490.     for (size_t i = 0; i < handlers.size (); i++) {
  491.         handlers[i]->munmapFile (data);
  492.     }
  493. }
  494.  
  495. }; // Namespace Zip
  496.  
  497. }; // Namespace Miletos
  498.  
  499.