This commit is contained in:
parent
f5cddbc0f3
commit
189be660f5
12 changed files with 613 additions and 135 deletions
|
@ -8,4 +8,4 @@ add_global_arguments('-DVERSION='+meson.version(), language: 'cpp')
|
|||
|
||||
subdir('src')
|
||||
subdir('doc')
|
||||
subdir('test')
|
||||
# subdir('test')
|
||||
|
|
87
src/Archiver.cpp
Normal file
87
src/Archiver.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
#include "Archiver.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "Common.hpp"
|
||||
|
||||
namespace xwim {
|
||||
using namespace std;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// Extract longest known extension from path
|
||||
fs::path archive_extension(const fs::path& path) {
|
||||
// TODO: creates lots of paths, refactor
|
||||
fs::path ext;
|
||||
fs::path tmp_path = path;
|
||||
while (tmp_path.has_extension()) {
|
||||
fs::path tmp_ext = tmp_path.extension() += ext;
|
||||
auto search = extensions_format.find(tmp_ext);
|
||||
|
||||
// (Combined) extension not known, return last known extension
|
||||
if (search == extensions_format.end()) return ext;
|
||||
|
||||
// Continue extending extension
|
||||
ext = tmp_ext;
|
||||
tmp_path = tmp_path.stem();
|
||||
}
|
||||
|
||||
return ext;
|
||||
}
|
||||
|
||||
// Strip longest known extension from path
|
||||
fs::path strip_archive_extension(const fs::path& path) {
|
||||
// TODO: creates lots of paths, refactor
|
||||
fs::path ext;
|
||||
fs::path tmp_path = path;
|
||||
while (tmp_path.has_extension()) {
|
||||
fs::path tmp_ext = tmp_path.extension() += ext;
|
||||
auto search = extensions_format.find(tmp_ext);
|
||||
|
||||
// (Combined) extension not known, return stripped path
|
||||
if (search == extensions_format.end()) return tmp_path;
|
||||
|
||||
// Continue stripping path
|
||||
ext = tmp_ext;
|
||||
tmp_path = tmp_path.stem();
|
||||
}
|
||||
|
||||
return tmp_path;
|
||||
}
|
||||
|
||||
bool can_extract(const fs::path& path) {
|
||||
fs::path ext = archive_extension(path);
|
||||
if (format_extensions.find(ext.string()) != format_extensions.end()) {
|
||||
spdlog::debug("Found {} in known formats", ext);
|
||||
return true;
|
||||
}
|
||||
|
||||
spdlog::debug("Could not find {} in known formats", ext);
|
||||
return false;
|
||||
}
|
||||
|
||||
Format parse_format(const fs::path& path) {
|
||||
fs::path ext = archive_extension(path);
|
||||
auto search = extensions_format.find(ext);
|
||||
if (search == extensions_format.end()) {
|
||||
throw XwimError{"No known archiver for {}", path};
|
||||
}
|
||||
|
||||
return search->second;
|
||||
}
|
||||
|
||||
unique_ptr<Archiver> make_archiver(const string& archive_name) {
|
||||
switch (parse_format(archive_name)) {
|
||||
case Format::TAR_GZ:
|
||||
case Format::ZIP:
|
||||
return make_unique<LibArchiver>();
|
||||
default:
|
||||
throw XwimError{
|
||||
"Cannot construct archiver for {}. `extension_format` surjection "
|
||||
"invariant violated?",
|
||||
archive_name};
|
||||
};
|
||||
}
|
||||
} // namespace xwim
|
48
src/Archiver.hpp
Normal file
48
src/Archiver.hpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include "Common.hpp"
|
||||
|
||||
namespace xwim {
|
||||
|
||||
// Invariant:
|
||||
// `extensions_format` defines a surjection from `format_extensions`
|
||||
// to `Formats`
|
||||
const std::set<std::string> format_extensions{".tar.gz", ".zip"};
|
||||
enum class Format { TAR_GZ, ZIP };
|
||||
const std::map<std::string, Format> extensions_format{
|
||||
{".tar.gz", Format::TAR_GZ}, {".zip", Format::ZIP}};
|
||||
|
||||
class Archiver {
|
||||
public:
|
||||
virtual void compress(std::set<std::filesystem::path> ins,
|
||||
std::filesystem::path archive_out) = 0;
|
||||
|
||||
virtual void extract(std::filesystem::path archive_in,
|
||||
std::filesystem::path out) = 0;
|
||||
|
||||
virtual ~Archiver() = default;
|
||||
};
|
||||
|
||||
class LibArchiver : public Archiver {
|
||||
public:
|
||||
void compress(std::set<std::filesystem::path> ins,
|
||||
std::filesystem::path archive_out);
|
||||
|
||||
void extract(std::filesystem::path archive_in, std::filesystem::path out);
|
||||
};
|
||||
|
||||
std::filesystem::path archive_extension(const std::filesystem::path& path);
|
||||
std::filesystem::path strip_archive_extension(const std::filesystem::path& path);
|
||||
Format parse_format(const std::filesystem::path& path);
|
||||
bool can_extract(const std::filesystem::path& path);
|
||||
|
||||
std::unique_ptr<Archiver> make_archiver(const std::string& archive_name);
|
||||
|
||||
} // namespace xwim
|
30
src/Common.hpp
Normal file
30
src/Common.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <random>
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<std::filesystem::path> {
|
||||
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const std::filesystem::path& path, FormatContext& ctx) {
|
||||
return format_to(ctx.out(), path.string());
|
||||
}
|
||||
};
|
||||
|
||||
class XwimError : public std::runtime_error {
|
||||
public:
|
||||
template <typename... Args>
|
||||
XwimError(const std::string& fmt, const Args... args)
|
||||
: std::runtime_error(fmt::format(fmt, args...)){}
|
||||
};
|
||||
|
||||
inline int rand_int(int from, int to) {
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> distrib(from, to);
|
||||
return distrib(gen);
|
||||
}
|
75
src/Log.hpp
Normal file
75
src/Log.hpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <cstdlib>
|
||||
#ifdef NDEBUG
|
||||
#define XWIM_LOGLEVEL SPDLOG_LEVEL_ERROR
|
||||
#else
|
||||
#define XWIM_LOGLEVEL SPDLOG_LEVEL_DEBUG
|
||||
#endif
|
||||
|
||||
namespace xwim::log {
|
||||
|
||||
/**
|
||||
* Get log level from XWIM_LOGLEVEL environment variable.
|
||||
* For valid values see SPDLOG_LEVEL_NAMES in spdlog/common.h
|
||||
*
|
||||
* @returns spdlog::level::level_enum::off if no valid XWIM_LOGLEVEL defined
|
||||
*/
|
||||
spdlog::level::level_enum _init_from_env() {
|
||||
char* env_lvl = std::getenv("XWIM_LOGLEVEL");
|
||||
if (!env_lvl) {
|
||||
return spdlog::level::level_enum::off;
|
||||
}
|
||||
|
||||
spdlog::level::level_enum lvl = spdlog::level::from_str(env_lvl);
|
||||
|
||||
//`::from_str` returns `off` if no match found
|
||||
if (spdlog::level::level_enum::off == lvl) {
|
||||
spdlog::debug("No environment definition for log level"); // uses default
|
||||
// logger/level
|
||||
}
|
||||
|
||||
return lvl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get log level from compile time definition.
|
||||
*
|
||||
* @return spdlog::level::level_enum::error for release builds (-DNDEBUG)
|
||||
* spdlog::level::level_enum::debug for debug builds
|
||||
*/
|
||||
spdlog::level::level_enum _init_from_compile() {
|
||||
return static_cast<spdlog::level::level_enum>(XWIM_LOGLEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the log level from various sources at runtime.
|
||||
*
|
||||
* The log level is determined from sources in the following order (first
|
||||
* wins):
|
||||
* 1. The `level` argument
|
||||
* 2. The XWIM_LOGLEVEL environment variable
|
||||
* 3. The default for the build type (-DNDEBUG)
|
||||
* -> ERROR for release builds
|
||||
* -> DEBUG for debug builds
|
||||
*
|
||||
* The determined level is then set for the default logger via
|
||||
* `spdlog::set_level`.
|
||||
*/
|
||||
void init(spdlog::level::level_enum level = spdlog::level::level_enum::off) {
|
||||
if (spdlog::level::level_enum::off != level) {
|
||||
spdlog::set_level(level);
|
||||
return;
|
||||
}
|
||||
|
||||
level = _init_from_env();
|
||||
if (spdlog::level::level_enum::off != level) {
|
||||
spdlog::set_level(level);
|
||||
return;
|
||||
}
|
||||
|
||||
spdlog::set_level(_init_from_compile());
|
||||
}
|
||||
|
||||
} // namespace xwim::log
|
135
src/Xwim.cpp
Normal file
135
src/Xwim.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
#include "Xwim.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <ios>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
#include "Archiver.hpp"
|
||||
#include "Common.hpp"
|
||||
|
||||
namespace xwim {
|
||||
using namespace std;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
#if defined(unix) || defined(__unix__) || defined(__unix)
|
||||
std::string default_extension = ".tar.gz";
|
||||
#elif defined(_win32) || defined(__win32__) || defined(__windows__)
|
||||
std::string default_extension = ".zip";
|
||||
#else
|
||||
std::string default_extension = ".zip";
|
||||
#endif
|
||||
|
||||
Xwim::Xwim() : action{Action::UNKNOWN} {}
|
||||
|
||||
void Xwim::try_infer() {
|
||||
infer_action();
|
||||
infer_output();
|
||||
|
||||
if (action == Action::COMPRESS) {
|
||||
archiver = make_archiver(out.string());
|
||||
} else if (action == Action::EXTRACT) {
|
||||
// we can only handle one archive for extraction at a time.
|
||||
// Checked in `infer_extraction_output`
|
||||
archiver = make_archiver(ins.begin()->string());
|
||||
}
|
||||
}
|
||||
void Xwim::dwim() {
|
||||
switch (action) {
|
||||
case Action::COMPRESS:
|
||||
this->archiver->compress(ins, out);
|
||||
break;
|
||||
case Action::EXTRACT:
|
||||
this->archiver->extract(*ins.begin(), out);
|
||||
break;
|
||||
default:
|
||||
spdlog::error("Unknown action");
|
||||
}
|
||||
}
|
||||
|
||||
void Xwim::infer_action() {
|
||||
if (action != Action::UNKNOWN) return;
|
||||
|
||||
if (ins.size() == 1 && can_extract(*ins.begin())) {
|
||||
action = Action::EXTRACT;
|
||||
} else {
|
||||
action = Action::COMPRESS;
|
||||
}
|
||||
spdlog::debug("Inferred action: {}", action);
|
||||
}
|
||||
|
||||
void Xwim::infer_output() {
|
||||
if (!out.empty()) return;
|
||||
|
||||
switch (action) {
|
||||
case Action::COMPRESS:
|
||||
infer_compression_output();
|
||||
break;
|
||||
case Action::EXTRACT:
|
||||
infer_extraction_output();
|
||||
break;
|
||||
default:
|
||||
throw XwimError{"Cannot infer output, action is unknown"};
|
||||
}
|
||||
|
||||
spdlog::debug("Inferred out: {}", out.string());
|
||||
}
|
||||
|
||||
void Xwim::infer_compression_output() {
|
||||
if (ins.size() == 1) {
|
||||
// archive name is just the name of the input with default archive
|
||||
// extension
|
||||
|
||||
fs::path p = *ins.begin();
|
||||
while (p.has_extension()) p = p.stem();
|
||||
p += default_extension;
|
||||
out = p;
|
||||
} else {
|
||||
// We cannot guess the name of the output archive
|
||||
|
||||
// TODO use readline/lineoise/editline for path completion
|
||||
cout << "Archive name: ";
|
||||
cin >> out;
|
||||
out = fs::path(out);
|
||||
}
|
||||
}
|
||||
void Xwim::infer_extraction_output() {
|
||||
if (ins.size() > 1) {
|
||||
throw XwimError{"Cannot extract more than one archive at a time"};
|
||||
}
|
||||
|
||||
// create a temporary path for extraction
|
||||
fs::path archive_stem = xwim::strip_archive_extension(*ins.begin());
|
||||
|
||||
// note: we use here what is considered an `extensions` by `fs::path` so that
|
||||
// we can strip it again easily later on
|
||||
archive_stem += ".";
|
||||
archive_stem += to_string(rand_int(999, 99999));
|
||||
archive_stem += ".tmp";
|
||||
this->out = archive_stem;
|
||||
}
|
||||
|
||||
void Xwim::setCompress() {
|
||||
this->action = Action::COMPRESS;
|
||||
spdlog::debug("Set action to {}", this->action);
|
||||
}
|
||||
void Xwim::setExtract() {
|
||||
this->action = Action::EXTRACT;
|
||||
spdlog::debug("Set action to {}", this->action);
|
||||
}
|
||||
void Xwim::setOut(fs::path path) {
|
||||
this->out = path;
|
||||
spdlog::debug("Set out to {}", this->out);
|
||||
}
|
||||
void Xwim::setIns(vector<fs::path> ins) {
|
||||
this->ins.insert(ins.begin(), ins.end());
|
||||
if (this->ins.size() != ins.size()) {
|
||||
spdlog::warn("Duplicate input files found. Removed {} duplicate(s).",
|
||||
(ins.size() - this->ins.size()));
|
||||
}
|
||||
}
|
||||
}
|
62
src/Xwim.hpp
Normal file
62
src/Xwim.hpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Common.hpp"
|
||||
#include "Archiver.hpp"
|
||||
|
||||
namespace xwim {
|
||||
using namespace std;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
enum class Action { UNKNOWN, EXTRACT, COMPRESS };
|
||||
|
||||
class Xwim {
|
||||
private:
|
||||
Action action;
|
||||
fs::path out;
|
||||
set<fs::path> ins;
|
||||
unique_ptr<Archiver> archiver;
|
||||
|
||||
void infer_action();
|
||||
void infer_output();
|
||||
void infer_compression_output();
|
||||
void infer_extraction_output();
|
||||
|
||||
public:
|
||||
Xwim();
|
||||
|
||||
void try_infer();
|
||||
void dwim();
|
||||
|
||||
void setCompress();
|
||||
void setExtract();
|
||||
void setOut(fs::path);
|
||||
void setIns(vector<fs::path> ins);
|
||||
};
|
||||
|
||||
} // namespace xwim
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<xwim::Action> {
|
||||
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const xwim::Action& action, FormatContext& ctx) {
|
||||
switch (action) {
|
||||
case xwim::Action::UNKNOWN:
|
||||
return format_to(ctx.out(), "UNKNOWN");
|
||||
case xwim::Action::EXTRACT:
|
||||
return format_to(ctx.out(), "EXTRACT");
|
||||
case xwim::Action::COMPRESS:
|
||||
return format_to(ctx.out(), "COMPRESS");
|
||||
};
|
||||
return format_to(ctx.out(), "");
|
||||
}
|
||||
};
|
103
src/archiver/LibArchiver.cpp
Normal file
103
src/archiver/LibArchiver.cpp
Normal file
|
@ -0,0 +1,103 @@
|
|||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
#include <fmt/core.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "Archiver.hpp"
|
||||
#include "Common.hpp"
|
||||
|
||||
namespace xwim {
|
||||
using namespace std;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
static int copy_data(shared_ptr<archive> reader, shared_ptr<archive> writer);
|
||||
|
||||
void LibArchiver::compress(set<fs::path> ins, fs::path archive_out) { return; }
|
||||
|
||||
void LibArchiver::extract(fs::path archive_in, fs::path out) {
|
||||
spdlog::debug("Extracting archive {} to {}", archive_in, out);
|
||||
int r; // libarchive error handling
|
||||
|
||||
// cannot use unique_ptr here since unique_ptr requires a
|
||||
// complete type. `archive` is forward declared only.
|
||||
shared_ptr<archive> reader;
|
||||
reader = shared_ptr<archive>(archive_read_new(), archive_read_free);
|
||||
archive_read_support_filter_all(reader.get());
|
||||
archive_read_support_format_all(reader.get());
|
||||
r = archive_read_open_filename(reader.get(), archive_in.c_str(), 10240);
|
||||
if (r != ARCHIVE_OK) {
|
||||
throw XwimError{"Failed opening archive {}. {}", archive_in,
|
||||
archive_error_string(reader.get())};
|
||||
}
|
||||
|
||||
shared_ptr<archive> writer;
|
||||
writer = shared_ptr<archive>(archive_write_disk_new(), archive_write_free);
|
||||
archive_write_disk_set_standard_lookup(writer.get());
|
||||
|
||||
fs::create_directories(out);
|
||||
fs::path cur_path = fs::current_path();
|
||||
fs::current_path(out);
|
||||
|
||||
archive_entry* entry;
|
||||
for (;;) {
|
||||
r = archive_read_next_header(reader.get(), &entry);
|
||||
if (r == ARCHIVE_EOF) break;
|
||||
|
||||
if (r != ARCHIVE_OK) {
|
||||
throw XwimError{"Failed extracting archive entry. {}", archive_error_string(reader.get())};
|
||||
}
|
||||
|
||||
r = archive_write_header(writer.get(), entry);
|
||||
if (r != ARCHIVE_OK) {
|
||||
throw XwimError{"Failed writing archive entry header. {}", archive_error_string(writer.get())};
|
||||
}
|
||||
|
||||
if (archive_entry_size(entry) > 0) {
|
||||
r = copy_data(reader, writer);
|
||||
if (r != ARCHIVE_OK) {
|
||||
throw XwimError{"Failed writing archive entry data. {}",
|
||||
archive_error_string(writer.get())};
|
||||
}
|
||||
}
|
||||
|
||||
r = archive_write_finish_entry(writer.get());
|
||||
if (r != ARCHIVE_OK) {
|
||||
throw XwimError{"Failed finishing archive entry data. {}",
|
||||
archive_error_string(writer.get())};
|
||||
}
|
||||
}
|
||||
|
||||
if (r != ARCHIVE_OK && r != ARCHIVE_EOF) {
|
||||
throw XwimError{"Failed extracting archive {}. {}", archive_in,
|
||||
archive_error_string(reader.get())};
|
||||
}
|
||||
|
||||
fs::current_path(cur_path);
|
||||
}
|
||||
|
||||
static int copy_data(shared_ptr<archive> reader, shared_ptr<archive> writer) {
|
||||
int r;
|
||||
const void *buff;
|
||||
size_t size;
|
||||
int64_t offset;
|
||||
|
||||
for (;;) {
|
||||
r = archive_read_data_block(reader.get(), &buff, &size, &offset);
|
||||
if (r == ARCHIVE_EOF) {
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
if (r != ARCHIVE_OK) {
|
||||
return (r);
|
||||
}
|
||||
r = archive_write_data_block(writer.get(), buff, size, offset);
|
||||
if (r != ARCHIVE_OK) {
|
||||
return (r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xwim
|
|
@ -1,84 +0,0 @@
|
|||
#include "argparse.hpp"
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <tclap/ArgException.h>
|
||||
#include <tclap/CmdLine.h>
|
||||
#include <tclap/SwitchArg.h>
|
||||
#include <tclap/UnlabeledMultiArg.h>
|
||||
#include <tclap/ValueArg.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "archivinfo.hpp"
|
||||
#include "fileformats.hpp"
|
||||
|
||||
using namespace TCLAP;
|
||||
using namespace xwim;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
template <>
|
||||
struct TCLAP::ArgTraits<fs::path> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
ArgParse::ArgParse()
|
||||
: cmd{"xwim - Do What I Mean Extractor", ' ', "0.3.0"},
|
||||
arg_compress{"c", "compress", "Compress <files>", false},
|
||||
arg_extract{"x", "extract", "Extract <file>", false},
|
||||
arg_outfile{"o", "out", "Out <file-or-path>",
|
||||
false, fs::path{}, "A path on the filesystem"},
|
||||
arg_infiles{"Files", "Archive to extract or files to compress", true,
|
||||
"A path on the filesystem"} {
|
||||
cmd.xorAdd(arg_compress, arg_extract);
|
||||
cmd.add(arg_outfile);
|
||||
cmd.add(arg_infiles);
|
||||
};
|
||||
|
||||
void ArgParse::parse(int argc, char** argv) {
|
||||
try {
|
||||
cmd.parse(argc, argv);
|
||||
} catch (ArgException& e) {
|
||||
throw new xwim::ArgParseException(e.error());
|
||||
}
|
||||
|
||||
this->extract = parse_extract();
|
||||
this->outfile = arg_outfile.getValue();
|
||||
this->infiles = arg_infiles.getValue();
|
||||
}
|
||||
|
||||
bool ArgParse::parse_extract() {
|
||||
// extract/compress explicitly given; xor ensured in `cmd`
|
||||
if (this->arg_compress.getValue()) {
|
||||
return false;
|
||||
} else if (this->arg_extract.getValue()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not explicitly given, check if we can guess from input
|
||||
|
||||
// An outfile is given
|
||||
if (this->arg_outfile.isSet()) {
|
||||
// outfile looks like an archive
|
||||
if (xwim::archivinfo::has_known_extension(this->arg_outfile.getValue())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// outfile is not a known archive, assume it meant as folder for extraction
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// one infile which is an archive, so intention is probably to extract this
|
||||
if (this->arg_infiles.getValue().size() == 1 &&
|
||||
xwim::archivinfo::is_archive(this->arg_infiles.getValue().at(0))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// all other cases, in particular multiple infiles, assume we want to compress
|
||||
return false;
|
||||
}
|
||||
bool ArgParse::compressp() { return !this->extract; }
|
||||
bool ArgParse::extractp() { return this->extract; }
|
|
@ -1,45 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <tclap/CmdLine.h>
|
||||
#include <tclap/SwitchArg.h>
|
||||
#include <tclap/UnlabeledMultiArg.h>
|
||||
#include <tclap/ValueArg.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
|
||||
namespace xwim {
|
||||
class ArgParse {
|
||||
private:
|
||||
bool extract;
|
||||
std::filesystem::path outfile;
|
||||
std::vector<std::filesystem::path> infiles;
|
||||
|
||||
TCLAP::CmdLine cmd;
|
||||
TCLAP::SwitchArg arg_compress;
|
||||
TCLAP::SwitchArg arg_extract;
|
||||
TCLAP::ValueArg<std::filesystem::path> arg_outfile;
|
||||
TCLAP::UnlabeledMultiArg<std::filesystem::path> arg_infiles;
|
||||
|
||||
protected:
|
||||
bool parse_extract();
|
||||
|
||||
public:
|
||||
ArgParse();
|
||||
void parse(int argc, char** argv);
|
||||
bool compressp();
|
||||
bool extractp();
|
||||
};
|
||||
|
||||
class ArgParseException : public std::exception {
|
||||
private:
|
||||
std::string _what;
|
||||
|
||||
public:
|
||||
ArgParseException(std::string what) : _what{what} {};
|
||||
template<typename... Args>
|
||||
ArgParseException(std::string fmt_string, Args&&... args) : _what{fmt::format(fmt_string, args...)} {};
|
||||
virtual const char* what() const noexcept { return this->_what.c_str(); }
|
||||
};
|
||||
} // namespace xwim
|
69
src/main.cpp
69
src/main.cpp
|
@ -1,6 +1,75 @@
|
|||
#include <spdlog/common.h>
|
||||
#include <spdlog/logger.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <tclap/ArgException.h>
|
||||
#include <tclap/CmdLine.h>
|
||||
#include <tclap/StdOutput.h>
|
||||
#include <tclap/SwitchArg.h>
|
||||
#include <tclap/UnlabeledMultiArg.h>
|
||||
#include <tclap/ValueArg.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
|
||||
#include "Common.hpp"
|
||||
#include "Log.hpp"
|
||||
#include "Xwim.hpp"
|
||||
|
||||
using namespace xwim;
|
||||
using namespace std;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
template <>
|
||||
struct TCLAP::ArgTraits<std::filesystem::path> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
log::init();
|
||||
|
||||
TCLAP::CmdLine cmd{"xwim - Do What I Mean Extractor", ' ', "0.3.0"};
|
||||
|
||||
TCLAP::SwitchArg arg_compress{"c", "compress", "Compress <files>", cmd,
|
||||
false};
|
||||
TCLAP::SwitchArg arg_extract{"x", "extract", "Extract <file>", cmd, false};
|
||||
|
||||
TCLAP::ValueArg<fs::path> arg_outfile{
|
||||
"o", "out", "Out <file-or-path>",
|
||||
false, fs::path{}, "A path on the filesystem",
|
||||
cmd};
|
||||
TCLAP::UnlabeledMultiArg<fs::path> arg_infiles{
|
||||
"Files", "Archive to extract or files to compress", true,
|
||||
"A path on the filesystem", cmd};
|
||||
|
||||
Xwim xwim;
|
||||
|
||||
cmd.parse(argc, argv);
|
||||
|
||||
if (arg_extract.isSet() && arg_compress.isSet()) {
|
||||
// This is a bit ugly but `none-or-xor` only available in
|
||||
// tclap-1.4 which is not well supported in current
|
||||
// distributions
|
||||
auto out = TCLAP::StdOutput{};
|
||||
TCLAP::ArgException e{
|
||||
"Cannot compress `-c` and extract `-x` simultaneously"};
|
||||
try {
|
||||
out.failure(cmd, e);
|
||||
} catch (TCLAP::ExitException& e) {
|
||||
exit(e.getExitStatus());
|
||||
}
|
||||
}
|
||||
|
||||
// `none-or-xor` ensured already
|
||||
if (arg_extract.isSet()) xwim.setExtract();
|
||||
if (arg_compress.isSet()) xwim.setCompress();
|
||||
|
||||
if (arg_outfile.isSet()) xwim.setOut(arg_outfile.getValue());
|
||||
if (arg_infiles.isSet()) xwim.setIns(arg_infiles.getValue());
|
||||
|
||||
try {
|
||||
xwim.try_infer();
|
||||
xwim.dwim();
|
||||
} catch (XwimError& e) {
|
||||
spdlog::error(e.what());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
xwim_src = ['main.cpp',
|
||||
'archive.cpp',
|
||||
'archive_sys.cpp',
|
||||
'util/argparse.cpp']
|
||||
xwim_src = ['main.cpp', 'Xwim.cpp', 'Archiver.cpp']
|
||||
xwim_archiver = ['archiver/LibArchiver.cpp']
|
||||
|
||||
xwim_libs = [dependency('libarchive', required: true),
|
||||
dependency('fmt', required: true),
|
||||
dependency('spdlog', required: true),
|
||||
dependency('tclap', required: true)]
|
||||
|
||||
executable('xwim', xwim_src, dependencies: xwim_libs)
|
||||
executable('xwim', xwim_src+xwim_archiver, dependencies: xwim_libs)
|
||||
|
|
Loading…
Reference in a new issue