diff options
| author | David Moc <personal@cdatgoose.org> | 2026-05-31 03:47:04 +0200 |
|---|---|---|
| committer | David Moc <personal@cdatgoose.org> | 2026-05-31 03:47:04 +0200 |
| commit | 6aeaa171dc1ca43392f53cbd02097f76e1b1c5a0 (patch) | |
| tree | b16f559f5a701123ebe7b15ecebb9325263b4a3c /src/path.c | |
| parent | e930cc6bdc7f62befac063d7d9d016ffb0a64f1a (diff) | |
Hardened API, tetris, MD-View
Diffstat (limited to 'src/path.c')
| -rw-r--r-- | src/path.c | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/src/path.c b/src/path.c new file mode 100644 index 0000000..1e002d7 --- /dev/null +++ b/src/path.c @@ -0,0 +1,208 @@ +#include "path.h" + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +extern char *realpath(const char *restrict path, char *restrict resolved_path); + +int ecex_path_copy(char *out, size_t out_size, const char *text) { + if (!out || out_size == 0) return -1; + if (!text) text = ""; + size_t len = strlen(text); + if (len >= out_size) { + memcpy(out, text, out_size - 1); + out[out_size - 1] = '\0'; + return -1; + } + memcpy(out, text, len + 1); + return 0; +} + +char *ecex_path_expand_user(const char *path) { + if (!path) return NULL; + if (path[0] != '~' || (path[1] != '\0' && path[1] != '/')) { + char *copy = malloc(strlen(path) + 1); + if (copy) strcpy(copy, path); + return copy; + } + + const char *home = getenv("HOME"); + if (!home || !home[0]) { + char *copy = malloc(strlen(path) + 1); + if (copy) strcpy(copy, path); + return copy; + } + + size_t home_len = strlen(home); + size_t rest_len = strlen(path + 1); + char *expanded = malloc(home_len + rest_len + 1); + if (!expanded) return NULL; + memcpy(expanded, home, home_len); + memcpy(expanded + home_len, path + 1, rest_len + 1); + return expanded; +} + +char *ecex_path_join(const char *dir, const char *name) { + if (!name) return NULL; + if (name[0] == '/') { + char *copy = malloc(strlen(name) + 1); + if (copy) strcpy(copy, name); + return copy; + } + if (!dir || !dir[0]) dir = "."; + + size_t dl = strlen(dir); + size_t nl = strlen(name); + int need_slash = dl > 0 && dir[dl - 1] != '/'; + char *out = malloc(dl + (size_t)need_slash + nl + 1); + if (!out) return NULL; + memcpy(out, dir, dl); + if (need_slash) out[dl++] = '/'; + memcpy(out + dl, name, nl + 1); + return out; +} + +char *ecex_path_dirname(const char *path) { + if (!path || !path[0]) { + char *dot = malloc(2); + if (dot) strcpy(dot, "."); + return dot; + } + + char *expanded = ecex_path_expand_user(path); + if (!expanded) return NULL; + + size_t len = strlen(expanded); + while (len > 1 && expanded[len - 1] == '/') expanded[--len] = '\0'; + + char *slash = strrchr(expanded, '/'); + if (!slash) { + strcpy(expanded, "."); + } else if (slash == expanded) { + expanded[1] = '\0'; + } else { + *slash = '\0'; + } + return expanded; +} + +char *ecex_path_basename_dup(const char *path) { + const char *base = "untitled"; + if (path && path[0]) { + const char *slash = strrchr(path, '/'); + if (slash && slash[1]) base = slash + 1; + else if (slash && slash == path) base = "/"; + else base = path; + } + char *out = malloc(strlen(base) + 1); + if (out) strcpy(out, base); + return out; +} + +char *ecex_path_normalize(const char *path) { + char *expanded = ecex_path_expand_user(path); + if (!expanded) return NULL; + + char *resolved = realpath(expanded, NULL); + if (resolved) { + free(expanded); + return resolved; + } + + if (expanded[0] == '/') return expanded; + + char cwd[4096]; + if (!getcwd(cwd, sizeof(cwd))) return expanded; + char *joined = ecex_path_join(cwd, expanded); + free(expanded); + return joined; +} + +int ecex_path_exists(const char *path) { + if (!path) return 0; + char *expanded = ecex_path_expand_user(path); + if (!expanded) return 0; + struct stat st; + int ok = stat(expanded, &st) == 0; + free(expanded); + return ok; +} + +int ecex_path_is_dir(const char *path) { + if (!path) return 0; + char *expanded = ecex_path_expand_user(path); + if (!expanded) return 0; + struct stat st; + int ok = stat(expanded, &st) == 0 && S_ISDIR(st.st_mode); + free(expanded); + return ok; +} + +int ecex_path_is_file(const char *path) { + if (!path) return 0; + char *expanded = ecex_path_expand_user(path); + if (!expanded) return 0; + struct stat st; + int ok = stat(expanded, &st) == 0 && S_ISREG(st.st_mode); + free(expanded); + return ok; +} + +long long ecex_path_file_size(const char *path) { + if (!path) return -1; + char *expanded = ecex_path_expand_user(path); + if (!expanded) return -1; + struct stat st; + long long size = (stat(expanded, &st) == 0 && S_ISREG(st.st_mode)) ? (long long)st.st_size : -1; + free(expanded); + return size; +} + +static int ext_eq(const char *path, const char *ext) { + if (!path || !ext) return 0; + size_t pl = strlen(path), el = strlen(ext); + if (pl < el) return 0; + const char *p = path + pl - el; + for (size_t i = 0; i < el; i++) { + if (tolower((unsigned char)p[i]) != tolower((unsigned char)ext[i])) return 0; + } + return 1; +} + +int ecex_path_is_image(const char *path) { + return ext_eq(path, ".png") || ext_eq(path, ".jpg") || ext_eq(path, ".jpeg") || + ext_eq(path, ".bmp") || ext_eq(path, ".ppm") || ext_eq(path, ".pnm") || + ext_eq(path, ".pgm") || ext_eq(path, ".gif") || ext_eq(path, ".webp") || + ext_eq(path, ".tif") || ext_eq(path, ".tiff") || ext_eq(path, ".avif") || + ext_eq(path, ".heic") || ext_eq(path, ".heif") || ext_eq(path, ".jxl") || + ext_eq(path, ".qoi") || ext_eq(path, ".tga") || ext_eq(path, ".dds") || + ext_eq(path, ".exr") || ext_eq(path, ".hdr") || ext_eq(path, ".ico"); +} + +int ecex_path_is_previewable_image(const char *path) { + return ecex_path_is_image(path); +} + +int ecex_path_is_video(const char *path) { + return ext_eq(path, ".mp4") || ext_eq(path, ".m4v") || ext_eq(path, ".mov") || + ext_eq(path, ".mkv") || ext_eq(path, ".webm") || ext_eq(path, ".avi") || + ext_eq(path, ".wmv") || ext_eq(path, ".flv") || ext_eq(path, ".gif") || + ext_eq(path, ".ogv") || ext_eq(path, ".mpeg") || ext_eq(path, ".mpg"); +} + +int ecex_path_is_media(const char *path) { + return ecex_path_is_image(path) || ecex_path_is_video(path); +} + +int ecex_path_cwd(char *out, size_t out_size) { + if (!out || out_size == 0) return -1; + if (!getcwd(out, out_size)) { + ecex_path_copy(out, out_size, "."); + return -1; + } + return 0; +} |
