From d75df67e70e4335b13bae4d698ef61974c796a4b Mon Sep 17 00:00:00 2001 From: Michael McMaster Date: Sat, 21 May 2011 00:24:27 +1000 Subject: [PATCH] zip and gzip complete. Debian packaging in progress. --- debian/changelog | 5 + debian/compat | 1 + debian/control | 27 +++ debian/copyright | 40 +++++ debian/docs | 2 + debian/libzipper-dev.dirs | 2 + debian/libzipper-dev.install | 6 + debian/libzipper1.dirs | 1 + debian/libzipper1.install | 1 + debian/rules | 4 + debian/source/format | 1 + Unzip.cc => deflate.cc | 327 ++++++++++++++++++++++++++++++----- Unzip.hh => deflate.hh | 9 +- Ungzip.cc => gzip.cc | 0 Ungzip.hh => gzip.hh | 0 libzipper-1.0.pc.in | 11 ++ Zip.cc => zip.cc | 0 Zip.hh => zip.hh | 0 18 files changed, 387 insertions(+), 50 deletions(-) create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/docs create mode 100644 debian/libzipper-dev.dirs create mode 100644 debian/libzipper-dev.install create mode 100644 debian/libzipper1.dirs create mode 100644 debian/libzipper1.install create mode 100755 debian/rules create mode 100644 debian/source/format rename Unzip.cc => deflate.cc (55%) rename Unzip.hh => deflate.hh (80%) rename Ungzip.cc => gzip.cc (100%) rename Ungzip.hh => gzip.hh (100%) create mode 100644 libzipper-1.0.pc.in rename Zip.cc => zip.cc (100%) rename Zip.hh => zip.hh (100%) diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..be1dcb7 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +libzipper (1.0.0-1) unstable; urgency=low + + * Initial release (Closes: #nnnn) + + -- Michael McMaster Sat, 21 May 2011 00:01:11 +1000 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +7 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..d57a16e --- /dev/null +++ b/debian/control @@ -0,0 +1,27 @@ +Source: libzipper +Priority: extra +Maintainer: Michael McMaster +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 index 0000000..5ae1531 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,40 @@ +Format: http://dep.debian.net/deps/dep5 +Upstream-Name: libzipper +Source: + +Files: * +Copyright: + +License: GPL-3.0+ + +Files: debian/* +Copyright: 2011 Michael McMaster +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 . + . + 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 index 0000000..50bd824 --- /dev/null +++ b/debian/docs @@ -0,0 +1,2 @@ +NEWS +README diff --git a/debian/libzipper-dev.dirs b/debian/libzipper-dev.dirs new file mode 100644 index 0000000..4418816 --- /dev/null +++ b/debian/libzipper-dev.dirs @@ -0,0 +1,2 @@ +usr/lib +usr/include diff --git a/debian/libzipper-dev.install b/debian/libzipper-dev.install new file mode 100644 index 0000000..3c996c8 --- /dev/null +++ b/debian/libzipper-dev.install @@ -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 index 0000000..6845771 --- /dev/null +++ b/debian/libzipper1.dirs @@ -0,0 +1 @@ +usr/lib diff --git a/debian/libzipper1.install b/debian/libzipper1.install new file mode 100644 index 0000000..d0dbfd1 --- /dev/null +++ b/debian/libzipper1.install @@ -0,0 +1 @@ +usr/lib/lib*.so.* diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..114ab44 --- /dev/null +++ b/debian/rules @@ -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 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/Unzip.cc b/deflate.cc similarity index 55% rename from Unzip.cc rename to deflate.cc index 7845d91..0819b80 100644 --- a/Unzip.cc +++ b/deflate.cc @@ -16,7 +16,8 @@ // along with libzipper. If not, see . #include "zipper.hh" -#include "Unzip.hh" +#include "zip.hh" +#include "util.hh" #include @@ -30,31 +31,6 @@ using namespace zipper; namespace { - template - 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& 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: @@ -68,6 +44,19 @@ namespace 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: @@ -126,7 +115,7 @@ namespace m_reader->readData( m_localHeaderOffset, MinRecordBytes, &localRecord[0] ); - if (read32(localRecord, 0) != Signature) + if (read32_le(localRecord, 0) != Signature) { throw FormatException("Invalid local ZIP record"); } @@ -134,8 +123,8 @@ namespace // 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 filenameLength(read16_le(localRecord, 26)); + zsize_t extraLength(read16_le(localRecord, 28)); zsize_t startCompressedBytes( m_localHeaderOffset + @@ -254,9 +243,9 @@ namespace 12 - stream.avail_in, &dataDescriptor[stream.avail_in]); } - m_crc = read32(dataDescriptor, 0); - m_compressedSize = read32(dataDescriptor, 4); - m_uncompressedSize = read32(dataDescriptor, 8); + m_crc = read32_le(dataDescriptor, 0); + m_compressedSize = read32_le(dataDescriptor, 4); + m_uncompressedSize = read32_le(dataDescriptor, 8); } if (crc != m_crc) @@ -323,20 +312,20 @@ namespace ssize_t pos(bufSize - MinRecordBytes); for (; pos >= 0; --pos) { - recordFound = (read32(buffer, pos) == Signature); + recordFound = (read32_le(buffer, pos) == Signature); break; } if (recordFound) { - if (read16(buffer, pos + 4) != 0) + if (read16_le(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); + centralDirectoryBytes = read32_le(buffer, pos + 12); + centralDirectoryOffset = read32_le(buffer, pos + 16); + centralDirectoryEntries = read16_le(buffer, pos + 10); } return recordFound; } @@ -374,23 +363,23 @@ namespace std::vector entries; while ((pos + MinRecordBytes) < buffer.size()) { - if (read32(buffer, pos) != Signature) + if (read32_le(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)); + 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() @@ -424,6 +413,248 @@ namespace } 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(&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(&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& 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 diff --git a/Unzip.hh b/deflate.hh similarity index 80% rename from Unzip.hh rename to deflate.hh index ab78f9f..9acf552 100644 --- a/Unzip.hh +++ b/deflate.hh @@ -21,8 +21,13 @@ namespace zipper { - bool isZip(const ReaderPtr& reader); + bool isGzip(const ReaderPtr& reader); - std::vector unzip(const ReaderPtr& reader); + void gzip( + const std::string& filename, + const Reader& reader, + const WriterPtr& writer); + + std::vector ungzip(const ReaderPtr& reader); } diff --git a/Ungzip.cc b/gzip.cc similarity index 100% rename from Ungzip.cc rename to gzip.cc diff --git a/Ungzip.hh b/gzip.hh similarity index 100% rename from Ungzip.hh rename to gzip.hh diff --git a/libzipper-1.0.pc.in b/libzipper-1.0.pc.in new file mode 100644 index 0000000..6e192d7 --- /dev/null +++ b/libzipper-1.0.pc.in @@ -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 similarity index 100% rename from Zip.cc rename to zip.cc diff --git a/Zip.hh b/zip.hh similarity index 100% rename from Zip.hh rename to zip.hh -- 2.38.5