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/CMakeLists.txt | 6 ++- filesystem/include/filesystem.h | 6 --- filesystem/include/path.h | 38 +++++++++++++++ filesystem/src/filesystem.c | 40 +--------------- filesystem/src/path.c | 104 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+), 46 deletions(-) create mode 100644 filesystem/include/path.h create mode 100644 filesystem/src/path.c diff --git a/filesystem/CMakeLists.txt b/filesystem/CMakeLists.txt index 55430e6..04390f4 100644 --- a/filesystem/CMakeLists.txt +++ b/filesystem/CMakeLists.txt @@ -3,9 +3,13 @@ cmake_minimum_required(VERSION 3.0) project(filesystem) add_library(filesystem - src/filesystem.c) + src/filesystem.c + src/path.c) target_include_directories(filesystem PUBLIC include) +target_link_libraries(filesystem PRIVATE + cassert) + target_compile_options(filesystem PRIVATE -Wall -Wextra) diff --git a/filesystem/include/filesystem.h b/filesystem/include/filesystem.h index 3dce87f..1c354b7 100644 --- a/filesystem/include/filesystem.h +++ b/filesystem/include/filesystem.h @@ -3,7 +3,6 @@ */ #pragma once -#include #include #include @@ -12,8 +11,3 @@ size_t get_file_size(FILE* file); /// Read the entire contents of the file into memory. void* read_file(const char* filepath); - -/// Make a path relative to the parent directory of a file. -bool make_relative_path( - const char* filepath, const char* path, char* relative, - size_t relative_length); diff --git a/filesystem/include/path.h b/filesystem/include/path.h new file mode 100644 index 0000000..8ad885d --- /dev/null +++ b/filesystem/include/path.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +typedef struct path { + char* data; + size_t size; // Does not include null terminator. 0 if data is null. +} path; + +/// Create a new path. +path path_new(const char*); + +/// Free the path's memory. +void path_del(path*); + +/// Return a C string from the path. +static inline const char* path_cstr(path p) { return p.data; } + +/// Return true if the path is empty, false otherwise. +static inline bool path_empty(path p) { + assert((p.data != 0) || (p.size == 0)); // null data -> 0 size + return p.data != 0; +} + +/// Returns the parent directory, or empty path if the given path has no parent. +/// The returned path is a slice of the input path; this function does not +/// allocate. +path path_parent_dir(path); + +/// Concatenate two paths. +path path_concat(path left, path right); + +/// Make a path relative to the parent directory of a file. +bool path_make_relative( + const char* filepath, const char* path, char* relative, + size_t relative_length); diff --git a/filesystem/src/filesystem.c b/filesystem/src/filesystem.c index f6bb693..b228e85 100644 --- a/filesystem/src/filesystem.c +++ b/filesystem/src/filesystem.c @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -54,42 +55,3 @@ cleanup: } return 0; } - -bool make_relative_path( - 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; -} 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