diff options
| author | Ethan Werner <mail@ethanwerner.com> | 2026-06-09 01:17:41 +0400 |
|---|---|---|
| committer | Ethan Werner <mail@ethanwerner.com> | 2026-06-09 01:17:41 +0400 |
| commit | 96ecf29e8be977f51f351308ef2c7c492bb829aa (patch) | |
| tree | c664aa61262e6f7627a732f0e4fbb84281b99952 /include/flat.h | |
| parent | f6a144987cc403de8ad6bddd055f756120f8d405 (diff) | |
refactor
Diffstat (limited to 'include/flat.h')
| -rwxr-xr-x | include/flat.h | 293 |
1 files changed, 261 insertions, 32 deletions
diff --git a/include/flat.h b/include/flat.h index 17a1cc4..9bbde7a 100755 --- a/include/flat.h +++ b/include/flat.h @@ -1,16 +1,30 @@ // flat.h - Directory traversal utility functions +// +// Thread safety: Not thread-safe. External synchronization required. +// Memory: flat_directory_entries returns a NULL-terminated array of +// heap-allocated strings. Use flat_free_entries to release both. +// Portability: Provides POSIX (dirent.h) and Windows (FindFirstFileA) impls. #ifndef FLAT_H +#define FLAT_H -#include <stdint.h> +#include "type.h" #include <sys/stat.h> +#ifdef _WIN32 +#include <direct.h> +#include <windows.h> +#define MKDIR(x) (mkdir(x)) +#else +#include <dirent.h> +#define MKDIR(x) (mkdir(x, 0755)) +#endif + char *flat_path(int, ...); -int64_t flat_directory_entry_count(char const *); +void flat_free_entries(char **); +i64 flat_directory_entry_count(char const *); char **flat_directory_entries(char const *); -#define MKDIR(x) (mkdir(x, 0777)) - #endif // FLAT_H #ifdef FLAT_IMPLEMENTATION @@ -19,7 +33,16 @@ char **flat_directory_entries(char const *); #include <stdlib.h> #include <string.h> -#include <dirent.h> +#ifndef _WIN32 + +// flat_path() +// +// Join path components into a single allocated string. Uses '/' separator +// on POSIX, '\\' on Windows. Caller must free the result. +// +// s_n - Number of string components +// +// return - Allocated path string, or NULL on failure char *flat_path(int s_n, ...) { va_list list; @@ -28,45 +51,55 @@ char *flat_path(int s_n, ...) { if (s_n < 1) return NULL; - // First, measure the total length required. va_start(list, s_n); for (int i = 0; i < s_n; i++) { const char *s = va_arg(list, char *); - c_n += strlen(s); + if (s) + c_n += strlen(s); } va_end(list); - // Allocate return buffer. - char *r = malloc(c_n + 1); + size_t separators = (s_n > 1 ? (size_t)(s_n - 1) : 0); + if (c_n > SIZE_MAX - separators - 1) + return NULL; + size_t total = c_n + separators + 1; + char *r = malloc(total); if (r == NULL) return NULL; - // Concatenate all the strings into the return buffer. char *d = r; va_start(list, s_n); for (int i = 0; i < s_n; i++) { const char *s = va_arg(list, char *); - // This loop is a strcpy. - while ((*d++ = *s++)) - ; - d--; + // Add separator between components (not before the first). + if (i > 0) + *d++ = '/'; + + if (s) { + while ((*d++ = *s++)) + ; + d--; // Step back over the null terminator. + } } va_end(list); + *d = '\0'; return r; } -int64_t flat_directory_entry_count(char const *path) { +i64 flat_directory_entry_count(char const *path) { DIR *dir; struct dirent *ent; + if (path == NULL) + return 0; if (!(dir = opendir(path))) { return 0; } - int64_t n = 0; + i64 n = 0; while ((ent = readdir(dir))) { if (!strcmp(ent->d_name, ".")) @@ -83,38 +116,234 @@ int64_t flat_directory_entry_count(char const *path) { return n; } -char **flat_directory_entries(char const *path) { - int64_t n = flat_directory_entry_count(path); - char **entries = calloc(n + 1, sizeof(char **)); +static int flat_skip_entry(struct dirent *ent) { + if (!ent) return 1; + if (!strcmp(ent->d_name, ".")) return 1; + if (!strcmp(ent->d_name, "..")) return 1; + return 0; +} +char **flat_directory_entries(char const *path) { DIR *dir; struct dirent *ent; - int64_t i = 0; - int64_t entry_n = 0; - + if (path == NULL) + return NULL; if (!(dir = opendir(path))) { return NULL; } - while (i < n) { - ent = readdir(dir); - if (!strcmp(ent->d_name, ".")) - continue; + i64 capacity = 16; + char **entries = calloc((size_t)capacity, sizeof(char *)); + if (!entries) { + closedir(dir); + return NULL; + } - if (!strcmp(ent->d_name, "..")) + i64 count = 0; + while ((ent = readdir(dir))) { + if (flat_skip_entry(ent)) continue; - entry_n = strlen(ent->d_name); - entries[i] = malloc(entry_n + 1); - strcpy(entries[i], ent->d_name); + if (count >= capacity - 1) { + if (capacity > INT64_MAX / 2) { + flat_free_entries(entries); + closedir(dir); + return NULL; + } + i64 new_capacity = capacity * 2; + if ((size_t)new_capacity > SIZE_MAX / sizeof(char *)) { + flat_free_entries(entries); + closedir(dir); + return NULL; + } + char **new_entries = realloc(entries, (size_t)new_capacity * sizeof(char *)); + if (!new_entries) { + flat_free_entries(entries); + closedir(dir); + return NULL; + } + entries = new_entries; + capacity = new_capacity; + } - i++; + size_t entry_n = strlen(ent->d_name); + entries[count] = malloc(entry_n + 1); + if (!entries[count]) { + flat_free_entries(entries); + closedir(dir); + return NULL; + } + strcpy(entries[count], ent->d_name); + count++; } + entries[count] = NULL; + closedir(dir); return entries; } -#endif // FLAT_IMPELEMTATION +void flat_free_entries(char **entries) { + if (!entries) + return; + i64 i = 0; + while (entries[i]) { + free(entries[i]); + i++; + } + free(entries); +} + +#else /* _WIN32 */ + +#include <errno.h> +#include <windows.h> + +static int flat_skip_entry_win(char const *name) { + if (!name) return 1; + if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) + return 1; + return 0; +} + +char *flat_path(int s_n, ...) { + va_list list; + size_t c_n = 0; + + if (s_n < 1) + return NULL; + + va_start(list, s_n); + for (int i = 0; i < s_n; i++) { + const char *s = va_arg(list, char *); + if (s) + c_n += strlen(s); + } + va_end(list); + + size_t separators = (s_n > 1 ? (size_t)(s_n - 1) : 0); + if (c_n > SIZE_MAX - separators - 1) + return NULL; + size_t total = c_n + separators + 1; + char *r = malloc(total); + if (r == NULL) + return NULL; + + char *d = r; + va_start(list, s_n); + for (int i = 0; i < s_n; i++) { + const char *s = va_arg(list, char *); + if (i > 0) + *d++ = '\\'; + if (s) { + while ((*d++ = *s++)) + ; + d--; + } + } + va_end(list); + *d = '\0'; + return r; +} + +i64 flat_directory_entry_count(char const *path) { + if (path == NULL) + return 0; + char pattern[4096]; + if (strlen(path) > sizeof(pattern) - 3) + return 0; + sprintf(pattern, "%s\\*", path); + + WIN32_FIND_DATAA fd; + HANDLE h = FindFirstFileA(pattern, &fd); + if (h == INVALID_HANDLE_VALUE) + return 0; + + i64 n = 0; + do { + if (!flat_skip_entry_win(fd.cFileName)) + n++; + } while (FindNextFileA(h, &fd)); + + FindClose(h); + return n; +} + +char **flat_directory_entries(char const *path) { + if (path == NULL) + return NULL; + char pattern[4096]; + if (strlen(path) > sizeof(pattern) - 3) + return NULL; + sprintf(pattern, "%s\\*", path); + + WIN32_FIND_DATAA fd; + HANDLE h = FindFirstFileA(pattern, &fd); + if (h == INVALID_HANDLE_VALUE) + return NULL; + + i64 capacity = 16; + char **entries = calloc((size_t)capacity, sizeof(char *)); + if (!entries) { + FindClose(h); + return NULL; + } + + i64 count = 0; + do { + if (flat_skip_entry_win(fd.cFileName)) + continue; + + if (count >= capacity - 1) { + if (capacity > INT64_MAX / 2) { + flat_free_entries(entries); + FindClose(h); + return NULL; + } + i64 new_capacity = capacity * 2; + if ((size_t)new_capacity > SIZE_MAX / sizeof(char *)) { + flat_free_entries(entries); + FindClose(h); + return NULL; + } + char **new_entries = realloc(entries, (size_t)new_capacity * sizeof(char *)); + if (!new_entries) { + flat_free_entries(entries); + FindClose(h); + return NULL; + } + entries = new_entries; + capacity = new_capacity; + } + + size_t entry_n = strlen(fd.cFileName); + entries[count] = malloc(entry_n + 1); + if (!entries[count]) { + flat_free_entries(entries); + FindClose(h); + return NULL; + } + strcpy(entries[count], fd.cFileName); + count++; + } while (FindNextFileA(h, &fd)); + + entries[count] = NULL; + FindClose(h); + return entries; +} + +void flat_free_entries(char **entries) { + if (!entries) + return; + i64 i = 0; + while (entries[i]) { + free(entries[i]); + i++; + } + free(entries); +} + +#endif /* _WIN32 */ +#endif // FLAT_IMPLEMENTATION |