diff options
| author | Markus Mittendrein <git@maxmitti.tk> | 2019-08-17 21:09:41 +0200 |
|---|---|---|
| committer | Markus Mittendrein <git@maxmitti.tk> | 2019-08-19 13:43:23 +0200 |
| commit | 9f6da4965422b9f32c6a92b03b662bde025cc72c (patch) | |
| tree | 0c37a604d3770aff1b0564ea7d8be4d6e5953633 /src | |
| parent | f9084d17239791334dbac304160f3e3759f0955d (diff) | |
| download | cc4group-9f6da4965422b9f32c6a92b03b662bde025cc72c.tar.gz cc4group-9f6da4965422b9f32c6a92b03b662bde025cc72c.zip | |
Add support to load normal directories as groups
Diffstat (limited to 'src')
| -rw-r--r-- | src/cc4group.c | 517 |
1 files changed, 441 insertions, 76 deletions
diff --git a/src/cc4group.c b/src/cc4group.c index 3a78073..ce4dbb1 100644 --- a/src/cc4group.c +++ b/src/cc4group.c @@ -20,6 +20,7 @@ #include <sys/time.h> #include <time.h> #include <utime.h> +#include <dirent.h> #include <assert.h> #include <stdio.h> @@ -64,6 +65,7 @@ typedef struct C4GroupEntryData_t { struct C4GroupEntryData_t* parent; size_t absolutePosition; + const char* path; } C4GroupEntryData; LIST_AUTO(C4GroupEntryData, GroupEntryList) @@ -143,13 +145,15 @@ static void cc4group_applyMemoryManagementEnd(CC4Group_MemoryManagement const ma management->end((void*)data, management->arg); } -static const uint8_t* cc4group_getOnlyEntryData(CC4Group* const this, const C4GroupEntryData* entry); -static const C4GroupEntryData* cc4group_getEntryByPath(CC4Group* const this, const char* const entryPath, bool allowRoot, bool* error); -static const C4GroupEntryData* cc4group_getDirectoryByPath(CC4Group* const this, const char* const entryPath, bool allowRoot); -static const C4GroupEntryData* cc4group_getFileByPath(CC4Group* const this, const char* const entryPath); -static const C4GroupEntryData* cc4group_getFileOrDirectoryByPath(CC4Group* const this, const char* const entryPath, bool allowRoot); -static GroupEntryList* cc4group_getChildren(CC4Group* const this, const C4GroupEntryData* entry); -static C4GroupHeader* cc4group_getHeader(CC4Group* const this, const C4GroupEntryData* entry); +static const uint8_t* cc4group_getOnlyEntryData(CC4Group* const this, const C4GroupEntryData* const entry); +static const C4GroupEntryData* cc4group_getEntryByPath(CC4Group* const this, const char* const entryPath, bool const allowRoot, bool* const error, const C4GroupEntryData* const inParent); +static const C4GroupEntryData* cc4group_getDirectoryByPath(CC4Group* const this, const char* const entryPath, bool const allowRoot, const C4GroupEntryData* const inParent); +static const C4GroupEntryData* cc4group_getFileByPath(CC4Group* const this, const char* const entryPath, const C4GroupEntryData* const inParent); +static const C4GroupEntryData* cc4group_getFileOrDirectoryByPath(CC4Group* const this, const char* const entryPath, bool const allowRoot, const C4GroupEntryData* const inParent); +static GroupEntryList* cc4group_getChildren(CC4Group* const this, const C4GroupEntryData* const entry); +static C4GroupHeader* cc4group_getHeader(CC4Group* const this, const C4GroupEntryData* const entry); +static C4GroupEntryData* cc4group_createEntry(CC4Group* const this, const char* const path, C4GroupEntryData* const parent); +static C4GroupEntryData* cc4group_createDirectory(CC4Group* const this, const char* const path, C4GroupEntryData* const parent); static char* cc4group_strerrorFormatter(int32_t const code, const char* const method, const char* const causer, void* const data) { @@ -313,7 +317,7 @@ static bool buildChildren(CC4Group* const this, C4GroupEntryData* const entry, s return false; } - C4GroupEntryData* childEntry = &GroupEntryListAppend(entry->children, (C4GroupEntryData){.core = *core, .data = childData + core->Offset, .memoryManagement = cc4group.MemoryManagement.Reference, .children = NULL, .parent = entry, .absolutePosition = entry->absolutePosition + childDataOffset + core->Offset})->value; + C4GroupEntryData* childEntry = &GroupEntryListAppend(entry->children, (C4GroupEntryData){.core = *core, .data = childData + core->Offset, .memoryManagement = cc4group.MemoryManagement.Reference, .children = NULL, .parent = entry, .absolutePosition = entry->absolutePosition + childDataOffset + core->Offset, .path = NULL})->value; if(this->readState.position < childEntry->absolutePosition) { @@ -340,6 +344,10 @@ static void deleteChildren(GroupEntryList* const entries) { cc4group_applyMemoryManagementEnd(entry->value.memoryManagement, entry->value.data); } + if(entry->value.path != NULL) + { + free((void*)entry->value.path); + } // if they are not loaded yet, there is no need to delete them if(entry->value.core.Directory && entry->value.children != NULL) @@ -1025,7 +1033,7 @@ static bool cc4group_setSubRoot(CC4Group* const this, const char* const subPath) { if(subPath != NULL && *subPath != '\0') { - const C4GroupEntryData* subRoot = cc4group_getDirectoryByPath(this, subPath, false); + const C4GroupEntryData* subRoot = cc4group_getDirectoryByPath(this, subPath, false, NULL); if(subRoot == NULL || cc4group_getChildren(this, subRoot) == NULL) { @@ -1223,7 +1231,7 @@ static bool cc4group_setMaker(CC4Group* const this, const char* const maker, con assert(this); assert(maker); - const C4GroupEntryData* entry = cc4group_getDirectoryByPath(this, path, true); + const C4GroupEntryData* entry = cc4group_getDirectoryByPath(this, path, true, NULL); if(entry == NULL) { @@ -1277,7 +1285,7 @@ static bool cc4group_setOfficial(CC4Group* const this, bool const official, cons { assert(this); - const C4GroupEntryData* entry = cc4group_getDirectoryByPath(this, path, true); + const C4GroupEntryData* entry = cc4group_getDirectoryByPath(this, path, true, NULL); if(entry == NULL) { @@ -1332,7 +1340,7 @@ static bool cc4group_setCreation(CC4Group* const this, int32_t const creation, c { assert(this); - C4GroupEntryData* entry = (C4GroupEntryData*)cc4group_getFileOrDirectoryByPath(this, path, true); + C4GroupEntryData* entry = (C4GroupEntryData*)cc4group_getFileOrDirectoryByPath(this, path, true, NULL); if(entry == NULL) { @@ -1433,6 +1441,235 @@ static bool cc4group_openFilePointer(CC4Group* const this, FILE* const file) return cc4group_uncompressGroup(this, cc4group_readFilePointerReadCallback, arg, cc4group.MemoryManagement.Reference, cc4group_initChunkBufferCallback, cc4group_deinitChunkBufferCallback); } +static bool cc4group_isDirectory(const char* const path) +{ + struct stat st; + return stat(path, &st) != -1 && S_ISDIR(st.st_mode); +} + +static char* cc4group_pathCombine(const char* const firstPart, const char* secondPart) +{ + if(firstPart == NULL) + { + assert(secondPart); + return strdup(secondPart); + } + if(secondPart == NULL) + { + assert(firstPart); + return strdup(firstPart); + } + + if(*secondPart == '/') + { + ++secondPart; + } + + size_t firstLen = strlen(firstPart); + if(firstLen && firstPart[firstLen - 1] == '/') + { + --firstLen; + } + + char* const result = malloc(firstLen + 1 + strlen(secondPart) + 1); // '/' and '\0' + if(result != NULL) + { + strncpy(result, firstPart, firstLen); + result[firstLen] = '/'; + strcpy(result + firstLen + 1, secondPart); + } + return result; +} + +// takes over memory management of filePath +static C4GroupEntryData* cc4group_addFileFromDisk(CC4Group* const this, const char* const filePath, const char* const targetPath, C4GroupEntryData* const parent) +{ + assert(this); + assert(filePath); + assert(targetPath); + + C4GroupEntryData* result = NULL; + + struct stat st; + if(stat(filePath, &st) == -1) + { + SET_ERRNO_ERROR("stat: getting information of a contained file"); + goto ret; + } + + result = cc4group_createEntry(this, targetPath, parent); + if(result == NULL) + { + goto ret; + } + + if(!S_ISREG(st.st_mode)) + { + SET_MESSAGE_ERROR("Unsupported directory entry type found. Only regular files and directories are supported."); + goto ret; + } + + result->core.Executable = (st.st_mode & S_IXUSR) ? 1 : 0; + result->core.Modified = st.st_mtime; + result->core.Size = st.st_size; + result->path = filePath; + +ret: + if(result == NULL || result->path == NULL) + { + free((void*)filePath); + } + + return result; +} + +// takes over memory management of directoryPath +static bool cc4group_addDirectoryFromDisk(CC4Group* const this, const char* directoryPath, const char* const targetPath, C4GroupEntryData* const parent) +{ + assert(this); + assert(directoryPath); + + bool success = false; + + if(!cc4group_isDirectory(directoryPath)) + { + SET_MESSAGE_ERROR("The specified path is not a directory"); + goto ret; + } + + C4GroupEntryData* existingDirectory = targetPath == NULL ? &this->root : (C4GroupEntryData*)cc4group_getDirectoryByPath(this, targetPath, false, parent); + if(existingDirectory == NULL) + { + existingDirectory = cc4group_createDirectory(this, targetPath, parent); + if(existingDirectory == NULL) + { + goto ret; + } + } + + struct stat st; + if(stat(directoryPath, &st) == -1) + { + SET_ERRNO_ERROR("stat: getting information of a directory"); + goto ret; + } + existingDirectory->core.Modified = st.st_mtime; + + existingDirectory->path = directoryPath; + directoryPath = NULL; + + success = true; +ret: + if(directoryPath != NULL) + { + free((void*)directoryPath); + } + + return success; +} +static bool cc4group_populateDirectoryFromDisk(CC4Group* const this, C4GroupEntryData* const entry) +{ + assert(this); + assert(entry); + assert(entry->core.Directory); + assert(entry->path); + assert(!entry->children); + + entry->children = GroupEntryListNew(); + + DIR* dir = opendir(entry->path); + if(dir == NULL) + { + SET_ERRNO_ERROR("opendir: Opening the directoryPath"); + return false; + } + + bool success = false; + for(;;) + { + errno = 0; + struct dirent* dirEntry = readdir(dir); + if(dirEntry == NULL) + { + success = errno == 0; + if(!success) + { + SET_ERRNO_ERROR("readdir: reading the next entry"); + } + break; + } + + if(strcmp(dirEntry->d_name, ".") == 0 || strcmp(dirEntry->d_name, "..") == 0) + { + continue; + } + + char* directoryEntryPath = cc4group_pathCombine(entry->path, dirEntry->d_name); + if(directoryEntryPath == NULL) + { + SET_ERRNO_ERROR("malloc: allocating memory for the temporary path of an entry in the directory"); + break; + } + + if(cc4group_isDirectory(directoryEntryPath)) + { + if(!cc4group_addDirectoryFromDisk(this, directoryEntryPath, dirEntry->d_name, entry)) + { + success = false; + break; + } + } + else + { + if(!cc4group_addFileFromDisk(this, directoryEntryPath, dirEntry->d_name, entry)) + { + success = false; + break; + } + } + } + + entry->header->Entries = GroupEntryListSize(entry->children); + + if(closedir(dir) == -1) + { + cc4group_warn(this, "closedir: Closing the directory stream \"%s\" failed: %s", entry->path, strerror(errno)); + } + + return success; +} + +static bool cc4group_openDirectory(CC4Group* const this, const char* const path) +{ + assert(this); + assert(path); + + if(!cc4group_isDirectory(path)) + { + SET_MESSAGE_ERROR("The specified path is not a directory"); + return false; + } + + this->root.core.Directory = 1; + this->root.core.FileName[0] = '\0'; + this->lazy = false; + + if(!cc4group_initNewHeader(this)) + { + return false; + } + + if(cc4group_addDirectoryFromDisk(this, strdup(path), NULL, NULL) && cc4group_populateDirectoryFromDisk(this, &this->root)) + { + AddCleanUpJob(deleteChildren, this->root.children); + return true; + } + else + { + return false; + } +} + static bool cc4group_openExisting(CC4Group* const this, const char* const path) { assert(this); @@ -1443,6 +1680,10 @@ static bool cc4group_openExisting(CC4Group* const this, const char* const path) SET_BINARY(STDIN_FILENO); return cc4group_openFd(this, STDIN_FILENO); } + else if(cc4group_isDirectory(path)) + { + return cc4group_openDirectory(this, path); + } return cc4group_uncompressGroupFromFile(this, path); } @@ -1598,16 +1839,17 @@ static bool cc4group_extractAll(CC4Group* const this, const char* const targetPa // sets *error to true if any error is set -static const C4GroupEntryData* cc4group_getEntryByPath(CC4Group* const this, const char* const entryPath, bool allowRoot, bool* error) +static const C4GroupEntryData* cc4group_getEntryByPath(CC4Group* const this, const char* const entryPath, bool const allowRoot, bool* const error, const C4GroupEntryData* const inParent) { assert(this); assert(this->root.children); + const C4GroupEntryData* currentParent = inParent ? inParent : &this->root; if(entryPath == NULL || *entryPath == '\0') { if(allowRoot) { - return &this->root; + return currentParent; } else { @@ -1617,7 +1859,6 @@ static const C4GroupEntryData* cc4group_getEntryByPath(CC4Group* const this, con } } - const C4GroupEntryData* currentParent = &this->root; char* path = strdup(entryPath); if(path == NULL) @@ -1674,11 +1915,11 @@ ret: } // sets error -static const C4GroupEntryData* cc4group_getDirectoryByPath(CC4Group* const this, const char* const entryPath, bool allowRoot) +static const C4GroupEntryData* cc4group_getDirectoryByPath(CC4Group* const this, const char* const entryPath, bool const allowRoot, const C4GroupEntryData* const inParent) { // asserts are in cc4group_getEntryByPath bool error = false; - const C4GroupEntryData* entry = cc4group_getEntryByPath(this, entryPath, allowRoot, &error); + const C4GroupEntryData* entry = cc4group_getEntryByPath(this, entryPath, allowRoot, &error, inParent); if(error) { return NULL; @@ -1686,7 +1927,7 @@ static const C4GroupEntryData* cc4group_getDirectoryByPath(CC4Group* const this, if(entry == NULL) { - SET_MESSAGE_ERROR("The dirctory was not found in the group file"); + SET_MESSAGE_ERROR("The directory was not found in the group file"); return NULL; } @@ -1700,11 +1941,11 @@ static const C4GroupEntryData* cc4group_getDirectoryByPath(CC4Group* const this, } // sets error -static const C4GroupEntryData* cc4group_getFileByPath(CC4Group* const this, const char* const entryPath) +static const C4GroupEntryData* cc4group_getFileByPath(CC4Group* const this, const char* const entryPath, const C4GroupEntryData* const inParent) { // asserts are in cc4group_getEntryByPath bool error = false; - const C4GroupEntryData* entry = cc4group_getEntryByPath(this, entryPath, true, &error); + const C4GroupEntryData* entry = cc4group_getEntryByPath(this, entryPath, true, &error, inParent); if(error) { return NULL; @@ -1726,11 +1967,11 @@ static const C4GroupEntryData* cc4group_getFileByPath(CC4Group* const this, cons } // sets error -static const C4GroupEntryData* cc4group_getFileOrDirectoryByPath(CC4Group* const this, const char* const entryPath, bool allowRoot) +static const C4GroupEntryData* cc4group_getFileOrDirectoryByPath(CC4Group* const this, const char* const entryPath, bool const allowRoot, const C4GroupEntryData* const inParent) { // asserts are in cc4group_getEntryByPath bool error = false; - const C4GroupEntryData* entry = cc4group_getEntryByPath(this, entryPath, allowRoot, &error); + const C4GroupEntryData* entry = cc4group_getEntryByPath(this, entryPath, allowRoot, &error, inParent); if(error) { return false; @@ -1750,7 +1991,7 @@ static bool cc4group_extractSingle(CC4Group* const this, const char* const entry assert(this); assert(targetPath); - const C4GroupEntryData* entry = cc4group_getFileOrDirectoryByPath(this, entryPath, true); + const C4GroupEntryData* entry = cc4group_getFileOrDirectoryByPath(this, entryPath, true, NULL); if(entry == NULL) { return false; @@ -1759,16 +2000,97 @@ static bool cc4group_extractSingle(CC4Group* const this, const char* const entry return cc4group_extractChildren(this, entry, targetPath); } -static const uint8_t* cc4group_getOnlyEntryData(CC4Group* const this, const C4GroupEntryData* const entry) +static void* cc4group_mmappedFileManagementStart(void* const data, size_t const size, void* const arg) +{ + (void)size; + (void)arg; + return data; +} + +typedef struct { + CC4Group_MemoryManagement_t management; + size_t size; + int fd; + const CC4Group* group; +} CC4GroupMMappedFileManagement; + +static void cc4group_mmappedFileManagementEnd(void* const data, void* const arg) +{ + (void)data; + CC4GroupMMappedFileManagement* argData = arg; + if(cc4group_munmap(data, argData->size) == -1) + { + cc4group_warn(argData->group, "munmap: unmapping a mmapped file failed: %s", strerror(errno)); + } + if(argData->fd != -1) + { + if(close(argData->fd) == -1) + { + cc4group_warn(argData->group, "close: closing a file after unmapping (and also directly after mapping) it failed: %s", strerror(errno)); + } + } + free(argData); +} + +static const uint8_t* cc4group_getOnlyEntryData(CC4Group* const this, const C4GroupEntryData* entry) { if(entry->data == NULL) { - if(!cc4group_uncompressUntilPosition(this, entry->absolutePosition + entry->core.Size)) + if(entry->path != NULL) { - return NULL; + int file = open(entry->path, O_RDONLY | O_BINARY); + if(file == -1) + { + SET_ERRNO_ERROR("open: Opening the needed file"); + return NULL; + } + + void* mapped = cc4group_mmap(NULL, entry->core.Size, PROT_READ, MAP_PRIVATE, file, 0); + if(mapped == MAP_FAILED) + { + SET_ERRNO_ERROR("mmap: Mapping the file to add/load from disk"); + if(close(file) == -1) + { + cc4group_warn(this, "close: after mmapping the file \"%s\" failed, closing it failed too: %s", entry->path, strerror(errno)); + } + return NULL; + } + + CC4GroupMMappedFileManagement* management = malloc(sizeof(*management)); + if(management == NULL) + { + SET_ERRNO_ERROR("malloc: allocating memory for temporary memory management"); + return NULL; + } + + *management = (CC4GroupMMappedFileManagement){ + .management = { + .start = cc4group_mmappedFileManagementStart, + .end = cc4group_mmappedFileManagementEnd, + .arg = management + }, + .size = entry->core.Size, + .fd = -1, + .group = this + }; + + if(close(file) == -1) + { + management->fd = file; + } + + ((C4GroupEntryData*)entry)->data = mapped; + ((C4GroupEntryData*)entry)->memoryManagement = &management->management; } + else + { + if(!cc4group_uncompressUntilPosition(this, entry->absolutePosition + entry->core.Size)) + { + return NULL; + } - ((C4GroupEntryData*)entry)->data = this->uncompressedData + entry->absolutePosition; + ((C4GroupEntryData*)entry)->data = this->uncompressedData + entry->absolutePosition; + } } return entry->data; @@ -1776,13 +2098,23 @@ static const uint8_t* cc4group_getOnlyEntryData(CC4Group* const this, const C4Gr static C4GroupHeader* cc4group_getHeader(CC4Group* const this, const C4GroupEntryData* const entry) { + assert(this); + assert(entry); assert(entry->core.Directory); - if(entry->header == NULL) + if(entry->header == NULL || (entry->path != NULL && entry->children == NULL)) { - if(!cc4group_uncompressUntilPosition(this, entry->absolutePosition + sizeof(C4GroupHeader))) + if(entry->path != NULL) { - return NULL; + cc4group_populateDirectoryFromDisk(this, (C4GroupEntryData*)entry); + return entry->header; + } + else + { + if(!cc4group_uncompressUntilPosition(this, entry->absolutePosition + sizeof(C4GroupHeader))) + { + return NULL; + } } ((C4GroupEntryData*)entry)->data = this->uncompressedData + entry->absolutePosition; @@ -1806,14 +2138,22 @@ static GroupEntryList* cc4group_getChildren(CC4Group* const this, const C4GroupE return NULL; } - if(!cc4group_uncompressUntilPosition(this, entry->absolutePosition + sizeof(C4GroupHeader) + entry->header->Entries * sizeof(C4GroupEntryCore))) + if(entry->path != NULL) { - return NULL; + // should have been handled by getHeader if possible + return entry->children; } - - if(!buildChildren(this, (C4GroupEntryData*)entry, entry->core.Size)) + else { - return NULL; + if(!cc4group_uncompressUntilPosition(this, entry->absolutePosition + sizeof(C4GroupHeader) + entry->header->Entries * sizeof(C4GroupEntryCore))) + { + return NULL; + } + + if(!buildChildren(this, (C4GroupEntryData*)entry, entry->core.Size)) + { + return NULL; + } } } @@ -1826,7 +2166,7 @@ static bool cc4group_getEntryData(CC4Group* const this, const char* const entryP assert(data); assert(size); - const C4GroupEntryData* entry = cc4group_getFileByPath(this, entryPath); + const C4GroupEntryData* entry = cc4group_getFileByPath(this, entryPath, NULL); if(entry == NULL) { return false; @@ -2289,7 +2629,7 @@ static bool cc4group_saveOverwrite(CC4Group* const this, const char* const path) static bool cc4group_getEntryInfoForEntry(CC4Group* const this, const C4GroupEntryData* const entry, CC4Group_EntryInfo* const info, bool const lazy) { C4GroupHeader* header = NULL; - if(entry->core.Directory && !(this->lazy && lazy)) + if(entry->core.Directory && !((this->lazy || entry->path != NULL) && lazy)) { header = cc4group_getHeader(this, entry); if(header == NULL) @@ -2317,7 +2657,7 @@ static bool cc4group_getEntryInfo(CC4Group* const this, const char* const path, assert(this); assert(info); - const C4GroupEntryData* entry = cc4group_getFileOrDirectoryByPath(this, path, true); + const C4GroupEntryData* entry = cc4group_getFileOrDirectoryByPath(this, path, true, NULL); if(entry == NULL) { return false; @@ -2336,7 +2676,7 @@ static bool cc4group_getEntryInfos(CC4Group* const this, const char* const path, if(path != NULL && *path != '\0') { - const C4GroupEntryData* entry = cc4group_getDirectoryByPath(this, path, true); + const C4GroupEntryData* entry = cc4group_getDirectoryByPath(this, path, true, NULL); if(entry == NULL) { return false; @@ -2418,7 +2758,7 @@ static bool cc4group_getParentAndChildEntries(CC4Group* const this, const char* const char* parentPath; char* entryName = cc4group_splitParentAndChildPaths(myPath, &parentPath); - parent = cc4group_getDirectoryByPath(this, parentPath, true); + parent = cc4group_getDirectoryByPath(this, parentPath, true, NULL); if(parent == NULL) { @@ -2488,6 +2828,10 @@ static bool cc4group_deleteEntry(CC4Group* const this, const char* const path, b { cc4group_applyMemoryManagementEnd(deleteEntry->value.memoryManagement, deleteEntry->value.data); } + if(deleteEntry->value.path) + { + free((void*)deleteEntry->value.path); + } GroupEntryList* parentChildren = cc4group_getChildren(this, parent); assert(parentChildren); // if there was an error, it should have really been catched already in cc4group_getParentAndChildEntries @@ -2522,7 +2866,7 @@ static bool cc4group_renameEntry(CC4Group* const this, const char* const oldPath assert(newPath); bool error = false; - if(cc4group_getEntryByPath(this, newPath, false, &error) != NULL) + if(cc4group_getEntryByPath(this, newPath, false, &error, NULL) != NULL) { SET_MESSAGE_ERROR("The desired target path already exists"); return false; @@ -2552,7 +2896,7 @@ static bool cc4group_renameEntry(CC4Group* const this, const char* const oldPath const char* targetDirectory; char* targetName = cc4group_splitParentAndChildPaths(targetPath, &targetDirectory); - C4GroupEntryData* newParent = (C4GroupEntryData*)cc4group_getDirectoryByPath(this, targetDirectory, true); + C4GroupEntryData* newParent = (C4GroupEntryData*)cc4group_getDirectoryByPath(this, targetDirectory, true, NULL); if(newParent == NULL) { free(targetPath); @@ -2573,10 +2917,10 @@ static bool cc4group_renameEntry(CC4Group* const this, const char* const oldPath return true; } -static C4GroupEntryData* cc4group_createEntry(CC4Group* const this, const char* const path) +static C4GroupEntryData* cc4group_createEntry(CC4Group* const this, const char* const path, C4GroupEntryData* inParent) { bool error = false; - if(cc4group_getEntryByPath(this, path, false, &error) != NULL) + if(cc4group_getEntryByPath(this, path, false, &error, inParent) != NULL) { SET_MESSAGE_ERROR("The desired target path already exists"); return NULL; @@ -2587,50 +2931,62 @@ static C4GroupEntryData* cc4group_createEntry(CC4Group* const this, const char* return NULL; } - char* targetPath = strdup(path); - - if(targetPath == NULL) + const char* targetName; + C4GroupEntryData* parent; + char* targetPath; + if(inParent == NULL) { - SET_ERRNO_ERROR("strdup: duplicating the path"); - return NULL; - } - - const char* targetDirectory; - char* targetName = cc4group_splitParentAndChildPaths(targetPath, &targetDirectory); + targetPath = strdup(path); + if(targetPath == NULL) + { + SET_ERRNO_ERROR("strdup: duplicating the path"); + return NULL; + } + const char* targetDirectory; + targetName = cc4group_splitParentAndChildPaths(targetPath, &targetDirectory); - C4GroupEntryData* parent = (C4GroupEntryData*)cc4group_getDirectoryByPath(this, targetDirectory, true); - if(parent == NULL) + parent = (C4GroupEntryData*)cc4group_getDirectoryByPath(this, targetDirectory, true, NULL); + if(parent == NULL) + { + free(targetPath); + return NULL; + } + } + else { - free(targetPath); - return NULL; + parent = inParent; + targetName = path; } C4GroupEntryData entry; entry.data = NULL; + entry.path = NULL; entry.memoryManagement = cc4group.MemoryManagement.Reference; C4GroupEntryCore_init(&entry.core); C4GroupEntryCore_setFileName(&entry.core, targetName); - free(targetPath); + if(inParent == NULL) + { + free(targetPath); + } return cc4group_addEntryToDirectory(this, parent, &entry); } -static bool cc4group_createDirectory(CC4Group* const this, const char* const path) +static C4GroupEntryData* cc4group_createDirectory(CC4Group* const this, const char* const path, C4GroupEntryData* const parent) { assert(this); assert(path); - C4GroupEntryData* entry = cc4group_createEntry(this, path); + C4GroupEntryData* entry = cc4group_createEntry(this, path, parent); if(entry == NULL) { - return false; + return NULL; } - C4GroupHeader* header = malloc(sizeof(C4GroupHeader)); if(header == NULL) @@ -2639,7 +2995,7 @@ static bool cc4group_createDirectory(CC4Group* const this, const char* const pat return NULL; } - entry->children = GroupEntryListNew(); + entry->children = NULL; C4GroupHeader_init(header); C4GroupHeader_setOfficial(header, C4GroupHeader_isOfficial(entry->parent->header)); @@ -2649,7 +3005,17 @@ static bool cc4group_createDirectory(CC4Group* const this, const char* const pat entry->core.Directory = 1; entry->memoryManagement = cc4group.MemoryManagement.Take; - return true; + return entry; +} + +static bool cc4group_createEmptyDirectory(CC4Group* const this, const char* const path) +{ + C4GroupEntryData* entry = cc4group_createDirectory(this, path, NULL); + if(entry != NULL) + { + entry->children = GroupEntryListNew(); + } + return entry != NULL; } static bool cc4group_createFile(CC4Group* const this, const char* const path) @@ -2657,14 +3023,7 @@ static bool cc4group_createFile(CC4Group* const this, const char* const path) assert(this); assert(path); - C4GroupEntryData* entry = cc4group_createEntry(this, path); - - if(entry == NULL) - { - return false; - } - - return true; + return cc4group_createEntry(this, path, NULL) != NULL; } static bool cc4group_setEntryData(CC4Group* const this, const char* const entryPath, const void* const data, size_t const size, CC4Group_MemoryManagement const memoryManagement) @@ -2672,7 +3031,7 @@ static bool cc4group_setEntryData(CC4Group* const this, const char* const entryP assert(this); assert(entryPath); - C4GroupEntryData* entry = (C4GroupEntryData*)cc4group_getFileByPath(this, entryPath); + C4GroupEntryData* entry = (C4GroupEntryData*)cc4group_getFileByPath(this, entryPath, NULL); if(entry == NULL) { return false; @@ -2687,6 +3046,12 @@ static bool cc4group_setEntryData(CC4Group* const this, const char* const entryP entry->memoryManagement = cc4group.MemoryManagement.Reference; } + if(entry->path != NULL) + { + free((void*)entry->path); + entry->path = NULL; + } + if(data != NULL && size != 0) { const uint8_t* ownData = data; @@ -2712,7 +3077,7 @@ static bool cc4group_setExecutable(CC4Group* const this, bool const executable, { assert(this); - C4GroupEntryData* entry = (C4GroupEntryData*)cc4group_getFileByPath(this, path); + C4GroupEntryData* entry = (C4GroupEntryData*)cc4group_getFileByPath(this, path, NULL); if(entry == NULL) { @@ -2727,7 +3092,7 @@ static CC4Group* cc4group_openAsChild(CC4Group* const this, const char* const pa { assert(this); - const C4GroupEntryData* entry = cc4group_getDirectoryByPath(this, path, false); + const C4GroupEntryData* entry = cc4group_getDirectoryByPath(this, path, false, NULL); if(entry == NULL) { @@ -2888,7 +3253,7 @@ CC4Group_API cc4group = { .setExecutable = cc4group_setExecutable, - .createDirectory = cc4group_createDirectory, + .createDirectory = cc4group_createEmptyDirectory, .createFile = cc4group_createFile, .renameEntry = cc4group_renameEntry, .deleteEntry = cc4group_deleteEntry, |
