/
platform_unix.c
344 lines (276 loc) · 9.07 KB
1
2
3
/*
* Unix support routines for PhysicsFS.
*
4
* Please see the file LICENSE.txt in the source's root directory.
5
6
7
8
*
* This file written by Ryan C. Gordon.
*/
9
10
11
12
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_UNIX
13
14
15
16
17
18
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <sys/stat.h>
19
#include <sys/param.h>
20
21
22
#include <dirent.h>
#include <time.h>
#include <errno.h>
23
24
25
26
27
28
29
30
31
#if PHYSFS_PLATFORM_LINUX && !defined(PHYSFS_HAVE_MNTENT_H)
#define PHYSFS_HAVE_MNTENT_H 1
#elif PHYSFS_PLATFORM_SOLARIS && !defined(PHYSFS_HAVE_SYS_MNTTAB_H)
#define PHYSFS_HAVE_SYS_MNTTAB_H 1
#elif PHYSFS_PLATFORM_BSD && !defined(PHYSFS_HAVE_SYS_UCRED_H)
#define PHYSFS_HAVE_SYS_UCRED_H 1
#endif
32
33
34
35
#ifdef PHYSFS_HAVE_SYS_UCRED_H
# ifdef PHYSFS_HAVE_MNTENT_H
# undef PHYSFS_HAVE_MNTENT_H /* don't do both... */
# endif
36
# include <sys/mount.h>
37
# include <sys/ucred.h>
38
#endif
39
40
41
42
#ifdef PHYSFS_HAVE_MNTENT_H
#include <mntent.h>
#endif
43
44
45
46
47
#ifdef PHYSFS_HAVE_SYS_MNTTAB_H
#include <sys/mnttab.h>
#endif
48
49
#include "physfs_internal.h"
50
51
int __PHYSFS_platformInit(void)
{
52
return 1; /* always succeed. */
53
54
55
56
57
} /* __PHYSFS_platformInit */
int __PHYSFS_platformDeinit(void)
{
58
return 1; /* always succeed. */
59
60
61
} /* __PHYSFS_platformDeinit */
62
/* Stub version for platforms without CD-ROM support. */
63
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
64
{
65
66
#if (defined PHYSFS_NO_CDROM_SUPPORT)
/* no-op. */
67
68
#elif (defined PHYSFS_HAVE_SYS_UCRED_H)
69
int i;
70
71
struct statfs *mntbufp = NULL;
int mounts = getmntinfo(&mntbufp, MNT_WAIT);
72
73
74
for (i = 0; i < mounts; i++)
{
75
76
int add_it = 0;
77
if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0)
78
add_it = 1;
79
else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0)
80
add_it = 1;
81
82
/* add other mount types here */
83
84
if (add_it)
85
cb(data, mntbufp[i].f_mntonname);
86
} /* for */
87
88
#elif (defined PHYSFS_HAVE_MNTENT_H)
89
90
91
92
FILE *mounts = NULL;
struct mntent *ent = NULL;
mounts = setmntent("/etc/mtab", "r");
93
BAIL_IF_MACRO(mounts == NULL, PHYSFS_ERR_IO, /*return void*/);
94
95
96
97
98
99
while ( (ent = getmntent(mounts)) != NULL )
{
int add_it = 0;
if (strcmp(ent->mnt_type, "iso9660") == 0)
add_it = 1;
100
101
102
103
104
105
106
107
else if (strcmp(ent->mnt_type, "udf") == 0)
add_it = 1;
/* !!! FIXME: these might pick up floppy drives, right? */
else if (strcmp(ent->mnt_type, "auto") == 0)
add_it = 1;
else if (strcmp(ent->mnt_type, "supermount") == 0)
add_it = 1;
108
109
110
/* !!! FIXME: udf? automount? */
111
/* add other mount types here */
112
113
if (add_it)
114
cb(data, ent->mnt_dir);
115
116
117
} /* while */
endmntent(mounts);
118
119
120
121
122
#elif (defined PHYSFS_HAVE_SYS_MNTTAB_H)
FILE *mounts = fopen(MNTTAB, "r");
struct mnttab ent;
123
BAIL_IF_MACRO(mounts == NULL, PHYSFS_ERR_IO, /*return void*/);
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
while (getmntent(mounts, &ent) == 0)
{
int add_it = 0;
if (strcmp(ent.mnt_fstype, "hsfs") == 0)
add_it = 1;
/* add other mount types here */
if (add_it)
cb(data, ent.mnt_mountp);
} /* while */
fclose(mounts);
#else
#error Unknown platform. Should have defined PHYSFS_NO_CDROM_SUPPORT, perhaps.
140
#endif
141
} /* __PHYSFS_platformDetectAvailableCDs */
142
143
144
145
146
147
148
149
/*
* See where program (bin) resides in the $PATH specified by (envr).
* returns a copy of the first element in envr that contains it, or NULL
* if it doesn't exist or there were other problems. PHYSFS_SetError() is
* called if we have a problem.
*
150
* (envr) will be scribbled over, and you are expected to allocator.Free() the
151
152
153
* return value when you're done with it.
*/
static char *findBinaryInPath(const char *bin, char *envr)
154
{
155
156
157
size_t alloc_size = 0;
char *exe = NULL;
char *start = envr;
158
159
char *ptr;
160
161
assert(bin != NULL);
assert(envr != NULL);
162
163
164
do
{
165
size_t size;
166
167
size_t binlen;
168
ptr = strchr(start, ':'); /* find next $PATH separator. */
169
170
171
if (ptr)
*ptr = '\0';
172
173
binlen = strlen(bin);
size = strlen(start) + binlen + 2;
174
if (size > alloc_size)
175
{
176
char *x = (char *) allocator.Realloc(exe, size);
177
if (!x)
178
179
{
if (exe != NULL)
180
allocator.Free(exe);
181
BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
182
183
184
185
} /* if */
alloc_size = size;
exe = x;
186
} /* if */
187
188
/* build full binary path... */
189
strcpy(exe, start);
190
if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
191
strcat(exe, "/");
192
193
194
strcat(exe, bin);
if (access(exe, X_OK) == 0) /* Exists as executable? We're done. */
195
{
196
exe[size - binlen] = '\0'; /* chop off filename, leave '/' */
197
return exe;
198
} /* if */
199
200
start = ptr + 1; /* start points to beginning of next element. */
201
202
} while (ptr != NULL);
203
if (exe != NULL)
204
allocator.Free(exe);
205
206
return NULL; /* doesn't exist in path. */
207
208
209
} /* findBinaryInPath */
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
static char *readSymLink(const char *path)
{
ssize_t len = 64;
ssize_t rc = -1;
char *retval = NULL;
while (1)
{
char *ptr = (char *) allocator.Realloc(retval, (size_t) len);
if (ptr == NULL)
break; /* out of memory. */
retval = ptr;
rc = readlink(path, retval, len);
if (rc == -1)
break; /* not a symlink, i/o error, etc. */
else if (rc < len)
{
retval[rc] = '\0'; /* readlink doesn't null-terminate. */
return retval; /* we're good to go. */
} /* else if */
len *= 2; /* grow buffer, try again. */
} /* while */
if (retval != NULL)
allocator.Free(retval);
return NULL;
} /* readSymLink */
242
243
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
244
char *retval = NULL;
245
const char *envr = NULL;
246
247
248
249
250
251
/*
* Try to avoid using argv0 unless forced to. If there's a Linux-like
* /proc filesystem, you can get the full path to the current process from
* the /proc/self/exe symlink.
*/
252
retval = readSymLink("/proc/self/exe");
253
254
255
256
257
258
259
260
261
262
if (retval == NULL)
{
/* older kernels don't have /proc/self ... try PID version... */
const unsigned long long pid = (unsigned long long) getpid();
char path[64];
const int rc = (int) snprintf(path,sizeof(path),"/proc/%llu/exe",pid);
if ( (rc > 0) && (rc < sizeof(path)) )
retval = readSymLink(path);
} /* if */
263
if (retval != NULL) /* chop off filename. */
264
{
265
266
char *ptr = strrchr(retval, '/');
if (ptr != NULL)
267
*(ptr+1) = '\0';
268
269
270
271
272
else /* shouldn't happen, but just in case... */
{
allocator.Free(retval);
retval = NULL;
} /* else */
273
} /* if */
274
275
/* No /proc/self/exe, but we have an argv[0] we can parse? */
276
277
if ((retval == NULL) && (argv0 != NULL))
{
278
279
280
281
/* fast path: default behaviour can handle this. */
if (strchr(argv0, '/') != NULL)
return NULL; /* higher level parses out real path from argv0. */
282
/* If there's no dirsep on argv0, then look through $PATH for it. */
283
284
285
286
287
288
289
290
291
envr = getenv("PATH");
if (envr != NULL)
{
char *path = (char *) __PHYSFS_smallAlloc(strlen(envr) + 1);
BAIL_IF_MACRO(!path, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
strcpy(path, envr);
retval = findBinaryInPath(argv0, path);
__PHYSFS_smallFree(path);
} /* if */
292
} /* if */
293
294
295
296
297
298
299
300
301
if (retval != NULL)
{
/* try to shrink buffer... */
char *ptr = (char *) allocator.Realloc(retval, strlen(retval) + 1);
if (ptr != NULL)
retval = ptr; /* oh well if it failed. */
} /* if */
302
return retval;
303
304
305
} /* __PHYSFS_platformCalcBaseDir */
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
/*
* We use XDG's base directory spec, even if you're not on Linux.
* This isn't strictly correct, but the results are relatively sane
* in any case.
*
* http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
*/
const char *envr = getenv("XDG_DATA_HOME");
const char *append = "/";
char *retval = NULL;
size_t len = 0;
if (!envr)
{
/* You end up with "$HOME/.local/share/Game Name 2" */
envr = __PHYSFS_getUserDir();
BAIL_IF_MACRO(!envr, ERRPASS, NULL); /* oh well. */
append = ".local/share/";
} /* if */
328
len = strlen(envr) + strlen(append) + strlen(app) + 2;
329
330
retval = (char *) allocator.Malloc(len);
BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
331
snprintf(retval, len, "%s%s%s/", envr, append, app);
332
333
334
335
return retval;
} /* __PHYSFS_platformCalcPrefDir */
336
337
int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
{
338
return 0; /* just use malloc() and friends. */
339
340
} /* __PHYSFS_platformSetDefaultAllocator */
341
#endif /* PHYSFS_PLATFORM_UNIX */
342
343
/* end of unix.c ... */