aboutsummaryrefslogtreecommitdiff
path: root/crealpath.c
diff options
context:
space:
mode:
Diffstat (limited to 'crealpath.c')
-rw-r--r--crealpath.c174
1 files changed, 174 insertions, 0 deletions
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;
+}