#define _POSIX_C_SOURCE 200809L #include "crealpath.h" #include #include #include #include #include #include #include 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; }