aboutsummaryrefslogtreecommitdiff
path: root/src/path.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/path.c')
-rw-r--r--src/path.c208
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;
+}