SDL  2.0
SDL_sysfilesystem.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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 
36 #if defined(__FREEBSD__) || defined(__OPENBSD__)
37 #include <sys/sysctl.h>
38 #endif
39 
40 #include "SDL_error.h"
41 #include "SDL_stdinc.h"
42 #include "SDL_filesystem.h"
43 
44 static char *
45 readSymLink(const char *path)
46 {
47  char *retval = NULL;
48  ssize_t len = 64;
49  ssize_t rc = -1;
50 
51  while (1)
52  {
53  char *ptr = (char *) SDL_realloc(retval, (size_t) len);
54  if (ptr == NULL) {
56  break;
57  }
58 
59  retval = ptr;
60 
61  rc = readlink(path, retval, len);
62  if (rc == -1) {
63  break; /* not a symlink, i/o error, etc. */
64  } else if (rc < len) {
65  retval[rc] = '\0'; /* readlink doesn't null-terminate. */
66  return retval; /* we're good to go. */
67  }
68 
69  len *= 2; /* grow buffer, try again. */
70  }
71 
72  SDL_free(retval);
73  return NULL;
74 }
75 
76 
77 char *
78 SDL_GetBasePath(void)
79 {
80  char *retval = NULL;
81 
82 #if defined(__FREEBSD__)
83  char fullpath[PATH_MAX];
84  size_t buflen = sizeof (fullpath);
85  const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
86  if (sysctl(mib, SDL_arraysize(mib), fullpath, &buflen, NULL, 0) != -1) {
87  retval = SDL_strdup(fullpath);
88  if (!retval) {
90  return NULL;
91  }
92  }
93 #endif
94 #if defined(__OPENBSD__)
95  char **retvalargs;
96  size_t len;
97  const int mib[] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
98  if (sysctl(mib, 4, NULL, &len, NULL, 0) != -1) {
99  retvalargs = SDL_malloc(len);
100  if (!retvalargs) {
101  SDL_OutOfMemory();
102  return NULL;
103  }
104  sysctl(mib, 4, retvalargs, &len, NULL, 0);
105  retval = SDL_malloc(PATH_MAX + 1);
106  if (retval)
107  realpath(retvalargs[0], retval);
108 
109  SDL_free(retvalargs);
110  }
111 #endif
112 #if defined(__SOLARIS__)
113  const char *path = getexecname();
114  if ((path != NULL) && (path[0] == '/')) { /* must be absolute path... */
115  retval = SDL_strdup(path);
116  if (!retval) {
117  SDL_OutOfMemory();
118  return NULL;
119  }
120  }
121 #endif
122 
123  /* is a Linux-style /proc filesystem available? */
124  if (!retval && (access("/proc", F_OK) == 0)) {
125 #if defined(__FREEBSD__)
126  retval = readSymLink("/proc/curproc/file");
127 #elif defined(__NETBSD__)
128  retval = readSymLink("/proc/curproc/exe");
129 #else
130  retval = readSymLink("/proc/self/exe"); /* linux. */
131 #endif
132  if (retval == NULL) {
133  /* older kernels don't have /proc/self ... try PID version... */
134  char path[64];
135  const int rc = (int) SDL_snprintf(path, sizeof(path),
136  "/proc/%llu/exe",
137  (unsigned long long) getpid());
138  if ( (rc > 0) && (rc < sizeof(path)) ) {
139  retval = readSymLink(path);
140  }
141  }
142  }
143 
144  /* If we had access to argv[0] here, we could check it for a path,
145  or troll through $PATH looking for it, too. */
146 
147  if (retval != NULL) { /* chop off filename. */
148  char *ptr = SDL_strrchr(retval, '/');
149  if (ptr != NULL) {
150  *(ptr+1) = '\0';
151  } else { /* shouldn't happen, but just in case... */
152  SDL_free(retval);
153  retval = NULL;
154  }
155  }
156 
157  if (retval != NULL) {
158  /* try to shrink buffer... */
159  char *ptr = (char *) SDL_realloc(retval, strlen(retval) + 1);
160  if (ptr != NULL)
161  retval = ptr; /* oh well if it failed. */
162  }
163 
164  return retval;
165 }
166 
167 char *
168 SDL_GetPrefPath(const char *org, const char *app)
169 {
170  /*
171  * We use XDG's base directory spec, even if you're not on Linux.
172  * This isn't strictly correct, but the results are relatively sane
173  * in any case.
174  *
175  * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
176  */
177  const char *envr = SDL_getenv("XDG_DATA_HOME");
178  const char *append;
179  char *retval = NULL;
180  char *ptr = NULL;
181  size_t len = 0;
182 
183  if (!envr) {
184  /* You end up with "$HOME/.local/share/Game Name 2" */
185  envr = SDL_getenv("HOME");
186  if (!envr) {
187  /* we could take heroic measures with /etc/passwd, but oh well. */
188  SDL_SetError("neither XDG_DATA_HOME nor HOME environment is set");
189  return NULL;
190  }
191  append = "/.local/share/";
192  } else {
193  append = "/";
194  }
195 
196  len = SDL_strlen(envr);
197  if (envr[len - 1] == '/')
198  append += 1;
199 
200  len += SDL_strlen(append) + SDL_strlen(org) + SDL_strlen(app) + 3;
201  retval = (char *) SDL_malloc(len);
202  if (!retval) {
203  SDL_OutOfMemory();
204  return NULL;
205  }
206 
207  SDL_snprintf(retval, len, "%s%s%s/%s/", envr, append, org, app);
208 
209  for (ptr = retval+1; *ptr; ptr++) {
210  if (*ptr == '/') {
211  *ptr = '\0';
212  if (mkdir(retval, 0700) != 0 && errno != EEXIST)
213  goto error;
214  *ptr = '/';
215  }
216  }
217  if (mkdir(retval, 0700) != 0 && errno != EEXIST) {
218 error:
219  SDL_SetError("Couldn't create directory '%s': '%s'", retval, strerror(errno));
220  SDL_free(retval);
221  return NULL;
222  }
223 
224  return retval;
225 }
226 
227 #endif /* SDL_FILESYSTEM_UNIX */
228 
229 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_realloc
GLenum GLsizei len
GLuint GLint GLboolean GLint GLenum access
SDL_bool retval
void SDL_free(void *mem)
#define SDL_getenv
#define NULL
Definition: begin_code.h:143
#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:90
#define SDL_malloc
GLsizei const GLchar *const * path
char * SDL_GetBasePath(void)
Get the path where the application resides.
#define SDL_strrchr