Implement zip::read_file() – Read file in archive; add test.
Also added zip::open_file() and zip::close_file() to deduplicate code.
This commit is contained in:
parent
6334b7051f
commit
e773d4b78a
|
@ -80,7 +80,10 @@ int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
for (const auto &entry : epubgrep::zip::list(file))
|
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)
|
catch (const epubgrep::zip::exception &e)
|
||||||
|
|
73
src/zip.cpp
73
src/zip.cpp
|
@ -24,6 +24,8 @@
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <fmt/ostream.h> // For compatibility with fmt 4.
|
#include <fmt/ostream.h> // For compatibility with fmt 4.
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -35,16 +37,7 @@ using fmt::format;
|
||||||
|
|
||||||
std::vector<std::string> list(const fs::path &filepath)
|
std::vector<std::string> list(const fs::path &filepath)
|
||||||
{
|
{
|
||||||
auto *zipfile{archive_read_new()};
|
auto *zipfile{open_file(filepath)};
|
||||||
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())};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct archive_entry *entry{};
|
struct archive_entry *entry{};
|
||||||
std::vector<std::string> toc;
|
std::vector<std::string> toc;
|
||||||
|
@ -53,15 +46,69 @@ std::vector<std::string> list(const fs::path &filepath)
|
||||||
toc.emplace_back(archive_entry_pathname_utf8(entry));
|
toc.emplace_back(archive_entry_pathname_utf8(entry));
|
||||||
archive_read_data_skip(zipfile);
|
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<size_t>(archive_entry_size(entry))};
|
||||||
|
std::string filecontents;
|
||||||
|
filecontents.resize(length);
|
||||||
|
auto result_length{static_cast<size_t>(
|
||||||
|
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)
|
if (result != ARCHIVE_OK)
|
||||||
{
|
{
|
||||||
throw exception{format(translate("Could not close {0:s}.").str(),
|
throw exception{format(translate("Could not close {0:s}.").str(),
|
||||||
filepath.string())};
|
filepath.string())};
|
||||||
}
|
}
|
||||||
|
|
||||||
return toc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace epubgrep::zip
|
} // namespace epubgrep::zip
|
||||||
|
|
12
src/zip.hpp
12
src/zip.hpp
|
@ -19,8 +19,11 @@
|
||||||
|
|
||||||
#include "fs-compat.hpp"
|
#include "fs-compat.hpp"
|
||||||
|
|
||||||
|
#include <archive.h>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace epubgrep::zip
|
namespace epubgrep::zip
|
||||||
|
@ -29,6 +32,15 @@ namespace epubgrep::zip
|
||||||
//! List the contents of a zip file.
|
//! List the contents of a zip file.
|
||||||
std::vector<std::string> list(const fs::path &filepath);
|
std::vector<std::string> 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.
|
// It's std::runtime_error, but with another name.
|
||||||
class exception : public std::runtime_error
|
class exception : public std::runtime_error
|
||||||
{
|
{
|
||||||
|
|
BIN
tests/test.zip
BIN
tests/test.zip
Binary file not shown.
72
tests/test_zip.cpp
Normal file
72
tests/test_zip.cpp
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#include "fs-compat.hpp"
|
||||||
|
#include "zip.hpp"
|
||||||
|
|
||||||
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include <clocale>
|
||||||
|
#include <exception>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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<std::string> 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,44 +0,0 @@
|
||||||
#include "fs-compat.hpp"
|
|
||||||
#include "zip.hpp"
|
|
||||||
|
|
||||||
#include <catch.hpp>
|
|
||||||
|
|
||||||
#include <clocale>
|
|
||||||
#include <exception>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
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<std::string> 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/😊");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user