zip and gzip complete. Debian packaging in progress.
authorMichael McMaster <email@michaelmcmaster.name>
Fri, 20 May 2011 14:24:27 +0000 (00:24 +1000)
committerMichael McMaster <email@michaelmcmaster.name>
Fri, 20 May 2011 14:24:27 +0000 (00:24 +1000)
24 files changed:
Ungzip.cc [deleted file]
Ungzip.hh [deleted file]
Unzip.cc [deleted file]
Unzip.hh [deleted file]
Zip.cc [deleted file]
Zip.hh [deleted file]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/docs [new file with mode: 0644]
debian/libzipper-dev.dirs [new file with mode: 0644]
debian/libzipper-dev.install [new file with mode: 0644]
debian/libzipper1.dirs [new file with mode: 0644]
debian/libzipper1.install [new file with mode: 0644]
debian/rules [new file with mode: 0755]
debian/source/format [new file with mode: 0644]
deflate.cc [new file with mode: 0644]
deflate.hh [new file with mode: 0644]
gzip.cc [new file with mode: 0644]
gzip.hh [new file with mode: 0644]
libzipper-1.0.pc.in [new file with mode: 0644]
zip.cc [new file with mode: 0644]
zip.hh [new file with mode: 0644]

diff --git a/Ungzip.cc b/Ungzip.cc
deleted file mode 100644 (file)
index 774d6c6..0000000
--- a/Ungzip.cc
+++ /dev/null
@@ -1,309 +0,0 @@
-//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
-//
-//     This file is part of libzipper.
-//
-//     libzipper is free software: you can redistribute it and/or modify
-//     it under the terms of the GNU General Public License as published by
-//     the Free Software Foundation, either version 3 of the License, or
-//     (at your option) any later version.
-//
-//     libzipper is distributed in the hope that it will be useful,
-//     but WITHOUT ANY WARRANTY; without even the implied warranty of
-//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//     GNU General Public License for more details.
-//
-//     You should have received a copy of the GNU General Public License
-//     along with libzipper.  If not, see <http://www.gnu.org/licenses/>.
-
-#include "zipper.hh"
-#include "Ungzip.hh"
-
-#include <zlib.h>
-
-#include <algorithm>
-#include <cassert>
-#include <iostream>
-#include <vector>
-
-#include <string.h>
-
-using namespace zipper;
-
-namespace
-{
-       uint32_t
-       read32(const uint8_t* zipData)
-       {
-               // Read 4 bytes in little-endian order.
-               // Return results in host-endian.
-               return uint32_t(
-                       zipData[0] |
-                       (uint32_t(zipData[1]) << 8) |
-                       (uint32_t(zipData[2]) << 16) |
-                       (uint32_t(zipData[3]) << 24)
-                       );
-       }
-
-       uint16_t
-       read16(const std::vector<uint8_t>& zipData, size_t pos)
-       {
-               // Read 2 bytes in little-endian order.
-               // Return results in host-endian.
-               return uint16_t(
-                       zipData[pos] |
-                       (uint16_t(zipData[pos+1]) << 8)
-                       );
-       }
-
-
-       size_t
-       findNull(const std::vector<uint8_t>& zipData, size_t start)
-       {
-               if (start >= zipData.size())
-               {
-                       throw FormatException("Unexpected end-of-file");
-               }
-
-               while (zipData[start] != 0)
-               {
-                       ++start;
-                       if (start >= zipData.size())
-                       {
-                               throw FormatException("Unexpected end-of-file");
-                       }
-               }
-               return start;
-       }
-
-       struct InflateDeleter
-       {
-       public:
-               InflateDeleter(z_stream* stream) : m_stream(stream) {}
-               ~InflateDeleter()
-               {
-                       inflateEnd(m_stream);
-
-               }
-       private:
-               z_stream* m_stream;
-       };
-
-       class FileEntry : public CompressedFile
-       {
-       public:
-               FileEntry(
-                       const ReaderPtr& reader,
-                       zsize_t dataOffset,
-                       const std::string& filename
-                       ) :
-                       m_reader(reader),
-                       m_dataOffset(dataOffset),
-                       m_fileName(filename)
-               {
-               }
-
-               virtual bool isDecompressSupported() const
-               {
-                       return true;
-               }
-
-               virtual const std::string& getPath() const
-               {
-                       return m_fileName;
-               }
-
-               virtual zsize_t getCompressedSize() const { return -1; }
-               virtual zsize_t getUncompressedSize() const { return -1; }
-
-               virtual void decompress(Writer& writer)
-               {
-                       enum
-                       {
-                               ChunkSize = 64*1024,
-                               WindowBits = 15
-                       };
-
-                       uint8_t inChunk[ChunkSize];
-                       uint8_t outChunk[ChunkSize];
-                       zsize_t endCompressedBytes = m_reader->getSize() - 8; // CRC+ISIZE
-                       uint32_t crc(crc32(0, NULL, 0));
-
-                       z_stream stream;
-                       stream.zalloc = NULL;
-                       stream.zfree = NULL;
-                       stream.opaque = NULL;
-                       int zlibErr(inflateInit2(&stream, -WindowBits));
-                       assert(zlibErr == Z_OK);
-                       InflateDeleter deleter(&stream);
-                       stream.next_in = NULL;
-                       stream.avail_in = 0;
-
-                       bool finished(false);
-                       zsize_t pos(m_dataOffset);
-                       zsize_t outPos(0);
-                       while (pos < endCompressedBytes)
-                       {
-                               if (stream.avail_in == 0)
-                               {
-                                       stream.avail_in =
-                                               std::min(
-                                                       zsize_t(ChunkSize),
-                                                       endCompressedBytes - pos
-                                                       );
-                                       m_reader->readData(
-                                               pos, stream.avail_in, &inChunk[0]
-                                               );
-                                       stream.next_in = reinterpret_cast<Bytef*>(&inChunk);
-                                       pos += stream.avail_in;
-                               }
-
-                               stream.next_out = reinterpret_cast<Bytef*>(&outChunk);
-                               stream.avail_out = sizeof(outChunk);
-
-                               zlibErr = inflate(&stream, Z_SYNC_FLUSH);
-
-                               finished = false;
-                               if (zlibErr == Z_STREAM_END)
-                               {
-                                       finished = true;
-                               }
-                               else if (zlibErr != Z_OK)
-                               {
-                                       throw FormatException("Corrupt Data");
-                               }
-
-                               zsize_t bytesToWrite(sizeof(outChunk) - stream.avail_out);
-                               writer.writeData(outPos, bytesToWrite, &outChunk[0]);
-                               outPos += bytesToWrite;
-                               crc = crc32(crc, &outChunk[0], bytesToWrite);
-
-                               if (finished) break;
-                       }
-
-                       if (!finished)
-                       {
-                               // Ran out of data to process
-                               throw FormatException("Corrupt Data");
-                       }
-
-                       uint8_t crcBuffer[4];
-                       ::memcpy(crcBuffer, stream.next_in, std::min(4u, stream.avail_in));
-
-                       if (stream.avail_in < 4)
-                       {
-                               m_reader->readData(
-                                       pos, 4 - stream.avail_in, &crcBuffer[stream.avail_in]
-                                       );
-                       }
-                       uint32_t savedCRC = read32(&crcBuffer[0]);
-                       if (savedCRC != crc)
-                       {
-                               throw FormatException("Corrupt Data (CRC Failure)");
-                       }
-               }
-
-       private:
-               ReaderPtr m_reader;
-               zsize_t m_dataOffset;
-               std::string m_fileName;
-       };
-}
-
-namespace zipper
-{
-       std::vector<CompressedFilePtr>
-       ungzip(const ReaderPtr& reader)
-       {
-               enum
-               {
-                       MaxHeader = 64*1024  // Artifical limit to simplify code
-               };
-
-               if (!isGzip(reader))
-               {
-                       throw FormatException("Invalid gzip file");
-               }
-
-               std::vector<uint8_t> header(
-                       std::min(reader->getSize(), zsize_t(MaxHeader)));
-               reader->readData(0, header.size(), &header[0]);
-
-               if (header[2] != 8) // "deflate" method
-               {
-                       throw UnsupportedException("Unknown gzip compression method");
-               }
-
-               bool fextra = (header[3] & 4) != 0;
-               bool fname = (header[3] & 8) != 0;
-               bool fcomment = (header[3] & 0x10) != 0;
-               bool fhcrc = (header[3] & 2) != 0;
-
-               size_t offset(10);
-
-               if (fextra)
-               {
-                       if (offset + 2 > header.size())
-                       {
-                               throw FormatException("Unexpected end-of-file");
-                       }
-                       uint16_t fextraBytes(read16(header, offset));
-                       offset += 2;
-
-                       offset += fextraBytes;
-               }
-
-               std::string embeddedName(reader->getSourceName());
-               if (fname)
-               {
-                       size_t nullOffset(findNull(header, offset));
-                       embeddedName =
-                               std::string(
-                                       reinterpret_cast<char*>(&header[offset]),
-                                       nullOffset - offset);
-                       offset = nullOffset + 1;
-               }
-
-               if (fcomment)
-               {
-                       size_t nullOffset(findNull(header, offset));
-                       offset = nullOffset + 1;
-               }
-
-               if (fhcrc)
-               {
-                       offset += 2;
-               }
-
-               if (offset >= header.size())
-               {
-                       throw FormatException("Unexpected end-of-file");
-               }
-
-               std::vector<CompressedFilePtr> result;
-               result.push_back(
-                       CompressedFilePtr(new FileEntry(reader, offset, embeddedName)));
-
-               return result;
-       }
-
-       bool
-       isGzip(const ReaderPtr& reader)
-       {
-               enum Constants
-               {
-                       MinFileBytes = 18, // Header + CRC + size
-                       ID1 = 0x1f,
-                       ID2 = 0x8b
-               };
-
-               bool isGzip(false);
-               if (reader->getSize() >= MinFileBytes)
-               {
-                       uint8_t magic[2];
-                       reader->readData(0, sizeof(magic), &magic[0]);
-                       isGzip = (magic[0] == ID1) && (magic[1] == ID2);
-               }
-               return isGzip;
-       }
-}
-
diff --git a/Ungzip.hh b/Ungzip.hh
deleted file mode 100644 (file)
index 95d445b..0000000
--- a/Ungzip.hh
+++ /dev/null
@@ -1,28 +0,0 @@
-//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
-//
-//     This file is part of libzipper.
-//
-//     libzipper is free software: you can redistribute it and/or modify
-//     it under the terms of the GNU General Public License as published by
-//     the Free Software Foundation, either version 3 of the License, or
-//     (at your option) any later version.
-//
-//     libzipper is distributed in the hope that it will be useful,
-//     but WITHOUT ANY WARRANTY; without even the implied warranty of
-//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//     GNU General Public License for more details.
-//
-//     You should have received a copy of the GNU General Public License
-//     along with libzipper.  If not, see <http://www.gnu.org/licenses/>.
-
-#include "zipper.hh"
-
-#include <vector>
-
-namespace zipper
-{
-       bool isGzip(const ReaderPtr& reader);
-
-       std::vector<CompressedFilePtr> ungzip(const ReaderPtr& reader);
-}
-
diff --git a/Unzip.cc b/Unzip.cc
deleted file mode 100644 (file)
index 7845d91..0000000
--- a/Unzip.cc
+++ /dev/null
@@ -1,451 +0,0 @@
-//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
-//
-//     This file is part of libzipper.
-//
-//     libzipper is free software: you can redistribute it and/or modify
-//     it under the terms of the GNU General Public License as published by
-//     the Free Software Foundation, either version 3 of the License, or
-//     (at your option) any later version.
-//
-//     libzipper is distributed in the hope that it will be useful,
-//     but WITHOUT ANY WARRANTY; without even the implied warranty of
-//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//     GNU General Public License for more details.
-//
-//     You should have received a copy of the GNU General Public License
-//     along with libzipper.  If not, see <http://www.gnu.org/licenses/>.
-
-#include "zipper.hh"
-#include "Unzip.hh"
-
-#include <zlib.h>
-
-#include <algorithm>
-#include <cassert>
-#include <iostream>
-
-#include <string.h>
-
-using namespace zipper;
-
-namespace
-{
-       template<typename T>
-       uint32_t
-       read32(const T& zipData, size_t pos)
-       {
-               // Read 4 bytes in little-endian order.
-               // Return results in host-endian.
-               return uint32_t(
-                       zipData[pos] |
-                       (uint32_t(zipData[pos+1]) << 8) |
-                       (uint32_t(zipData[pos+2]) << 16) |
-                       (uint32_t(zipData[pos+3]) << 24)
-                       );
-       }
-
-       uint16_t
-       read16(const std::vector<uint8_t>& zipData, size_t pos)
-       {
-               // Read 2 bytes in little-endian order.
-               // Return results in host-endian.
-               return uint16_t(
-                       zipData[pos] |
-                       (uint16_t(zipData[pos+1]) << 8)
-                       );
-       }
-
-       struct InflateDeleter
-       {
-       public:
-               InflateDeleter(z_stream* stream) : m_stream(stream) {}
-               ~InflateDeleter()
-               {
-                       inflateEnd(m_stream);
-
-               }
-       private:
-               z_stream* m_stream;
-       };
-
-       class FileEntry : public CompressedFile
-       {
-       public:
-               FileEntry(
-                       const ReaderPtr& reader,
-                       uint16_t versionNeeded,
-                       uint16_t gpFlag,
-                       uint16_t compressionMethod,
-                       uint32_t crc,
-                       zsize_t compressedSize,
-                       zsize_t uncompressedSize,
-                       zsize_t localHeaderOffset,
-                       std::string fileName
-                       ) :
-                       m_reader(reader),
-                       m_versionNeeded(versionNeeded),
-                       m_gpFlag(gpFlag),
-                       m_compressionMethod(compressionMethod),
-                       m_crc(crc),
-                       m_compressedSize(compressedSize),
-                       m_uncompressedSize(uncompressedSize),
-                       m_localHeaderOffset(localHeaderOffset),
-                       m_fileName(fileName)
-               {
-               }
-
-               virtual bool isDecompressSupported() const
-               {
-                       return ((m_versionNeeded & 0xf) <= 20) &&
-                               ((m_gpFlag & 0x1) == 0) && // Not encrypted
-                               ((m_compressionMethod == 0) || (m_compressionMethod == 8));
-               }
-
-               virtual const std::string& getPath() const
-               {
-                       return m_fileName;
-               }
-
-               virtual zsize_t getCompressedSize() const { return m_compressedSize; }
-               virtual zsize_t getUncompressedSize() const
-               {
-                       return m_uncompressedSize;
-               }
-
-               virtual void decompress(Writer& writer)
-               {
-                       enum
-                       {
-                               Signature = 0x04034b50,
-                               MinRecordBytes = 30,
-                               ChunkSize = 64*1024,
-                               WindowBits = 15
-                       };
-
-                       std::vector<uint8_t> localRecord(MinRecordBytes);
-                       m_reader->readData(
-                               m_localHeaderOffset, MinRecordBytes, &localRecord[0]
-                               );
-                       if (read32(localRecord, 0) != Signature)
-                       {
-                               throw FormatException("Invalid local ZIP record");
-                       }
-
-                       // Don't trust the lengths for filename and extra content read from
-                       // the central records.  At least for extra, these DO differ for
-                       // unknown reasons
-                       zsize_t filenameLength(read16(localRecord, 26));
-                       zsize_t extraLength(read16(localRecord, 28));
-
-                       zsize_t startCompressedBytes(
-                               m_localHeaderOffset +
-                               MinRecordBytes +
-                               filenameLength +
-                               extraLength
-                               );
-
-                       zsize_t endCompressedBytes(
-                               startCompressedBytes + m_compressedSize
-                               );
-
-                       if (endCompressedBytes > m_reader->getSize())
-                       {
-                               throw FormatException("Compressed file size is too long");
-                       }
-
-                       switch (m_compressionMethod)
-                       {
-                       case 0: // No compression
-                       {
-                               for (zsize_t pos(startCompressedBytes);
-                                       pos < endCompressedBytes;
-                                       pos += ChunkSize
-                                       )
-                               {
-                                       uint8_t buf[ChunkSize];
-                                       zsize_t bytes(
-                                               std::min(zsize_t(ChunkSize), endCompressedBytes - pos)
-                                               );
-                                       m_reader->readData(pos, bytes, &buf[0]);
-                                       writer.writeData(pos, bytes, &buf[0]);
-                               }
-                       }; break;
-
-                       case 8: // Deflate
-                       {
-                               uint8_t inChunk[ChunkSize];
-                               uint8_t outChunk[ChunkSize];
-
-                               z_stream stream;
-                               stream.zalloc = NULL;
-                               stream.zfree = NULL;
-                               stream.opaque = NULL;
-                               int zlibErr(inflateInit2(&stream, -WindowBits));
-                               assert(zlibErr == Z_OK);
-                               InflateDeleter deleter(&stream);
-                               stream.next_in = NULL;
-                               stream.avail_in = 0;
-                               bool finished(false);
-
-                               zsize_t pos(startCompressedBytes);
-                               zsize_t outPos(0);
-                               uint32_t crc(crc32(0, NULL, 0));
-                               while (pos < endCompressedBytes)
-                               {
-                                       if (stream.avail_in == 0)
-                                       {
-                                               stream.avail_in =
-                                                       std::min(
-                                                               zsize_t(ChunkSize),
-                                                               endCompressedBytes - pos
-                                                               );
-                                               m_reader->readData(
-                                                       pos, stream.avail_in, &inChunk[0]
-                                                       );
-                                               stream.next_in = reinterpret_cast<Bytef*>(&inChunk);
-                                               pos += stream.avail_in;
-                                       }
-
-                                       stream.next_out = reinterpret_cast<Bytef*>(&outChunk);
-                                       stream.avail_out = sizeof(outChunk);
-
-                                       zlibErr = inflate(&stream, Z_SYNC_FLUSH);
-
-                                       finished = false;
-                                       if (zlibErr == Z_STREAM_END)
-                                       {
-                                               finished = true;
-
-                                       }
-                                       else if (zlibErr != Z_OK)
-                                       {
-                                               throw FormatException("Corrupt Data");
-                                       }
-
-                                       zsize_t bytesToWrite(sizeof(outChunk) - stream.avail_out);
-                                       writer.writeData(
-                                               outPos,
-                                               bytesToWrite,
-                                               &outChunk[0]
-                                               );
-                                       outPos += bytesToWrite;
-                                       crc = crc32(crc, &outChunk[0], bytesToWrite);
-
-                                       if (finished) break;
-                               }
-                               if (!finished)
-                               {
-                                       // Ran out of data to process
-                                       throw FormatException("Corrupt Data");
-                               }
-
-                               if (m_gpFlag & 0x4) // CRC is after compressed data
-                               {
-                                       uint8_t dataDescriptor[12];
-                                       ::memcpy(
-                                               dataDescriptor,
-                                               stream.next_in,
-                                               std::min(12u, stream.avail_in));
-
-                                       if (stream.avail_in < 12)
-                                       {
-                                               m_reader->readData(
-                                                       pos,
-                                                       12 - stream.avail_in,
-                                                       &dataDescriptor[stream.avail_in]);
-                                       }
-                                       m_crc = read32(dataDescriptor, 0);
-                                       m_compressedSize = read32(dataDescriptor, 4);
-                                       m_uncompressedSize = read32(dataDescriptor, 8);
-                               }
-
-                               if (crc != m_crc)
-                               {
-                                       throw FormatException("Corrupt Data (CRC failure)");
-                               }
-
-                       }; break;
-                       default:
-                               throw UnsupportedException("Unsupported compression scheme");
-                       };
-               }
-
-       private:
-               ReaderPtr m_reader;
-               uint16_t m_versionNeeded;
-               uint16_t m_gpFlag;
-               uint16_t m_compressionMethod;
-               uint32_t m_crc;
-               zsize_t m_compressedSize;
-               zsize_t m_uncompressedSize;
-               zsize_t m_localHeaderOffset;
-               std::string m_fileName;
-       };
-
-       bool readEndCentralDirectory(
-               const ReaderPtr& reader,
-               zsize_t& centralDirectoryBytes,
-               zsize_t& centralDirectoryOffset,
-               zsize_t& centralDirectoryEntries
-               )
-       {
-               // Read the end of central directory record. This
-               // record enables us to find the remainding
-               // records without searching for record signatures.
-
-               // TODO does not consider the Zip64 entries.
-
-               enum
-               {
-                       MinRecordBytes = 22, // Minimum size with no comment
-                       MaxCommentBytes = 65535, // 2 bytes to store comment length
-                       Signature = 0x06054b50
-               };
-
-               zsize_t providerSize(reader->getSize());
-               if (providerSize < MinRecordBytes)
-               {
-                       throw FormatException("Too small");
-               }
-
-               size_t bufSize(
-                       std::min(zsize_t(MinRecordBytes + MaxCommentBytes), providerSize)
-                       );
-               std::vector<uint8_t> buffer(bufSize);
-               reader->readData(providerSize - bufSize, bufSize, &buffer[0]);
-
-               // Need to search for this record, as it ends in a variable-length
-               // comment field. Search backwards, with the assumption that the
-               // comment doesn't exist, or is much smaller than the maximum
-               // length
-
-               bool recordFound(false);
-               ssize_t pos(bufSize - MinRecordBytes);
-               for (; pos >= 0; --pos)
-               {
-                       recordFound = (read32(buffer, pos) == Signature);
-                       break;
-               }
-
-               if (recordFound)
-               {
-                       if (read16(buffer, pos + 4) != 0)
-                       {
-                               throw UnsupportedException("Spanned disks not supported");
-                       }
-
-                       centralDirectoryBytes = read32(buffer, pos + 12);
-                       centralDirectoryOffset = read32(buffer, pos + 16);
-                       centralDirectoryEntries = read16(buffer, pos + 10);
-               }
-               return recordFound;
-       }
-
-       std::vector<CompressedFilePtr>
-       readCentralDirectory(const ReaderPtr& reader)
-       {
-               enum Constants
-               {
-                       MinRecordBytes = 46,
-                       Signature = 0x02014b50
-               };
-
-               zsize_t centralDirectoryBytes(0);
-               zsize_t centralDirectoryOffset(0);
-               zsize_t centralDirectoryEntries(0);
-               bool isZip(
-                       readEndCentralDirectory(
-                               reader,
-                               centralDirectoryBytes,
-                               centralDirectoryOffset,
-                               centralDirectoryEntries
-                               )
-                       );
-               assert(isZip);
-
-               std::vector<uint8_t> buffer(centralDirectoryBytes);
-               reader->readData(
-                       centralDirectoryOffset,
-                       centralDirectoryBytes,
-                       &buffer[0]
-                       );
-
-               zsize_t pos(0);
-               std::vector<CompressedFilePtr> entries;
-               while ((pos + MinRecordBytes) < buffer.size())
-               {
-                       if (read32(buffer, pos) != Signature)
-                       {
-                               // Unknown record type.
-                               pos += 1;
-                               continue;
-                       }
-
-                       uint16_t versionNeeded(read16(buffer, pos + 6));
-                       uint16_t gpFlag(read16(buffer, pos + 8));
-                       uint16_t compressionMethod(read16(buffer, pos + 10));
-                       uint32_t crc(read32(buffer, pos + 16));
-                       uint32_t compressedSize(read32(buffer, pos + 20));
-                       uint32_t uncompressedSize(read32(buffer, pos + 24));
-                       size_t fileNameLen(read16(buffer, pos + 28));
-                       size_t extraLen(read16(buffer, pos + 30));
-                       size_t commentLen(read16(buffer, pos + 32));
-                       uint32_t localHeaderOffset(read32(buffer, pos + 42));
-
-                       if ((fileNameLen + extraLen + commentLen + MinRecordBytes + pos) >
-                               buffer.size()
-                               )
-                       {
-                               throw FormatException("File comments are too long");
-                       }
-
-                       std::string fileName(
-                               &buffer[pos + MinRecordBytes],
-                               &buffer[pos + MinRecordBytes + fileNameLen]
-                               );
-
-                       entries.push_back(
-                               CompressedFilePtr(
-                                       new FileEntry(
-                                               reader,
-                                               versionNeeded,
-                                               gpFlag,
-                                               compressionMethod,
-                                               crc,
-                                               compressedSize,
-                                               uncompressedSize,
-                                               localHeaderOffset,
-                                               fileName
-                                               )
-                                       )
-                               );
-
-                       pos += MinRecordBytes + fileNameLen + extraLen + commentLen;
-               }
-               return entries;
-       }
-}
-
-std::vector<CompressedFilePtr>
-zipper::unzip(const ReaderPtr& reader)
-{
-       return readCentralDirectory(reader);
-}
-
-bool
-zipper::isZip(const ReaderPtr& reader)
-{
-       zsize_t centralDirectoryBytes(0);
-       zsize_t centralDirectoryOffset(0);
-       zsize_t centralDirectoryEntries(0);
-       bool result(
-               readEndCentralDirectory(
-                       reader,
-                       centralDirectoryBytes,
-                       centralDirectoryOffset,
-                       centralDirectoryEntries
-                       )
-               );
-       return result;
-}
-
diff --git a/Unzip.hh b/Unzip.hh
deleted file mode 100644 (file)
index ab78f9f..0000000
--- a/Unzip.hh
+++ /dev/null
@@ -1,28 +0,0 @@
-//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
-//
-//     This file is part of libzipper.
-//
-//     libzipper is free software: you can redistribute it and/or modify
-//     it under the terms of the GNU General Public License as published by
-//     the Free Software Foundation, either version 3 of the License, or
-//     (at your option) any later version.
-//
-//     libzipper is distributed in the hope that it will be useful,
-//     but WITHOUT ANY WARRANTY; without even the implied warranty of
-//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//     GNU General Public License for more details.
-//
-//     You should have received a copy of the GNU General Public License
-//     along with libzipper.  If not, see <http://www.gnu.org/licenses/>.
-
-#include "zipper.hh"
-
-#include <vector>
-
-namespace zipper
-{
-       bool isZip(const ReaderPtr& reader);
-
-       std::vector<CompressedFilePtr> unzip(const ReaderPtr& reader);
-}
-
diff --git a/Zip.cc b/Zip.cc
deleted file mode 100644 (file)
index 8048ece..0000000
--- a/Zip.cc
+++ /dev/null
@@ -1,296 +0,0 @@
-//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
-//
-//     This file is part of libzipper.
-//
-//     libzipper is free software: you can redistribute it and/or modify
-//     it under the terms of the GNU General Public License as published by
-//     the Free Software Foundation, either version 3 of the License, or
-//     (at your option) any later version.
-//
-//     libzipper is distributed in the hope that it will be useful,
-//     but WITHOUT ANY WARRANTY; without even the implied warranty of
-//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//     GNU General Public License for more details.
-//
-//     You should have received a copy of the GNU General Public License
-//     along with libzipper.  If not, see <http://www.gnu.org/licenses/>.
-
-#include "zipper.hh"
-#include "Zip.hh"
-
-#include <zlib.h>
-
-#include <algorithm>
-#include <cassert>
-#include <iostream>
-
-#include <string.h>
-
-using namespace zipper;
-
-namespace
-{
-       void
-       write32(uint32_t value, uint8_t* zipData)
-       {
-               // Write 4 bytes in little-endian order.
-               zipData[0] = value & 0xff;
-               zipData[1] = (value >> 8) & 0xff;
-               zipData[2] = (value >> 16) & 0xff;
-               zipData[3] = (value >> 24) & 0xff;
-       }
-
-       struct DeflateDeleter
-       {
-       public:
-               DeflateDeleter(z_stream* stream) : m_stream(stream) {}
-               ~DeflateDeleter()
-               {
-                       deflateEnd(m_stream);
-
-               }
-       private:
-               z_stream* m_stream;
-       };
-}
-
-
-void
-zipper::zip(
-       const std::string& filename,
-       const Reader& reader,
-       const WriterPtr& writer,
-       ZipFileRecord& outRecord)
-{
-       enum Constants
-       {
-               ChunkSize = 64*1024,
-               WindowBits = 15,
-               CRC32Pos = 14
-       };
-
-       static uint8_t Header[] =
-       {
-               0x50, 0x4b, 0x03, 0x04,  // Header
-               20, // Version (2.0)
-               0, // File attributes
-               0,0, // gp flag.
-               8,0, // deflate method
-               0,0, // file time
-               0,0, // file date
-               0,0,0,0, // CRC32
-               0,0,0,0, // Compressed size
-               0,0,0,0 // Uncompressed size
-       };
-
-       zsize_t outPos(writer->getSize());
-       outRecord.localHeaderOffset = outPos;
-       outRecord.filename = filename;
-
-       // Write header
-       {
-               uint8_t buffer[ChunkSize];
-               memcpy(buffer, Header, sizeof(Header));
-               zsize_t pos(sizeof(Header));
-
-               std::string::size_type filenameSize(filename.size());
-               if (filenameSize > (ChunkSize - pos))
-               {
-                       filenameSize = ChunkSize - pos;
-               }
-               buffer[pos++] = filenameSize & 0xff;
-               buffer[pos++] = (filenameSize >> 8);
-               buffer[pos++] = 0; // extra field len
-               buffer[pos++] = 0; // extra field len
-               memcpy(buffer + pos, filename.data(), filenameSize);
-               pos += filenameSize;
-               writer->writeData(outPos, pos, &buffer[0]);
-               outPos += pos;
-       }
-
-       // Write compressed data
-
-       uint8_t inChunk[ChunkSize];
-       uint8_t outChunk[ChunkSize];
-
-       outRecord.uncompressedSize = 0;
-       outRecord.compressedSize = 0;
-
-       z_stream stream;
-       stream.zalloc = NULL;
-       stream.zfree = NULL;
-       stream.opaque = NULL;
-       int zlibErr(
-               deflateInit2(
-                       &stream,
-                       Z_DEFAULT_COMPRESSION,
-                       Z_DEFLATED,
-                       -WindowBits,
-                       MAX_MEM_LEVEL,
-                       Z_DEFAULT_STRATEGY)
-                       );
-
-       assert(zlibErr == Z_OK);
-       DeflateDeleter deleter(&stream);
-       stream.next_in = NULL;
-       stream.avail_in = 0;
-
-       zsize_t pos(0);
-       zsize_t end(reader.getSize());
-       outRecord.crc32 = crc32(0, NULL, 0);
-
-       while (pos < end)
-       {
-               if (stream.avail_in == 0)
-               {
-                       stream.avail_in =
-                               std::min(zsize_t(ChunkSize), end - pos);
-                       reader.readData(
-                               pos, stream.avail_in, &inChunk[0]);
-                       stream.next_in = reinterpret_cast<Bytef*>(&inChunk);
-                       pos += stream.avail_in;
-                       outRecord.uncompressedSize += stream.avail_in;
-                       outRecord.crc32 =
-                               crc32(outRecord.crc32, stream.next_in, stream.avail_in);
-               }
-
-               stream.next_out = reinterpret_cast<Bytef*>(&outChunk);
-               stream.avail_out = sizeof(outChunk);
-
-               zlibErr = deflate(&stream, (pos < end) ? Z_NO_FLUSH : Z_FINISH);
-
-               if (zlibErr == Z_STREAM_END)
-               {
-                       if (pos < end)
-                       {
-                               assert(!"zlib buffer unexpectedly empty");
-                               std::terminate();
-                       }
-               }
-               else if (zlibErr != Z_OK)
-               {
-                       throw FormatException("Corrupt Data");
-               }
-
-               zsize_t bytesToWrite(sizeof(outChunk) - stream.avail_out);
-               writer->writeData(
-                               outPos,
-                               bytesToWrite,
-                               &outChunk[0]);
-               outPos += bytesToWrite;
-               outRecord.compressedSize += bytesToWrite;
-       }
-
-       // Go back and complete the header.
-       uint8_t trailer[12];
-       write32(outRecord.crc32, &trailer[0]);
-       write32(outRecord.compressedSize, &trailer[4]);
-       write32(outRecord.uncompressedSize, &trailer[8]);
-       writer->writeData(
-               outRecord.localHeaderOffset + CRC32Pos, sizeof(trailer), &trailer[0]);
-}
-
-void
-zipper::zipFinalise(
-       const std::vector<ZipFileRecord>& records,
-       const WriterPtr& writer)
-{
-       enum Constants
-       {
-               ChunkSize = 64*1024
-       };
-
-       static uint8_t FileHeader[] =
-       {
-               0x50, 0x4b, 0x01, 0x02,  // Header
-               20, 0x00, // Version (2.0)
-               20, 0x00, // Version Needed to extract (2.0)
-               0,0, // gp flag.
-               8,0, // deflate method
-               0,0, // file time
-               0,0 // file date
-       };
-
-       zsize_t outPos(writer->getSize());
-       uint32_t centralDirOffset(outPos);
-
-       for (size_t i = 0; i < records.size(); ++i)
-       {
-               uint8_t buffer[ChunkSize];
-               memcpy(buffer, FileHeader, sizeof(FileHeader));
-               zsize_t pos(sizeof(FileHeader));
-
-               write32(records[i].crc32, &buffer[pos]);
-               pos += 4;
-
-               write32(records[i].compressedSize, &buffer[pos]);
-               pos += 4;
-
-               write32(records[i].uncompressedSize, &buffer[pos]);
-               pos += 4;
-
-               std::string::size_type filenameSize(records[i].filename.size());
-               if (filenameSize > (ChunkSize - pos))
-               {
-                       filenameSize = ChunkSize - pos;
-               }
-               buffer[pos++] = filenameSize & 0xff;
-               buffer[pos++] = (filenameSize >> 8);
-               buffer[pos++] = 0; // extra field len
-               buffer[pos++] = 0; // extra field len
-
-               buffer[pos++] = 0; // file comment len
-               buffer[pos++] = 0; // file comment len
-
-               buffer[pos++] = 0; // disk number
-               buffer[pos++] = 0; // disk number
-
-               buffer[pos++] = 0; // internal file attributes
-               buffer[pos++] = 0; // internal file attributes
-
-               buffer[pos++] = 0; // external file attributes
-               buffer[pos++] = 0; // external file attributes
-               buffer[pos++] = 0; // external file attributes
-               buffer[pos++] = 0; // external file attributes
-
-               write32(records[i].localHeaderOffset, &buffer[pos]);
-               pos += 4;
-
-               memcpy(buffer + pos, records[i].filename.data(), filenameSize);
-               pos += filenameSize;
-
-               writer->writeData(outPos, pos, &buffer[0]);
-               outPos += pos;
-       }
-
-       uint32_t centralDirSize(writer->getSize() - centralDirOffset);
-
-       {
-               // End-of-directory record.
-               static uint8_t EndDirectory[] =
-               {
-                       0x50, 0x4b, 0x05, 0x06,  // Header
-                       0x00, 0x00, // Disk num
-                       0x00, 0x00 // Disk with central dir
-               };
-               uint8_t buffer[ChunkSize];
-               memcpy(buffer, EndDirectory, sizeof(EndDirectory));
-               zsize_t pos(sizeof(EndDirectory));
-
-               buffer[pos++] = records.size() & 0xff; // Entries on this disk
-               buffer[pos++] = records.size() >> 8;
-               buffer[pos++] = records.size() & 0xff; // Total entries
-               buffer[pos++] = records.size() >> 8;
-
-               write32(centralDirSize, &buffer[pos]);
-               pos += 4;
-               write32(centralDirOffset, &buffer[pos]);
-               pos += 4;
-
-               buffer[pos++] = 0; // Zip comment length
-               buffer[pos++] = 0; // Zip comment length
-
-               writer->writeData(outPos, pos, &buffer[0]);
-               outPos += pos;
-       }
-}
diff --git a/Zip.hh b/Zip.hh
deleted file mode 100644 (file)
index 7680a61..0000000
--- a/Zip.hh
+++ /dev/null
@@ -1,44 +0,0 @@
-//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
-//
-//     This file is part of libzipper.
-//
-//     libzipper is free software: you can redistribute it and/or modify
-//     it under the terms of the GNU General Public License as published by
-//     the Free Software Foundation, either version 3 of the License, or
-//     (at your option) any later version.
-//
-//     libzipper is distributed in the hope that it will be useful,
-//     but WITHOUT ANY WARRANTY; without even the implied warranty of
-//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//     GNU General Public License for more details.
-//
-//     You should have received a copy of the GNU General Public License
-//     along with libzipper.  If not, see <http://www.gnu.org/licenses/>.
-
-#include "zipper.hh"
-
-#include <string>
-#include <vector>
-
-namespace zipper
-{
-       struct ZipFileRecord
-       {
-               zsize_t localHeaderOffset;
-               uint32_t crc32;
-               uint32_t compressedSize;
-               uint32_t uncompressedSize;
-               std::string filename;
-       };
-
-       void zip(
-               const std::string& filename,
-               const Reader& reader,
-               const WriterPtr& writer,
-               ZipFileRecord& outRecord);
-
-       void zipFinalise(
-               const std::vector<ZipFileRecord>& records,
-               const WriterPtr& writer);
-}
-
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..be1dcb7
--- /dev/null
@@ -0,0 +1,5 @@
+libzipper (1.0.0-1) unstable; urgency=low
+
+  * Initial release (Closes: #nnnn)  <nnnn is the bug number of your ITP>
+
+ -- Michael McMaster <michael@codesrc.com>  Sat, 21 May 2011 00:01:11 +1000
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..7f8f011
--- /dev/null
@@ -0,0 +1 @@
+7
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..d57a16e
--- /dev/null
@@ -0,0 +1,27 @@
+Source: libzipper
+Priority: extra
+Maintainer: Michael McMaster <michael@codesrc.com>
+Build-Depends: debhelper (>= 7.0.50~), autotools-dev, cdbs, zlib1g-dev
+Standards-Version: 3.9.1
+Section: libs
+Homepage: http://www.codesrc.com/src/libzipper
+#Vcs-Git: git://git.debian.org/collab-maint/libzipper.git
+#Vcs-Browser: http://git.debian.org/?p=collab-maint/libzipper.git;a=summary
+
+Package: libzipper-dev
+Section: libdevel
+Architecture: any
+Depends: libzipper (= ${binary:Version})
+Homepage: http://www.codesrc.com/src/libzipper
+Description: A C++ interface for reading compressed.
+ libzipper offers a flexible C++ interface for reading compressed files in
+ multiple formats.
+
+Package: libzipper
+Section: libs
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Homepage: http://www.codesrc.com/src/libzipper
+Description: A C++ interface for reading compressed.
+ libzipper offers a flexible C++ interface for reading compressed files in
+ multiple formats.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..5ae1531
--- /dev/null
@@ -0,0 +1,40 @@
+Format: http://dep.debian.net/deps/dep5
+Upstream-Name: libzipper
+Source: <url://example.com>
+
+Files: *
+Copyright: <years> <put author's name and email here>
+           <years> <likewise for another author>
+License: GPL-3.0+
+
+Files: debian/*
+Copyright: 2011 Michael McMaster <michael@codesrc.com>
+License: GPL-3.0+
+
+License: GPL-3.0+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ .
+ This package is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
+
+# Please choose a license for your packaging work. If the program you package
+# uses a mainstream license, using the same license is the safest choice.
+# Please avoid to pick license terms that are more restrictive than the
+# packaged work, as it may make Debian's contributions unacceptable upstream.
+# If you just want it to be GPL version 3, leave the following line in.
+
+and is licensed under the GPL version 3, see above.
+
+# Please also look if there are files or directories which have a
+# different copyright/license attached and list them here.
diff --git a/debian/docs b/debian/docs
new file mode 100644 (file)
index 0000000..50bd824
--- /dev/null
@@ -0,0 +1,2 @@
+NEWS
+README
diff --git a/debian/libzipper-dev.dirs b/debian/libzipper-dev.dirs
new file mode 100644 (file)
index 0000000..4418816
--- /dev/null
@@ -0,0 +1,2 @@
+usr/lib
+usr/include
diff --git a/debian/libzipper-dev.install b/debian/libzipper-dev.install
new file mode 100644 (file)
index 0000000..3c996c8
--- /dev/null
@@ -0,0 +1,6 @@
+usr/include/*
+usr/lib/lib*.a
+usr/lib/lib*.so
+usr/lib/pkgconfig/*
+usr/lib/*.la
+usr/share/pkgconfig/*
diff --git a/debian/libzipper1.dirs b/debian/libzipper1.dirs
new file mode 100644 (file)
index 0000000..6845771
--- /dev/null
@@ -0,0 +1 @@
+usr/lib
diff --git a/debian/libzipper1.install b/debian/libzipper1.install
new file mode 100644 (file)
index 0000000..d0dbfd1
--- /dev/null
@@ -0,0 +1 @@
+usr/lib/lib*.so.*
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..114ab44
--- /dev/null
@@ -0,0 +1,4 @@
+#!/usr/bin/make -f
+include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/cdbs/1/class/autotools.mk
+
diff --git a/debian/source/format b/debian/source/format
new file mode 100644 (file)
index 0000000..163aaf8
--- /dev/null
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/deflate.cc b/deflate.cc
new file mode 100644 (file)
index 0000000..0819b80
--- /dev/null
@@ -0,0 +1,682 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of libzipper.
+//
+//     libzipper is free software: you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation, either version 3 of the License, or
+//     (at your option) any later version.
+//
+//     libzipper is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+//
+//     You should have received a copy of the GNU General Public License
+//     along with libzipper.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "zipper.hh"
+#include "zip.hh"
+#include "util.hh"
+
+#include <zlib.h>
+
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+
+#include <string.h>
+
+using namespace zipper;
+
+namespace
+{
+       struct InflateDeleter
+       {
+       public:
+               InflateDeleter(z_stream* stream) : m_stream(stream) {}
+               ~InflateDeleter()
+               {
+                       inflateEnd(m_stream);
+
+               }
+       private:
+               z_stream* m_stream;
+       };
+
+       struct DeflateDeleter
+       {
+       public:
+               DeflateDeleter(z_stream* stream) : m_stream(stream) {}
+               ~DeflateDeleter()
+               {
+                       deflateEnd(m_stream);
+
+               }
+       private:
+               z_stream* m_stream;
+       };
+
+       class FileEntry : public CompressedFile
+       {
+       public:
+               FileEntry(
+                       const ReaderPtr& reader,
+                       uint16_t versionNeeded,
+                       uint16_t gpFlag,
+                       uint16_t compressionMethod,
+                       uint32_t crc,
+                       zsize_t compressedSize,
+                       zsize_t uncompressedSize,
+                       zsize_t localHeaderOffset,
+                       std::string fileName
+                       ) :
+                       m_reader(reader),
+                       m_versionNeeded(versionNeeded),
+                       m_gpFlag(gpFlag),
+                       m_compressionMethod(compressionMethod),
+                       m_crc(crc),
+                       m_compressedSize(compressedSize),
+                       m_uncompressedSize(uncompressedSize),
+                       m_localHeaderOffset(localHeaderOffset),
+                       m_fileName(fileName)
+               {
+               }
+
+               virtual bool isDecompressSupported() const
+               {
+                       return ((m_versionNeeded & 0xf) <= 20) &&
+                               ((m_gpFlag & 0x1) == 0) && // Not encrypted
+                               ((m_compressionMethod == 0) || (m_compressionMethod == 8));
+               }
+
+               virtual const std::string& getPath() const
+               {
+                       return m_fileName;
+               }
+
+               virtual zsize_t getCompressedSize() const { return m_compressedSize; }
+               virtual zsize_t getUncompressedSize() const
+               {
+                       return m_uncompressedSize;
+               }
+
+               virtual void decompress(Writer& writer)
+               {
+                       enum
+                       {
+                               Signature = 0x04034b50,
+                               MinRecordBytes = 30,
+                               ChunkSize = 64*1024,
+                               WindowBits = 15
+                       };
+
+                       std::vector<uint8_t> localRecord(MinRecordBytes);
+                       m_reader->readData(
+                               m_localHeaderOffset, MinRecordBytes, &localRecord[0]
+                               );
+                       if (read32_le(localRecord, 0) != Signature)
+                       {
+                               throw FormatException("Invalid local ZIP record");
+                       }
+
+                       // Don't trust the lengths for filename and extra content read from
+                       // the central records.  At least for extra, these DO differ for
+                       // unknown reasons
+                       zsize_t filenameLength(read16_le(localRecord, 26));
+                       zsize_t extraLength(read16_le(localRecord, 28));
+
+                       zsize_t startCompressedBytes(
+                               m_localHeaderOffset +
+                               MinRecordBytes +
+                               filenameLength +
+                               extraLength
+                               );
+
+                       zsize_t endCompressedBytes(
+                               startCompressedBytes + m_compressedSize
+                               );
+
+                       if (endCompressedBytes > m_reader->getSize())
+                       {
+                               throw FormatException("Compressed file size is too long");
+                       }
+
+                       switch (m_compressionMethod)
+                       {
+                       case 0: // No compression
+                       {
+                               for (zsize_t pos(startCompressedBytes);
+                                       pos < endCompressedBytes;
+                                       pos += ChunkSize
+                                       )
+                               {
+                                       uint8_t buf[ChunkSize];
+                                       zsize_t bytes(
+                                               std::min(zsize_t(ChunkSize), endCompressedBytes - pos)
+                                               );
+                                       m_reader->readData(pos, bytes, &buf[0]);
+                                       writer.writeData(pos, bytes, &buf[0]);
+                               }
+                       }; break;
+
+                       case 8: // Deflate
+                       {
+                               uint8_t inChunk[ChunkSize];
+                               uint8_t outChunk[ChunkSize];
+
+                               z_stream stream;
+                               stream.zalloc = NULL;
+                               stream.zfree = NULL;
+                               stream.opaque = NULL;
+                               int zlibErr(inflateInit2(&stream, -WindowBits));
+                               assert(zlibErr == Z_OK);
+                               InflateDeleter deleter(&stream);
+                               stream.next_in = NULL;
+                               stream.avail_in = 0;
+                               bool finished(false);
+
+                               zsize_t pos(startCompressedBytes);
+                               zsize_t outPos(0);
+                               uint32_t crc(crc32(0, NULL, 0));
+                               while (pos < endCompressedBytes)
+                               {
+                                       if (stream.avail_in == 0)
+                                       {
+                                               stream.avail_in =
+                                                       std::min(
+                                                               zsize_t(ChunkSize),
+                                                               endCompressedBytes - pos
+                                                               );
+                                               m_reader->readData(
+                                                       pos, stream.avail_in, &inChunk[0]
+                                                       );
+                                               stream.next_in = reinterpret_cast<Bytef*>(&inChunk);
+                                               pos += stream.avail_in;
+                                       }
+
+                                       stream.next_out = reinterpret_cast<Bytef*>(&outChunk);
+                                       stream.avail_out = sizeof(outChunk);
+
+                                       zlibErr = inflate(&stream, Z_SYNC_FLUSH);
+
+                                       finished = false;
+                                       if (zlibErr == Z_STREAM_END)
+                                       {
+                                               finished = true;
+
+                                       }
+                                       else if (zlibErr != Z_OK)
+                                       {
+                                               throw FormatException("Corrupt Data");
+                                       }
+
+                                       zsize_t bytesToWrite(sizeof(outChunk) - stream.avail_out);
+                                       writer.writeData(
+                                               outPos,
+                                               bytesToWrite,
+                                               &outChunk[0]
+                                               );
+                                       outPos += bytesToWrite;
+                                       crc = crc32(crc, &outChunk[0], bytesToWrite);
+
+                                       if (finished) break;
+                               }
+                               if (!finished)
+                               {
+                                       // Ran out of data to process
+                                       throw FormatException("Corrupt Data");
+                               }
+
+                               if (m_gpFlag & 0x4) // CRC is after compressed data
+                               {
+                                       uint8_t dataDescriptor[12];
+                                       ::memcpy(
+                                               dataDescriptor,
+                                               stream.next_in,
+                                               std::min(12u, stream.avail_in));
+
+                                       if (stream.avail_in < 12)
+                                       {
+                                               m_reader->readData(
+                                                       pos,
+                                                       12 - stream.avail_in,
+                                                       &dataDescriptor[stream.avail_in]);
+                                       }
+                                       m_crc = read32_le(dataDescriptor, 0);
+                                       m_compressedSize = read32_le(dataDescriptor, 4);
+                                       m_uncompressedSize = read32_le(dataDescriptor, 8);
+                               }
+
+                               if (crc != m_crc)
+                               {
+                                       throw FormatException("Corrupt Data (CRC failure)");
+                               }
+
+                       }; break;
+                       default:
+                               throw UnsupportedException("Unsupported compression scheme");
+                       };
+               }
+
+       private:
+               ReaderPtr m_reader;
+               uint16_t m_versionNeeded;
+               uint16_t m_gpFlag;
+               uint16_t m_compressionMethod;
+               uint32_t m_crc;
+               zsize_t m_compressedSize;
+               zsize_t m_uncompressedSize;
+               zsize_t m_localHeaderOffset;
+               std::string m_fileName;
+       };
+
+       bool readEndCentralDirectory(
+               const ReaderPtr& reader,
+               zsize_t& centralDirectoryBytes,
+               zsize_t& centralDirectoryOffset,
+               zsize_t& centralDirectoryEntries
+               )
+       {
+               // Read the end of central directory record. This
+               // record enables us to find the remainding
+               // records without searching for record signatures.
+
+               // TODO does not consider the Zip64 entries.
+
+               enum
+               {
+                       MinRecordBytes = 22, // Minimum size with no comment
+                       MaxCommentBytes = 65535, // 2 bytes to store comment length
+                       Signature = 0x06054b50
+               };
+
+               zsize_t providerSize(reader->getSize());
+               if (providerSize < MinRecordBytes)
+               {
+                       throw FormatException("Too small");
+               }
+
+               size_t bufSize(
+                       std::min(zsize_t(MinRecordBytes + MaxCommentBytes), providerSize)
+                       );
+               std::vector<uint8_t> buffer(bufSize);
+               reader->readData(providerSize - bufSize, bufSize, &buffer[0]);
+
+               // Need to search for this record, as it ends in a variable-length
+               // comment field. Search backwards, with the assumption that the
+               // comment doesn't exist, or is much smaller than the maximum
+               // length
+
+               bool recordFound(false);
+               ssize_t pos(bufSize - MinRecordBytes);
+               for (; pos >= 0; --pos)
+               {
+                       recordFound = (read32_le(buffer, pos) == Signature);
+                       break;
+               }
+
+               if (recordFound)
+               {
+                       if (read16_le(buffer, pos + 4) != 0)
+                       {
+                               throw UnsupportedException("Spanned disks not supported");
+                       }
+
+                       centralDirectoryBytes = read32_le(buffer, pos + 12);
+                       centralDirectoryOffset = read32_le(buffer, pos + 16);
+                       centralDirectoryEntries = read16_le(buffer, pos + 10);
+               }
+               return recordFound;
+       }
+
+       std::vector<CompressedFilePtr>
+       readCentralDirectory(const ReaderPtr& reader)
+       {
+               enum Constants
+               {
+                       MinRecordBytes = 46,
+                       Signature = 0x02014b50
+               };
+
+               zsize_t centralDirectoryBytes(0);
+               zsize_t centralDirectoryOffset(0);
+               zsize_t centralDirectoryEntries(0);
+               bool isZip(
+                       readEndCentralDirectory(
+                               reader,
+                               centralDirectoryBytes,
+                               centralDirectoryOffset,
+                               centralDirectoryEntries
+                               )
+                       );
+               assert(isZip);
+
+               std::vector<uint8_t> buffer(centralDirectoryBytes);
+               reader->readData(
+                       centralDirectoryOffset,
+                       centralDirectoryBytes,
+                       &buffer[0]
+                       );
+
+               zsize_t pos(0);
+               std::vector<CompressedFilePtr> entries;
+               while ((pos + MinRecordBytes) < buffer.size())
+               {
+                       if (read32_le(buffer, pos) != Signature)
+                       {
+                               // Unknown record type.
+                               pos += 1;
+                               continue;
+                       }
+
+                       uint16_t versionNeeded(read16_le(buffer, pos + 6));
+                       uint16_t gpFlag(read16_le(buffer, pos + 8));
+                       uint16_t compressionMethod(read16_le(buffer, pos + 10));
+                       uint32_t crc(read32_le(buffer, pos + 16));
+                       uint32_t compressedSize(read32_le(buffer, pos + 20));
+                       uint32_t uncompressedSize(read32_le(buffer, pos + 24));
+                       size_t fileNameLen(read16_le(buffer, pos + 28));
+                       size_t extraLen(read16_le(buffer, pos + 30));
+                       size_t commentLen(read16_le(buffer, pos + 32));
+                       uint32_t localHeaderOffset(read32_le(buffer, pos + 42));
+
+                       if ((fileNameLen + extraLen + commentLen + MinRecordBytes + pos) >
+                               buffer.size()
+                               )
+                       {
+                               throw FormatException("File comments are too long");
+                       }
+
+                       std::string fileName(
+                               &buffer[pos + MinRecordBytes],
+                               &buffer[pos + MinRecordBytes + fileNameLen]
+                               );
+
+                       entries.push_back(
+                               CompressedFilePtr(
+                                       new FileEntry(
+                                               reader,
+                                               versionNeeded,
+                                               gpFlag,
+                                               compressionMethod,
+                                               crc,
+                                               compressedSize,
+                                               uncompressedSize,
+                                               localHeaderOffset,
+                                               fileName
+                                               )
+                                       )
+                               );
+
+                       pos += MinRecordBytes + fileNameLen + extraLen + commentLen;
+               }
+               return entries;
+       }
+
+}
+
+
+void
+zipper::zip(
+       const std::string& filename,
+       const Reader& reader,
+       const WriterPtr& writer,
+       ZipFileRecord& outRecord)
+{
+       enum Constants
+       {
+               ChunkSize = 64*1024,
+               WindowBits = 15,
+               CRC32Pos = 14
+       };
+
+       static uint8_t Header[] =
+       {
+               0x50, 0x4b, 0x03, 0x04,  // Header
+               20, // Version (2.0)
+               0, // File attributes
+               0,0, // gp flag.
+               8,0, // deflate method
+               0,0, // file time
+               0,0, // file date
+               0,0,0,0, // CRC32
+               0,0,0,0, // Compressed size
+               0,0,0,0 // Uncompressed size
+       };
+
+       zsize_t outPos(writer->getSize());
+       outRecord.localHeaderOffset = outPos;
+       outRecord.filename = filename;
+
+       // Write header
+       {
+               uint8_t buffer[ChunkSize];
+               memcpy(buffer, Header, sizeof(Header));
+               zsize_t pos(sizeof(Header));
+
+               std::string::size_type filenameSize(filename.size());
+               if (filenameSize > (ChunkSize - pos))
+               {
+                       filenameSize = ChunkSize - pos;
+               }
+               buffer[pos++] = filenameSize & 0xff;
+               buffer[pos++] = (filenameSize >> 8);
+               buffer[pos++] = 0; // extra field len
+               buffer[pos++] = 0; // extra field len
+               memcpy(buffer + pos, filename.data(), filenameSize);
+               pos += filenameSize;
+               writer->writeData(outPos, pos, &buffer[0]);
+               outPos += pos;
+       }
+
+       // Write compressed data
+
+       uint8_t inChunk[ChunkSize];
+       uint8_t outChunk[ChunkSize];
+
+       outRecord.uncompressedSize = 0;
+       outRecord.compressedSize = 0;
+
+       z_stream stream;
+       stream.zalloc = NULL;
+       stream.zfree = NULL;
+       stream.opaque = NULL;
+       int zlibErr(
+               deflateInit2(
+                       &stream,
+                       Z_DEFAULT_COMPRESSION,
+                       Z_DEFLATED,
+                       -WindowBits,
+                       MAX_MEM_LEVEL,
+                       Z_DEFAULT_STRATEGY)
+                       );
+
+       assert(zlibErr == Z_OK);
+       DeflateDeleter deleter(&stream);
+       stream.next_in = NULL;
+       stream.avail_in = 0;
+
+       zsize_t pos(0);
+       zsize_t end(reader.getSize());
+       outRecord.crc32 = crc32(0, NULL, 0);
+
+       while (pos < end)
+       {
+               if (stream.avail_in == 0)
+               {
+                       stream.avail_in =
+                               std::min(zsize_t(ChunkSize), end - pos);
+                       reader.readData(
+                               pos, stream.avail_in, &inChunk[0]);
+                       stream.next_in = reinterpret_cast<Bytef*>(&inChunk);
+                       pos += stream.avail_in;
+                       outRecord.uncompressedSize += stream.avail_in;
+                       outRecord.crc32 =
+                               crc32(outRecord.crc32, stream.next_in, stream.avail_in);
+               }
+
+               stream.next_out = reinterpret_cast<Bytef*>(&outChunk);
+               stream.avail_out = sizeof(outChunk);
+
+               zlibErr = deflate(&stream, (pos < end) ? Z_NO_FLUSH : Z_FINISH);
+
+               if (zlibErr == Z_STREAM_END)
+               {
+                       if (pos < end)
+                       {
+                               assert(!"zlib buffer unexpectedly empty");
+                               std::terminate();
+                       }
+               }
+               else if (zlibErr != Z_OK)
+               {
+                       throw FormatException("Corrupt Data");
+               }
+
+               zsize_t bytesToWrite(sizeof(outChunk) - stream.avail_out);
+               writer->writeData(
+                               outPos,
+                               bytesToWrite,
+                               &outChunk[0]);
+               outPos += bytesToWrite;
+               outRecord.compressedSize += bytesToWrite;
+       }
+
+       // Go back and complete the header.
+       uint8_t trailer[12];
+       write32_le(outRecord.crc32, &trailer[0]);
+       write32_le(outRecord.compressedSize, &trailer[4]);
+       write32_le(outRecord.uncompressedSize, &trailer[8]);
+       writer->writeData(
+               outRecord.localHeaderOffset + CRC32Pos, sizeof(trailer), &trailer[0]);
+}
+
+void
+zipper::zipFinalise(
+       const std::vector<ZipFileRecord>& records,
+       const WriterPtr& writer)
+{
+       enum Constants
+       {
+               ChunkSize = 64*1024
+       };
+
+       static uint8_t FileHeader[] =
+       {
+               0x50, 0x4b, 0x01, 0x02,  // Header
+               20, 0x00, // Version (2.0)
+               20, 0x00, // Version Needed to extract (2.0)
+               0,0, // gp flag.
+               8,0, // deflate method
+               0,0, // file time
+               0,0 // file date
+       };
+
+       zsize_t outPos(writer->getSize());
+       uint32_t centralDirOffset(outPos);
+
+       for (size_t i = 0; i < records.size(); ++i)
+       {
+               uint8_t buffer[ChunkSize];
+               memcpy(buffer, FileHeader, sizeof(FileHeader));
+               zsize_t pos(sizeof(FileHeader));
+
+               write32_le(records[i].crc32, &buffer[pos]);
+               pos += 4;
+
+               write32_le(records[i].compressedSize, &buffer[pos]);
+               pos += 4;
+
+               write32_le(records[i].uncompressedSize, &buffer[pos]);
+               pos += 4;
+
+               std::string::size_type filenameSize(records[i].filename.size());
+               if (filenameSize > (ChunkSize - pos))
+               {
+                       filenameSize = ChunkSize - pos;
+               }
+               buffer[pos++] = filenameSize & 0xff;
+               buffer[pos++] = (filenameSize >> 8);
+               buffer[pos++] = 0; // extra field len
+               buffer[pos++] = 0; // extra field len
+
+               buffer[pos++] = 0; // file comment len
+               buffer[pos++] = 0; // file comment len
+
+               buffer[pos++] = 0; // disk number
+               buffer[pos++] = 0; // disk number
+
+               buffer[pos++] = 0; // internal file attributes
+               buffer[pos++] = 0; // internal file attributes
+
+               buffer[pos++] = 0; // external file attributes
+               buffer[pos++] = 0; // external file attributes
+               buffer[pos++] = 0; // external file attributes
+               buffer[pos++] = 0; // external file attributes
+
+               write32_le(records[i].localHeaderOffset, &buffer[pos]);
+               pos += 4;
+
+               memcpy(buffer + pos, records[i].filename.data(), filenameSize);
+               pos += filenameSize;
+
+               writer->writeData(outPos, pos, &buffer[0]);
+               outPos += pos;
+       }
+
+       uint32_t centralDirSize(writer->getSize() - centralDirOffset);
+
+       {
+               // End-of-directory record.
+               static uint8_t EndDirectory[] =
+               {
+                       0x50, 0x4b, 0x05, 0x06,  // Header
+                       0x00, 0x00, // Disk num
+                       0x00, 0x00 // Disk with central dir
+               };
+               uint8_t buffer[ChunkSize];
+               memcpy(buffer, EndDirectory, sizeof(EndDirectory));
+               zsize_t pos(sizeof(EndDirectory));
+
+               buffer[pos++] = records.size() & 0xff; // Entries on this disk
+               buffer[pos++] = records.size() >> 8;
+               buffer[pos++] = records.size() & 0xff; // Total entries
+               buffer[pos++] = records.size() >> 8;
+
+               write32_le(centralDirSize, &buffer[pos]);
+               pos += 4;
+               write32_le(centralDirOffset, &buffer[pos]);
+               pos += 4;
+
+               buffer[pos++] = 0; // Zip comment length
+               buffer[pos++] = 0; // Zip comment length
+
+               writer->writeData(outPos, pos, &buffer[0]);
+               outPos += pos;
+       }
+}
+
+std::vector<CompressedFilePtr>
+zipper::unzip(const ReaderPtr& reader)
+{
+       return readCentralDirectory(reader);
+}
+
+bool
+zipper::isZip(const ReaderPtr& reader)
+{
+       zsize_t centralDirectoryBytes(0);
+       zsize_t centralDirectoryOffset(0);
+       zsize_t centralDirectoryEntries(0);
+       bool result(
+               readEndCentralDirectory(
+                       reader,
+                       centralDirectoryBytes,
+                       centralDirectoryOffset,
+                       centralDirectoryEntries
+                       )
+               );
+       return result;
+}
+
diff --git a/deflate.hh b/deflate.hh
new file mode 100644 (file)
index 0000000..9acf552
--- /dev/null
@@ -0,0 +1,33 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of libzipper.
+//
+//     libzipper is free software: you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation, either version 3 of the License, or
+//     (at your option) any later version.
+//
+//     libzipper is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+//
+//     You should have received a copy of the GNU General Public License
+//     along with libzipper.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "zipper.hh"
+
+#include <vector>
+
+namespace zipper
+{
+       bool isGzip(const ReaderPtr& reader);
+
+       void gzip(
+               const std::string& filename,
+               const Reader& reader,
+               const WriterPtr& writer);
+
+       std::vector<CompressedFilePtr> ungzip(const ReaderPtr& reader);
+}
+
diff --git a/gzip.cc b/gzip.cc
new file mode 100644 (file)
index 0000000..774d6c6
--- /dev/null
+++ b/gzip.cc
@@ -0,0 +1,309 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of libzipper.
+//
+//     libzipper is free software: you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation, either version 3 of the License, or
+//     (at your option) any later version.
+//
+//     libzipper is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+//
+//     You should have received a copy of the GNU General Public License
+//     along with libzipper.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "zipper.hh"
+#include "Ungzip.hh"
+
+#include <zlib.h>
+
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+#include <vector>
+
+#include <string.h>
+
+using namespace zipper;
+
+namespace
+{
+       uint32_t
+       read32(const uint8_t* zipData)
+       {
+               // Read 4 bytes in little-endian order.
+               // Return results in host-endian.
+               return uint32_t(
+                       zipData[0] |
+                       (uint32_t(zipData[1]) << 8) |
+                       (uint32_t(zipData[2]) << 16) |
+                       (uint32_t(zipData[3]) << 24)
+                       );
+       }
+
+       uint16_t
+       read16(const std::vector<uint8_t>& zipData, size_t pos)
+       {
+               // Read 2 bytes in little-endian order.
+               // Return results in host-endian.
+               return uint16_t(
+                       zipData[pos] |
+                       (uint16_t(zipData[pos+1]) << 8)
+                       );
+       }
+
+
+       size_t
+       findNull(const std::vector<uint8_t>& zipData, size_t start)
+       {
+               if (start >= zipData.size())
+               {
+                       throw FormatException("Unexpected end-of-file");
+               }
+
+               while (zipData[start] != 0)
+               {
+                       ++start;
+                       if (start >= zipData.size())
+                       {
+                               throw FormatException("Unexpected end-of-file");
+                       }
+               }
+               return start;
+       }
+
+       struct InflateDeleter
+       {
+       public:
+               InflateDeleter(z_stream* stream) : m_stream(stream) {}
+               ~InflateDeleter()
+               {
+                       inflateEnd(m_stream);
+
+               }
+       private:
+               z_stream* m_stream;
+       };
+
+       class FileEntry : public CompressedFile
+       {
+       public:
+               FileEntry(
+                       const ReaderPtr& reader,
+                       zsize_t dataOffset,
+                       const std::string& filename
+                       ) :
+                       m_reader(reader),
+                       m_dataOffset(dataOffset),
+                       m_fileName(filename)
+               {
+               }
+
+               virtual bool isDecompressSupported() const
+               {
+                       return true;
+               }
+
+               virtual const std::string& getPath() const
+               {
+                       return m_fileName;
+               }
+
+               virtual zsize_t getCompressedSize() const { return -1; }
+               virtual zsize_t getUncompressedSize() const { return -1; }
+
+               virtual void decompress(Writer& writer)
+               {
+                       enum
+                       {
+                               ChunkSize = 64*1024,
+                               WindowBits = 15
+                       };
+
+                       uint8_t inChunk[ChunkSize];
+                       uint8_t outChunk[ChunkSize];
+                       zsize_t endCompressedBytes = m_reader->getSize() - 8; // CRC+ISIZE
+                       uint32_t crc(crc32(0, NULL, 0));
+
+                       z_stream stream;
+                       stream.zalloc = NULL;
+                       stream.zfree = NULL;
+                       stream.opaque = NULL;
+                       int zlibErr(inflateInit2(&stream, -WindowBits));
+                       assert(zlibErr == Z_OK);
+                       InflateDeleter deleter(&stream);
+                       stream.next_in = NULL;
+                       stream.avail_in = 0;
+
+                       bool finished(false);
+                       zsize_t pos(m_dataOffset);
+                       zsize_t outPos(0);
+                       while (pos < endCompressedBytes)
+                       {
+                               if (stream.avail_in == 0)
+                               {
+                                       stream.avail_in =
+                                               std::min(
+                                                       zsize_t(ChunkSize),
+                                                       endCompressedBytes - pos
+                                                       );
+                                       m_reader->readData(
+                                               pos, stream.avail_in, &inChunk[0]
+                                               );
+                                       stream.next_in = reinterpret_cast<Bytef*>(&inChunk);
+                                       pos += stream.avail_in;
+                               }
+
+                               stream.next_out = reinterpret_cast<Bytef*>(&outChunk);
+                               stream.avail_out = sizeof(outChunk);
+
+                               zlibErr = inflate(&stream, Z_SYNC_FLUSH);
+
+                               finished = false;
+                               if (zlibErr == Z_STREAM_END)
+                               {
+                                       finished = true;
+                               }
+                               else if (zlibErr != Z_OK)
+                               {
+                                       throw FormatException("Corrupt Data");
+                               }
+
+                               zsize_t bytesToWrite(sizeof(outChunk) - stream.avail_out);
+                               writer.writeData(outPos, bytesToWrite, &outChunk[0]);
+                               outPos += bytesToWrite;
+                               crc = crc32(crc, &outChunk[0], bytesToWrite);
+
+                               if (finished) break;
+                       }
+
+                       if (!finished)
+                       {
+                               // Ran out of data to process
+                               throw FormatException("Corrupt Data");
+                       }
+
+                       uint8_t crcBuffer[4];
+                       ::memcpy(crcBuffer, stream.next_in, std::min(4u, stream.avail_in));
+
+                       if (stream.avail_in < 4)
+                       {
+                               m_reader->readData(
+                                       pos, 4 - stream.avail_in, &crcBuffer[stream.avail_in]
+                                       );
+                       }
+                       uint32_t savedCRC = read32(&crcBuffer[0]);
+                       if (savedCRC != crc)
+                       {
+                               throw FormatException("Corrupt Data (CRC Failure)");
+                       }
+               }
+
+       private:
+               ReaderPtr m_reader;
+               zsize_t m_dataOffset;
+               std::string m_fileName;
+       };
+}
+
+namespace zipper
+{
+       std::vector<CompressedFilePtr>
+       ungzip(const ReaderPtr& reader)
+       {
+               enum
+               {
+                       MaxHeader = 64*1024  // Artifical limit to simplify code
+               };
+
+               if (!isGzip(reader))
+               {
+                       throw FormatException("Invalid gzip file");
+               }
+
+               std::vector<uint8_t> header(
+                       std::min(reader->getSize(), zsize_t(MaxHeader)));
+               reader->readData(0, header.size(), &header[0]);
+
+               if (header[2] != 8) // "deflate" method
+               {
+                       throw UnsupportedException("Unknown gzip compression method");
+               }
+
+               bool fextra = (header[3] & 4) != 0;
+               bool fname = (header[3] & 8) != 0;
+               bool fcomment = (header[3] & 0x10) != 0;
+               bool fhcrc = (header[3] & 2) != 0;
+
+               size_t offset(10);
+
+               if (fextra)
+               {
+                       if (offset + 2 > header.size())
+                       {
+                               throw FormatException("Unexpected end-of-file");
+                       }
+                       uint16_t fextraBytes(read16(header, offset));
+                       offset += 2;
+
+                       offset += fextraBytes;
+               }
+
+               std::string embeddedName(reader->getSourceName());
+               if (fname)
+               {
+                       size_t nullOffset(findNull(header, offset));
+                       embeddedName =
+                               std::string(
+                                       reinterpret_cast<char*>(&header[offset]),
+                                       nullOffset - offset);
+                       offset = nullOffset + 1;
+               }
+
+               if (fcomment)
+               {
+                       size_t nullOffset(findNull(header, offset));
+                       offset = nullOffset + 1;
+               }
+
+               if (fhcrc)
+               {
+                       offset += 2;
+               }
+
+               if (offset >= header.size())
+               {
+                       throw FormatException("Unexpected end-of-file");
+               }
+
+               std::vector<CompressedFilePtr> result;
+               result.push_back(
+                       CompressedFilePtr(new FileEntry(reader, offset, embeddedName)));
+
+               return result;
+       }
+
+       bool
+       isGzip(const ReaderPtr& reader)
+       {
+               enum Constants
+               {
+                       MinFileBytes = 18, // Header + CRC + size
+                       ID1 = 0x1f,
+                       ID2 = 0x8b
+               };
+
+               bool isGzip(false);
+               if (reader->getSize() >= MinFileBytes)
+               {
+                       uint8_t magic[2];
+                       reader->readData(0, sizeof(magic), &magic[0]);
+                       isGzip = (magic[0] == ID1) && (magic[1] == ID2);
+               }
+               return isGzip;
+       }
+}
+
diff --git a/gzip.hh b/gzip.hh
new file mode 100644 (file)
index 0000000..95d445b
--- /dev/null
+++ b/gzip.hh
@@ -0,0 +1,28 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of libzipper.
+//
+//     libzipper is free software: you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation, either version 3 of the License, or
+//     (at your option) any later version.
+//
+//     libzipper is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+//
+//     You should have received a copy of the GNU General Public License
+//     along with libzipper.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "zipper.hh"
+
+#include <vector>
+
+namespace zipper
+{
+       bool isGzip(const ReaderPtr& reader);
+
+       std::vector<CompressedFilePtr> ungzip(const ReaderPtr& reader);
+}
+
diff --git a/libzipper-1.0.pc.in b/libzipper-1.0.pc.in
new file mode 100644 (file)
index 0000000..6e192d7
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libzipper
+Description: libzipper offers a flexible C++ interface for reading compressed files in multiple formats.
+Requires: zlib
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lzipper-1.0
+Cflags: -I${includedir}
diff --git a/zip.cc b/zip.cc
new file mode 100644 (file)
index 0000000..8048ece
--- /dev/null
+++ b/zip.cc
@@ -0,0 +1,296 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of libzipper.
+//
+//     libzipper is free software: you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation, either version 3 of the License, or
+//     (at your option) any later version.
+//
+//     libzipper is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+//
+//     You should have received a copy of the GNU General Public License
+//     along with libzipper.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "zipper.hh"
+#include "Zip.hh"
+
+#include <zlib.h>
+
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+
+#include <string.h>
+
+using namespace zipper;
+
+namespace
+{
+       void
+       write32(uint32_t value, uint8_t* zipData)
+       {
+               // Write 4 bytes in little-endian order.
+               zipData[0] = value & 0xff;
+               zipData[1] = (value >> 8) & 0xff;
+               zipData[2] = (value >> 16) & 0xff;
+               zipData[3] = (value >> 24) & 0xff;
+       }
+
+       struct DeflateDeleter
+       {
+       public:
+               DeflateDeleter(z_stream* stream) : m_stream(stream) {}
+               ~DeflateDeleter()
+               {
+                       deflateEnd(m_stream);
+
+               }
+       private:
+               z_stream* m_stream;
+       };
+}
+
+
+void
+zipper::zip(
+       const std::string& filename,
+       const Reader& reader,
+       const WriterPtr& writer,
+       ZipFileRecord& outRecord)
+{
+       enum Constants
+       {
+               ChunkSize = 64*1024,
+               WindowBits = 15,
+               CRC32Pos = 14
+       };
+
+       static uint8_t Header[] =
+       {
+               0x50, 0x4b, 0x03, 0x04,  // Header
+               20, // Version (2.0)
+               0, // File attributes
+               0,0, // gp flag.
+               8,0, // deflate method
+               0,0, // file time
+               0,0, // file date
+               0,0,0,0, // CRC32
+               0,0,0,0, // Compressed size
+               0,0,0,0 // Uncompressed size
+       };
+
+       zsize_t outPos(writer->getSize());
+       outRecord.localHeaderOffset = outPos;
+       outRecord.filename = filename;
+
+       // Write header
+       {
+               uint8_t buffer[ChunkSize];
+               memcpy(buffer, Header, sizeof(Header));
+               zsize_t pos(sizeof(Header));
+
+               std::string::size_type filenameSize(filename.size());
+               if (filenameSize > (ChunkSize - pos))
+               {
+                       filenameSize = ChunkSize - pos;
+               }
+               buffer[pos++] = filenameSize & 0xff;
+               buffer[pos++] = (filenameSize >> 8);
+               buffer[pos++] = 0; // extra field len
+               buffer[pos++] = 0; // extra field len
+               memcpy(buffer + pos, filename.data(), filenameSize);
+               pos += filenameSize;
+               writer->writeData(outPos, pos, &buffer[0]);
+               outPos += pos;
+       }
+
+       // Write compressed data
+
+       uint8_t inChunk[ChunkSize];
+       uint8_t outChunk[ChunkSize];
+
+       outRecord.uncompressedSize = 0;
+       outRecord.compressedSize = 0;
+
+       z_stream stream;
+       stream.zalloc = NULL;
+       stream.zfree = NULL;
+       stream.opaque = NULL;
+       int zlibErr(
+               deflateInit2(
+                       &stream,
+                       Z_DEFAULT_COMPRESSION,
+                       Z_DEFLATED,
+                       -WindowBits,
+                       MAX_MEM_LEVEL,
+                       Z_DEFAULT_STRATEGY)
+                       );
+
+       assert(zlibErr == Z_OK);
+       DeflateDeleter deleter(&stream);
+       stream.next_in = NULL;
+       stream.avail_in = 0;
+
+       zsize_t pos(0);
+       zsize_t end(reader.getSize());
+       outRecord.crc32 = crc32(0, NULL, 0);
+
+       while (pos < end)
+       {
+               if (stream.avail_in == 0)
+               {
+                       stream.avail_in =
+                               std::min(zsize_t(ChunkSize), end - pos);
+                       reader.readData(
+                               pos, stream.avail_in, &inChunk[0]);
+                       stream.next_in = reinterpret_cast<Bytef*>(&inChunk);
+                       pos += stream.avail_in;
+                       outRecord.uncompressedSize += stream.avail_in;
+                       outRecord.crc32 =
+                               crc32(outRecord.crc32, stream.next_in, stream.avail_in);
+               }
+
+               stream.next_out = reinterpret_cast<Bytef*>(&outChunk);
+               stream.avail_out = sizeof(outChunk);
+
+               zlibErr = deflate(&stream, (pos < end) ? Z_NO_FLUSH : Z_FINISH);
+
+               if (zlibErr == Z_STREAM_END)
+               {
+                       if (pos < end)
+                       {
+                               assert(!"zlib buffer unexpectedly empty");
+                               std::terminate();
+                       }
+               }
+               else if (zlibErr != Z_OK)
+               {
+                       throw FormatException("Corrupt Data");
+               }
+
+               zsize_t bytesToWrite(sizeof(outChunk) - stream.avail_out);
+               writer->writeData(
+                               outPos,
+                               bytesToWrite,
+                               &outChunk[0]);
+               outPos += bytesToWrite;
+               outRecord.compressedSize += bytesToWrite;
+       }
+
+       // Go back and complete the header.
+       uint8_t trailer[12];
+       write32(outRecord.crc32, &trailer[0]);
+       write32(outRecord.compressedSize, &trailer[4]);
+       write32(outRecord.uncompressedSize, &trailer[8]);
+       writer->writeData(
+               outRecord.localHeaderOffset + CRC32Pos, sizeof(trailer), &trailer[0]);
+}
+
+void
+zipper::zipFinalise(
+       const std::vector<ZipFileRecord>& records,
+       const WriterPtr& writer)
+{
+       enum Constants
+       {
+               ChunkSize = 64*1024
+       };
+
+       static uint8_t FileHeader[] =
+       {
+               0x50, 0x4b, 0x01, 0x02,  // Header
+               20, 0x00, // Version (2.0)
+               20, 0x00, // Version Needed to extract (2.0)
+               0,0, // gp flag.
+               8,0, // deflate method
+               0,0, // file time
+               0,0 // file date
+       };
+
+       zsize_t outPos(writer->getSize());
+       uint32_t centralDirOffset(outPos);
+
+       for (size_t i = 0; i < records.size(); ++i)
+       {
+               uint8_t buffer[ChunkSize];
+               memcpy(buffer, FileHeader, sizeof(FileHeader));
+               zsize_t pos(sizeof(FileHeader));
+
+               write32(records[i].crc32, &buffer[pos]);
+               pos += 4;
+
+               write32(records[i].compressedSize, &buffer[pos]);
+               pos += 4;
+
+               write32(records[i].uncompressedSize, &buffer[pos]);
+               pos += 4;
+
+               std::string::size_type filenameSize(records[i].filename.size());
+               if (filenameSize > (ChunkSize - pos))
+               {
+                       filenameSize = ChunkSize - pos;
+               }
+               buffer[pos++] = filenameSize & 0xff;
+               buffer[pos++] = (filenameSize >> 8);
+               buffer[pos++] = 0; // extra field len
+               buffer[pos++] = 0; // extra field len
+
+               buffer[pos++] = 0; // file comment len
+               buffer[pos++] = 0; // file comment len
+
+               buffer[pos++] = 0; // disk number
+               buffer[pos++] = 0; // disk number
+
+               buffer[pos++] = 0; // internal file attributes
+               buffer[pos++] = 0; // internal file attributes
+
+               buffer[pos++] = 0; // external file attributes
+               buffer[pos++] = 0; // external file attributes
+               buffer[pos++] = 0; // external file attributes
+               buffer[pos++] = 0; // external file attributes
+
+               write32(records[i].localHeaderOffset, &buffer[pos]);
+               pos += 4;
+
+               memcpy(buffer + pos, records[i].filename.data(), filenameSize);
+               pos += filenameSize;
+
+               writer->writeData(outPos, pos, &buffer[0]);
+               outPos += pos;
+       }
+
+       uint32_t centralDirSize(writer->getSize() - centralDirOffset);
+
+       {
+               // End-of-directory record.
+               static uint8_t EndDirectory[] =
+               {
+                       0x50, 0x4b, 0x05, 0x06,  // Header
+                       0x00, 0x00, // Disk num
+                       0x00, 0x00 // Disk with central dir
+               };
+               uint8_t buffer[ChunkSize];
+               memcpy(buffer, EndDirectory, sizeof(EndDirectory));
+               zsize_t pos(sizeof(EndDirectory));
+
+               buffer[pos++] = records.size() & 0xff; // Entries on this disk
+               buffer[pos++] = records.size() >> 8;
+               buffer[pos++] = records.size() & 0xff; // Total entries
+               buffer[pos++] = records.size() >> 8;
+
+               write32(centralDirSize, &buffer[pos]);
+               pos += 4;
+               write32(centralDirOffset, &buffer[pos]);
+               pos += 4;
+
+               buffer[pos++] = 0; // Zip comment length
+               buffer[pos++] = 0; // Zip comment length
+
+               writer->writeData(outPos, pos, &buffer[0]);
+               outPos += pos;
+       }
+}
diff --git a/zip.hh b/zip.hh
new file mode 100644 (file)
index 0000000..7680a61
--- /dev/null
+++ b/zip.hh
@@ -0,0 +1,44 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of libzipper.
+//
+//     libzipper is free software: you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation, either version 3 of the License, or
+//     (at your option) any later version.
+//
+//     libzipper is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+//
+//     You should have received a copy of the GNU General Public License
+//     along with libzipper.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "zipper.hh"
+
+#include <string>
+#include <vector>
+
+namespace zipper
+{
+       struct ZipFileRecord
+       {
+               zsize_t localHeaderOffset;
+               uint32_t crc32;
+               uint32_t compressedSize;
+               uint32_t uncompressedSize;
+               std::string filename;
+       };
+
+       void zip(
+               const std::string& filename,
+               const Reader& reader,
+               const WriterPtr& writer,
+               ZipFileRecord& outRecord);
+
+       void zipFinalise(
+               const std::vector<ZipFileRecord>& records,
+               const WriterPtr& writer);
+}
+