From f40a21f5a6ca674d93905269bcb4b21f92c41800 Mon Sep 17 00:00:00 2001 From: Markus Mittendrein Date: Sat, 17 Aug 2019 22:36:37 +0200 Subject: Add cc4group.addFromDisk with according cc4group.AllowEntryTypes --- examples/c4add.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/cc4group.c | 36 ++++++++++++++++++++++++++++++++++++ src/cc4group.h | 21 ++++++++++++++++++--- src/cppc4group.cpp | 26 +++++++++++++++++++++++--- src/cppc4group.hpp | 28 +++++++++++++++++++++++++++- 5 files changed, 146 insertions(+), 7 deletions(-) create mode 100644 examples/c4add.c diff --git a/examples/c4add.c b/examples/c4add.c new file mode 100644 index 0000000..2fa7e82 --- /dev/null +++ b/examples/c4add.c @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "cc4group.h" + +int main(int argc, char* argv[]) +{ + if(argc != 3 && argc != 4) + { + fprintf(stderr, "USAGE: %s [target path]\n", argv[0]); + return EXIT_FAILURE; + } + + CC4Group* group = cc4group.new(); + bool success = cc4group.openExisting(group, argv[1]); + if(!success) + { + fprintf(stderr, "ERROR: Can not open group file \"%s\": %s\n", argv[1], cc4group.getErrorMessage(group)); + } + else + { + success = cc4group.addFromDisk(group, argv[2], argc == 4 ? argv[3] : argv[2], cc4group.AllowedEntryTypes.All); + + if(!success) + { + fprintf(stderr, "ERROR: Can not add file/directory \"%s\" from disk: %s\n", argv[2], cc4group.getErrorMessage(group)); + } + else + { + success = cc4group.saveOverwrite(group, argv[1]); + + if(!success) + { + fprintf(stderr, "ERROR: Can not save group file \"%s\": %s\n", argv[2], cc4group.getErrorMessage(group)); + } + } + } + cc4group.delete(group); + + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/cc4group.c b/src/cc4group.c index ce4dbb1..0761370 100644 --- a/src/cc4group.c +++ b/src/cc4group.c @@ -51,6 +51,13 @@ static bool cc4group_getEntryData(CC4Group* const this, const char* const entryP #define CC4GROUP_UNEXPECTED_EOD -100 #define CC4GROUP_MEM_ERROR -101 +typedef enum { + CC4Group_AllowedEntryTypes_File = 0x01, + CC4Group_AllowedEntryTypes_Directory = 0x02, + + CC4Group_AllowedEntryTypes_All = CC4Group_AllowedEntryTypes_File | CC4Group_AllowedEntryTypes_Directory +} CC4Group_AllowedEntryTypes; + struct list_GroupEntryList; typedef struct C4GroupEntryData_t { C4GroupEntryCore core; @@ -3007,6 +3014,28 @@ static C4GroupEntryData* cc4group_createDirectory(CC4Group* const this, const ch return entry; } +static bool cc4group_addFromDisk(CC4Group* const this, const char* const path, const char* const targetEntryPath, int const allowedEntryTypes) +{ + CC4Group_AllowedEntryTypes type = cc4group_isDirectory(path) ? CC4Group_AllowedEntryTypes_Directory : CC4Group_AllowedEntryTypes_File; + if(!(type & allowedEntryTypes)) + { + SET_MESSAGE_ERROR("The specified path doesn't match the specified allowed entry type"); + return false; + } + + switch(type) + { + case CC4Group_AllowedEntryTypes_File: + return cc4group_addFileFromDisk(this, strdup(path), targetEntryPath, NULL) != NULL; + case CC4Group_AllowedEntryTypes_Directory: + return cc4group_addDirectoryFromDisk(this, strdup(path), targetEntryPath, NULL); + case CC4Group_AllowedEntryTypes_All: + assert(!"All is not a valid type determined for the path; This code should never be reached"); + return false; + } + + return false; +} static bool cc4group_createEmptyDirectory(CC4Group* const this, const char* const path) { @@ -3199,6 +3228,12 @@ static CC4Group_MemoryManagement_t referenceMemoryManagement = { }; CC4Group_API cc4group = { + .AllowedEntryTypes = { + .File = CC4Group_AllowedEntryTypes_File, + .Directory = CC4Group_AllowedEntryTypes_Directory, + .All = CC4Group_AllowedEntryTypes_All + }, + .MemoryManagement = { .Take = &takeMemoryManagement, .Copy = ©MemoryManagement, @@ -3253,6 +3288,7 @@ CC4Group_API cc4group = { .setExecutable = cc4group_setExecutable, + .addFromDisk = cc4group_addFromDisk, .createDirectory = cc4group_createEmptyDirectory, .createFile = cc4group_createFile, .renameEntry = cc4group_renameEntry, diff --git a/src/cc4group.h b/src/cc4group.h index c8ff6f6..d2d978c 100644 --- a/src/cc4group.h +++ b/src/cc4group.h @@ -11,9 +11,9 @@ // just keep in mind that it may return NULL in the rare case that memory allocation fails for whatever reason // - after creating a fresh CC4Group object it's intended use needs to be decided on, by either calling cc4group.create on it... // ...to create an empty group in-memory where contents may be added later -// or call cc4group.openExisting together with the path to a physical group file on disk to load it's contents into memory +// or call cc4group.openExisting together with the path to a physical group file (or normal folder) on disk to load it's contents into memory // (for more sophisticated cases, one of the other open functions may be used) -// it is important that only one of this function is called only on freshly created groups +// it is important that only one of these functions is called only on freshly created groups // otherwise in the lucky case some assertion may trigger or other undefined things may happen // this two step process is needed to ensure informational error reporting when something goes wrong while opening existing groups // - after calling cc4group.create or some cc4group.open-function, the group can be inspected and modified to your heart's content through the rest of the available API @@ -176,6 +176,13 @@ typedef bool (*CC4Group_WriteCallback)(const void* const data, size_t const size // this is the main API struct of cc4group // it contains all available methods and constants typedef struct { + struct { + int File; // only a single regular file is allowed + int Directory; // only a directory is allowed, contents are handled recursively + int All; // currently File | Directory + } const AllowedEntryTypes; + + struct { CC4Group_MemoryManagement Take; // cc4group will free the data when its not needed anymore; e.g. in the destructor or when setting the file's data again CC4Group_MemoryManagement Copy; // cc4group will copy the data to use it; the original data is untouched and needs to be freed by the caller whenever desired @@ -226,7 +233,7 @@ typedef struct { // initializes the group to be a fresh, empty group bool (*create)(CC4Group* const this); - // opens a group on the filesystem; path may point to a directory inside a group; path "-" can be used to read the group from stdin + // opens a group or a normal folder on the filesystem; path may point to a directory inside a group; path "-" can be used to read the group from stdin bool (*openExisting)(CC4Group* const this, const char* const path); // opens a group that is stored entirely in memory @@ -338,6 +345,14 @@ typedef struct { // modifying the group + // adds (also only in-memory until saving) a file or folder as is on disk + // be careful, the contents are not stored in memory, files and folders are accessed in the same strategy as the lazy-mode works (on first demand, then cached) + // thus, later modifications may result in inconsistent states if saving is delayed for long + // targetEntryPath denotes full path where the entry should be stored, including its filename + // allowedEntryTypes tells cc4group what type of entries it should expect. unexpected types will produce an error + // see the definition of AllowedEntryTypes for details + bool (*addFromDisk)(CC4Group* const this, const char* const path, const char* const targetEntryPath, int const allowedEntryTypes); + // creates an empty directory inside the group at the place with the name as denoted by path // the parent directory (if any) must exist alredy bool (*createDirectory)(CC4Group* const this, const char* const path); diff --git a/src/cppc4group.cpp b/src/cppc4group.cpp index 5e8225b..bacf1f7 100644 --- a/src/cppc4group.cpp +++ b/src/cppc4group.cpp @@ -151,15 +151,30 @@ const CppC4Group::MemoryManagement CppC4Group::MemoryManagement::Copy{reinterpre const CppC4Group::MemoryManagement CppC4Group::MemoryManagement::Reference{reinterpret_cast(cc4group.MemoryManagement.Reference)}; namespace { + int convertAllowedEntryTypes(const CppC4Group::AllowedEntryTypes allowedEntryTypes) + { + int result = 0; + if(allowedEntryTypes & CppC4Group::AllowedEntryTypes::File) + { + result |= cc4group.AllowedEntryTypes.File; + } + if(allowedEntryTypes & CppC4Group::AllowedEntryTypes::Directory) + { + result |= cc4group.AllowedEntryTypes.Directory; + } + // All is handled by the above two + return result; + } + CC4Group_TmpMemoryStrategy convertTmpMemoryStrategy(const CppC4Group::TmpMemoryStrategy strategy) { switch(strategy) { - case CppC4Group::Auto: + case CppC4Group::TmpMemoryStrategy::Auto: return cc4group.TmpMemoryStrategies.Auto; - case CppC4Group::File: + case CppC4Group::TmpMemoryStrategy::File: return cc4group.TmpMemoryStrategies.File; - case CppC4Group::Memory: + case CppC4Group::TmpMemoryStrategy::Memory: return cc4group.TmpMemoryStrategies.Memory; } return cc4group.TmpMemoryStrategies.Auto; @@ -403,6 +418,11 @@ bool CppC4Group::renameEntry(const std::string& oldPath, const std::string& newP return cc4group.renameEntry(p->g, oldPath.c_str(), newPath.c_str()); } +bool CppC4Group::addFromDisk(const std::string& path, const std::string& targetPath, const CppC4Group::AllowedEntryTypes allowedEntryTypes) +{ + return cc4group.addFromDisk(p->g, path.c_str(), targetPath.c_str(), convertAllowedEntryTypes(allowedEntryTypes)); +} + bool CppC4Group::createDirectory(const std::string& path) { return cc4group.createDirectory(p->g, path.c_str()); diff --git a/src/cppc4group.hpp b/src/cppc4group.hpp index 420893c..7f51629 100644 --- a/src/cppc4group.hpp +++ b/src/cppc4group.hpp @@ -14,6 +14,7 @@ #include #include #include +#include class CppC4Group { // all C-related stuff is hidden from this header so it doesn't land in the precious C++-only code this might be used in... @@ -111,12 +112,18 @@ public: using WriteCallback = bool(*)(const void* const data, size_t const size, void* const arg); // these enums are just mapped to their C-counterparts internally - enum TmpMemoryStrategy { + enum class TmpMemoryStrategy { Memory, File, Auto }; + enum class AllowedEntryTypes { + File, + Directory, + All = File | Directory + }; + // use this to set one of the predefined strategies static void setTmpMemoryStrategy(const TmpMemoryStrategy strategy); @@ -167,6 +174,8 @@ public: bool deleteEntry(const std::string& path, const bool recursive = false); bool renameEntry(const std::string& oldPath, const std::string& newPath); + bool addFromDisk(const std::string& path, const std::string& targetPath, const AllowedEntryTypes allowedEntryTypes = AllowedEntryTypes::All); + bool createDirectory(const std::string& path); bool createFile(const std::string& path); @@ -176,3 +185,20 @@ public: // to get the child group out of the optional in case of success, construct a new CppC4Group with the move constructor: CppC4Group child{std::move(*optionalChild)}; std::optional openAsChild(const std::string& path); }; + +inline bool operator&(CppC4Group::AllowedEntryTypes lhs, CppC4Group::AllowedEntryTypes rhs) +{ + using T = std::underlying_type_t ; + return static_cast(lhs) & static_cast(rhs); +} + +inline CppC4Group::AllowedEntryTypes operator|(CppC4Group::AllowedEntryTypes lhs, CppC4Group::AllowedEntryTypes rhs) +{ + using T = std::underlying_type_t ; + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +inline CppC4Group::AllowedEntryTypes& operator|=(CppC4Group::AllowedEntryTypes& lhs, CppC4Group::AllowedEntryTypes rhs) +{ + return lhs = lhs | rhs; +} -- cgit v1.2.3-54-g00ecf