summaryrefslogtreecommitdiffstats
path: root/include/flat.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/flat.h')
-rwxr-xr-xinclude/flat.h293
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