SDL  2.0
SDL_sysfilesystem.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 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) {
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) {
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: */
#define SDL_InvalidParamError(param)
Definition: SDL_error.h:54
#define SDL_realloc
GLenum GLsizei len
GLuint GLint GLboolean GLint GLenum access
SDL_bool retval
#define SDL_free
#define SDL_getenv
#define NULL
Definition: begin_code.h:164
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
#define SDL_SetError
#define SDL_strlen
#define SDL_strdup
#define SDL_GetPrefPath
Include file for filesystem SDL API functions.
#define SDL_snprintf
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:93
#define SDL_malloc
GLsizei const GLchar *const * path
char * SDL_GetBasePath(void)
Get the path where the application resides.
#define SDL_strrchr
#define SDL_LoadFile(file, datasize)
Definition: SDL_rwops.h:214