/
unix.c
456 lines (367 loc) · 12.6 KB
1
2
3
4
5
6
7
8
/*
* Unix support routines for PhysicsFS.
*
* Please see the file LICENSE in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
9
10
11
12
#if HAVE_CONFIG_H
# include <config.h>
#endif
13
14
/* BeOS uses beos.cpp and posix.c ... Cygwin and such use win32.c ... */
#if ((!defined __BEOS__) && (!defined WIN32))
15
16
17
#include <stdio.h>
#include <stdlib.h>
18
19
20
21
22
23
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <sys/stat.h>
24
#include <sys/param.h>
25
26
27
#include <dirent.h>
#include <time.h>
#include <errno.h>
28
#include <sys/mount.h>
29
30
31
32
33
#if (!defined PHYSFS_NO_PTHREADS_SUPPORT)
#include <pthread.h>
#endif
34
35
36
37
38
#ifdef PHYSFS_HAVE_SYS_UCRED_H
# ifdef PHYSFS_HAVE_MNTENT_H
# undef PHYSFS_HAVE_MNTENT_H /* don't do both... */
# endif
# include <sys/ucred.h>
39
#endif
40
41
42
43
#ifdef PHYSFS_HAVE_MNTENT_H
#include <mntent.h>
#endif
44
45
46
47
48
49
50
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
const char *__PHYSFS_platformDirSeparator = "/";
51
52
53
54
55
56
57
58
59
60
61
62
63
int __PHYSFS_platformInit(void)
{
return(1); /* always succeed. */
} /* __PHYSFS_platformInit */
int __PHYSFS_platformDeinit(void)
{
return(1); /* always succeed. */
} /* __PHYSFS_platformDeinit */
64
65
66
67
68
69
70
71
72
73
74
75
76
#ifdef PHYSFS_NO_CDROM_SUPPORT
/* Stub version for platforms without CD-ROM support. */
char **__PHYSFS_platformDetectAvailableCDs(void)
{
char **retval = (char **) malloc(sizeof (char *));
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
*retval = NULL;
return(retval);
} /* __PHYSFS_platformDetectAvailableCDs */
#else
77
78
#ifdef PHYSFS_HAVE_SYS_UCRED_H
79
80
81
82
83
char **__PHYSFS_platformDetectAvailableCDs(void)
{
char **retval = (char **) malloc(sizeof (char *));
int cd_count = 1; /* We count the NULL entry. */
84
struct statfs *mntbufp = NULL;
85
int mounts;
86
int i;
87
88
89
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
90
mounts = getmntinfo(&mntbufp, MNT_WAIT);
91
92
93
for (i = 0; i < mounts; i++)
{
94
95
int add_it = 0;
96
if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0)
97
add_it = 1;
98
else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0)
99
add_it = 1;
100
101
/* add other mount types here */
102
103
104
if (add_it)
{
105
char **tmp = realloc(retval, sizeof (char *) * (cd_count + 1));
106
107
108
if (tmp)
{
retval = tmp;
109
retval[cd_count - 1] = (char *)
110
malloc(strlen(mntbufp[i].f_mntonname) + 1);
111
if (retval[cd_count - 1])
112
{
113
strcpy(retval[cd_count - 1], mntbufp[i].f_mntonname);
114
115
116
117
cd_count++;
} /* if */
} /* if */
} /* if */
118
} /* for */
119
120
121
122
123
retval[cd_count - 1] = NULL;
return(retval);
} /* __PHYSFS_platformDetectAvailableCDs */
124
#endif
125
126
127
#ifdef PHYSFS_HAVE_MNTENT_H
128
129
130
char **__PHYSFS_platformDetectAvailableCDs(void)
{
131
132
133
134
135
char **retval = (char **) malloc(sizeof (char *));
int cd_count = 1; /* We count the NULL entry. */
FILE *mounts = NULL;
struct mntent *ent = NULL;
136
137
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
138
139
140
141
142
143
144
145
146
*retval = NULL;
mounts = setmntent("/etc/mtab", "r");
BAIL_IF_MACRO(mounts == NULL, ERR_IO_ERROR, retval);
while ( (ent = getmntent(mounts)) != NULL )
{
int add_it = 0;
if (strcmp(ent->mnt_type, "iso9660") == 0)
add_it = 1;
147
148
/* add other mount types here */
149
150
151
if (add_it)
{
152
char **tmp = realloc(retval, sizeof (char *) * (cd_count + 1));
153
154
155
156
if (tmp)
{
retval = tmp;
retval[cd_count-1] = (char *) malloc(strlen(ent->mnt_dir) + 1);
157
if (retval[cd_count - 1])
158
{
159
strcpy(retval[cd_count - 1], ent->mnt_dir);
160
161
162
163
164
165
166
cd_count++;
} /* if */
} /* if */
} /* if */
} /* while */
endmntent(mounts);
167
168
retval[cd_count - 1] = NULL;
169
return(retval);
170
171
172
} /* __PHYSFS_platformDetectAvailableCDs */
#endif
173
174
175
#endif /* !PHYSFS_NO_CDROM_SUPPORT */
176
177
178
/* this is in posix.c ... */
extern char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname);
179
180
181
182
183
184
185
186
187
188
189
190
/*
* 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.
*
* (envr) will be scribbled over, and you are expected to free() the
* return value when you're done with it.
*/
static char *findBinaryInPath(const char *bin, char *envr)
191
{
192
193
194
size_t alloc_size = 0;
char *exe = NULL;
char *start = envr;
195
196
char *ptr;
197
198
BAIL_IF_MACRO(bin == NULL, ERR_INVALID_ARGUMENT, NULL);
BAIL_IF_MACRO(envr == NULL, ERR_INVALID_ARGUMENT, NULL);
199
200
201
do
{
202
203
size_t size;
ptr = strchr(start, ':'); /* find next $PATH separator. */
204
205
206
if (ptr)
*ptr = '\0';
207
208
size = strlen(start) + strlen(bin) + 2;
if (size > alloc_size)
209
{
210
211
212
213
214
215
216
217
218
219
char *x = (char *) realloc(exe, size);
if (x == NULL)
{
if (exe != NULL)
free(exe);
BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
} /* if */
alloc_size = size;
exe = x;
220
} /* if */
221
222
/* build full binary path... */
223
strcpy(exe, start);
224
if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
225
strcat(exe, "/");
226
227
228
strcat(exe, bin);
if (access(exe, X_OK) == 0) /* Exists as executable? We're done. */
229
{
230
231
232
strcpy(exe, start); /* i'm lazy. piss off. */
return(exe);
} /* if */
233
234
start = ptr + 1; /* start points to beginning of next element. */
235
236
} while (ptr != NULL);
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
if (exe != NULL)
free(exe);
return(NULL); /* doesn't exist in path. */
} /* findBinaryInPath */
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
/* If there isn't a path on argv0, then look through the $PATH for it. */
char *retval;
char *envr;
if (strchr(argv0, '/') != NULL) /* default behaviour can handle this. */
return(NULL);
254
envr = __PHYSFS_platformCopyEnvironmentVariable("PATH");
255
256
BAIL_IF_MACRO(!envr, NULL, NULL);
retval = findBinaryInPath(argv0, envr);
257
258
259
260
261
free(envr);
return(retval);
} /* __PHYSFS_platformCalcBaseDir */
262
263
264
/* Much like my college days, try to sleep for 10 milliseconds at a time... */
void __PHYSFS_platformTimeslice(void)
{
265
usleep( 10 * 1000 ); /* don't care if it fails. */
266
267
268
} /* __PHYSFS_platformTimeslice */
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
#if defined(__MACH__) && defined(__APPLE__)
/*
* This function is only for OSX. The problem is that Apple's applications
* can actually be directory structures with the actual executable nested
* several levels down. PhysFS computes the base directory from the Unix
* executable, but this may not be the correct directory. Apple tries to
* hide everything from the user, so from Finder, the user never sees the
* Unix executable, and the directory package (bundle) is considered the
* "executable". This means that the correct base directory is at the
* level where the directory structure starts.
* A typical bundle seems to look like this:
* MyApp.app/ <-- top level...this is what the user sees in Finder
* Contents/
* MacOS/
* MyApp <-- the actual executable
*
* Since anything below the app folder is considered hidden, most
* application files need to be at the top level if you intend to
* write portable software. Thus if the application resides in:
* /Applications/MyProgram
* and the executable is the bundle MyApp.app,
* PhysFS computes the following as the base directory:
* /Applications/MyProgram/MyApp.app/Contents/MacOS/
* We need to strip off the MyApp.app/Contents/MacOS/
*
* However, there are corner cases. OSX applications can be traditional
* Unix executables without the bundle. Also, it is not entirely clear
* to me what kinds of permutations bundle structures can have.
*
* For now, this is a temporary hack until a better solution
* can be made. This function will try to find a "/Contents/MacOS"
* inside the path. If it succeeds, then the path will be truncated
* to correct the directory. If it is not found, the path will be
* left alone and will presume it is a traditional Unix execuatable.
* Most programs also include the .app extention in the top level
* folder, but it doesn't seem to be a requirement (Acrobat doesn't
* have it). MacOS looks like it can also be MacOSClassic.
* This function will test for MacOS and hope it captures any
* other permutations.
*/
static void stripAppleBundle(char *path)
{
char *sub_str = "/contents/macos";
char *found_ptr = NULL;
char *tempbuf = NULL;
int i;
/* Calloc will place the \0 character in the proper place for us */
tempbuf = (char*)calloc( (strlen(path)+1), sizeof(char) );
/* Unlike other Unix filesystems, HFS is case insensitive
* It wouldn be nice to use strcasestr, but it doesn't seem
* to be available in the OSX gcc library right now.
* So we should make a lower case copy of the path to
* compare against
*/
for(i=0; i<strlen(path); i++)
{
/* convert to lower case */
tempbuf[i] = tolower(path[i]);
}
/* See if we can find "/contents/macos" in the path */
found_ptr = strstr(tempbuf, sub_str);
if(NULL == found_ptr)
{
/* It doesn't look like a bundle so we can keep the
* original path. Just return */
free(tempbuf);
return;
}
/* We have a bundle, so let's backstep character by character
* to erase the extra parts of the path. Quit when we hit
* the preceding '/' character.
*/
for(i=strlen(path)-strlen(found_ptr)-1; i>=0; i--)
{
if('/' == path[i])
{
break;
}
}
/* Safety check */
if(i<1)
{
/* This probably shouldn't happen. */
path[0] = '\0';
}
else
{
/* Back up one more to remove trailing '/' and set the '\0' */
path[i] = '\0';
}
free(tempbuf);
return;
}
#endif /* defined __MACH__ && defined __APPLE__ */
366
367
368
369
370
371
372
char *__PHYSFS_platformRealPath(const char *path)
{
char resolved_path[MAXPATHLEN];
char *retval = NULL;
errno = 0;
BAIL_IF_MACRO(!realpath(path, resolved_path), strerror(errno), NULL);
373
retval = (char *) malloc(strlen(resolved_path) + 1);
374
375
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
strcpy(retval, resolved_path);
376
377
378
379
380
#if defined(__MACH__) && defined(__APPLE__)
stripAppleBundle(retval);
#endif /* defined __MACH__ && defined __APPLE__ */
381
382
383
return(retval);
} /* __PHYSFS_platformRealPath */
384
385
#if (defined PHYSFS_NO_PTHREADS_SUPPORT)
386
387
388
389
390
391
392
393
394
PHYSFS_uint64 __PHYSFS_platformGetThreadID(void) { return(0x0001); }
void *__PHYSFS_platformCreateMutex(void) { return((void *) 0x0001); }
void __PHYSFS_platformDestroyMutex(void *mutex) {}
int __PHYSFS_platformGrabMutex(void *mutex) { return(1); }
void __PHYSFS_platformReleaseMutex(void *mutex) {}
#else
395
396
397
398
399
400
401
402
403
404
405
406
407
408
/* Just in case; this is a panic value. */
#if ((!defined SIZEOF_INT) || (SIZEOF_INT <= 0))
# define SIZEOF_INT 4
#endif
#if (SIZEOF_INT == 4)
# define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint32) (thr)) )
#elif (SIZEOF_INT == 2)
# define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint16) (thr)) )
#elif (SIZEOF_INT == 1)
# define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint8) (thr)) )
#else
# define PHTREAD_TO_UI64(thr) ((PHYSFS_uint64) (thr))
#endif
409
410
411
PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
{
412
return(PHTREAD_TO_UI64(pthread_self()));
413
414
415
} /* __PHYSFS_platformGetThreadID */
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
void *__PHYSFS_platformCreateMutex(void)
{
int rc;
pthread_mutex_t *m = (pthread_mutex_t *) malloc(sizeof (pthread_mutex_t));
BAIL_IF_MACRO(m == NULL, ERR_OUT_OF_MEMORY, NULL);
rc = pthread_mutex_init(m, NULL);
if (rc != 0)
{
free(m);
BAIL_MACRO(strerror(rc), NULL);
} /* if */
return((void *) m);
} /* __PHYSFS_platformCreateMutex */
void __PHYSFS_platformDestroyMutex(void *mutex)
{
pthread_mutex_destroy((pthread_mutex_t *) mutex);
free(mutex);
} /* __PHYSFS_platformDestroyMutex */
int __PHYSFS_platformGrabMutex(void *mutex)
{
return(pthread_mutex_lock((pthread_mutex_t *) mutex) == 0);
} /* __PHYSFS_platformGrabMutex */
void __PHYSFS_platformReleaseMutex(void *mutex)
{
pthread_mutex_unlock((pthread_mutex_t *) mutex);
} /* __PHYSFS_platformReleaseMutex */
450
451
452
#endif /* !PHYSFS_NO_PTHREADS_SUPPORT */
453
#endif /* !defined __BEOS__ && !defined WIN32 */
454
455
/* end of unix.c ... */