#include "../../SDL_internal.h"
#ifdef SDL_FILESYSTEM_UNIX
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <limits.h>
#include <fcntl.h>
#if defined(__FREEBSD__) || defined(__OPENBSD__)
#include <sys/sysctl.h>
#endif
#include "SDL_error.h"
#include "SDL_stdinc.h"
#include "SDL_filesystem.h"
#include "SDL_rwops.h"
#if !defined(__QNXNTO__)
static char *
readSymLink(const char *path)
{
char *retval = NULL;
ssize_t len = 64;
ssize_t rc = -1;
while (1)
{
char *ptr = (char *) SDL_realloc(retval, (size_t) len);
if (ptr == NULL) {
SDL_OutOfMemory();
break;
}
retval = ptr;
rc = readlink(path, retval, len);
if (rc == -1) {
break;
} else if (rc < len) {
retval[rc] = '\0';
return retval;
}
len *= 2;
}
SDL_free(retval);
return NULL;
}
#endif
#if defined(__OPENBSD__)
static char *search_path_for_binary(const char *bin)
{
char *envr = SDL_getenv("PATH");
size_t alloc_size;
char *exe = NULL;
char *start = envr;
char *ptr;
if (!envr) {
SDL_SetError("No $PATH set");
return NULL;
}
envr = SDL_strdup(envr);
if (!envr) {
SDL_OutOfMemory();
return NULL;
}
SDL_assert(bin != NULL);
alloc_size = SDL_strlen(bin) + SDL_strlen(envr) + 2;
exe = (char *) SDL_malloc(alloc_size);
do {
ptr = SDL_strchr(start, ':');
if (ptr != start) {
if (ptr) {
*ptr = '\0';
}
SDL_snprintf(exe, alloc_size, "%s%s%s", start, (ptr && (ptr[-1] == '/')) ? "" : "/", bin);
if (access(exe, X_OK) == 0) {
SDL_free(envr);
return exe;
}
}
start = ptr + 1;
} while (ptr != NULL);
SDL_free(envr);
SDL_free(exe);
SDL_SetError("Process not found in $PATH");
return NULL;
}
#endif
char *
SDL_GetBasePath(void)
{
char *retval = NULL;
#if defined(__FREEBSD__)
char fullpath[PATH_MAX];
size_t buflen = sizeof (fullpath);
const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
if (sysctl(mib, SDL_arraysize(mib), fullpath, &buflen, NULL, 0) != -1) {
retval = SDL_strdup(fullpath);
if (!retval) {
SDL_OutOfMemory();
return NULL;
}
}
#endif
#if defined(__OPENBSD__)
char **cmdline;
size_t len;
const int mib[] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
if (sysctl(mib, 4, NULL, &len, NULL, 0) != -1) {
char *exe, *pwddst;
char *realpathbuf = (char *) SDL_malloc(PATH_MAX + 1);
if (!realpathbuf) {
SDL_OutOfMemory();
return NULL;
}
cmdline = SDL_malloc(len);
if (!cmdline) {
SDL_free(realpathbuf);
SDL_OutOfMemory();
return NULL;
}
sysctl(mib, 4, cmdline, &len, NULL, 0);
exe = cmdline[0];
pwddst = NULL;
if (SDL_strchr(exe, '/') == NULL) {
exe = search_path_for_binary(cmdline[0]);
} else {
if (exe && *exe == '.') {
const char *pwd = SDL_getenv("PWD");
if (pwd && *pwd) {
SDL_asprintf(&pwddst, "%s/%s", pwd, exe);
}
}
}
if (exe) {
if (pwddst == NULL) {
if (realpath(exe, realpathbuf) != NULL) {
retval = realpathbuf;
}
} else {
if (realpath(pwddst, realpathbuf) != NULL) {
retval = realpathbuf;
}
SDL_free(pwddst);
}
if (exe != cmdline[0]) {
SDL_free(exe);
}
}
if (!retval) {
SDL_free(realpathbuf);
}
SDL_free(cmdline);
}
#endif
#if defined(__SOLARIS__)
const char *path = getexecname();
if ((path != NULL) && (path[0] == '/')) {
retval = SDL_strdup(path);
if (!retval) {
SDL_OutOfMemory();
return NULL;
}
}
#endif
if (!retval && (access("/proc", F_OK) == 0)) {
#if defined(__FREEBSD__)
retval = readSymLink("/proc/curproc/file");
#elif defined(__NETBSD__)
retval = readSymLink("/proc/curproc/exe");
#elif defined(__QNXNTO__)
retval = SDL_LoadFile("/proc/self/exefile", NULL);
#else
retval = readSymLink("/proc/self/exe");
if (retval == NULL) {
char path[64];
const int rc = SDL_snprintf(path, sizeof(path),
"/proc/%llu/exe",
(unsigned long long) getpid());
if ( (rc > 0) && (rc < sizeof(path)) ) {
retval = readSymLink(path);
}
}
#endif
}
if (retval != NULL) {
char *ptr = SDL_strrchr(retval, '/');
if (ptr != NULL) {
*(ptr+1) = '\0';
} else {
SDL_free(retval);
retval = NULL;
}
}
if (retval != NULL) {
char *ptr = (char *) SDL_realloc(retval, SDL_strlen(retval) + 1);
if (ptr != NULL)
retval = ptr;
}
return retval;
}
char *
SDL_GetPrefPath(const char *org, const char *app)
{
const char *envr = SDL_getenv("XDG_DATA_HOME");
const char *append;
char *retval = NULL;
char *ptr = NULL;
size_t len = 0;
if (!app) {
SDL_InvalidParamError("app");
return NULL;
}
if (!org) {
org = "";
}
if (!envr) {
envr = SDL_getenv("HOME");
if (!envr) {
SDL_SetError("neither XDG_DATA_HOME nor HOME environment is set");
return NULL;
}
append = "/.local/share/";
} else {
append = "/";
}
len = SDL_strlen(envr);
if (envr[len - 1] == '/')
append += 1;
len += SDL_strlen(append) + SDL_strlen(org) + SDL_strlen(app) + 3;
retval = (char *) SDL_malloc(len);
if (!retval) {
SDL_OutOfMemory();
return NULL;
}
if (*org) {
SDL_snprintf(retval, len, "%s%s%s/%s/", envr, append, org, app);
} else {
SDL_snprintf(retval, len, "%s%s%s/", envr, append, app);
}
for (ptr = retval+1; *ptr; ptr++) {
if (*ptr == '/') {
*ptr = '\0';
if (mkdir(retval, 0700) != 0 && errno != EEXIST)
goto error;
*ptr = '/';
}
}
if (mkdir(retval, 0700) != 0 && errno != EEXIST) {
error:
SDL_SetError("Couldn't create directory '%s': '%s'", retval, strerror(errno));
SDL_free(retval);
return NULL;
}
return retval;
}
#endif