src/filesystem/unix/SDL_sysfilesystem.c
author Ethan Lee <flibitijibibo@flibitijibibo.com>
Wed, 17 Jul 2019 23:20:57 -0400
changeset 12950 05dddfb66b85
parent 12503 806492103856
permissions -rw-r--r--
Copypaste SDL_NSLog to UIKit backend, document it as such
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #ifdef SDL_FILESYSTEM_UNIX
    24 
    25 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    26 /* System dependent filesystem routines                                */
    27 
    28 #include <errno.h>
    29 #include <stdio.h>
    30 #include <unistd.h>
    31 #include <stdlib.h>
    32 #include <sys/stat.h>
    33 #include <sys/types.h>
    34 #include <limits.h>
    35 #include <fcntl.h>
    36 
    37 #if defined(__FREEBSD__) || defined(__OPENBSD__)
    38 #include <sys/sysctl.h>
    39 #endif
    40 
    41 #include "SDL_error.h"
    42 #include "SDL_stdinc.h"
    43 #include "SDL_filesystem.h"
    44 #include "SDL_rwops.h"
    45 
    46 /* QNX's /proc/self/exefile is a text file and not a symlink. */
    47 #if !defined(__QNXNTO__)
    48 static char *
    49 readSymLink(const char *path)
    50 {
    51     char *retval = NULL;
    52     ssize_t len = 64;
    53     ssize_t rc = -1;
    54 
    55     while (1)
    56     {
    57         char *ptr = (char *) SDL_realloc(retval, (size_t) len);
    58         if (ptr == NULL) {
    59             SDL_OutOfMemory();
    60             break;
    61         }
    62 
    63         retval = ptr;
    64 
    65         rc = readlink(path, retval, len);
    66         if (rc == -1) {
    67             break;  /* not a symlink, i/o error, etc. */
    68         } else if (rc < len) {
    69             retval[rc] = '\0';  /* readlink doesn't null-terminate. */
    70             return retval;  /* we're good to go. */
    71         }
    72 
    73         len *= 2;  /* grow buffer, try again. */
    74     }
    75 
    76     SDL_free(retval);
    77     return NULL;
    78 }
    79 #endif
    80 
    81 char *
    82 SDL_GetBasePath(void)
    83 {
    84     char *retval = NULL;
    85 
    86 #if defined(__FREEBSD__)
    87     char fullpath[PATH_MAX];
    88     size_t buflen = sizeof (fullpath);
    89     const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
    90     if (sysctl(mib, SDL_arraysize(mib), fullpath, &buflen, NULL, 0) != -1) {
    91         retval = SDL_strdup(fullpath);
    92         if (!retval) {
    93             SDL_OutOfMemory();
    94             return NULL;
    95         }
    96     }
    97 #endif
    98 #if defined(__OPENBSD__)
    99     char **retvalargs;
   100     size_t len;
   101     const int mib[] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
   102     if (sysctl(mib, 4, NULL, &len, NULL, 0) != -1) {
   103         retvalargs = SDL_malloc(len);
   104         if (!retvalargs) {
   105             SDL_OutOfMemory();
   106             return NULL;
   107         }
   108         sysctl(mib, 4, retvalargs, &len, NULL, 0);
   109         retval = SDL_malloc(PATH_MAX + 1);
   110         if (retval)
   111             realpath(retvalargs[0], retval);
   112 
   113         SDL_free(retvalargs);
   114     }
   115 #endif
   116 #if defined(__SOLARIS__)
   117     const char *path = getexecname();
   118     if ((path != NULL) && (path[0] == '/')) { /* must be absolute path... */
   119         retval = SDL_strdup(path);
   120         if (!retval) {
   121             SDL_OutOfMemory();
   122             return NULL;
   123         }
   124     }
   125 #endif
   126 
   127     /* is a Linux-style /proc filesystem available? */
   128     if (!retval && (access("/proc", F_OK) == 0)) {
   129         /* !!! FIXME: after 2.0.6 ships, let's delete this code and just
   130                       use the /proc/%llu version. There's no reason to have
   131                       two copies of this plus all the #ifdefs. --ryan. */
   132 #if defined(__FREEBSD__)
   133         retval = readSymLink("/proc/curproc/file");
   134 #elif defined(__NETBSD__)
   135         retval = readSymLink("/proc/curproc/exe");
   136 #elif defined(__QNXNTO__)
   137         retval = SDL_LoadFile("/proc/self/exefile", NULL);
   138 #else
   139         retval = readSymLink("/proc/self/exe");  /* linux. */
   140         if (retval == NULL) {
   141             /* older kernels don't have /proc/self ... try PID version... */
   142             char path[64];
   143             const int rc = (int) SDL_snprintf(path, sizeof(path),
   144                                               "/proc/%llu/exe",
   145                                               (unsigned long long) getpid());
   146             if ( (rc > 0) && (rc < sizeof(path)) ) {
   147                 retval = readSymLink(path);
   148             }
   149         }
   150 #endif
   151     }
   152 
   153     /* If we had access to argv[0] here, we could check it for a path,
   154         or troll through $PATH looking for it, too. */
   155 
   156     if (retval != NULL) { /* chop off filename. */
   157         char *ptr = SDL_strrchr(retval, '/');
   158         if (ptr != NULL) {
   159             *(ptr+1) = '\0';
   160         } else {  /* shouldn't happen, but just in case... */
   161             SDL_free(retval);
   162             retval = NULL;
   163         }
   164     }
   165 
   166     if (retval != NULL) {
   167         /* try to shrink buffer... */
   168         char *ptr = (char *) SDL_realloc(retval, strlen(retval) + 1);
   169         if (ptr != NULL)
   170             retval = ptr;  /* oh well if it failed. */
   171     }
   172 
   173     return retval;
   174 }
   175 
   176 char *
   177 SDL_GetPrefPath(const char *org, const char *app)
   178 {
   179     /*
   180      * We use XDG's base directory spec, even if you're not on Linux.
   181      *  This isn't strictly correct, but the results are relatively sane
   182      *  in any case.
   183      *
   184      * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
   185      */
   186     const char *envr = SDL_getenv("XDG_DATA_HOME");
   187     const char *append;
   188     char *retval = NULL;
   189     char *ptr = NULL;
   190     size_t len = 0;
   191 
   192     if (!app) {
   193         SDL_InvalidParamError("app");
   194         return NULL;
   195     }
   196     if (!org) {
   197         org = "";
   198     }
   199 
   200     if (!envr) {
   201         /* You end up with "$HOME/.local/share/Game Name 2" */
   202         envr = SDL_getenv("HOME");
   203         if (!envr) {
   204             /* we could take heroic measures with /etc/passwd, but oh well. */
   205             SDL_SetError("neither XDG_DATA_HOME nor HOME environment is set");
   206             return NULL;
   207         }
   208         append = "/.local/share/";
   209     } else {
   210         append = "/";
   211     }
   212 
   213     len = SDL_strlen(envr);
   214     if (envr[len - 1] == '/')
   215         append += 1;
   216 
   217     len += SDL_strlen(append) + SDL_strlen(org) + SDL_strlen(app) + 3;
   218     retval = (char *) SDL_malloc(len);
   219     if (!retval) {
   220         SDL_OutOfMemory();
   221         return NULL;
   222     }
   223 
   224     if (*org) {
   225         SDL_snprintf(retval, len, "%s%s%s/%s/", envr, append, org, app);
   226     } else {
   227         SDL_snprintf(retval, len, "%s%s%s/", envr, append, app);
   228     }
   229 
   230     for (ptr = retval+1; *ptr; ptr++) {
   231         if (*ptr == '/') {
   232             *ptr = '\0';
   233             if (mkdir(retval, 0700) != 0 && errno != EEXIST)
   234                 goto error;
   235             *ptr = '/';
   236         }
   237     }
   238     if (mkdir(retval, 0700) != 0 && errno != EEXIST) {
   239 error:
   240         SDL_SetError("Couldn't create directory '%s': '%s'", retval, strerror(errno));
   241         SDL_free(retval);
   242         return NULL;
   243     }
   244 
   245     return retval;
   246 }
   247 
   248 #endif /* SDL_FILESYSTEM_UNIX */
   249 
   250 /* vi: set ts=4 sw=4 expandtab: */