aboutsummaryrefslogtreecommitdiff
path: root/src/Path
diff options
context:
space:
mode:
authorfrosty <gabriel@bwaaa.monster>2026-09-16 16:32:06 -0400
committerfrosty <gabriel@bwaaa.monster>2026-09-16 16:32:06 -0400
commit4c8d764d171ec77ff626cec273c44ec7e7a995fc (patch)
treedc36193e611ece53c8f7e74d1dea71f8d9e7004e /src/Path
downloadyapssg-master.tar.gz
init: initHEADmaster
Diffstat (limited to 'src/Path')
-rw-r--r--src/Path/Path.c185
-rw-r--r--src/Path/Path.h10
2 files changed, 195 insertions, 0 deletions
diff --git a/src/Path/Path.c b/src/Path/Path.c
new file mode 100644
index 0000000..8a0ab2b
--- /dev/null
+++ b/src/Path/Path.c
@@ -0,0 +1,185 @@
+#define _POSIX_C_SOURCE 200809L
+#define _XOPEN_SOURCE 700
+
+#include "Path/Path.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const char output_prefix[] = "yapssg_";
+
+static int checked_add(size_t left, size_t right, size_t *sum) {
+ if (left > SIZE_MAX - right) {
+ return -1;
+ }
+ *sum = left + right;
+ return 0;
+}
+
+static char *duplicate_string(const char *value) {
+ size_t length = strlen(value);
+ char *copy = malloc(length + 1);
+
+ if (copy != NULL) {
+ memcpy(copy, value, length + 1);
+ }
+ return copy;
+}
+
+char *path_join(const char *left, const char *right) {
+ size_t left_length = strlen(left);
+ size_t right_length = strlen(right);
+ int needs_separator = left_length > 0 && left[left_length - 1] != '/';
+ size_t path_length;
+ size_t allocation_size;
+ char *path;
+
+ if (checked_add(left_length, (size_t)needs_separator, &path_length) != 0 ||
+ checked_add(path_length, right_length, &path_length) != 0 ||
+ checked_add(path_length, 1, &allocation_size) != 0) {
+ return NULL;
+ }
+
+ path = malloc(allocation_size);
+ if (path == NULL) {
+ return NULL;
+ }
+ memcpy(path, left, left_length);
+ if (needs_separator) {
+ path[left_length++] = '/';
+ }
+ memcpy(path + left_length, right, right_length + 1);
+ return path;
+}
+
+char *path_make_default_output(const char *input_path) {
+ const char *end = input_path + strlen(input_path);
+ const char *base;
+ size_t parent_length;
+ size_t base_length;
+ size_t prefix_length = sizeof(output_prefix) - 1;
+ size_t output_length;
+ size_t allocation_size;
+ char *output_path;
+
+ while (end > input_path + 1 && end[-1] == '/') {
+ --end;
+ }
+ base = end;
+ while (base > input_path && base[-1] != '/') {
+ --base;
+ }
+
+ parent_length = (size_t)(base - input_path);
+ base_length = (size_t)(end - base);
+ if (base_length == 0) {
+ return NULL;
+ }
+
+ if (checked_add(parent_length, prefix_length, &output_length) != 0 ||
+ checked_add(output_length, base_length, &output_length) != 0 ||
+ checked_add(output_length, 1, &allocation_size) != 0) {
+ return NULL;
+ }
+
+ output_path = malloc(allocation_size);
+ if (output_path == NULL) {
+ return NULL;
+ }
+ memcpy(output_path, input_path, parent_length);
+ memcpy(output_path + parent_length, output_prefix, prefix_length);
+ memcpy(output_path + parent_length + prefix_length, base, base_length);
+ output_path[parent_length + prefix_length + base_length] = '\0';
+ return output_path;
+}
+
+char *path_resolve_new(const char *path) {
+ char *path_copy = duplicate_string(path);
+ char *slash;
+ char *parent;
+ char *resolved_parent;
+ char *resolved_path;
+ const char *base;
+ size_t length;
+
+ if (path_copy == NULL) {
+ return NULL;
+ }
+
+ length = strlen(path_copy);
+ while (length > 1 && path_copy[length - 1] == '/') {
+ path_copy[--length] = '\0';
+ }
+
+ slash = strrchr(path_copy, '/');
+ if (slash == NULL) {
+ parent = duplicate_string(".");
+ base = path_copy;
+ } else if (slash == path_copy) {
+ parent = duplicate_string("/");
+ base = slash + 1;
+ } else {
+ *slash = '\0';
+ parent = duplicate_string(path_copy);
+ base = slash + 1;
+ }
+
+ if (parent == NULL || base[0] == '\0') {
+ free(parent);
+ free(path_copy);
+ return NULL;
+ }
+
+ resolved_parent = realpath(parent, NULL);
+ free(parent);
+ if (resolved_parent == NULL) {
+ free(path_copy);
+ return NULL;
+ }
+
+ resolved_path = path_join(resolved_parent, base);
+ free(resolved_parent);
+ free(path_copy);
+ return resolved_path;
+}
+
+char *path_resolve_from_file(const char *file_path, const char *relative_path) {
+ const char *slash;
+ size_t directory_length;
+ size_t relative_length;
+ size_t path_length;
+ size_t allocation_size;
+ char *path;
+
+ if (relative_path[0] == '/') {
+ return duplicate_string(relative_path);
+ }
+
+ slash = strrchr(file_path, '/');
+ directory_length = slash == NULL ? 0 : (size_t)(slash - file_path + 1);
+ relative_length = strlen(relative_path);
+ if (checked_add(directory_length, relative_length, &path_length) != 0 ||
+ checked_add(path_length, 1, &allocation_size) != 0) {
+ return NULL;
+ }
+
+ path = malloc(allocation_size);
+ if (path == NULL) {
+ return NULL;
+ }
+
+ memcpy(path, file_path, directory_length);
+ memcpy(path + directory_length, relative_path, relative_length + 1);
+ return path;
+}
+
+int path_is_within(const char *parent, const char *candidate) {
+ size_t parent_length = strlen(parent);
+
+ if (parent_length == 1 && parent[0] == '/') {
+ return candidate[0] == '/';
+ }
+ return strncmp(parent, candidate, parent_length) == 0 &&
+ (candidate[parent_length] == '\0' || candidate[parent_length] == '/');
+}
diff --git a/src/Path/Path.h b/src/Path/Path.h
new file mode 100644
index 0000000..a4ac530
--- /dev/null
+++ b/src/Path/Path.h
@@ -0,0 +1,10 @@
+#ifndef YAPSSG_PATH_H
+#define YAPSSG_PATH_H
+
+char *path_join(const char *left, const char *right);
+char *path_make_default_output(const char *input_path);
+char *path_resolve_new(const char *path);
+char *path_resolve_from_file(const char *file_path, const char *relative_path);
+int path_is_within(const char *parent, const char *candidate);
+
+#endif