PageRenderTime 73ms CodeModel.GetById 40ms app.highlight 10ms RepoModel.GetById 21ms app.codeStats 0ms

/mordor/zip.h

http://github.com/mozy/mordor
C Header | 175 lines | 88 code | 22 blank | 65 comment | 0 complexity | 5ba9ee648fabd818dd85bc2fb6a59b41 MD5 | raw file
  1#ifndef __MORDOR_ZIP_H__
  2#define __MORDOR_ZIP_H__
  3// Copyright (c) 2010 - Mozy, Inc.
  4
  5#include <boost/noncopyable.hpp>
  6#include <boost/shared_ptr.hpp>
  7
  8#include "exception.h"
  9
 10namespace Mordor {
 11
 12class CRC32Stream;
 13class DeflateStream;
 14class LimitedStream;
 15class NotifyStream;
 16class Stream;
 17class Zip;
 18
 19struct CorruptZipException : virtual Exception {};
 20struct UnsupportedCompressionMethodException : virtual Exception {};
 21struct SpannedZipNotSupportedException : virtual Exception {};
 22
 23/// A single file within a Zip archive
 24class ZipEntry
 25{
 26    friend class Zip;
 27private:
 28    ZipEntry(Zip &outer)
 29        : m_size(-1ll),
 30          m_compressedSize(-1ll),
 31          m_startOffset(0ll),
 32          m_outer(&outer),
 33          m_crc(0),
 34          m_flags(0x0800),
 35          m_extraFieldsLength(0),
 36          m_compressionMethod(8) // DEFLATED
 37    {}
 38
 39public:
 40    const std::string &filename() const { return m_filename; }
 41    void filename(const std::string &filename);
 42
 43    const std::string &comment() const { return m_comment; }
 44    void comment(const std::string &comment);
 45
 46    long long size() const { return m_size; }
 47    /// Providing size is optional; if it is not provided, Zip64 extensions are
 48    /// assumed
 49    void size(long long size);
 50
 51    /// Supported method:
 52    /// 0 - no compression
 53    /// 8 - deflate
 54    unsigned short compressionMethod() const { return m_compressionMethod; }
 55    void compressionMethod(unsigned short method);
 56
 57    long long compressedSize() const { return m_compressedSize; }
 58
 59    /// @note Only one ZipEntry stream can be accessed at any one time from a
 60    /// single Zip; accessing the stream() of another ZipEntry will implicitly
 61    /// close the previously accessed one
 62    boost::shared_ptr<Stream> stream();
 63    boost::shared_ptr<Stream> stream() const;
 64
 65private:
 66    /// Writes the local file header (and extra field, if any)
 67    void commit();
 68    /// Flushes the deflate stream, and stores the CRC and actual sizes
 69    void close();
 70    /// Resets all fields
 71    void clear();
 72
 73private:
 74    std::string m_filename, m_comment;
 75    long long m_size, m_compressedSize;
 76    long long m_startOffset;
 77    mutable Zip *m_outer;
 78    unsigned int m_crc;
 79    unsigned short m_flags;
 80    unsigned short m_extraFieldsLength;
 81    unsigned short m_compressionMethod;
 82};
 83
 84class ZipEntries : public std::multimap<std::string, ZipEntry>, boost::noncopyable
 85{};
 86
 87/// @brief Zip Archive Format access
 88///
 89/// Supports reading or writing a Zip, but not both at the same time.  It fully
 90/// supports non-seekable streams for both reading and writing, though many
 91/// utilities (i.e. Windows) do not support Zips that weren't seekable when
 92/// written.  When writing a zip, it will automatically use Zip64 extensions
 93/// if necessary, which means if you want to avoid Zip64 extensions, you need
 94/// to provide the filesize (under 4GB) to ZipEntry prior to writing to
 95/// ZipEntry::stream().
 96///
 97/// Example of randomly accessing a zip file:
 98/// @code
 99/// Zip zip(stream);
100/// const ZipEntries &entries = zip.getAllEntries();
101/// ZipEntries::const_iterator it = entries.find("somefile.txt");
102/// transferStream(it->second.stream(), NullStream::get());
103/// @endcode
104/// Example of sequentially accessing a (possibly non-seekable) zip file:
105/// @code
106/// Zip zip(stream);
107/// ZipEntry const *entry = zip.getNextEntry();
108/// while (entry) {
109///     FileStream outputStream(entry->filename(), FileStream::WRITE,
110///         FileStream::OVERWRITE_OR_CREATE);
111///     transferStream(entry->stream, outputStream);
112///     entry = zip.getNextEntry();
113/// }
114/// @endcode
115/// Example of writing a (possibly non-seekable) zip file:
116/// @code
117/// Zip zip(stream);
118/// for (std::vector<std::string>::const_iterator it = files.begin();
119///     it != files.end();
120///     ++it) {
121///     ZipEntry &entry = zip.addEntry();
122///     entry.filename(*it);
123///     FileStream inputStream(*it, FileStream::READ);
124///     entry.size(inputStream.size());
125///     transferStream(inputStream, entry.stream());
126/// }
127/// zip.close();
128/// @endcode
129class Zip : boost::noncopyable
130{
131    friend class ZipEntry;
132public:
133    enum OpenMode
134    {
135        /// Opens the zip file for reading
136        READ,
137        /// Opens the zip file for writing (truncating existing files)
138        WRITE,
139        /// Infer the OpenMode.  If the stream supportsWrite(), it will open in
140        /// WRITE mode, otherwise READ
141        INFER
142    };
143public:
144    Zip(boost::shared_ptr<Stream> stream, OpenMode mode = INFER);
145
146    /// @pre stream->supportsWrite()
147    ZipEntry &addFile();
148    /// Writes the central directory
149    /// @pre stream->supportsWrite()
150    void close();
151
152    /// @pre stream->supportsRead()
153    ZipEntry const *getNextEntry();
154    /// @pre stream->supportsRead()
155    /// @pre stream->supportsSeek() && stream->supportsSize()
156    const ZipEntries &getAllEntries();
157
158private:
159    void onFileEOF();
160
161private:
162    boost::shared_ptr<Stream> m_stream, m_fileStream;
163    boost::shared_ptr<LimitedStream> m_uncompressedStream, m_compressedStream;
164    boost::shared_ptr<DeflateStream> m_deflateStream;
165    boost::shared_ptr<CRC32Stream> m_crcStream;
166    boost::shared_ptr<NotifyStream> m_notifyStream;
167    ZipEntry *m_currentFile;
168    ZipEntry m_scratchFile;
169    OpenMode m_mode;
170    ZipEntries m_centralDirectory;
171};
172
173}
174
175#endif