summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/c4add.c42
-rw-r--r--src/cc4group.c36
-rw-r--r--src/cc4group.h21
-rw-r--r--src/cppc4group.cpp26
-rw-r--r--src/cppc4group.hpp28
5 files changed, 146 insertions, 7 deletions
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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "cc4group.h"
+
+int main(int argc, char* argv[])
+{
+ if(argc != 3 && argc != 4)
+ {
+ fprintf(stderr, "USAGE: %s <group> <file|directory> [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 = &copyMemoryManagement,
@@ -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
@@ -177,6 +177,13 @@ typedef bool (*CC4Group_WriteCallback)(const void* const data, size_t const size
// 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
CC4Group_MemoryManagement Reference; // cc4group will use the data as is (i.e. store the pointer and use it); the caller must guarantee it's validity throughout the groups lifetime (or until the file's data is set to a new pointer) and needs to take care of freeing it afterwards
@@ -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<MemoryManagement::MemoryManagementStrategy>(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 <optional>
#include <cstdio>
#include <functional>
+#include <type_traits>
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<CppC4Group> openAsChild(const std::string& path);
};
+
+inline bool operator&(CppC4Group::AllowedEntryTypes lhs, CppC4Group::AllowedEntryTypes rhs)
+{
+ using T = std::underlying_type_t <CppC4Group::AllowedEntryTypes>;
+ return static_cast<T>(lhs) & static_cast<T>(rhs);
+}
+
+inline CppC4Group::AllowedEntryTypes operator|(CppC4Group::AllowedEntryTypes lhs, CppC4Group::AllowedEntryTypes rhs)
+{
+ using T = std::underlying_type_t <CppC4Group::AllowedEntryTypes>;
+ return static_cast<CppC4Group::AllowedEntryTypes>(static_cast<T>(lhs) | static_cast<T>(rhs));
+}
+
+inline CppC4Group::AllowedEntryTypes& operator|=(CppC4Group::AllowedEntryTypes& lhs, CppC4Group::AllowedEntryTypes rhs)
+{
+ return lhs = lhs | rhs;
+}