From c822be080473ab74352c5805a6a80aceeed8c590 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Thu, 13 Feb 2025 18:03:09 +0000 Subject: [PATCH] io: implement path manipulation and directory iteration on darwin --- io-test/io-units.c | 7 +- io-test/tree.c | 2 +- io/include/blue/io/directory.h | 4 +- io/include/blue/io/path.h | 14 ++- io/sys/darwin/directory.c | 201 ++++++++++++++++++++++++++++++-- io/sys/darwin/path.c | 206 +++++++++++++++++++++++++++++++++ 6 files changed, 419 insertions(+), 15 deletions(-) diff --git a/io-test/io-units.c b/io-test/io-units.c index 750eeda..0d172e5 100644 --- a/io-test/io-units.c +++ b/io-test/io-units.c @@ -1,7 +1,8 @@ #include +#include #include #include -#include +#include void test_path_1(CuTest *tc) { @@ -17,9 +18,9 @@ void test_path_1(CuTest *tc) b_path *path2 = b_path_create_from_cstr("path1\\path2\\"); b_path *path3 = b_path_create_from_cstr("path3\\path4\\"); - b_path *paths[] = {path, path2, path3}; + const b_path *paths[] = {path, path2, path3}; - b_path *path4 = b_path_join(paths, sizeof paths / sizeof *paths); + b_path *path4 = b_path_join(paths, sizeof paths / sizeof paths[0]); b_stringstream_begin(&str, buf, sizeof buf); b_to_string(B_OBJECT(path4), &str); diff --git a/io-test/tree.c b/io-test/tree.c index 03e84ac..bde3f5d 100644 --- a/io-test/tree.c +++ b/io-test/tree.c @@ -24,7 +24,7 @@ int main(int argc, const char **argv) b_directory_iterator it = {0}; b_directory_iterator_begin(dir, &it, B_DIRECTORY_ITERATE_PARENT_FIRST); while (b_directory_iterator_is_valid(&it)) { - printf("%s\n", it.filename); + printf("%s\n", it.filepath); b_directory_iterator_next(&it); } diff --git a/io/include/blue/io/directory.h b/io/include/blue/io/directory.h index 1138a98..6c40751 100644 --- a/io/include/blue/io/directory.h +++ b/io/include/blue/io/directory.h @@ -20,6 +20,7 @@ typedef enum b_directory_iterator_flags { typedef struct b_directory_iterator { b_iterator _base; b_directory_iterator_flags flags; + b_directory *root; char *filename; char *filepath; struct z__b_directory_iterator *_z; @@ -33,7 +34,8 @@ BLUE_API b_status b_directory_open( b_directory *root, const b_path *path, b_directory **out); BLUE_API int b_directory_iterator_begin( - b_directory *directory, b_directory_iterator *it, b_directory_iterator_flags flags); + b_directory *directory, b_directory_iterator *it, + b_directory_iterator_flags flags); BLUE_API bool b_directory_iterator_next(b_directory_iterator *it); BLUE_API b_status b_directory_iterator_erase(b_directory_iterator *it); BLUE_API bool b_directory_iterator_is_valid(const b_directory_iterator *it); diff --git a/io/include/blue/io/path.h b/io/include/blue/io/path.h index e0cd248..7118dc8 100644 --- a/io/include/blue/io/path.h +++ b/io/include/blue/io/path.h @@ -6,6 +6,8 @@ #include #include +#define B_PATH(p) ((b_path *)p) + typedef struct b_path b_path; BLUE_API b_path *b_path_create(); @@ -25,8 +27,16 @@ BLUE_API bool b_path_is_file(const b_path *path); BLUE_API bool b_path_is_directory(const b_path *path); BLUE_API const char *b_path_ptr(const b_path *path); +BLUE_API size_t b_path_length(const b_path *path); -BLUE_API b_path *b_path_retain(b_path *path); -BLUE_API void b_path_release(b_path *path); +static inline b_path *b_path_retain(b_path *path) +{ + return B_PATH(b_retain(B_OBJECT(path))); +} + +static inline void b_path_release(b_path *path) +{ + b_release(B_OBJECT(path)); +} #endif diff --git a/io/sys/darwin/directory.c b/io/sys/darwin/directory.c index 1c63c8a..f022eaf 100644 --- a/io/sys/darwin/directory.c +++ b/io/sys/darwin/directory.c @@ -1,31 +1,204 @@ #include +#include +#include #include +#include +#include +#include struct b_directory { + struct b_object base; int fd; - char *abs_path; + struct b_path *abs_path; }; struct z__b_directory_iterator { FTS *fts; - FTSENT *node; + FTSENT *ent; }; -enum b_status b_directory_open( - struct b_directory *root, const char *path, struct b_directory **out) +static void directory_release(struct b_object *obj); + +static struct b_object_type directory_type = { + .t_name = "corelib::directory", + .t_flags = B_OBJECT_FUNDAMENTAL, + .t_id = B_OBJECT_TYPE_PATH, + .t_instance_size = sizeof(struct b_directory), + .t_release = directory_release, +}; + +static enum b_status status_from_errno(int error, enum b_status default_value) { + switch (error) { + case ENOENT: + return B_ERR_NO_ENTRY; + default: + return default_value; + } +} + +enum b_status b_directory_open( + struct b_directory *root, const struct b_path *path, + struct b_directory **out) +{ + enum b_status status = B_SUCCESS; + + const b_path *parts[] = { + root ? root->abs_path : NULL, + path, + }; + + b_path *new_path = b_path_join(parts, sizeof parts / sizeof *parts); + if (!new_path) { + return B_ERR_NO_MEMORY; + } + + int root_fd = root ? root->fd : AT_FDCWD; + + int fd = openat(root_fd, b_path_ptr(path), O_DIRECTORY); + + if (fd == -1) { + status = status_from_errno(errno, B_ERR_IO_FAILURE); + + b_path_release(new_path); + + return status; + } + + struct b_directory *dir + = (struct b_directory *)b_object_type_instantiate(&directory_type); + + dir->abs_path = new_path; + dir->fd = fd; + + *out = dir; + return B_SUCCESS; } -int b_directory_iterator_begin( - struct b_directory *directory, struct b_directory_iterator *it) +static int ftsent_compare(const FTSENT **one, const FTSENT **two) { + return (strcmp((*one)->fts_name, (*two)->fts_name)); +} + +int b_directory_iterator_begin( + struct b_directory *directory, struct b_directory_iterator *it, + enum b_directory_iterator_flags flags) +{ + it->flags = flags; + it->root = directory; + + struct z__b_directory_iterator *it_data = malloc(sizeof *it_data); + memset(it_data, 0x0, sizeof *it_data); + + it->_z = it_data; + + int fts_flags = FTS_COMFOLLOW | FTS_NOCHDIR; + + const char *path_list[] = { + b_path_ptr(directory->abs_path), + NULL, + }; + + it_data->fts + = fts_open((char *const *)path_list, fts_flags, ftsent_compare); + + if (!it_data) { + return -1; + } + + bool done = false; + + while (!done) { + it_data->ent = fts_read(it_data->fts); + + if (!it_data->ent) { + return -1; + } + + if (it_data->ent->fts_level == 0) { + continue; + } + + switch (it_data->ent->fts_info) { + case FTS_DOT: + continue; + case FTS_F: + done = true; + break; + case FTS_D: + if (it->flags & B_DIRECTORY_ITERATE_PARENT_LAST) { + continue; + } + + done = true; + break; + case FTS_DP: + if (it->flags & B_DIRECTORY_ITERATE_PARENT_FIRST) { + continue; + } + done = true; + break; + default: + done = true; + break; + } + } + + it->filename = it_data->ent->fts_name; + it->filepath + = it_data->ent->fts_path + b_path_length(it->root->abs_path) + 1; return 0; } bool b_directory_iterator_next(struct b_directory_iterator *it) { - return false; + struct z__b_directory_iterator *it_data = it->_z; + if (!it_data || !it_data->fts) { + return false; + } + + bool done = false; + + while (!done) { + it_data->ent = fts_read(it_data->fts); + + if (!it_data->ent) { + return false; + } + + if (it_data->ent->fts_level == 0) { + continue; + } + + switch (it_data->ent->fts_info) { + case FTS_DOT: + continue; + case FTS_F: + done = true; + break; + case FTS_D: + if (it->flags & B_DIRECTORY_ITERATE_PARENT_LAST) { + continue; + } + done = true; + break; + case FTS_DP: + if (it->flags & B_DIRECTORY_ITERATE_PARENT_FIRST) { + continue; + } + done = true; + break; + default: + done = true; + break; + } + } + + it->filename = it_data->ent->fts_name; + it->filepath + = it_data->ent->fts_path + b_path_length(it->root->abs_path) + 1; + return true; } enum b_status b_directory_iterator_erase(struct b_directory_iterator *it) @@ -35,5 +208,17 @@ enum b_status b_directory_iterator_erase(struct b_directory_iterator *it) bool b_directory_iterator_is_valid(const struct b_directory_iterator *it) { - return false; + if (!it->_z) { + return false; + } + + if (!it->_z->ent) { + return false; + } + + return true; +} + +static void directory_release(struct b_object *obj) +{ } diff --git a/io/sys/darwin/path.c b/io/sys/darwin/path.c index e69de29..982b0b8 100644 --- a/io/sys/darwin/path.c +++ b/io/sys/darwin/path.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include + +struct b_path { + struct b_object base; + struct b_string *pathstr; +}; + +static void path_release(struct b_object *obj); +static void path_to_string(struct b_object *obj, struct b_stringstream *out); + +static struct b_object_type path_type = { + .t_name = "corelib::path", + .t_flags = B_OBJECT_FUNDAMENTAL, + .t_id = B_OBJECT_TYPE_PATH, + .t_instance_size = sizeof(struct b_path), + .t_release = path_release, + .t_to_string = path_to_string, +}; + +struct b_path *b_path_create() +{ + struct b_path *path + = (struct b_path *)b_object_type_instantiate(&path_type); + if (!path) { + return NULL; + } + + path->pathstr = b_string_create(); + if (!path->pathstr) { + b_path_release(path); + return NULL; + } + + return path; +} + +struct b_path *b_path_create_root() +{ + const char *system_drive = "/"; + size_t system_drive_len = strlen(system_drive); + + struct b_path *path = b_path_create(); + if (!path) { + return NULL; + } + + b_string_append_cstr(path->pathstr, system_drive); + if (system_drive[system_drive_len - 1] != '\\') { + b_string_append_cstr(path->pathstr, "\\"); + } + + return path; +} + +struct b_path *b_path_create_cwd() +{ + char *buf = malloc(_PC_PATH_MAX); + if (!buf) { + return NULL; + } + + if (!getcwd(buf, _PC_PATH_MAX)) { + free(buf); + return NULL; + } + + struct b_path *path = b_path_create(); + if (!path) { + free(buf); + return NULL; + } + + b_string_append_cstr(path->pathstr, buf); + free(buf); + + return path; +} + +struct b_path *b_path_create_from_cstr(const char *cstr) +{ + struct b_path *path = b_path_create(); + if (!path) { + return NULL; + } + + char prev = 0; + + for (size_t i = 0; cstr[i]; i++) { + char c = cstr[i]; + if (c == '\\') { + c = '/'; + } + + if (prev == '/' && c == '/') { + continue; + } + + char s[] = {c, 0}; + b_string_append_cstr(path->pathstr, s); + prev = c; + } + + while (b_string_back(path->pathstr) == '/') { + b_string_pop_back(path->pathstr); + } + + return path; +} + +static void append_path(struct b_path *dest, const struct b_path *src) +{ + if (b_path_is_absolute(src)) { + b_string_clear(dest->pathstr); + b_string_append_s(dest->pathstr, src->pathstr); + return; + } + + if (b_string_get_size(dest->pathstr, B_STRLEN_NORMAL) > 0 + && b_string_back(dest->pathstr) != '/' + && b_string_front(src->pathstr) != '/') { + char s[] = {'/', 0}; + b_string_append_cstr(dest->pathstr, s); + } + + b_string_append_s(dest->pathstr, src->pathstr); +} + +struct b_path *b_path_join(const b_path *paths[], size_t nr_paths) +{ + struct b_path *result = b_path_create(); + if (!result) { + return NULL; + } + + for (size_t i = 0; i < nr_paths; i++) { + if (paths[i]) { + append_path(result, paths[i]); + } + } + + return result; +} + +struct b_path *b_path_make_absolute(const struct b_path *in) +{ + return NULL; +} + +struct b_path *b_path_make_relative(const struct b_path *in) +{ + return NULL; +} + +struct b_path *b_path_make_canonical(const struct b_path *in) +{ + return NULL; +} + +bool b_path_is_absolute(const struct b_path *path) +{ + const char *s = b_string_ptr(path->pathstr); + return s[0] == '/'; +} + +bool b_path_exists(const struct b_path *path) +{ + return false; +} + +bool b_path_is_file(const struct b_path *path) +{ + return false; +} + +bool b_path_is_directory(const struct b_path *path) +{ + return false; +} + +const char *b_path_ptr(const struct b_path *path) +{ + return b_string_ptr(path->pathstr); +} + +size_t b_path_length(const struct b_path *path) +{ + return b_string_get_size(path->pathstr, B_STRLEN_NORMAL); +} + +void path_release(struct b_object *obj) +{ + b_path_release((struct b_path *)obj); +} + +void path_to_string(struct b_object *obj, struct b_stringstream *out) +{ + struct b_path *path = (struct b_path *)obj; + + b_stringstream_add(out, b_string_ptr(path->pathstr)); +}