From 04e3ded4c28c0b559620609daaae7b939d776b61 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 15 Jun 2024 11:42:48 -0700 Subject: Add path. --- filesystem/src/path.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 filesystem/src/path.c (limited to 'filesystem/src/path.c') diff --git a/filesystem/src/path.c b/filesystem/src/path.c new file mode 100644 index 0000000..2ce5a04 --- /dev/null +++ b/filesystem/src/path.c @@ -0,0 +1,104 @@ +#include + +#include +#include +#include + +static const path Empty = (path){0, 0}; + +path path_new(const char* str) { + assert(str); + const size_t size = strlen(str); + if (size > 0) { + char* data = calloc(size + 1, sizeof(char)); // +1 for null + memcpy(data, str, size); + data[size] = 0; + return (path){data, size}; + } + return Empty; +} + +void path_del(path* path) { + if (path) { + free(path->data); + path->data = 0; + path->size = 0; + } +} + +path path_parent_dir(path p) { + assert(p.data); + + if (p.size == 0) { + return Empty; + } + size_t i = p.size - 1; + // If the path ends with '/', skip the characters. + while ((i > 0) && (p.data[i] == '/')) { + i--; + } + // Search backwards for the parent dir. + for (; i > 0; --i) { + if (p.data[i] == '/') { + return (path){p.data, i + 1}; + } + } + return Empty; // No parent. +} + +path path_concat(path left, path right) { + assert(left.data); + assert(right.data); + + // +1 for separator. + const size_t out_size = left.size + right.size + 1; + // +1 for null. + char* out = calloc(out_size + 1, sizeof(char)); + ASSERT(out); + + memcpy(out, left.data, left.size); + out[left.size] = '/'; + memcpy(out + left.size + 1, right.data, right.size); + out[out_size] = 0; + + return (path){out, out_size}; +} + +bool path_make_relative( + const char* filepath, const char* path, char* relative, + size_t relative_length) { + assert(filepath); + assert(path); + assert(relative); + + const size_t filepath_len = strlen(filepath); + const size_t path_len = strlen(path); + assert(filepath_len < relative_length); + assert(path_len < relative_length); + + // Handle empty filepath. + if (filepath_len == 0) { + memcpy(relative, path, path_len); + return true; + } + + // Search for the last / in the file path to get its parent directory. + assert(filepath_len > 0); + size_t tm_dir_len = 0; + for (tm_dir_len = strlen(filepath) - 1; tm_dir_len > 0; --tm_dir_len) { + if (filepath[tm_dir_len] == '/') { + break; + } + } + tm_dir_len++; // Preserve the backslash. + + // Copy the file path where the parent dir ends. + // Make sure there is enough space in the output. + if ((tm_dir_len + path_len + 1) >= relative_length) { + return false; + } + memcpy(relative, filepath, tm_dir_len); + memcpy(&relative[tm_dir_len], path, path_len); + + return true; +} -- cgit v1.2.3