diff --git a/.drone.yml b/.drone.yml index 09e15bf..9820308 100644 --- a/.drone.yml +++ b/.drone.yml @@ -8,7 +8,6 @@ steps: commands: - meson build - ninja -C build - - build/src/xwim ../compile-commands.json trigger: event: diff --git a/meson.build b/meson.build index 4912022..e38d083 100644 --- a/meson.build +++ b/meson.build @@ -1,2 +1,2 @@ project('xwim', 'cpp', default_options : ['cpp_std=c++17']) -subdir('src') \ No newline at end of file +subdir('src') diff --git a/src/archive.cpp b/src/archive.cpp index fd51974..1439b4b 100644 --- a/src/archive.cpp +++ b/src/archive.cpp @@ -1,26 +1,125 @@ -#include "archive.hpp" +#include +#include +namespace logger = spdlog; + +#include +#include #include #include #include +#include +#include "archive.hpp" #include "spec.hpp" namespace xwim { -Archive::Archive(std::string path) : path{std::filesystem::path(path)} {} -Archive::Archive(std::filesystem::path&& path) : path{path} {} -ArchiveSpec Archive::check() { - return ArchiveSpec{.is_root_dir = true, - .is_root_dir_filename = true, - .has_subarchive = false}; +Archive::Archive(std::string path) : path{std::filesystem::path(path)} { + int r; // libarchive error handling + + logger::trace("Setting up archive reader"); + this->xwim_archive = archive_read_new(); + archive_read_support_filter_all(this->xwim_archive); + archive_read_support_format_all(this->xwim_archive); + + logger::trace("Reading archive at {}", path.c_str()); + r = archive_read_open_filename(this->xwim_archive, path.c_str(), 10240); + if (r != ARCHIVE_OK) + throw ArchiveException{"Could not open archive file", this->xwim_archive}; + + logger::trace("Archive read succesfully"); } -void Archive::extract(ExtractSpec extract_spec) { - std::cout << "Make dir:" << extract_spec.make_dir - << "Dirname: " << extract_spec.dirname - << "Archname: " << this->path - << std::endl; +Archive::~Archive() { + archive_read_free(this->xwim_archive); +} + +static archive_entry* _archive_next_entry(archive* archive) { + int r; // libarchive error handling + archive_entry* entry; + logger::trace("Listing next archive entry"); + r = archive_read_next_header(archive, &entry); + if (r != ARCHIVE_OK) + throw(ArchiveException{"Could not list archive", archive}); + + logger::trace("Got archive header"); + return entry; +} +static void _spec_is_root_filename(ArchiveSpec* spec, + archive_entry* entry, + std::filesystem::path* filepath) { + std::filesystem::path entry_path{archive_entry_pathname(entry)}; + std::filesystem::path norm_stem = filepath->filename(); + + while (norm_stem.has_extension()) + norm_stem = norm_stem.stem(); + + if (*entry_path.begin() != norm_stem) { + logger::debug("Archive root does not match archive name"); + spec->is_root_filename = false; + } else { + logger::debug("Archive root matches archive name"); + spec->is_root_filename = true; + } + logger::debug("\t-> Archive root: {}", entry_path.begin()->string()); + logger::debug("\t-> Archive stem: {}", norm_stem.string()); +} + +static void _spec_is_root_dir(ArchiveSpec* spec, archive_entry* entry) { + if (S_ISDIR(archive_entry_filetype(entry))) { + logger::debug("Archive root is directory"); + spec->is_root_dir = true; + } else { + logger::debug("Archive root is not a directory"); + spec->is_root_dir = false; + } + logger::debug("\t-> Archive mode_t: {0:o}", archive_entry_filetype(entry)); +} + +static void _spec_has_single_root(ArchiveSpec* spec, + archive_entry* first_entry, + archive* archive) { + + std::filesystem::path first_entry_root = *(std::filesystem::path{archive_entry_pathname(first_entry)}.begin()); + logger::trace("Testing roots"); + + spec->has_single_root = true; + archive_entry* entry; + while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) { + std::filesystem::path next_entry{archive_entry_pathname(entry)}; + logger::trace("Path: {}, Root: {}", next_entry.string(), next_entry.begin()->string()); + + if (first_entry_root != *next_entry.begin()) { + logger::debug("Archive has multiple roots"); + logger::debug("\t-> Archive root I: {}", first_entry_root.begin()->string()); + logger::debug("\t-> Archive root II: {}", next_entry.begin()->string()); + + spec->has_single_root = false; + break; + } + } + if (spec->has_single_root) + logger::debug("Archive has single root: {}", first_entry_root.string()); +} + +ArchiveSpec Archive::check() { + logger::trace("Creating archive spec for {}", this->path.string()); + + ArchiveSpec archive_spec; + + archive_entry* first_entry = _archive_next_entry(this->xwim_archive); + if (first_entry == nullptr) { // archive is empty + logger::debug("Archive is empty"); + return {false, false, false}; + } + logger::trace("Found archive entry {}", archive_entry_pathname(first_entry)); + + _spec_is_root_filename(&archive_spec, first_entry, &this->path); + _spec_is_root_dir(&archive_spec, first_entry); + _spec_has_single_root(&archive_spec, first_entry, this->xwim_archive); + + return archive_spec; } } // namespace xwim diff --git a/src/archive.hpp b/src/archive.hpp index d5a4012..7535230 100644 --- a/src/archive.hpp +++ b/src/archive.hpp @@ -1,8 +1,13 @@ #ifndef ARCHIVE_H #define ARCHIVE_H +#include +#include + #include +#include #include +#include #include "spec.hpp" @@ -11,15 +16,33 @@ namespace xwim { class Archive { private: std::filesystem::path path; + archive* xwim_archive; public: explicit Archive(std::string path); - explicit Archive(std::filesystem::path&& path); + ~Archive(); ArchiveSpec check(); void extract(ExtractSpec normalize_spec); }; +class ArchiveException : public std::exception { + private: + std::string _what; + + public: + ArchiveException(std::string what, archive* archive) { + if (archive_error_string(archive)) { + _what = fmt::format("{}: {}", what, archive_error_string(archive)); + } else { + _what = fmt::format("{}", what); + } + } + + virtual const char* what() const noexcept + { return this->_what.c_str(); } +}; + } // namespace xwim #endif diff --git a/src/main.cpp b/src/main.cpp index 2fb4a3e..14671f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,7 @@ +#include +#include +namespace logger = spdlog; + #include #include #include @@ -6,15 +10,17 @@ #include "spec.hpp" int main(int argc, char** argv) { - std::string filename {argv[1]}; - xwim::Archive archive {filename}; + logger::set_level(logger::level::trace); + logger::flush_on(logger::level::trace); - xwim::ArchiveSpec asp = archive.check(); + try { + std::string filename{argv[1]}; + xwim::Archive archive{filename}; + xwim::ArchiveSpec spec = archive.check(); - std::cout << "Has subarch: " << asp.has_subarchive - << "Is root: " << asp.is_root_dir - << "Is root dir filename: " << asp.is_root_dir_filename - << std::endl; + logger::info("{}", spec); - archive.extract(xwim::ExtractSpec {.make_dir=true, .dirname="Test"}); + } catch (xwim::ArchiveException& ae) { + logger::error("{}", ae.what()); + } } diff --git a/src/spec.hpp b/src/spec.hpp index 1fce6d1..dfa77e7 100644 --- a/src/spec.hpp +++ b/src/spec.hpp @@ -1,18 +1,43 @@ #ifndef SPEC_H #define SPEC_H -#include -namespace xwim { - struct ArchiveSpec { - bool is_root_dir; - bool is_root_dir_filename; - bool has_subarchive; - }; +#include +#include - struct ExtractSpec { - bool make_dir; - std::filesystem::path dirname; - }; -} +#include +#include + +namespace xwim { + +struct ArchiveSpec { + bool has_single_root = false; + bool is_root_filename = false; + bool is_root_dir = false; + bool has_subarchive = false; +}; + +struct ExtractSpec { + bool make_dir; + std::filesystem::path dirname; +}; + +} // namespace xwim + +template <> +struct fmt::formatter { + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + + template + auto format(const xwim::ArchiveSpec& spec, FormatContext& ctx) { + return format_to(ctx.out(),"Archive[" + " .has_single_root={}," + " .is_root_filename={}" + " .is_root_dir={}" + " .has_subarchive={}" + " ]", + spec.has_single_root, spec.is_root_filename, + spec.is_root_dir, spec.has_subarchive); + } +}; #endif