#ifndef _library__zip__hpp__included__ #define _library__zip__hpp__included__ #include #include #include #include #include #include #include #include #include "string.hpp" namespace zip { /** * This class opens ZIP archive and offers methods to read members off it. */ class reader { public: /** * This iterator iterates members of ZIP archive. */ template class iterator_class { public: /** * C++ iterators stuff. */ typedef std::bidirectional_iterator_tag iterator_category; typedef V value_type; typedef int difference_type; typedef const V& reference; typedef const V* pointer; /** * This constructs new iteration sequence. Only the first component (keys) are taken into * account, the second component (values) are ignored. * * parameter _itr: The underlying map iterator. * throws std::bad_alloc: Not enough memory. */ iterator_class(T _itr) throw(std::bad_alloc) : itr(_itr) { } /** * Get name of current member. * * returns: Name of member. * throws std::bad_alloc: Not enough memory. */ reference operator*() throw(std::bad_alloc) { return itr->first; } /** * Get name of current member. * * returns: Name of member. * throws std::bad_alloc: Not enough memory. */ pointer operator->() throw(std::bad_alloc) { return &(itr->first); } /** * Are these two iterators the same? * * parameter i: The another iterator * returns: True if iterators are the same, false otherwise. */ bool operator==(const iterator_class& i) const throw() { return itr == i.itr; } /** * Are these two iterators diffrent? * * paramer i: The another iterator * returns: True if iterators are diffrent, false otherwise. */ bool operator!=(const iterator_class& i) const throw() { return itr != i.itr; } /** * Advance iterator one step. * * returns: The old value of iterator. * throws std::bad_alloc: Not enough memory. */ const iterator_class operator++(int) throw(std::bad_alloc) { iterator_class c(*this); ++itr; return c; } /** * Regress iterator one step. * * returns: The old value of iterator. * throws std::bad_alloc: Not enough memory. */ const iterator_class operator--(int) throw(std::bad_alloc) { iterator_class c(*this); --itr; return c; } /** * Advance iterator one step. * * returns: Reference to this iterator. */ iterator_class& operator++() throw() { ++itr; return *this; } /** * Regress iterator one step. * * returns: Reference to this iterator. */ iterator_class& operator--() throw() { --itr; return *this; } private: T itr; }; /** * This iterator iterates members of ZIP archive in forward order. */ typedef iterator_class::iterator, std::string> iterator; /** * This iterator iterates members of ZIP archive in reverse order */ typedef iterator_class::reverse_iterator, std::string> riterator; /** * Opens specified ZIP archive for reading. * * parameter zipfile: The name of ZIP file to open. * throws std::bad_alloc: Not enough memory. * throws std::runtime_error: Can't open the ZIP file. */ reader(const std::string& zipfile) throw(std::bad_alloc, std::runtime_error); /** * Destroy the ZIP reader. Opened input streams continue to be valid. */ ~reader() throw(); /** * Gives the name of the first member, or "" if empty archive. * * returns: The member name * throws std::bad_alloc: Not enough memory. */ std::string find_first() throw(std::bad_alloc); /** * Gives the name of the next member after specified, or "" if that member is the last. * * parameter name: The name to start the search from. * returns: The member name * throws std::bad_alloc: Not enough memory. */ std::string find_next(const std::string& name) throw(std::bad_alloc); /** * Starting iterator * * returns: The iterator pointing to first name. * throws std::bad_alloc: Not enough memory. */ iterator begin() throw(std::bad_alloc); /** * Ending iterator (one past the end). * * returns: The iterator pointing to one past the last name. * throws std::bad_alloc: Not enough memory. */ iterator end() throw(std::bad_alloc); /** * Starting reverse iterator * * returns: The iterator pointing to last name and acting in reverse. * throws std::bad_alloc: Not enough memory. */ riterator rbegin() throw(std::bad_alloc); /** * Ending reverse iterator (one past the start). * returrns: The iterator pointing to one before the first name and acting in reverse. * throws std::bad_alloc: Not enough memory. */ riterator rend() throw(std::bad_alloc); /** * Check if member with specified name exists. * * parameter name: The name of the member to check * returns: True if specified member exists, false otherwise. */ bool has_member(const std::string& name) throw(); /** * Opens specified member. The resulting stream is not seekable, allocated using new and continues to be valid * after ZIP reader has been destroyed. * * parameter name: The name of member to open. * returns: The stream corresponding to member. * throws std::bad_alloc: Not enough memory. * throws std::runtime_error: The specified member does not exist */ std::istream& operator[](const std::string& name) throw(std::bad_alloc, std::runtime_error); /** * Reads a file consisting of single line. * * Parameter member: Name of the member to read. * Parameter out: String to write the output to. * Parameter conditional: If true and the file does not exist, return false instead of throwing. * Returns: True on success, false on failure. * Throws std::bad_alloc: Not enough memory. * Throws std::runtime_error: Error reading file. */ bool read_linefile(const std::string& member, std::string& out, bool conditional = false) throw(std::bad_alloc, std::runtime_error); /** * Read a raw file. * * Parameter member: Name of the member to read. * Parameter out: Buffer to write the output to. * Throws std::bad_alloc: Not enough memory. * Throws std::runtime_error: Error reading file. */ void read_raw_file(const std::string& member, std::vector& out) throw(std::bad_alloc, std::runtime_error); /** * Reads a file consisting of single numeric constant. * * Parameter member: Name of the member to read. * Parameter out: The output value. * Parameter conditional: If true and the file does not exist, return false instead of throwing. * Returns: True on success, false on failure. * Throws std::bad_alloc: Not enough memory. * Throws std::runtime_error: Error reading file. */ template bool read_numeric_file(const std::string& member, T& out, bool conditional = false) throw(std::bad_alloc, std::runtime_error) { std::string _out; if(!read_linefile(member, _out, conditional)) return false; out = parse_value(_out); return true; } private: reader(reader&); reader& operator=(reader&); std::map offsets; std::ifstream* zipstream; size_t* refcnt; }; /** * Opens the file named by name parameter, which is interpretted relative to file designated by referencing_path. * The file can be inside ZIP archive. The resulting stream may or may not be seekable. * * If referencing_path is "", then name is traditional relative/absolute path. Otherwise if name is relative, * it is relative to directory containing referencing_path, not current directory. * * parameter name: The name of file to open. * parameter referencing_path: The path to file name is interpretted against. * returns: The new stream, allocated by new. * throw std::bad_alloc: Not enough memory. * throw std::runtime_error: The file does not exist or can't be opened. */ std::istream& openrel(const std::string& name, const std::string& referencing_path) throw(std::bad_alloc, std::runtime_error); /** * As zip::openrel, but instead of returning handle to file, reads the entiere contents of the file and returns * that. * * parameter name: As in zip::openrel * parameter referencing_path: As in zip::openrel. * returns: The file contents. * throws std::bad_alloc: Not enough memory. * throws std::runtime_error: The file does not exist or can't be opened. */ std::vector readrel(const std::string& name, const std::string& referencing_path) throw(std::bad_alloc, std::runtime_error); /** * Resolves the final file path that zip::openrel/zip::readrel would open. * * parameter name: As in zip::openrel * parameter referencing_path: As in zip::openrel * returns: The file absolute path. * throws std::bad_alloc: Not enough memory. * throws std::runtime_error: Bad path. */ std::string resolverel(const std::string& name, const std::string& referencing_path) throw(std::bad_alloc, std::runtime_error); /** * Does the specified file (maybe inside .zip) exist? * * parameter name: The name of file. * returns: True if file exists, false if not. * throws std::bad_alloc: Not enough memory. */ bool file_exists(const std::string& name) throw(std::bad_alloc); /** * This class handles writing a ZIP archives. */ class writer { public: /** * Creates new empty ZIP archive. The members will be compressed according to specified compression. * * parameter zipfile: The zipfile to create. * parameter stream: The stream to write the ZIP to. * parameter _compression: Compression. 0 is uncompressed, 1-9 are deflate compression levels. * throws std::bad_alloc: Not enough memory. * throws std::runtime_error: Can't open archive or invalid argument. */ writer(const std::string& zipfile, unsigned _compression) throw(std::bad_alloc, std::runtime_error); writer(std::ostream& stream, unsigned _compression) throw(std::bad_alloc, std::runtime_error); /** * Destroys ZIP writer, aborting the transaction (unless commit() has been called). */ ~writer() throw(); /** * Commits the ZIP file. Does atomic replace of existing file if possible. * * throws std::bad_alloc: Not enough memory. * throws std::logic_error: Existing file open. * throws std::runtime_error: Can't commit archive (OS error or member open). */ void commit() throw(std::bad_alloc, std::logic_error, std::runtime_error); /** * Create a new member inside ZIP file. No existing member may be open. * * parameter name: The name for new member. * returns: Writing stream for the file (don't free). * throws std::bad_alloc: Not enough memory. * throws std::logic_error: Existing file open. * throws std::runtime_error: Illegal name. */ std::ostream& create_file(const std::string& name) throw(std::bad_alloc, std::logic_error, std::runtime_error); /** * Closes open member and destroys stream corresponding to it. * * throws std::bad_alloc: Not enough memory. * throws std::logic_error: No file open. * throws std::runtime_error: Error from operating system. */ void close_file() throw(std::bad_alloc, std::logic_error, std::runtime_error); /** * Write a file consisting of single line. No existing member may be open. * * Parameter member: The name of the member. * Parameter value: The value to write. * Parameter conditional: If true and the value is empty, skip the write. * throws std::bad_alloc: Not enough memory. * throws std::runtime_error: Error from operating system. */ void write_linefile(const std::string& member, const std::string& value, bool conditional = false) throw(std::bad_alloc, std::runtime_error); /** * Write a raw file. No existing member may be open. * * Parameter member: The name of the member. * Parameter content: The contents for the file. * throws std::bad_alloc: Not enough memory. * throws std::runtime_error: Error from operating system. */ void write_raw_file(const std::string& member, const std::vector& content) throw(std::bad_alloc, std::runtime_error); /** * Write a file consisting of a single number. No existing member may be open. * * Parameter member: The name of the member. * Parameter value: The value to write. * throws std::bad_alloc: Not enough memory. * throws std::runtime_error: Error from operating system. */ template void write_numeric_file(const std::string& member, T value) throw(std::bad_alloc, std::runtime_error) { write_linefile(member, (stringfmt() << value).str()); } private: struct file_info { unsigned long crc; unsigned long uncompressed_size; unsigned long compressed_size; unsigned long offset; }; writer(writer&); writer& operator=(writer&); std::ostream* zipstream; bool system_stream; std::string temp_path; std::string zipfile_path; std::string open_file; uint32_t base_offset; std::vector current_compressed_file; std::map files; unsigned compression; boost::iostreams::filtering_ostream* s; uint32_t basepos; bool committed; }; int rename_overwrite(const char* oldname, const char* newname); } #endif