slcl/crealpath.c

175 lines
3.4 KiB
C

#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;
}