175 lines
3.4 KiB
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;
|
|
}
|