changeset 281:88f9d8b406c6

Add Zip, safe wrapper around libzip
author David Demelier <markand@malikania.fr>
date Tue, 11 Nov 2014 14:03:42 +0100
parents 91eb0583df52
children 9be2cd100167
files C++/Tests/Zip/CMakeLists.txt C++/Tests/Zip/data/stats.zip C++/Tests/Zip/main.cpp C++/ZipArchive.cpp C++/ZipArchive.h CMakeLists.txt cmake/FindZIP.cmake
diffstat 7 files changed, 788 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C++/Tests/Zip/CMakeLists.txt	Tue Nov 11 14:03:42 2014 +0100
@@ -0,0 +1,34 @@
+#
+# CMakeLists.txt -- tests for Zip
+#
+# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+project(zip)
+
+find_package(ZIP REQUIRED)
+
+set(
+	SOURCES
+	${code_SOURCE_DIR}/C++/ZipArchive.cpp
+	${code_SOURCE_DIR}/C++/ZipArchive.h
+	main.cpp
+)
+
+define_test(zip "${SOURCES}")
+
+target_include_directories(zip PRIVATE ${ZIP_INCLUDE_DIRS})
+target_link_libraries(zip ${ZIP_LIBRARIES})
+target_compile_definitions(zip PRIVATE "SOURCE=\"${zip_SOURCE_DIR}\"" "BINARY=\"${zip_BINARY_DIR}\"")
Binary file C++/Tests/Zip/data/stats.zip has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C++/Tests/Zip/main.cpp	Tue Nov 11 14:03:42 2014 +0100
@@ -0,0 +1,85 @@
+/*
+ * main.cpp -- test the zip wrapper functions
+ *
+ * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <gtest/gtest.h>
+
+#include <ZipArchive.h>
+
+using namespace source;
+
+TEST(Basic, stat)
+{
+	try {
+		ZipArchive archive{SOURCE "/data/stats.zip"};
+		ZipStat stats = archive.stat("README");
+
+		ASSERT_EQ(static_cast<decltype(stats.size)>(15), stats.size);
+		ASSERT_STREQ("README", stats.name);
+	} catch (const std::exception &ex) {
+		std::cerr << ex.what() << std::endl;
+	}
+}
+
+TEST(Basic, read)
+{
+	try {
+		ZipArchive archive{SOURCE "/data/stats.zip"};
+
+		auto file = archive.open("README");
+		auto stats = archive.stat("README");
+		auto text = file.read(stats.size);
+
+		ASSERT_EQ("This is a test\n", text);
+	} catch (const std::exception &ex) {
+		std::cerr << "warning: " << ex.what() << std::endl;
+	}
+}
+
+TEST(Basic, write)
+{
+	remove(BINARY "/output.zip");
+
+	// Open first and save some data
+	try {
+		ZipArchive archive{BINARY "/output.zip", ZIP_CREATE};
+
+		archive.add(Buffer{"hello world!"}, "DATA");
+	} catch (const std::exception &ex) {
+		std::cerr << "warning: " << ex.what() << std::endl;
+	}
+
+	try {
+		ZipArchive archive{BINARY "/output.zip"};
+
+		auto stats = archive.stat("DATA");
+		auto file = archive.open("DATA");
+		auto content = file.read(stats.size);
+
+		ASSERT_EQ(static_cast<decltype(stats.size)>(12), stats.size);
+		ASSERT_EQ("hello world!", content);
+	} catch (const std::exception &ex) {
+		std::cerr << "warning: " << ex.what() << std::endl;
+	}
+}
+
+int main(int argc, char **argv)
+{
+	testing::InitGoogleTest(&argc, argv);
+
+	return RUN_ALL_TESTS();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C++/ZipArchive.cpp	Tue Nov 11 14:03:42 2014 +0100
@@ -0,0 +1,248 @@
+/*
+ * ZipArchive.cpp -- wrapper around libzip
+ *
+ * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <stdexcept>
+
+#include "ZipArchive.h"
+
+namespace source {
+
+/* --------------------------------------------------------
+ * Buffer (zip_source_buffer)
+ * -------------------------------------------------------- */
+
+Buffer::Buffer(std::string data)
+	: m_data(std::move(data))
+{
+}
+
+struct zip_source *Buffer::source(struct zip *archive) const
+{
+	auto size = m_data.size();
+	auto data = static_cast<char *>(std::malloc(size));
+
+	if (!data)
+		throw std::runtime_error(std::strerror(errno));
+
+	std::memcpy(data, m_data.data(), size);
+
+	auto src = zip_source_buffer(archive, data, size, 1);
+
+	if (src == nullptr) {
+		std::free(data);
+		throw std::runtime_error(zip_strerror(archive));
+	}
+
+	return src;
+}
+
+/* --------------------------------------------------------
+ * File (zip_source_file)
+ * -------------------------------------------------------- */
+
+File::File(std::string path, ZipUint64 start, ZipInt64 length)
+	: m_path(std::move(path))
+	, m_start(start)
+	, m_length(length)
+{
+}
+
+struct zip_source *File::source(struct zip *archive) const
+{
+	auto src = zip_source_file(archive, m_path.c_str(), m_start, m_length);
+
+	if (!src)
+		throw std::runtime_error(zip_strerror(archive));
+
+	return src;
+}
+
+} // !source
+
+/* --------------------------------------------------------
+ * ZipArchive
+ * ------------------------------------------------------- */
+
+ZipArchive::ZipArchive(const std::string &path, ZipFlags flags)
+	: m_handle(nullptr, nullptr)
+{
+	int error;
+	struct zip *archive = zip_open(path.c_str(), flags, &error);
+
+	if (archive == nullptr)
+	{
+		char buf[128]{};
+
+		zip_error_to_str(buf, sizeof (buf), error, errno);
+
+		throw std::runtime_error(buf);
+	}
+
+	m_handle = { archive, zip_close };
+}
+
+void ZipArchive::setFileComment(ZipUint64 index, const std::string &text, ZipFlags flags)
+{
+	auto size = text.size();
+	auto cstr = (size == 0) ? nullptr : text.c_str();
+
+	if (zip_file_set_comment(m_handle.get(), index, cstr, size, flags) < 0)
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+}
+
+std::string ZipArchive::getFileComment(ZipUint64 index, ZipFlags flags) const
+{
+	zip_uint32_t length{};
+	auto text = zip_file_get_comment(m_handle.get(), index, &length, flags);
+
+	if (text == nullptr)
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+
+	return { text, length };
+}
+
+void ZipArchive::setComment(const std::string &comment)
+{
+	if (zip_set_archive_comment(m_handle.get(), comment.c_str(), comment.size()) < 0)
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+}
+
+std::string ZipArchive::getComment(ZipFlags flags) const
+{
+	int length{};
+	auto text = zip_get_archive_comment(m_handle.get(), &length, flags);
+
+	if (text == nullptr)
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+
+	return { text, static_cast<size_t>(length) };
+}
+
+ZipInt64 ZipArchive::find(const std::string &name, ZipFlags flags)
+{
+	auto index = zip_name_locate(m_handle.get(), name.c_str(), flags);
+
+	if (index < 0)
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+
+	return index;
+}
+
+ZipStat ZipArchive::stat(const std::string &name, ZipFlags flags)
+{
+	ZipStat st;
+
+	if (zip_stat(m_handle.get(), name.c_str(), flags, &st) < 0)
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+
+	return st;
+}
+
+ZipStat ZipArchive::stat(ZipUint64 index, ZipFlags flags)
+{
+	ZipStat st;
+
+	if (zip_stat_index(m_handle.get(), index, flags, &st) < 0)
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+
+	return st;
+}
+
+ZipInt64 ZipArchive::add(const ZipSource &source, const std::string &name, ZipFlags flags)
+{
+	auto src = source.source(m_handle.get());
+	auto ret = zip_file_add(m_handle.get(), name.c_str(), src, flags);
+
+	if (ret < 0) {
+		zip_source_free(src);
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+	}
+
+	return ret;
+}
+
+ZipInt64 ZipArchive::addDirectory(const std::string &directory, ZipFlags flags)
+{
+	auto ret = zip_dir_add(m_handle.get(), directory.c_str(), flags);
+
+	if (ret < 0)
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+
+	return ret;
+}
+
+void ZipArchive::replace(const ZipSource &source, ZipUint64 index, ZipFlags flags)
+{
+	auto src = source.source(m_handle.get());
+
+	if (zip_file_replace(m_handle.get(), index, src, flags) < 0) {
+		zip_source_free(src);
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+	}
+}
+
+ZipFile ZipArchive::open(const std::string &name, ZipFlags flags, const std::string &password)
+{
+	struct zip_file *file;
+
+	if (password.size() > 0)
+		file = zip_fopen_encrypted(m_handle.get(), name.c_str(), flags, password.c_str());
+	else
+		file = zip_fopen(m_handle.get(), name.c_str(), flags);
+
+	if (file == nullptr)
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+
+	return file;
+}
+
+ZipFile ZipArchive::open(ZipUint64 index, ZipFlags flags, const std::string &password)
+{
+	struct zip_file *file;
+
+	if (password.size() > 0)
+		file = zip_fopen_index_encrypted(m_handle.get(), index, flags, password.c_str());
+	else
+		file = zip_fopen_index(m_handle.get(), index, flags);
+
+	if (file == nullptr)
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+
+	return file;
+}
+
+void ZipArchive::rename(ZipUint64 index, const std::string &name, ZipFlags flags)
+{
+	if (zip_file_rename(m_handle.get(), index, name.c_str(), flags) < 0)
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+}
+
+void ZipArchive::setFileCompression(ZipUint64 index, ZipInt32 comp, ZipUint32 flags)
+{
+	if (zip_set_file_compression(m_handle.get(), index, comp, flags) < 0)
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+}
+
+void ZipArchive::remove(ZipUint64 index)
+{
+	if (zip_delete(m_handle.get(), index) < 0)
+		throw std::runtime_error(zip_strerror(m_handle.get()));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C++/ZipArchive.h	Tue Nov 11 14:03:42 2014 +0100
@@ -0,0 +1,374 @@
+/*
+ * ZipArchive.h -- wrapper around libzip
+ *
+ * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _ZIP_ARCHIVE_H_
+#define _ZIP_ARCHIVE_H_
+
+#include <memory>
+#include <string>
+
+#include <zip.h>
+
+using ZipStat = struct zip_stat;
+using ZipSourceCommand = enum zip_source_cmd;
+using ZipCallback = zip_source_callback;
+
+using ZipFlags	= zip_flags_t;
+using ZipInt8	= zip_int8_t;
+using ZipUint8	= zip_uint8_t;
+using ZipInt16	= zip_int16_t;
+using ZipUint16	= zip_uint16_t;
+using ZipInt32	= zip_int32_t;
+using ZipUint32	= zip_uint32_t;
+using ZipInt64	= zip_int64_t;
+using ZipUint64	= zip_uint64_t;
+
+/**
+ * @class ZipSource
+ * @brief Source for adding file
+ */
+class ZipSource {
+public:
+	/**
+	 * Default constructor.
+	 */
+	ZipSource() = default;
+
+	/**
+	 * Virtual destructor.
+	 */
+	virtual ~ZipSource() = default;
+
+	/**
+	 * Create a zip_source structure. Must not be null, throw an exception
+	 * instead.
+	 *
+	 * @return a zip_source ready to be used
+	 * @post must not return null
+	 */
+	virtual struct zip_source *source(struct zip *zip) const = 0;
+};
+
+/**
+ * @class ZipFile
+ * @brief File for reading
+ */
+class ZipFile {
+private:
+	std::unique_ptr<struct zip_file, int (*)(struct zip_file *)> m_handle;
+
+	ZipFile(const ZipFile &) = delete;
+	ZipFile &operator=(const ZipFile &) = delete;
+public:
+	/**
+	 * Create a ZipFile with a zip_file structure.
+	 *
+	 * @param file the file ready to be used
+	 */
+	inline ZipFile(struct zip_file *file)
+		: m_handle(file, zip_fclose)
+	{
+	}
+
+	/**
+	 * Move constructor defaulted.
+	 *
+	 * @param other the other ZipFile
+	 */
+	ZipFile(ZipFile &&other) = default;
+
+	/**
+	 * Move operator defaulted.
+	 *
+	 * @param other the other ZipFile
+	 * @return *this
+	 */
+	ZipFile &operator=(ZipFile &&) = default;
+
+	/**
+	 * Read some data.
+	 *
+	 * @param data the destination buffer
+	 * @param length the length
+	 * @return the number of bytes written or -1 on failure
+	 */
+	inline int read(void *data, ZipUint64 length) noexcept
+	{
+		return zip_fread(m_handle.get(), data, length);
+	}
+
+	/**
+	 * Read some data to a fixed size array.
+	 *
+	 * @param data the array
+	 * @return the number of bytes written or -1 on failure
+	 */
+	template <size_t Size>
+	inline int read(char (&data)[Size]) noexcept
+	{
+		return read(data, Size);
+	}
+
+	/**
+	 * Optimized function for reading all characters with only one allocation.
+	 * Ideal for combining with ZipArchive::stat.
+	 *
+	 * @param length the length of the file
+	 * @return the whole string
+	 * @see ZipArchive::stat
+	 */
+	std::string read(unsigned length)
+	{
+		std::string result;
+
+		result.resize(length);
+		auto count = read(&result[0], length);
+
+		if (count < 0)
+			return "";
+
+		return result;
+	}
+};
+
+namespace source {
+
+/**
+ * @class Buffer
+ * @brief Create a source from a buffer
+ */
+class Buffer : public ZipSource {
+private:
+	std::string m_data;
+
+public:
+	/**
+	 * Buffer constructor. Moves the data.
+	 *
+	 * @param data the data
+	 */
+	Buffer(std::string data);
+
+	/**
+	 * @copydoc ZipSource::source
+	 */
+	struct zip_source *source(struct zip *archive) const override;
+};
+
+/**
+ * @class File
+ * @brief Create a source from a file on the disk
+ */
+class File : public ZipSource {
+private:
+	std::string m_path;
+	ZipUint64 m_start;
+	ZipInt64 m_length;
+
+public:
+	/**
+	 * File constructor.
+	 *
+	 * @param path the path to the file
+	 * @param start the beginning in the file
+	 * @param length the maximum length
+	 */
+	File(std::string path, ZipUint64 start = 0, ZipInt64 length = -1);
+
+	/**
+	 * @copydoc ZipSource::source
+	 */
+	struct zip_source *source(struct zip *archive) const override;
+};
+
+} // !source
+
+/**
+ * @class ZipArchive
+ * @brief Safe wrapper on the struct zip structure
+ */
+class ZipArchive {
+private:
+	using Handle = std::unique_ptr<struct zip, int (*)(struct zip *)>;
+
+	Handle m_handle;
+
+	ZipArchive(const ZipArchive &) = delete;
+	ZipArchive &operator=(const ZipArchive &) = delete;
+
+public:
+	/**
+	 * Open an archive on the disk.
+	 *
+	 * @param path the path
+	 * @param flags the optional flags
+	 */
+	ZipArchive(const std::string &path, ZipFlags flags = 0);
+
+	/**
+	 * Move constructor defaulted.
+	 *
+	 * @param other the other ZipArchive
+	 */
+	ZipArchive(ZipArchive &&other) noexcept = default;
+
+	/**
+	 * Move operator defaulted.
+	 *
+	 * @param other the other ZipArchive
+	 * @return *this
+	 */
+	ZipArchive &operator=(ZipArchive &&other) noexcept = default;
+
+	/**
+	 * Set a comment on a file.
+	 *
+	 * @param index the file index in the archive
+	 * @param text the text or empty to remove the comment
+	 * @param flags the optional flags
+	 */
+	void setFileComment(ZipUint64 index, const std::string &text = "", ZipFlags flags = 0);
+
+	/**
+	 * Get a comment from a file.
+	 *
+	 * @param index the file index in the archive
+	 * @param flags the optional flags
+	 * @return the comment
+	 */
+	std::string getFileComment(ZipUint64 index, ZipFlags flags = 0) const;
+
+	/**
+	 * Set the archive comment.
+	 *
+	 * @param comment the comment
+	 */
+	void setComment(const std::string &comment);
+
+	/**
+	 * Get the archive comment.
+	 *
+	 * @param flags the optional flags
+	 * @return the comment
+	 */
+	std::string getComment(ZipFlags flags = 0) const;
+
+	/**
+	 * Locate a file on the archive.
+	 *
+	 * @param name the name
+	 * @param flags the optional flags
+	 * @return the index
+	 */
+	ZipInt64 find(const std::string &name, ZipFlags flags = 0);
+
+	/**
+	 * Get information about a file.
+	 *
+	 * @param name the name
+	 * @param flags the optional flags
+	 * @return the structure
+	 */
+	ZipStat stat(const std::string &name, ZipFlags flags = 0);
+
+	/**
+	 * Get information about a file. Overloaded function.
+	 *
+	 * @param index the file index in the archive
+	 * @param flags the optional flags
+	 * @return the structure
+	 */
+	ZipStat stat(ZipUint64 index, ZipFlags flags = 0);
+
+	/**
+	 * Add a file to the archive.
+	 *
+	 * @param source the source
+	 * @param name the name entry in the archive
+	 * @param flags the optional flags
+	 * @return the new index in the archive
+	 * @see source::File
+	 * @see source::Buffer
+	 */
+	ZipInt64 add(const ZipSource &source, const std::string &name, ZipFlags flags = 0);
+
+	/**
+	 * Add a directory to the archive. Not a directory from the disk.
+	 *
+	 * @param directory the directory name
+	 * @param flags the optional flags
+	 * @return the new index in the archive
+	 */
+	ZipInt64 addDirectory(const std::string &directory, ZipFlags flags = 0);
+
+	/**
+	 * Replace an existing file in the archive.
+	 *
+	 * @param source the source
+	 * @param index the file index in the archiev
+	 * @param flags the optional flags
+	 */
+	void replace(const ZipSource &source, ZipUint64 index, ZipFlags flags = 0);
+
+	/**
+	 * Open a file in the archive.
+	 *
+	 * @param name the name
+	 * @param flags the optional flags
+	 * @param password the optional password
+	 * @return the opened file
+	 */
+	ZipFile open(const std::string &name, ZipFlags flags = 0, const std::string &password = "");
+
+	/**
+	 * Open a file in the archive. Overloaded function.
+	 *
+	 * @param index the file index in the archive
+	 * @param flags the optional flags
+	 * @param password the optional password
+	 * @return the opened file
+	 */
+	ZipFile open(ZipUint64 index, ZipFlags flags = 0, const std::string &password = "");
+
+	/**
+	 * Rename an existing entry in the archive.
+	 *
+	 * @param index the file index in the archive
+	 * @param name the new name
+	 * @param flags the optional flags
+	 */
+	void rename(ZipUint64 index, const std::string &name, ZipFlags flags = 0);
+
+	/**
+	 * Set file compression.
+	 *
+	 * @param index the file index in the archive
+	 * @param comp the compression
+	 * @param flags the optional flags
+	 */
+	void setFileCompression(ZipUint64 index, ZipInt32 comp, ZipUint32 flags = 0);
+
+	/**
+	 * Delete a file from the archive.
+	 *
+	 * @param index the file index in the archive
+	 */
+	void remove(ZipUint64 index);
+};
+
+#endif // !_ZIP_ARCHIVE_H_
--- a/CMakeLists.txt	Fri Oct 24 18:28:39 2014 +0200
+++ b/CMakeLists.txt	Tue Nov 11 14:03:42 2014 +0100
@@ -59,6 +59,7 @@
 option(WITH_TREENODE "Enable treenode tests" On)
 option(WITH_UTF8 "Enable Utf8 functions tests" On)
 option(WITH_XMLPARSER "Enable XML tests" On)
+option(WITH_ZIP "Enable ZipArchive tests" On)
 
 if (UNIX)
 	option(WITH_XDG "Enable XDG standard directories tests" On)
@@ -107,3 +108,7 @@
 if (WITH_XDG AND UNIX)
 	add_subdirectory(C++/Tests/Xdg)
 endif ()
+
+if (WITH_ZIP)
+	add_subdirectory(C++/Tests/Zip)
+endif ()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmake/FindZIP.cmake	Tue Nov 11 14:03:42 2014 +0100
@@ -0,0 +1,42 @@
+find_package(ZLIB QUIET)
+
+find_path(
+	ZIP_INCLUDE_DIR
+	NAMES zip.h
+)
+
+find_library(
+	ZIP_LIBRARY
+	NAMES zip libzip
+)
+
+find_path(
+	ZIPCONF_INCLUDE_DIR
+	NAMES zipconf.h
+)
+
+if (NOT ZIPCONF_INCLUDE_DIR)
+	# zipconf.h is sometimes directly in the include/ folder but on some systems
+	# like Windows, it is installed in the lib/ directory.
+	get_filename_component(_ZIP_PRIVATE_LIBRARY "${ZIP_LIBRARY}" DIRECTORY)
+
+	find_path(
+		ZIPCONF_INCLUDE_DIR
+		NAMES zipconf.h
+		PATHS "${_ZIP_PRIVATE_LIBRARY}/libzip/include"
+	)
+endif ()
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(
+	ZIP
+	REQUIRED_VARS ZLIB_LIBRARIES ZLIB_INCLUDE_DIRS ZIP_LIBRARY ZIP_INCLUDE_DIR ZIPCONF_INCLUDE_DIR
+)
+
+if (ZIP_FOUND)
+	set(ZIP_LIBRARIES ${ZIP_LIBRARY} ${ZLIB_LIBRARIES})
+	set(ZIP_INCLUDE_DIRS ${ZIP_INCLUDE_DIR} ${ZIPCONF_INCLUDE_DIR} ${ZLIB_INCLUDE_DIRS})
+endif ()
+
+mark_as_advanced(ZIP_LIBRARY ZIP_INCLUDE_DIR ZIPCONF_INCLUDE_DIR)