diff --git a/.clang-format b/.clang-format index 34fe704..f2dd0de 100644 --- a/.clang-format +++ b/.clang-format @@ -1 +1,168 @@ -BasedOnStyle: Chromium \ No newline at end of file +--- +Language: Cpp +# BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + - Regex: '.*' + Priority: 3 + SortPriority: 0 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: true +IndentGotoLabels: true +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: Auto +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +... + diff --git a/README.md b/README.md index 0e9f116..0dae38a 100644 --- a/README.md +++ b/README.md @@ -126,12 +126,12 @@ Per default xwim chooses an appropriate log level according to your build type - off # Contributing -While xwim is still in incubator phase (i.e. before version 1.0) it's main +While xwim is still in incubator phase (i.e. before version 1.0) its main repository is hosted on https://git.friedl.net/incubator/xwim with a mirror on https://github.com/arminfriedl/xwim. With the first stable release it will most -likely move to GitHub as it's main repository. +likely move to GitHub as its main repository. -If you want to contribute, you can either issue a pull request on it's Github +If you want to contribute, you can either issue a pull request on its Github mirror (will be cherry picked into the main repository) or send patches to dev[at]friedl[dot]net. diff --git a/meson.build b/meson.build index d382a12..a315056 100644 --- a/meson.build +++ b/meson.build @@ -1,8 +1,9 @@ project('xwim', 'cpp', - version: '0.2', + version: '0.3', default_options: ['cpp_std=c++17', 'warning_level=3', - 'b_coverage=true']) + 'b_coverage=true', + 'b_ndebug=if-release']) subdir('src') subdir('doc') diff --git a/src/archive_sys.cpp b/src/archive_sys.cpp index 7c2fdc0..e46b1bc 100644 --- a/src/archive_sys.cpp +++ b/src/archive_sys.cpp @@ -1,20 +1,22 @@ -#include #include +#include #include + #include "archive.hpp" #include "fileformats.hpp" #include "spec.hpp" namespace logger = spdlog; -#include "archive_sys.hpp" - #include + #include #include -bool xwim::ArchiveEntryView::is_empty() { - return (this->ae == nullptr); -} +#include "archive_sys.hpp" + +namespace fs = std::filesystem; + +bool xwim::ArchiveEntryView::is_empty() { return (this->ae == nullptr); } std::string xwim::ArchiveEntryView::path_name() { if (!this->ae) throw ArchiveSysException{"Access to invalid archive entry"}; @@ -22,9 +24,9 @@ std::string xwim::ArchiveEntryView::path_name() { return archive_entry_pathname(this->ae); } -std::filesystem::path xwim::ArchiveEntryView::path() { +fs::path xwim::ArchiveEntryView::path() { if (!this->ae) throw ArchiveSysException{"Access to invalid archive entry"}; - return std::filesystem::path{this->path_name()}; + return fs::path{this->path_name()}; } mode_t xwim::ArchiveEntryView::file_type() { @@ -36,7 +38,7 @@ bool xwim::ArchiveEntryView::is_directory() { return S_ISDIR(this->file_type()); } -xwim::ArchiveReaderSys::ArchiveReaderSys(std::filesystem::path& path) { +xwim::ArchiveReaderSys::ArchiveReaderSys(fs::path &path) { int r; // libarchive error handling logger::trace("Setting up archive reader"); @@ -63,8 +65,12 @@ bool xwim::ArchiveReaderSys::advance() { logger::trace("Advancing reader to next archive entry"); r = archive_read_next_header(this->ar, &this->ae); - if (r == ARCHIVE_EOF) { this->ae = nullptr; return false; } - if (r != ARCHIVE_OK) throw(ArchiveSysException{"Could not list archive", this->ar}); + if (r == ARCHIVE_EOF) { + this->ae = nullptr; + return false; + } + if (r != ARCHIVE_OK) + throw(ArchiveSysException{"Could not list archive", this->ar}); logger::trace("Got entry {}", archive_entry_pathname(ae)); return true; @@ -74,16 +80,17 @@ const xwim::ArchiveEntryView xwim::ArchiveReaderSys::cur() { return ArchiveEntryView{this->ae}; } -xwim::ArchiveExtractorSys::ArchiveExtractorSys(std::filesystem::path& root) { +xwim::ArchiveExtractorSys::ArchiveExtractorSys(fs::path &root) { logger::trace("Constructing ArchiveExtractorSys with path {}", root.string()); - std::filesystem::create_directories(root); - std::filesystem::current_path(root); + fs::create_directories(root); + fs::current_path(root); this->writer = archive_write_disk_new(); archive_write_disk_set_standard_lookup(this->writer); - logger::trace("Constructed ArchiveExtractorSys at {:p}", (void*) this->writer); + logger::trace("Constructed ArchiveExtractorSys at {:p}", + (void *)this->writer); } xwim::ArchiveExtractorSys::ArchiveExtractorSys() { @@ -91,19 +98,20 @@ xwim::ArchiveExtractorSys::ArchiveExtractorSys() { this->writer = archive_write_disk_new(); archive_write_disk_set_standard_lookup(this->writer); - logger::trace("Constructed ArchiveExtractorSys at {:p}", (void*) this->writer); + logger::trace("Constructed ArchiveExtractorSys at {:p}", + (void *)this->writer); } -void xwim::ArchiveExtractorSys::extract_all(xwim::ArchiveReaderSys& reader) { - while(reader.advance()) { +void xwim::ArchiveExtractorSys::extract_all(xwim::ArchiveReaderSys &reader) { + while (reader.advance()) { this->extract_entry(reader); } } // forward declared -static int copy_data(struct archive* ar, struct archive* aw); +static int copy_data(struct archive *ar, struct archive *aw); -void xwim::ArchiveExtractorSys::extract_entry(xwim::ArchiveReaderSys& reader) { +void xwim::ArchiveExtractorSys::extract_entry(xwim::ArchiveReaderSys &reader) { int r; r = archive_write_header(this->writer, reader.ae); if (r != ARCHIVE_OK) { @@ -116,90 +124,73 @@ void xwim::ArchiveExtractorSys::extract_entry(xwim::ArchiveReaderSys& reader) { } } -xwim::ArchiveExtractorSys::~ArchiveExtractorSys(){ - logger::trace("Destructing ArchiveExtractorSys at {:p}", (void*) this->writer); - if(this->writer) { +xwim::ArchiveExtractorSys::~ArchiveExtractorSys() { + logger::trace("Destructing ArchiveExtractorSys at {:p}", + (void *)this->writer); + if (this->writer) { archive_write_close(this->writer); archive_write_free(this->writer); } } -xwim::ArchiveCompressorSys::ArchiveCompressorSys(std::filesystem::path& root, xwim::CompressSpec compress_spec): root{root}, compress_spec{compress_spec} { +xwim::ArchiveCompressorSys::ArchiveCompressorSys( + fs::path &root, xwim::CompressSpec compress_spec) + : root{root}, compress_spec{compress_spec} { this->new_archive = archive_write_new(); - for(xwim::archive_filter filter: this->compress_spec.filters) { + for (xwim::archive_filter filter : this->compress_spec.filters) { archive_write_add_filter(this->new_archive, filter); } archive_write_set_format(this->new_archive, this->compress_spec.format); } +// forward declared +static fs::path archive_path_norm(const fs::path &root, + const xwim::CompressSpec &compress_spec); + void xwim::ArchiveCompressorSys::compress() { - std::filesystem::path archive_path{this->root}; - if(!std::filesystem::exists(archive_path)) { - logger::error("Non-existing path: {}", archive_path.string()); - throw ArchiveSysException{"Path does not exists"}; - } + fs::path archive_path = archive_path_norm(this->root, this->compress_spec); - std::filesystem::file_status file_status = std::filesystem::status(archive_path); + logger::debug("Writing archive at: {}", archive_path.filename().c_str()); - if(file_status.type() != std::filesystem::file_type::directory - && file_status.type() != std::filesystem::file_type::regular) { - logger::error("Unknown path type: {}", file_status.type()); - throw ArchiveSysException{"Unknown path type"}; - } + archive_write_open_filename(this->new_archive, + archive_path.filename().c_str()); - if ((file_status.permissions() & std::filesystem::perms::owner_read) == - std::filesystem::perms::none && - (file_status.permissions() & std::filesystem::perms::group_read) == - std::filesystem::perms::none && - (file_status.permissions() & std::filesystem::perms::others_read) == - std::filesystem::perms::none) { - logger::error("Cannot read path with permissions: {}", - file_status.permissions()); - throw ArchiveSysException{"Unreadable path"}; - } - - if(file_status.type() == std::filesystem::file_type::regular) { - while(archive_path.has_extension()) { - archive_path.replace_extension(); - } - } - - archive_path.concat(this->compress_spec.extension); - logger::debug("Writing archive at: {}", std::filesystem::absolute(archive_path).c_str()); - archive_write_open_filename(this->new_archive, std::filesystem::absolute(archive_path).c_str()); - - archive* disk = archive_read_disk_new(); + archive *disk = archive_read_disk_new(); archive_read_disk_set_standard_lookup(disk); int r; - r = archive_read_disk_open(disk, std::filesystem::relative(this->root).c_str()); - if(r != ARCHIVE_OK) { + r = archive_read_disk_open(disk, fs::relative(this->root).c_str()); + if (r != ARCHIVE_OK) { throw ArchiveSysException("Could not open path for archiving", disk); } - archive_entry* entry; + archive_entry *entry; char buff[16384]; for (;;) { entry = archive_entry_new(); r = archive_read_next_header2(disk, entry); - if (r == ARCHIVE_EOF) - break; + if (r == ARCHIVE_EOF) break; if (r != ARCHIVE_OK) { throw ArchiveSysException("Could not read next archive entry", disk); } archive_read_disk_descend(disk); + + const char* ae_path = archive_entry_pathname(entry); + fs::path ae_rel_path = fs::relative(fs::path(ae_path), this->root.parent_path()); + archive_entry_set_pathname(entry, ae_rel_path.c_str()); logger::trace("Processing entry {}", archive_entry_pathname(entry)); r = archive_write_header(this->new_archive, entry); if (r < ARCHIVE_OK) { throw ArchiveSysException("Could not write header for archive entry", - this->new_archive); + this->new_archive); } + if (r > ARCHIVE_FAILED) { int fd = open(archive_entry_sourcepath(entry), O_RDONLY); ssize_t len = read(fd, buff, sizeof(buff)); @@ -209,21 +200,84 @@ void xwim::ArchiveCompressorSys::compress() { } close(fd); } + logger::trace("Entry written {}", archive_entry_pathname(entry)); archive_entry_free(entry); } } xwim::ArchiveCompressorSys::~ArchiveCompressorSys() { - logger::trace("Destructing ArchiveExtractorSys at {:p}", (void*) this->new_archive); - if(this->new_archive) { + logger::trace("Destructing ArchiveExtractorSys at {:p}", + (void *)this->new_archive); + if (this->new_archive) { archive_write_close(this->new_archive); archive_write_free(this->new_archive); } } -static int copy_data(struct archive* ar, struct archive* aw) { +/** Creates an archive path from the path to compress and normalizes it + * + * Note that currently only single arguments are allowed for `xwim` to + * minimize ambiguity. + * + * The archive path is determined from the argument file/directory by: + * 1. If file: + * 1.1. Stem the filename + * 1.2. Append an extension appropriate for the archive format (from the + * spec) + * 2. If directory: + * 2.1. Remove any trailing '/' + * 2.2. Append an extension appropriate for the archive format (from the + * spec) + */ +static fs::path archive_path_norm(const fs::path &root, + const xwim::CompressSpec &compress_spec) { + fs::path archive_path{root}; + fs::file_status archive_path_stat = fs::status(archive_path); + + std::set known_types = {fs::file_type::directory, fs::file_type::regular}; + fs::perms flag_mask = + fs::perms::owner_read | fs::perms::group_read | fs::perms::others_read; + + if (!fs::exists(archive_path)) { + logger::error("Non-existing path: {}", archive_path.string()); + throw xwim::ArchiveSysException{"Path does not exists"}; + } + + if (!known_types.count(archive_path_stat.type())) { + logger::error("Unknown path type: {}", archive_path_stat.type()); + throw xwim::ArchiveSysException{"Unknown path type"}; + } + + if ((archive_path_stat.permissions() & flag_mask) == fs::perms::none) { + logger::error("Cannot read path with permissions: {}", + archive_path_stat.permissions()); + throw xwim::ArchiveSysException{"Unreadable path"}; + } + + if (archive_path_stat.type() == fs::file_type::regular) { + while (archive_path.has_extension()) { + archive_path.replace_extension(); + } + } + + if (archive_path_stat.type() == fs::file_type::directory) { + if (archive_path.string().back() == '/') { + logger::trace("Found trailing / in path"); + std::string ps = archive_path.string(); + ps.erase(ps.size() - 1, 1); + + archive_path = fs::path{ps}; + logger::trace("Normalized path to {}", archive_path.string()); + } + } + + archive_path.concat(compress_spec.extension); + return archive_path; +} + +static int copy_data(struct archive *ar, struct archive *aw) { int r; - const void* buff; + const void *buff; size_t size; int64_t offset;