diff options
| -rw-r--r-- | kon/konfs.h | 352 | ||||
| -rw-r--r-- | src/impl/konfs.c | 3 |
2 files changed, 355 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 */ diff --git a/src/impl/konfs.c b/src/impl/konfs.c new file mode 100644 index 0000000..ec5f332 --- /dev/null +++ b/src/impl/konfs.c @@ -0,0 +1,3 @@ +#define _POSIX_C_SOURCE 200809L +#define KONFS_IMPLEMENTATION +#include "konfs.h" |
