From e773d4b78a888fb58b587a13a0e3e35da2b5fea7 Mon Sep 17 00:00:00 2001 From: tastytea Date: Sun, 23 May 2021 08:56:58 +0200 Subject: [PATCH] =?UTF-8?q?Implement=20zip::read=5Ffile()=20=E2=80=93=20Re?= =?UTF-8?q?ad=20file=20in=20archive;=20add=20test.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also added zip::open_file() and zip::close_file() to deduplicate code. --- src/main.cpp | 5 ++- src/zip.cpp | 73 +++++++++++++++++++++++++++++++++------- src/zip.hpp | 12 +++++++ tests/test.zip | Bin 504 -> 505 bytes tests/test_zip.cpp | 72 +++++++++++++++++++++++++++++++++++++++ tests/test_zip_list.cpp | 44 ------------------------ 6 files changed, 148 insertions(+), 58 deletions(-) create mode 100644 tests/test_zip.cpp delete mode 100644 tests/test_zip_list.cpp diff --git a/src/main.cpp b/src/main.cpp index 3d4df4e..ecf3d95 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -80,7 +80,10 @@ int main(int argc, char *argv[]) { for (const auto &entry : epubgrep::zip::list(file)) { - cout << " " << entry << '\n'; + cout << " " << entry; + cout << "\n CONTENT:\n"; + cout << "START" << epubgrep::zip::read_file(file, entry) + << "END\n"; } } catch (const epubgrep::zip::exception &e) diff --git a/src/zip.cpp b/src/zip.cpp index 3fe0d86..12d0fa6 100644 --- a/src/zip.cpp +++ b/src/zip.cpp @@ -24,6 +24,8 @@ #include #include // For compatibility with fmt 4. +#include +#include #include #include @@ -35,16 +37,7 @@ using fmt::format; std::vector list(const fs::path &filepath) { - auto *zipfile{archive_read_new()}; - archive_read_support_filter_all(zipfile); - archive_read_support_format_all(zipfile); - - auto result{archive_read_open_filename(zipfile, filepath.c_str(), 10240)}; - if (result != ARCHIVE_OK) - { - throw exception{format(translate("Could not open {0:s}.").str(), - filepath.string())}; - } + auto *zipfile{open_file(filepath)}; struct archive_entry *entry{}; std::vector toc; @@ -53,15 +46,69 @@ std::vector list(const fs::path &filepath) toc.emplace_back(archive_entry_pathname_utf8(entry)); archive_read_data_skip(zipfile); } + close_file(zipfile, filepath); - result = archive_read_free(zipfile); + return toc; +} + +std::string read_file(const fs::path &filepath, std::string_view entry_path) +{ + auto *zipfile{open_file(filepath)}; + + struct archive_entry *entry{}; + while (archive_read_next_header(zipfile, &entry) == ARCHIVE_OK) + { + const auto *path{archive_entry_pathname_utf8(entry)}; + if (std::strcmp(path, entry_path.data()) == 0) + { + const auto length{static_cast(archive_entry_size(entry))}; + std::string filecontents; + filecontents.resize(length); + auto result_length{static_cast( + archive_read_data(zipfile, &filecontents[0], length))}; + + if (result_length != length) + { + throw exception{ + format(translate("Could not read {0:s} in {1:s}.").str(), + entry_path, filepath.string())}; + } + + return filecontents; + } + archive_read_data_skip(zipfile); + } + + close_file(zipfile, filepath); + + throw exception{format(translate("{0:s} not found in {1:s}.").str(), + entry_path, filepath.string())}; +} + +struct archive *open_file(const fs::path &filepath) +{ + auto *zipfile{archive_read_new()}; + archive_read_support_filter_all(zipfile); + archive_read_support_format_zip(zipfile); + + auto result{archive_read_open_filename(zipfile, filepath.c_str(), 10240)}; + if (result != ARCHIVE_OK) + { + throw exception{format(translate("Could not open {0:s}.").str(), + filepath.string())}; + } + + return zipfile; +} + +void close_file(struct archive *zipfile, const fs::path &filepath) +{ + auto result{archive_read_free(zipfile)}; if (result != ARCHIVE_OK) { throw exception{format(translate("Could not close {0:s}.").str(), filepath.string())}; } - - return toc; } } // namespace epubgrep::zip diff --git a/src/zip.hpp b/src/zip.hpp index ad036ec..4af2de7 100644 --- a/src/zip.hpp +++ b/src/zip.hpp @@ -19,8 +19,11 @@ #include "fs-compat.hpp" +#include + #include #include +#include #include namespace epubgrep::zip @@ -29,6 +32,15 @@ namespace epubgrep::zip //! List the contents of a zip file. std::vector list(const fs::path &filepath); +//! Read a file from a zip archive. +std::string read_file(const fs::path &filepath, std::string_view entry_path); + +//! Open zip file and return handle. +struct archive *open_file(const fs::path &filepath); + +//! Close zip file. +void close_file(struct archive *zipfile, const fs::path &filepath); + // It's std::runtime_error, but with another name. class exception : public std::runtime_error { diff --git a/tests/test.zip b/tests/test.zip index 371c403975e8e0e4182f35fceda159f12d203bb6..96deb4e4dd98f8cfacf9e3a2f888dd868d52a396 100644 GIT binary patch delta 185 zcmeyt{F8aYOe+y)1`ZAeEA#C^T^~8G2{JG+2m`SI5SOGDmnfv==cJ?->3^6%qwBXp z{U;MWJq1hVj^)fBee@>Y+f-uDj7z@?&;$@rXjsxX6-c9-#>Jojl4f9JkYHdqk)c1q g|2kvCrK%VGKpMsm@MdKLsbK~}entj{DKFh5^3`jtLL586uwYWqfEk7qEwMhTN{25)LA)E}%Hjc{^ uY$oS3icP$+)=G{Umu?BTSxXwH0@>(hP3B{i5@BTnsbU5}Mh1raU@ZU}lOJ9H diff --git a/tests/test_zip.cpp b/tests/test_zip.cpp new file mode 100644 index 0000000..ad9ede4 --- /dev/null +++ b/tests/test_zip.cpp @@ -0,0 +1,72 @@ +#include "fs-compat.hpp" +#include "zip.hpp" + +#include + +#include +#include +#include +#include + +SCENARIO("Zip file handling works") +{ + GIVEN("Our test zip file") + { + fs::path zipfile{"test.zip"}; + std::setlocale(LC_CTYPE, ""); // Needed for utf-8 support in libarchive. + bool exception{false}; + + REQUIRE(fs::exists(zipfile)); + + SECTION("list() doesn't fail and returns the right file list") + { + std::vector filelist; + + WHEN("We list the file contents") + { + try + { + filelist = epubgrep::zip::list(zipfile); + } + catch (const std::exception &) + { + exception = true; + } + + THEN("No exception is thrown") + AND_THEN("It returns the TOC correctly") + { + REQUIRE_FALSE(exception); + REQUIRE(filelist.at(0) == "test folder/"); + REQUIRE(filelist.at(1) == "test folder/test file"); + REQUIRE(filelist.at(2) == "test folder/šŸ˜Š"); + } + } + } + + SECTION("read_file() doesn't fail and returns the right file contents") + { + std::string filecontents; + + WHEN("We list the file contents") + { + try + { + filecontents = epubgrep::zip::read_file(zipfile, + "test folder/šŸ˜Š"); + } + catch (const std::exception &) + { + exception = true; + } + + THEN("No exception is thrown") + AND_THEN("It returns the file contents correctly") + { + REQUIRE_FALSE(exception); + REQUIRE(filecontents == "šŸ“–\n\nšŸ“˜šŸ“—šŸ“™\n"); + } + } + } + } +} diff --git a/tests/test_zip_list.cpp b/tests/test_zip_list.cpp deleted file mode 100644 index e7f76ba..0000000 --- a/tests/test_zip_list.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "fs-compat.hpp" -#include "zip.hpp" - -#include - -#include -#include -#include -#include - -SCENARIO("epubgrep::zip::list() doesn't fail and returns the right file list") -{ - std::setlocale(LC_CTYPE, ""); // Needed for utf-8 support in libarchive. - bool exception{false}; - std::vector filelist; - - GIVEN("Our test zip file") - { - fs::path zipfile{"test.zip"}; - - REQUIRE(fs::exists(zipfile)); - - WHEN("We list the file contents") - { - try - { - filelist = epubgrep::zip::list(zipfile); - } - catch (const std::exception &) - { - exception = true; - } - - THEN("No exception is thrown") - AND_THEN("It returns the file contents correctly") - { - REQUIRE_FALSE(exception); - REQUIRE(filelist.at(0) == "test folder/"); - REQUIRE(filelist.at(1) == "test folder/test file"); - REQUIRE(filelist.at(2) == "test folder/šŸ˜Š"); - } - } - } -}