From 4c8d764d171ec77ff626cec273c44ec7e7a995fc Mon Sep 17 00:00:00 2001 From: frosty Date: Wed, 16 Sep 2026 16:32:06 -0400 Subject: init: init --- src/DirectoryWalker/DirectoryWalker.c | 249 ++++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 src/DirectoryWalker/DirectoryWalker.c (limited to 'src/DirectoryWalker/DirectoryWalker.c') diff --git a/src/DirectoryWalker/DirectoryWalker.c b/src/DirectoryWalker/DirectoryWalker.c new file mode 100644 index 0000000..dc3729a --- /dev/null +++ b/src/DirectoryWalker/DirectoryWalker.c @@ -0,0 +1,249 @@ +#define _POSIX_C_SOURCE 200809L + +#include "DirectoryWalker/DirectoryWalker.h" + +#include "Path/Path.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define WALKER_METATABLE "yapssg.directory_walker" + +typedef struct { + DIR *directory; + char *absolute_path; + char *display_path; + size_t depth; +} WalkFrame; + +typedef struct { + WalkFrame *frames; + size_t length; + size_t capacity; +} DirectoryWalker; + +static void close_frame(WalkFrame *frame) { + if (frame->directory != NULL) { + closedir(frame->directory); + } + free(frame->absolute_path); + free(frame->display_path); +} + +static void clear_walker(DirectoryWalker *walker) { + while (walker->length > 0) { + close_frame(&walker->frames[--walker->length]); + } + free(walker->frames); + walker->frames = NULL; + walker->capacity = 0; +} + +static int walker_gc(lua_State *lua) { + DirectoryWalker *walker = lua_touserdata(lua, 1); + + clear_walker(walker); + return 0; +} + +static int grow_walker(DirectoryWalker *walker) { + size_t larger_capacity; + WalkFrame *larger_frames; + + if (walker->capacity > SIZE_MAX / (2 * sizeof(*walker->frames))) { + errno = ENOMEM; + return -1; + } + + larger_capacity = walker->capacity == 0 ? 8 : walker->capacity * 2; + larger_frames = + realloc(walker->frames, larger_capacity * sizeof(*walker->frames)); + if (larger_frames == NULL) { + errno = ENOMEM; + return -1; + } + + walker->frames = larger_frames; + walker->capacity = larger_capacity; + return 0; +} + +static int push_frame(DirectoryWalker *walker, char *absolute_path, + char *display_path, size_t depth) { + DIR *directory = opendir(absolute_path); + WalkFrame *frame; + + if (directory == NULL) { + return -1; + } + if (walker->length == walker->capacity && grow_walker(walker) != 0) { + int error_number = errno; + + closedir(directory); + errno = error_number; + return -1; + } + + frame = &walker->frames[walker->length++]; + frame->directory = directory; + frame->absolute_path = absolute_path; + frame->display_path = display_path; + frame->depth = depth; + return 0; +} + +static const char *entry_type(mode_t mode) { + if (S_ISREG(mode)) { + return "file"; + } + if (S_ISDIR(mode)) { + return "directory"; + } + if (S_ISLNK(mode)) { + return "symlink"; + } + return "other"; +} + +static void push_entry(lua_State *lua, const char *path, const char *name, + const struct stat *status, size_t depth) { + lua_newtable(lua); + + lua_pushstring(lua, path); + lua_setfield(lua, -2, "path"); + + lua_pushstring(lua, name); + lua_setfield(lua, -2, "name"); + + lua_pushstring(lua, entry_type(status->st_mode)); + lua_setfield(lua, -2, "type"); + + lua_pushnumber(lua, (lua_Number)depth); + lua_setfield(lua, -2, "depth"); + + if (S_ISREG(status->st_mode)) { + lua_pushnumber(lua, (lua_Number)status->st_size); + lua_setfield(lua, -2, "size"); + } +} + +static int walker_next(lua_State *lua) { + DirectoryWalker *walker = lua_touserdata(lua, lua_upvalueindex(1)); + + while (walker->length > 0) { + WalkFrame *frame = &walker->frames[walker->length - 1]; + struct dirent *entry; + char *absolute_path; + char *display_path; + struct stat status; + size_t depth; + + errno = 0; + entry = readdir(frame->directory); + if (entry == NULL) { + int error_number = errno; + + close_frame(frame); + --walker->length; + if (error_number != 0) { + clear_walker(walker); + return luaL_error(lua, "cannot read directory while walking: %s", + strerror(error_number)); + } + continue; + } + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + absolute_path = path_join(frame->absolute_path, entry->d_name); + display_path = path_join(frame->display_path, entry->d_name); + if (absolute_path == NULL || display_path == NULL) { + free(absolute_path); + free(display_path); + clear_walker(walker); + return luaL_error(lua, "unable to allocate directory-walk path"); + } + if (lstat(absolute_path, &status) != 0) { + int error_number = errno; + + free(absolute_path); + free(display_path); + clear_walker(walker); + return luaL_error(lua, "cannot inspect directory entry: %s", + strerror(error_number)); + } + + depth = frame->depth + 1; + push_entry(lua, display_path, entry->d_name, &status, depth); + + if (S_ISDIR(status.st_mode)) { + if (push_frame(walker, absolute_path, display_path, depth) != 0) { + int error_number = errno; + + free(absolute_path); + free(display_path); + clear_walker(walker); + return luaL_error(lua, "cannot open directory while walking: %s", + strerror(error_number)); + } + } else { + free(absolute_path); + free(display_path); + } + return 1; + } + clear_walker(walker); + return 0; +} + +static int walk(lua_State *lua) { + const char *input_path = lua_tostring(lua, lua_upvalueindex(1)); + const char *requested_path = luaL_checkstring(lua, 1); + char *absolute_path = path_resolve_from_file(input_path, requested_path); + char *display_path = path_join("", requested_path); + DirectoryWalker *walker; + + if (absolute_path == NULL || display_path == NULL) { + free(absolute_path); + free(display_path); + return luaL_error(lua, "unable to allocate directory-walk root"); + } + + walker = lua_newuserdata(lua, sizeof(*walker)); + memset(walker, 0, sizeof(*walker)); + luaL_getmetatable(lua, WALKER_METATABLE); + lua_setmetatable(lua, -2); + + if (push_frame(walker, absolute_path, display_path, 0) != 0) { + int error_number = errno; + + free(absolute_path); + free(display_path); + return luaL_error(lua, "cannot open directory '%s': %s", requested_path, + strerror(error_number)); + } + + lua_pushcclosure(lua, walker_next, 1); + return 1; +} + +void directory_walker_register(lua_State *lua, const char *input_path) { + if (luaL_newmetatable(lua, WALKER_METATABLE)) { + lua_pushcfunction(lua, walker_gc); + lua_setfield(lua, -2, "__gc"); + } + lua_pop(lua, 1); + + lua_newtable(lua); + lua_pushstring(lua, input_path); + lua_pushcclosure(lua, walk, 1); + lua_setfield(lua, -2, "walk"); + lua_setglobal(lua, "fs"); +} -- cgit v1.3