aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-07-24 23:17:48 +0200
committerXavier Del Campo Romero <xavi92@disroot.org>2025-09-24 11:03:39 +0200
commita74f4a72504d1c5923bd2b4e85941f34d9fce79f (patch)
tree10ddc5729cfa30ba1a15d26c2e385680c1e54e9d
parentacfc7b820429b7ce2a0a7d41c735388a5529ed6a (diff)
downloadslcl-a74f4a72504d1c5923bd2b4e85941f34d9fce79f.tar.gz
Introduce crealpath
crealpath (for "custom realpath") is a custom implementation of realpath(3) that aims to work similarly to GNU's realpath(1). This implementation is provided due to the following reasons: - Future commits will require extracting an absolute path from a relative path, and/or process relative components from a path, such as ".." or ".". - realpath(3) is defined by POSIX.1-2008 as a XSI extension, and extensions are generally avoided in this repository whenever possible. - Additionally, realpath(3) requires the file or directory pointed to by the path to exist, which might not always be the case for slcl. - auth.c uses its own implementation to extract a dynamically allocated string by repeatedly calling getcwd(3). Future commits will also require this future, so it makes sense to keep it on a separate component.
-rw-r--r--CMakeLists.txt1
-rwxr-xr-xconfigure1
-rw-r--r--crealpath.c174
-rw-r--r--crealpath.h8
4 files changed, 184 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8c216d7..d34a876 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,6 +4,7 @@ add_executable(${PROJECT_NAME}
auth.c
base64.c
cftw.c
+ crealpath.c
hex.c
jwt.c
main.c
diff --git a/configure b/configure
index afe9057..3a6b1ce 100755
--- a/configure
+++ b/configure
@@ -96,6 +96,7 @@ OBJECTS = \
auth.o \
base64.o \
cftw.o \
+ crealpath.o \
hex.o \
jwt.o \
main.o \
diff --git a/crealpath.c b/crealpath.c
new file mode 100644
index 0000000..9403911
--- /dev/null
+++ b/crealpath.c
@@ -0,0 +1,174 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include "crealpath.h"
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static char *alloc_cwd(void)
+{
+ size_t len = 1;
+ char *p = NULL;
+
+ for (;;)
+ {
+ char *const pp = realloc(p, len);
+
+ if (!pp)
+ {
+ fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
+ break;
+ }
+
+ p = pp;
+
+ if (!getcwd(pp, len))
+ {
+ if (errno != ERANGE)
+ {
+ fprintf(stderr, "%s: getcwd(3): %s\n",
+ __func__, strerror(errno));
+ break;
+ }
+ else
+ len++;
+ }
+ else
+ return p;
+ }
+
+ free(p);
+ return NULL;
+}
+
+static char *get_parent(char *p)
+{
+ char *last;
+ size_t len;
+
+ if (!p || !(last = strrchr(p, '/')) || strlen(last) < strlen("/a"))
+ {
+ fprintf(stderr, "%s: parent folder expected\n", __func__);
+ return NULL;
+ }
+
+ len = last == p ? strlen("/") : last - p;
+
+ if (!(p = realloc(p, len + 1)))
+ {
+ fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
+ return NULL;
+ }
+
+ p[len] = '\0';
+ return p;
+}
+
+static char *get_component(const char **const path)
+{
+ char *ret;
+
+ while (**path == '/')
+ (*path)++;
+
+ const char *const next = strchr(*path, '/');
+ const size_t len = next ? next - *path : strlen(*path);
+
+ if (!(ret = malloc(len + 1)))
+ {
+ fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
+ return NULL;
+ }
+
+ memcpy(ret, *path, len);
+ ret[len] = '\0';
+ *path += len;
+ return ret;
+}
+
+static char *resolve_component(const char **const path, char *p)
+{
+ char *const c = get_component(path);
+
+ if (!*c)
+ {
+ if (!p && !(p = strdup("/")))
+ {
+ fprintf(stderr, "%s: strdup(3): %s\n", __func__, strerror(errno));
+ goto end;
+ }
+
+ goto end;
+ }
+ else if (!strcmp(c, "..") && !(p = get_parent(p)))
+ {
+ fprintf(stderr, "%s: get_parent failed\n", __func__);
+ goto end;
+ }
+ else if (strcmp(c, "."))
+ {
+ const size_t len = p ? strlen(p) : 0, clen = strlen(c);
+
+ if (!(p = realloc(p, len + strlen("/") + clen + 1)))
+ {
+ fprintf(stderr, "%s: realloc(3): %s\n",
+ __func__, strerror(errno));
+ goto end;
+ }
+ else
+ {
+ p[len] = '/';
+ memcpy(&p[len + 1], c, clen);
+ p[len + 1 + clen] = '\0';
+ }
+ }
+
+end:
+ free(c);
+ return p;
+}
+
+static char *resolve(const char *path, char *p)
+{
+ while (*path)
+ if (!(p = resolve_component(&path, p)))
+ {
+ fprintf(stderr, "%s: resolve_component failed\n", __func__);
+ return NULL;
+ }
+
+ return p;
+}
+
+char *crealpath(const char *const path)
+{
+ char *p = NULL, *ret = NULL;
+
+ if (!*path)
+ {
+ fprintf(stderr, "%s: expected non-empty path\n", __func__);
+ goto end;
+ }
+ else if (*path != '/' && !(p = alloc_cwd()))
+ {
+ fprintf(stderr, "%s: alloc_cwd failed\n", __func__);
+ goto end;
+ }
+ else if (!(p = resolve(path, p)))
+ {
+ fprintf(stderr, "%s: resolve failed\n", __func__);
+ goto end;
+ }
+
+ ret = p;
+
+end:
+ if (!ret)
+ free(p);
+
+ return ret;
+}
diff --git a/crealpath.h b/crealpath.h
new file mode 100644
index 0000000..8a178c4
--- /dev/null
+++ b/crealpath.h
@@ -0,0 +1,8 @@
+#ifndef CREALPATH_H
+#define CREALPATH_H
+
+/* Custom implementation of a GNU's realpath(1)-like function that
+ * does not require GNU extensions. */
+char *crealpath(const char *path);
+
+#endif