summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt8
-rw-r--r--examples/c4ls_asChild.c100
-rw-r--r--examples/c4ls_asChildPp.cpp79
-rw-r--r--src/cc4group.c61
-rw-r--r--src/cc4group.h14
-rw-r--r--src/cppc4group.cpp29
-rw-r--r--src/cppc4group.hpp11
7 files changed, 298 insertions, 4 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 944b9eb..4604c81 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -55,14 +55,22 @@ function(example name)
target_link_libraries(${name} PRIVATE cc4group)
endfunction()
+function(examplepp name)
+ add_executable(${name} examples/${name}.cpp)
+ target_link_libraries(${name} PRIVATE cppc4group)
+endfunction()
+
example(unc4group)
example(c4copy)
example(c4info)
example(c4ls)
+example(c4ls_asChild)
example(c4touch)
example(c4rm)
example(c4mkdir)
+examplepp(c4ls_asChildPp)
+
add_executable(c4cat examples/c4cat.c)
target_link_libraries(c4cat PRIVATE cc4group)
target_compile_definitions(c4cat PRIVATE CC4GROUP_PLATFORM_${CC4GROUP_PLATFORM_DEF})
diff --git a/examples/c4ls_asChild.c b/examples/c4ls_asChild.c
new file mode 100644
index 0000000..8a9d00b
--- /dev/null
+++ b/examples/c4ls_asChild.c
@@ -0,0 +1,100 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <locale.h>
+
+#include "cc4group.h"
+
+const char* formatTime(int32_t time)
+{
+ time_t tTime = time;
+ static char ret[128];
+ strftime(ret, sizeof(ret), "%c", localtime(&tTime));
+ return ret;
+}
+
+const char* formatSize(uint32_t size)
+{
+ static char ret[128];
+ if(size < 1000)
+ {
+ snprintf(ret, 128, "%4u", size);
+ return ret;
+ }
+
+ static const char prefixes[] = " KMGTPE";
+
+ const char* prefix = prefixes;
+ uint32_t fract = 0;
+
+ while(size >= 1000 && *(prefix + 1) != '\0')
+ {
+ ++prefix;
+ fract = size % 1000;
+ size /= 1000;
+ }
+
+ if(size < 10)
+ {
+ snprintf(ret, 128, "%1.1f%c", (float)size + (float)fract / 1000, *prefix);
+ }
+ else
+ {
+ snprintf(ret, 128, "%3u%c", size, *prefix);
+ }
+
+ return ret;
+}
+
+int main(int argc, char* argv[])
+{
+ if(argc != 3)
+ {
+ fprintf(stderr, "USAGE: %s <group> <subgroup>\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
+ {
+ CC4Group* child = cc4group.openAsChild(group, argv[2]);
+ if(child == NULL)
+ {
+ fprintf(stderr, "ERROR: Can not open as child: %s\n", cc4group.getErrorMessage(group));
+ cc4group.delete(group);
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ cc4group.delete(group);
+
+ CC4Group_EntryInfo* infos;
+ size_t entries;
+
+ success = cc4group.getEntryInfos(child, NULL, &infos, &entries);
+
+ if(!success)
+ {
+ fprintf(stderr, "ERROR: Can not list group entries: %s\n", cc4group.getErrorMessage(child));
+ }
+ else
+ {
+ for(size_t i = 0; i < entries; ++i)
+ {
+ printf("%c %s %s %s\t%s\n", infos[i].directory ? 'd' : infos[i].executable ? 'x' : ' ', formatTime(infos[i].modified), formatSize(infos[i].totalSize), infos[i].fileName, infos[i].author);
+ }
+
+ free(infos);
+ }
+
+ cc4group.delete(child);
+ }
+ }
+
+ return success ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/examples/c4ls_asChildPp.cpp b/examples/c4ls_asChildPp.cpp
new file mode 100644
index 0000000..019b781
--- /dev/null
+++ b/examples/c4ls_asChildPp.cpp
@@ -0,0 +1,79 @@
+#include "cppc4group.hpp"
+#include <iostream>
+
+const char* formatTime(int32_t time)
+{
+ time_t tTime = time;
+ static char ret[128];
+ strftime(ret, sizeof(ret), "%c", localtime(&tTime));
+ return ret;
+}
+
+const char* formatSize(uint32_t size)
+{
+ static char ret[128];
+ if(size < 1000)
+ {
+ snprintf(ret, 128, "%4u", size);
+ return ret;
+ }
+
+ static const char prefixes[] = " KMGTPE";
+
+ const char* prefix = prefixes;
+ uint32_t fract = 0;
+
+ while(size >= 1000 && *(prefix + 1) != '\0')
+ {
+ ++prefix;
+ fract = size % 1000;
+ size /= 1000;
+ }
+
+ if(size < 10)
+ {
+ snprintf(ret, 128, "%1.1f%c", (float)size + (float)fract / 1000, *prefix);
+ }
+ else
+ {
+ snprintf(ret, 128, "%3u%c", size, *prefix);
+ }
+
+ return ret;
+}
+
+int main(int argc, char* argv[])
+{
+ if(argc != 3)
+ {
+ std::cerr << "USAGE: " << argv[0] << " <group> <subgroup>\n";
+ return EXIT_FAILURE;
+ }
+
+ CppC4Group g;
+ if(!g.openExisting(argv[1]))
+ {
+ std::cerr << "ERROR: Can't open group: " << g.getErrorMessage() << '\n';
+ return EXIT_FAILURE;
+ }
+
+ if(auto child = g.openAsChild(argv[2]))
+ {
+ if(auto entries = child->getEntryInfos())
+ {
+ for(const auto& entry : *entries)
+ {
+ std::cout << (entry.directory ? 'd' : entry.executable ? 'x' : ' ') << " " << formatTime(entry.modified) << ' ' << formatSize(entry.totalSize) << " " << entry.fileName << '\t' << entry.author << '\n';
+ }
+ }
+ else
+ {
+ std::cerr << "ERROR: Can't list subgroup contents: " << child->getErrorMessage();
+ }
+ }
+ else
+ {
+ std::cerr << "ERROR: Can't open subgroup: " << g.getErrorMessage();
+ return EXIT_FAILURE;
+ }
+} \ No newline at end of file
diff --git a/src/cc4group.c b/src/cc4group.c
index b00cc45..f257d6b 100644
--- a/src/cc4group.c
+++ b/src/cc4group.c
@@ -98,6 +98,9 @@ struct CC4Group_t {
char* lastFormattedMessage;
} error;
+
+ size_t referenceCounter;
+ CC4Group* parent;
};
static bool cc4group_applyMemoryManagementStart(CC4Group_MemoryManagement const management, const uint8_t** data, size_t size)
@@ -951,6 +954,9 @@ static void cc4group_init(CC4Group* const this)
{
assert(this);
+ this->referenceCounter = 1;
+ this->parent = NULL;
+
this->uncompressedData = NULL;
this->uncompressedSize = 0;
@@ -1144,6 +1150,16 @@ static bool cc4group_openExisting(CC4Group* const this, const char* const path)
return cc4group_uncompressGroupFromFile(this, path);
}
+static void cc4group_unreference(CC4Group* const this)
+{
+ assert(this);
+
+ if(--this->referenceCounter == 0)
+ {
+ cc4group_delete(this);
+ }
+}
+
static void cc4group_delete(CC4Group* const this)
{
assert(this);
@@ -1578,6 +1594,12 @@ static bool cc4group_saveIt(CC4Group* const this, const char* const path, bool c
assert(this);
assert(path);
+ if(this->parent != NULL)
+ {
+ SET_MESSAGE_ERROR("Saving is currently not implemented for groups opened with openAsChild");
+ return false;
+ }
+
bool success = false;
int file = open(path, O_WRONLY | O_BINARY | O_CREAT | (overwrite ? O_TRUNC : O_EXCL), 0644);
@@ -2047,6 +2069,38 @@ static bool cc4group_setExecutable(CC4Group* const this, bool const executable,
return true;
}
+static CC4Group* cc4group_openAsChild(CC4Group* const this, const char* const path)
+{
+ assert(this);
+
+ const C4GroupEntryData* entry = cc4group_getDirectoryByPath(this, path, false);
+
+ if(entry == NULL)
+ {
+ return NULL;
+ }
+
+ CC4Group* child = cc4group_new();
+ if(child == NULL)
+ {
+ SET_ERRNO_ERROR("malloc: allocating the child group object");
+ return NULL;
+ }
+
+ memcpy(&child->root.core, &entry->core, sizeof(child->root.core));
+ child->root.data = entry->data;
+ child->root.core.Directory = 1;
+ child->root.core.FileName[0] = '\0';
+ child->root.children = entry->children;
+ child->parent = this;
+
+ ++this->referenceCounter;
+
+ CleanUpJobListAppend(child->cleanupJobs, (CC4Group_CleanupJob){.func = (CC4Group_CleanupFunc)cc4group_unreference, .data = this});
+
+ return child;
+}
+
CC4Group_API cc4group = {
.MemoryManagement = {
.Take = Take,
@@ -2065,7 +2119,7 @@ CC4Group_API cc4group = {
.new = cc4group_new,
- .delete = cc4group_delete,
+ .delete = cc4group_unreference,
.create = cc4group_create,
@@ -2107,5 +2161,8 @@ CC4Group_API cc4group = {
.getErrorMessage = cc4group_getErrorMessage,
.getErrorCode = cc4group_getErrorCode,
.getErrorMethod = cc4group_getErrorMethod,
- .getErrorCauser = cc4group_getErrorCauser
+ .getErrorCauser = cc4group_getErrorCauser,
+
+
+ .openAsChild = cc4group_openAsChild
};
diff --git a/src/cc4group.h b/src/cc4group.h
index 8b86d9c..207ef15 100644
--- a/src/cc4group.h
+++ b/src/cc4group.h
@@ -29,7 +29,7 @@
// the directory separator used for all entry paths in cc4group is "/" _regardless of the platform_. yes, you better watch out Windows users!
// in contrast to paths names are only the the name of the entry itself, not the whole path
// - to free all resources used by the group when it is not needed anymore, call cc4group.delete with the group pointer and discard it afterwards (i.e. don't use it anymore)
-// - all functions, except cc4group.new, cc4group.delete and cc4group.setTmpMemoryStrategy (where the latter can't fail) follow the same scheme
+// - all functions, except cc4group.new, cc4group.openAsChild, cc4group.delete and cc4group.setTmpMemoryStrategy (where the latter two can't fail) follow the same scheme
// all of them can fail, either caused by wrong arguments or by things outside of the applications control
// they all return _true_ if everything went well and _false_ if any error occured
// information about the error (incredibly useful for debugging or asking for help with problems) can be obtained by using one the cc4group.getError* functions
@@ -188,6 +188,8 @@ typedef struct {
CC4Group* (*new)(void);
// destructs the group object and frees all memory used by group, like the operator delete
+ // the group object may be freed at a later time if cc4group.openAsChild was used on it and the child is not deleted yet
+ // child relations are tracked through reference counting, thus the group will be really destructed when all referencing child groups are gone
void (*delete)(CC4Group* const this);
@@ -316,6 +318,16 @@ typedef struct {
// returns human readable information during which operation the error occured
const char* (*getErrorCauser)(const CC4Group* const this);
+
+
+ // returns a new CC4Group object that represents a subgroup denoted by path
+ // path must point to a directory
+ // in any error case NULL will be returned
+ // the returned group will share most resources with its parent (the one passed as this)
+ // the parent may be deleted anytime, but it will stay in memory until the child is deleted as well (using reference counting)
+ // because both share most of their data, modifications of the child will affect the parent and the opposite direction is also possible if the modifications involve the subgroup
+ // saving through child groups is not implemented yet, it will result in an error
+ CC4Group* (*openAsChild)(CC4Group* const this, const char* const path);
} const CC4Group_API;
diff --git a/src/cppc4group.cpp b/src/cppc4group.cpp
index 1b4022e..950a714 100644
--- a/src/cppc4group.cpp
+++ b/src/cppc4group.cpp
@@ -25,6 +25,11 @@ struct CppC4Group::Private {
}
+ Private(CC4Group* g) : g{g}
+ {
+
+ }
+
~Private()
{
cc4group.delete_(g);
@@ -66,6 +71,17 @@ CppC4Group::CppC4Group() : p{new CppC4Group::Private{}}
}
+CppC4Group::CppC4Group(CppC4Group&& other) : p{std::move(other.p)}
+{
+
+}
+
+CppC4Group::CppC4Group(std::unique_ptr<Private>&& p) : p{std::move(p)}
+{
+
+}
+
+
CppC4Group::~CppC4Group()
{
@@ -281,3 +297,16 @@ bool CppC4Group::setEntryData(const std::string& path, const void*const data, co
{
return cc4group.setEntryData(p->g, path.c_str(), data, size, convertMemoryManagement(management));
}
+
+std::optional<CppC4Group> CppC4Group::openAsChild(const std::string& path)
+{
+ auto g = cc4group.openAsChild(p->g, path.c_str());
+ if(g == nullptr)
+ {
+ return {};
+ }
+ else
+ {
+ return {{std::make_unique<Private>(g)}};
+ }
+} \ No newline at end of file
diff --git a/src/cppc4group.hpp b/src/cppc4group.hpp
index a1bb28a..c30af44 100644
--- a/src/cppc4group.hpp
+++ b/src/cppc4group.hpp
@@ -5,7 +5,7 @@
// although, when dealing with raw memory (because cc4group can't know the type of the stored data) also users of this wrapper have to deal with some void*
// because most methods behave exactly the same, please look at cc4group.h for their description (they are named exactly the same with a few exceptions)
// in case their are real differences, they will be noted in this header also
-// until now, all examples only use cc4group's C-API, but as a C++-programmer you should have no problems reading them anyway
+// until now, all except one example only use cc4group's C-API, but as a C++-programmer you should have no problems reading them anyway
// one general difference to the C-API that is not mentioned at the individual methods is that functions actually returning data return an std::optional instead of a bool...
// ...the optional will be populated normally and empty in error cases
@@ -22,6 +22,9 @@ class CppC4Group {
std::unique_ptr<Private> p;
+ // this is only needed for internal purposes
+ CppC4Group(std::unique_ptr<Private>&& p);
+
public:
// this struct is used for the getEntryData-method to return them in a uniform way (instead of using reference- or pointer-arguments)
struct Data {
@@ -100,6 +103,9 @@ public:
// the constructor will automatically construct an internal CC4Group, so no new-equivalent method is needed
CppC4Group();
+ // the move constructor is needed to move the returned group out of the optional from openAsChild
+ CppC4Group(CppC4Group&& other);
+
// the destructor will automatically delete the internal CC4Group, so also no extra method needed
~CppC4Group();
@@ -138,4 +144,7 @@ public:
bool createFile(const std::string& path);
bool setEntryData(const std::string& path, const void* const data = nullptr, const size_t size = 0, const MemoryManagement management = Copy);
+
+ // 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);
};