diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2023-07-24 23:17:48 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-09-24 11:03:39 +0200 |
| commit | a74f4a72504d1c5923bd2b4e85941f34d9fce79f (patch) | |
| tree | 10ddc5729cfa30ba1a15d26c2e385680c1e54e9d | |
| parent | acfc7b820429b7ce2a0a7d41c735388a5529ed6a (diff) | |
| download | slcl-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.txt | 1 | ||||
| -rwxr-xr-x | configure | 1 | ||||
| -rw-r--r-- | crealpath.c | 174 | ||||
| -rw-r--r-- | crealpath.h | 8 |
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 @@ -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 |
