athena/src/gekko_support.c

322 lines
8.8 KiB
C

#if defined(GEKKO) || defined(__SWITCH__)
#include "athena/gekko_support.h"
#define SYMLOOP_MAX 8
#include <sys/stat.h>
#include <sys/param.h>
#include <stddef.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
/* A slightly modified copy of this file exists in libexec/ld.so */
/*
* char *realpath(const char *path, char resolved[PATH_MAX]);
*
* Find the real name of path, by removing all ".", ".." and symlink
* components. Returns (resolved) on success, or (NULL) on failure,
* in which case the path which caused trouble is left in (resolved).
*/
char *realpath(const char *path, char *resolved) {
struct stat sb;
char *p, *q, *s;
size_t left_len, resolved_len;
unsigned symlinks;
int serrno, slen, mem_allocated;
char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
if (path[0] == '\0') {
errno = ENOENT;
return (NULL);
}
serrno = errno;
if (resolved == NULL) {
resolved = malloc(PATH_MAX);
if (resolved == NULL) return (NULL);
mem_allocated = 1;
} else
mem_allocated = 0;
symlinks = 0;
if (path[0] == '/') {
resolved[0] = '/';
resolved[1] = '\0';
if (path[1] == '\0') return (resolved);
resolved_len = 1;
left_len = strlcpy(left, path + 1, sizeof(left));
} else {
if (getcwd(resolved, PATH_MAX) == NULL) {
if (mem_allocated)
free(resolved);
else
strlcpy(resolved, ".", PATH_MAX);
return (NULL);
}
resolved_len = strlen(resolved);
left_len = strlcpy(left, path, sizeof(left));
}
if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
errno = ENAMETOOLONG;
goto err;
}
/*
* Iterate over path components in `left'.
*/
while (left_len != 0) {
/*
* Extract the next path component and adjust `left'
* and its length.
*/
p = strchr(left, '/');
s = p ? p : left + left_len;
if (s - left >= sizeof(next_token)) {
errno = ENAMETOOLONG;
goto err;
}
memcpy(next_token, left, s - left);
next_token[s - left] = '\0';
left_len -= s - left;
if (p != NULL) memmove(left, s + 1, left_len + 1);
if (resolved[resolved_len - 1] != '/') {
if (resolved_len + 1 >= PATH_MAX) {
errno = ENAMETOOLONG;
goto err;
}
resolved[resolved_len++] = '/';
resolved[resolved_len] = '\0';
}
if (next_token[0] == '\0')
continue;
else if (strcmp(next_token, ".") == 0)
continue;
else if (strcmp(next_token, "..") == 0) {
/*
* Strip the last path component except when we have
* single "/"
*/
if (resolved_len > 1) {
resolved[resolved_len - 1] = '\0';
q = strrchr(resolved, '/') + 1;
*q = '\0';
resolved_len = q - resolved;
}
continue;
}
/*
* Append the next path component and lstat() it. If
* lstat() fails we still can return successfully if
* there are no more path components left.
*/
resolved_len = strlcat(resolved, next_token, PATH_MAX);
if (resolved_len >= PATH_MAX) {
errno = ENAMETOOLONG;
goto err;
}
if (lstat(resolved, &sb) != 0) {
if (errno == ENOENT && p == NULL) {
errno = serrno;
return (resolved);
}
goto err;
}
if (S_ISLNK(sb.st_mode)) {
if (symlinks++ > SYMLOOP_MAX) {
errno = ELOOP;
goto err;
}
slen = readlink(resolved, symlink, sizeof(symlink) - 1);
if (slen < 0) goto err;
symlink[slen] = '\0';
if (symlink[0] == '/') {
resolved[1] = 0;
resolved_len = 1;
} else if (resolved_len > 1) {
/* Strip the last path component. */
resolved[resolved_len - 1] = '\0';
q = strrchr(resolved, '/') + 1;
*q = '\0';
resolved_len = q - resolved;
}
/*
* If there are any path components left, then
* append them to symlink. The result is placed
* in `left'.
*/
if (p != NULL) {
if (symlink[slen - 1] != '/') {
if (slen + 1 >= sizeof(symlink)) {
errno = ENAMETOOLONG;
goto err;
}
symlink[slen] = '/';
symlink[slen + 1] = 0;
}
left_len = strlcat(symlink, left, sizeof(symlink));
if (left_len >= sizeof(left)) {
errno = ENAMETOOLONG;
goto err;
}
}
left_len = strlcpy(left, symlink, sizeof(left));
}
}
/*
* Remove trailing slash except when the resolved pathname
* is a single "/".
*/
if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
resolved[resolved_len - 1] = '\0';
return (resolved);
err:
if (mem_allocated) free(resolved);
return (NULL);
}
static int int_vasprintf(result, format, args) char **result;
const char *format;
va_list *args;
{
const char *p = format;
/* Add one to make sure that it is never zero, which might cause malloc
to return NULL. */
int total_width = strlen(format) + 1;
va_list ap;
memcpy((void *)&ap, (void *)args, sizeof(va_list));
while (*p != '\0') {
if (*p++ == '%') {
while (strchr("-+ #0", *p)) ++p;
if (*p == '*') {
++p;
total_width += abs(va_arg(ap, int));
} else
total_width += strtoul(p, (char **)&p, 10);
if (*p == '.') {
++p;
if (*p == '*') {
++p;
total_width += abs(va_arg(ap, int));
} else
total_width += strtoul(p, (char **)&p, 10);
}
while (strchr("hlL", *p)) ++p;
/* Should be big enough for any format specifier except %s and floats. */
total_width += 30;
switch (*p) {
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X':
case 'c':
(void)va_arg(ap, int);
break;
case 'f':
case 'e':
case 'E':
case 'g':
case 'G':
(void)va_arg(ap, double);
/* Since an ieee double can have an exponent of 307, we'll
make the buffer wide enough to cover the gross case. */
total_width += 307;
break;
case 's':
total_width += strlen(va_arg(ap, char *));
break;
case 'p':
case 'n':
(void)va_arg(ap, char *);
break;
}
}
}
#ifdef TEST
global_total_width = total_width;
#endif
*result = malloc(total_width);
if (*result != NULL)
return vsprintf(*result, format, *args);
else
return 0;
}
int vasprintf(result, format, args) char **result;
const char *format;
#if defined(_BSD_VA_LIST_) && defined(__FreeBSD__)
_BSD_VA_LIST_ args;
#else
va_list args;
#endif
{ return int_vasprintf(result, format, &args); }
int vsnprintf(char *s, size_t n, const char *format, va_list ap) {
char *buf = 0;
int result = vasprintf(&buf, format, ap);
if (!buf) return -1;
if (result < 0) {
free(buf);
return -1;
}
result = strlen(buf);
if (n > 0) {
if ((long)n > result)
memcpy(s, buf, result + 1);
else {
memcpy(s, buf, n - 1);
s[n - 1] = 0;
}
}
free(buf);
return result;
}
#endif
#include <sys/types.h>
#include <stdio.h>
#include <stdarg.h>
/*
* PUBLIC: #ifndef HAVE_SNPRINTF
* PUBLIC: int snprintf __P((char *, size_t, const char *, ...));
* PUBLIC: #endif
*/
int
snprintf(char *str, size_t n, const char *fmt, ...)
{
va_list ap;
int rval;
va_start(ap, fmt);
rval = vsprintf(str, fmt, ap);
va_end(ap);
return (rval);
}
/*
* Convert a string to a quad integer.
*/
long long strtoq(const char *nptr, char **endptr, int base) {
return strtoll(nptr, endptr, base);
}
/*
* Convert a string to an unsigned quad integer.
*/
unsigned long long strtouq(const char *nptr, char **endptr, int base) {
return strtoull(nptr, endptr, base);
}