summaryrefslogtreecommitdiff
path: root/kon/konfs.h
diff options
context:
space:
mode:
Diffstat (limited to 'kon/konfs.h')
-rw-r--r--kon/konfs.h352
1 files changed, 352 insertions, 0 deletions
diff --git a/kon/konfs.h b/kon/konfs.h
new file mode 100644
index 0000000..6fd4c64
--- /dev/null
+++ b/kon/konfs.h
@@ -0,0 +1,352 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * konfs.h
+ *
+ * Copyright (c) 2026 KonAki
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * folders and paths for the programs that browse them, on POSIX and on Windows.
+ * paths are written with / everywhere, Windows takes it too. on Windows an empty path is the list of drives
+ *
+ * #define KONFS_IMPLEMENTATION in one .c file before including this to get the code. on POSIX that file has to be built with
+ * _POSIX_C_SOURCE 200809L (src/impl/konfs.c does that)
+ */
+
+#ifndef KONFS_H
+#define KONFS_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*** definition ***/
+
+typedef struct kon_fsEntry {
+ char *name; /* no folder part */
+ int isDir;
+ long long size;
+} kon_fsEntry_t;
+
+typedef struct kon_fsList {
+ kon_fsEntry_t *items;
+ size_t count, cap;
+} kon_fsList_t;
+
+/*** declarations ***/
+
+/* fills list with what is in dir: folders first, then files, each sorted by name. names starting with a dot, and hidden files
+ on Windows, are left out. returns 0 when dir cannot be read, the list is empty then. free it with kon_fsFree either way */
+int kon_fsRead(const char *dir, kon_fsList_t *list);
+void kon_fsFree(kon_fsList_t *list);
+
+int kon_fsIsDir(const char *path);
+int kon_fsIsFile(const char *path);
+
+/* 1 when the folder is made or was already there. the folder above has to exist */
+int kon_fsMakeDir(const char *path);
+
+/* the current folder. returns 0 when it does not fit */
+int kon_fsCwd(char *out, size_t size);
+
+/* dir/name, the / is only added when dir does not end with one. cut off when it does not fit */
+void kon_fsJoin(char *out, size_t size, const char *dir, const char *name);
+
+/* takes the last part off path, in place. returns 0 when there is nothing to take off (the root, or "."). the parent of a drive on
+ Windows is "" */
+int kon_fsParent(char *path);
+
+/* the last part of path */
+const char *kon_fsBaseName(const char *path);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* KONFS_H */
+
+/*** implementation ***/
+
+#ifdef KONFS_IMPLEMENTATION
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#else
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#define KON_FS_PATH 1024
+
+static char *kon_fsDup_(const char *s)
+{
+ size_t n = strlen(s) + 1;
+ char *out = malloc(n);
+
+ if (out) memcpy(out, s, n);
+ return out;
+}
+
+static int kon_fsPush_(kon_fsList_t *list, const char *name, int isDir, long long size)
+{
+ kon_fsEntry_t *e;
+
+ if (list->count == list->cap) {
+ size_t cap = list->cap ? list->cap * 2 : 32;
+ kon_fsEntry_t *items = realloc(list->items, cap * sizeof(kon_fsEntry_t));
+
+ if (!items) return 0;
+ list->items = items;
+ list->cap = cap;
+ }
+ e = &list->items[list->count];
+ e->name = kon_fsDup_(name);
+ if (!e->name) return 0;
+ e->isDir = isDir;
+ e->size = size;
+ list->count++;
+ return 1;
+}
+
+static int kon_fsCompare_(const void *a, const void *b)
+{
+ const kon_fsEntry_t *x = a, *y = b;
+ const unsigned char *p, *q;
+
+ if (x->isDir != y->isDir) return y->isDir - x->isDir;
+ p = (const unsigned char *)x->name;
+ q = (const unsigned char *)y->name;
+ while (*p && *q) {
+ int c = tolower(*p) - tolower(*q);
+
+ if (c) return c;
+ p++;
+ q++;
+ }
+ if (*p != *q) return (int)*p - (int)*q;
+ return strcmp(x->name, y->name);
+}
+
+void kon_fsFree(kon_fsList_t *list)
+{
+ size_t i;
+
+ for (i = 0; i < list->count; i++) free(list->items[i].name);
+ free(list->items);
+ list->items = NULL;
+ list->count = list->cap = 0;
+}
+
+/* how much of the start of path is the root: / on POSIX, X:/ or X: on Windows. 0 for a relative path */
+static size_t kon_fsRootLength_(const char *path)
+{
+#ifdef _WIN32
+ if (isalpha((unsigned char)path[0]) && path[1] == ':') return path[2] == '/' ? 3 : 2;
+#endif
+ return path[0] == '/' ? 1 : 0;
+}
+
+int kon_fsParent(char *path)
+{
+ size_t n = strlen(path), root = kon_fsRootLength_(path);
+
+ if (n <= root) {
+#ifdef _WIN32
+ if (root && n) {
+ path[0] = 0;
+ return 1;
+ }
+#endif
+ return 0;
+ }
+ if (strcmp(path, ".") == 0) return 0;
+
+ while (n > root && path[n - 1] == '/') n--;
+ while (n > root && path[n - 1] != '/') n--;
+ while (n > root && path[n - 1] == '/') n--;
+
+ if (n == 0) {
+ strcpy(path, ".");
+ return 1;
+ }
+ path[n] = 0;
+ return 1;
+}
+
+const char *kon_fsBaseName(const char *path)
+{
+ const char *slash = strrchr(path, '/');
+
+ return slash ? slash + 1 : path;
+}
+
+void kon_fsJoin(char *out, size_t size, const char *dir, const char *name)
+{
+ size_t n = strlen(dir);
+
+ if (size == 0) return;
+ if (n == 0) {
+ snprintf(out, size, "%s", name);
+ } else if (dir[n - 1] == '/') {
+ snprintf(out, size, "%s%s", dir, name);
+ } else {
+ snprintf(out, size, "%s/%s", dir, name);
+ }
+}
+
+#ifdef _WIN32
+
+int kon_fsIsDir(const char *path)
+{
+ DWORD a = GetFileAttributesA(path);
+
+ return a != INVALID_FILE_ATTRIBUTES && (a & FILE_ATTRIBUTE_DIRECTORY) != 0;
+}
+
+int kon_fsIsFile(const char *path)
+{
+ DWORD a = GetFileAttributesA(path);
+
+ return a != INVALID_FILE_ATTRIBUTES && (a & FILE_ATTRIBUTE_DIRECTORY) == 0;
+}
+
+int kon_fsMakeDir(const char *path)
+{
+ if (CreateDirectoryA(path, NULL)) return 1;
+ return kon_fsIsDir(path);
+}
+
+int kon_fsCwd(char *out, size_t size)
+{
+ DWORD n = GetCurrentDirectoryA((DWORD)(size > 0xFFFF ? 0xFFFF : size), out);
+ DWORD i;
+
+ if (n == 0 || (size_t)n >= size) return 0;
+ for (i = 0; i < n; i++) {
+ if (out[i] == '\\') out[i] = '/';
+ }
+ return 1;
+}
+
+int kon_fsRead(const char *dir, kon_fsList_t *list)
+{
+ memset(list, 0, sizeof(*list));
+
+ if (dir[0] == 0) {
+ DWORD drives = GetLogicalDrives();
+ char name[4] = "A:/";
+ int i;
+
+ for (i = 0; i < 26; i++) {
+ if (!(drives & (1UL << i))) continue;
+ name[0] = (char)('A' + i);
+ if (!kon_fsPush_(list, name, 1, 0)) break;
+ }
+ return 1;
+ } else {
+ char pattern[KON_FS_PATH];
+ WIN32_FIND_DATAA data;
+ HANDLE h;
+
+ kon_fsJoin(pattern, sizeof(pattern), dir, "*");
+ h = FindFirstFileA(pattern, &data);
+ if (h == INVALID_HANDLE_VALUE) return 0;
+ do {
+ const char *name = data.cFileName;
+ long long size = ((long long)data.nFileSizeHigh << 32) | (long long)data.nFileSizeLow;
+
+ if (name[0] == '.') continue;
+ if (data.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) continue;
+ if (!kon_fsPush_(list, name, (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0, size)) break;
+ } while (FindNextFileA(h, &data));
+ FindClose(h);
+ }
+ if (list->count > 1) qsort(list->items, list->count, sizeof(kon_fsEntry_t), kon_fsCompare_);
+ return 1;
+}
+
+#else
+
+int kon_fsIsDir(const char *path)
+{
+ struct stat st;
+
+ return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
+}
+
+int kon_fsIsFile(const char *path)
+{
+ struct stat st;
+
+ return stat(path, &st) == 0 && S_ISREG(st.st_mode);
+}
+
+int kon_fsMakeDir(const char *path)
+{
+ if (mkdir(path, 0777) == 0) return 1;
+ return kon_fsIsDir(path);
+}
+
+int kon_fsCwd(char *out, size_t size)
+{
+ return getcwd(out, size) != NULL;
+}
+
+int kon_fsRead(const char *dir, kon_fsList_t *list)
+{
+ DIR *d;
+ struct dirent *ent;
+ char full[KON_FS_PATH];
+
+ memset(list, 0, sizeof(*list));
+ d = opendir(dir[0] ? dir : ".");
+ if (!d) return 0;
+
+ while ((ent = readdir(d)) != NULL) {
+ struct stat st;
+
+ if (ent->d_name[0] == '.') continue;
+ kon_fsJoin(full, sizeof(full), dir[0] ? dir : ".", ent->d_name);
+ if (strlen(full) >= sizeof(full) - 1) continue;
+ if (stat(full, &st) != 0) continue;
+ if (!kon_fsPush_(list, ent->d_name, S_ISDIR(st.st_mode) != 0, S_ISDIR(st.st_mode) ? 0 : (long long)st.st_size)) break;
+ }
+ closedir(d);
+
+ if (list->count > 1) qsort(list->items, list->count, sizeof(kon_fsEntry_t), kon_fsCompare_);
+ return 1;
+}
+
+#endif /* _WIN32 */
+
+#endif /* KONFS_IMPLEMENTATION */