2 * Unix support routines for PhysicsFS.
4 * Please see the file LICENSE.txt in the source's root directory.
6 * This file written by Ryan C. Gordon.
9 #define __PHYSICSFS_INTERNAL__
10 #include "physfs_platforms.h"
12 #ifdef PHYSFS_PLATFORM_UNIX
19 #include <sys/types.h>
22 #include <sys/param.h>
26 #include <sys/mount.h>
28 #if (!defined PHYSFS_NO_PTHREADS_SUPPORT)
32 #ifdef PHYSFS_HAVE_SYS_UCRED_H
33 # ifdef PHYSFS_HAVE_MNTENT_H
34 # undef PHYSFS_HAVE_MNTENT_H /* don't do both... */
36 # include <sys/ucred.h>
39 #ifdef PHYSFS_HAVE_MNTENT_H
43 #include "physfs_internal.h"
46 int __PHYSFS_platformInit(void)
48 return(1); /* always succeed. */
49 } /* __PHYSFS_platformInit */
52 int __PHYSFS_platformDeinit(void)
54 return(1); /* always succeed. */
55 } /* __PHYSFS_platformDeinit */
58 #ifdef PHYSFS_NO_CDROM_SUPPORT
60 /* Stub version for platforms without CD-ROM support. */
61 void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
63 } /* __PHYSFS_platformDetectAvailableCDs */
65 #elif (defined PHYSFS_HAVE_SYS_UCRED_H)
67 void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
70 struct statfs *mntbufp = NULL;
71 int mounts = getmntinfo(&mntbufp, MNT_WAIT);
73 for (i = 0; i < mounts; i++)
77 if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0)
79 else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0)
82 /* add other mount types here */
85 cb(data, mntbufp[i].f_mntonname);
87 } /* __PHYSFS_platformDetectAvailableCDs */
89 #elif (defined PHYSFS_HAVE_MNTENT_H)
91 void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
94 struct mntent *ent = NULL;
96 mounts = setmntent("/etc/mtab", "r");
97 BAIL_IF_MACRO(mounts == NULL, ERR_IO_ERROR, /*return void*/);
99 while ( (ent = getmntent(mounts)) != NULL )
102 if (strcmp(ent->mnt_type, "iso9660") == 0)
105 /* add other mount types here */
108 cb(data, ent->mnt_dir);
113 } /* __PHYSFS_platformDetectAvailableCDs */
118 /* this is in posix.c ... */
119 extern char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname);
123 * See where program (bin) resides in the $PATH specified by (envr).
124 * returns a copy of the first element in envr that contains it, or NULL
125 * if it doesn't exist or there were other problems. PHYSFS_SetError() is
126 * called if we have a problem.
128 * (envr) will be scribbled over, and you are expected to allocator.Free() the
129 * return value when you're done with it.
131 static char *findBinaryInPath(const char *bin, char *envr)
133 size_t alloc_size = 0;
138 BAIL_IF_MACRO(bin == NULL, ERR_INVALID_ARGUMENT, NULL);
139 BAIL_IF_MACRO(envr == NULL, ERR_INVALID_ARGUMENT, NULL);
144 ptr = strchr(start, ':'); /* find next $PATH separator. */
148 size = strlen(start) + strlen(bin) + 2;
149 if (size > alloc_size)
151 char *x = (char *) allocator.Realloc(exe, size);
156 BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
163 /* build full binary path... */
165 if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
169 if (access(exe, X_OK) == 0) /* Exists as executable? We're done. */
171 strcpy(exe, start); /* i'm lazy. piss off. */
175 start = ptr + 1; /* start points to beginning of next element. */
176 } while (ptr != NULL);
181 return(NULL); /* doesn't exist in path. */
182 } /* findBinaryInPath */
185 char *__PHYSFS_platformCalcBaseDir(const char *argv0)
187 const char *PROC_SELF_EXE = "/proc/self/exe";
192 /* fast path: default behaviour can handle this. */
193 if ( (argv0 != NULL) && (strchr(argv0, '/') != NULL) )
194 return(NULL); /* higher level will parse out real path from argv0. */
197 * Try to avoid using argv0 unless forced to. If there's a Linux-like
198 * /proc filesystem, you can get the full path to the current process from
199 * the /proc/self/exe symlink.
201 if ((lstat(PROC_SELF_EXE, &stbuf) != -1) && (S_ISLNK(stbuf.st_mode)))
203 const size_t len = stbuf.st_size;
204 char *buf = (char *) allocator.Malloc(len+1);
205 if (buf != NULL) /* if NULL, maybe you'll get lucky later. */
207 if (readlink(PROC_SELF_EXE, buf, len) != len)
211 buf[len] = '\0'; /* readlink doesn't null-terminate. */
212 retval = buf; /* we're good to go. */
217 if ((retval == NULL) && (argv0 != NULL))
219 /* If there's no dirsep on argv0, then look through $PATH for it. */
220 envr = __PHYSFS_platformCopyEnvironmentVariable("PATH");
221 BAIL_IF_MACRO(!envr, NULL, NULL);
222 retval = findBinaryInPath(argv0, envr);
223 allocator.Free(envr);
227 } /* __PHYSFS_platformCalcBaseDir */
230 char *__PHYSFS_platformRealPath(const char *path)
232 char resolved_path[MAXPATHLEN];
236 BAIL_IF_MACRO(!realpath(path, resolved_path), strerror(errno), NULL);
237 retval = (char *) allocator.Malloc(strlen(resolved_path) + 1);
238 BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
239 strcpy(retval, resolved_path);
242 } /* __PHYSFS_platformRealPath */
245 char *__PHYSFS_platformCurrentDir(void)
248 * This can't just do platformRealPath("."), since that would eventually
249 * just end up calling back into here.
259 ptr = (char *) allocator.Realloc(retval, allocSize);
263 allocator.Free(retval);
264 BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
268 ptr = getcwd(retval, allocSize);
269 } while (ptr == NULL && errno == ERANGE);
271 if (ptr == NULL && errno)
274 * getcwd() failed for some reason, for example current
275 * directory not existing.
278 allocator.Free(retval);
279 BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
283 } /* __PHYSFS_platformCurrentDir */
286 int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
288 return(0); /* just use malloc() and friends. */
289 } /* __PHYSFS_platformSetDefaultAllocator */
292 #if (defined PHYSFS_NO_PTHREADS_SUPPORT)
294 PHYSFS_uint64 __PHYSFS_platformGetThreadID(void) { return(0x0001); }
295 void *__PHYSFS_platformCreateMutex(void) { return((void *) 0x0001); }
296 void __PHYSFS_platformDestroyMutex(void *mutex) {}
297 int __PHYSFS_platformGrabMutex(void *mutex) { return(1); }
298 void __PHYSFS_platformReleaseMutex(void *mutex) {}
304 pthread_mutex_t mutex;
309 /* Just in case; this is a panic value. */
310 #if ((!defined SIZEOF_INT) || (SIZEOF_INT <= 0))
311 # define SIZEOF_INT 4
314 #if (SIZEOF_INT == 4)
315 # define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint32) (thr)) )
316 #elif (SIZEOF_INT == 2)
317 # define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint16) (thr)) )
318 #elif (SIZEOF_INT == 1)
319 # define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint8) (thr)) )
321 # define PHTREAD_TO_UI64(thr) ((PHYSFS_uint64) (thr))
324 PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
326 return(PHTREAD_TO_UI64(pthread_self()));
327 } /* __PHYSFS_platformGetThreadID */
330 void *__PHYSFS_platformCreateMutex(void)
333 PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex));
334 BAIL_IF_MACRO(m == NULL, ERR_OUT_OF_MEMORY, NULL);
335 rc = pthread_mutex_init(&m->mutex, NULL);
339 BAIL_MACRO(strerror(rc), NULL);
343 m->owner = (pthread_t) 0xDEADBEEF;
345 } /* __PHYSFS_platformCreateMutex */
348 void __PHYSFS_platformDestroyMutex(void *mutex)
350 PthreadMutex *m = (PthreadMutex *) mutex;
352 /* Destroying a locked mutex is a bug, but we'll try to be helpful. */
353 if ((m->owner == pthread_self()) && (m->count > 0))
354 pthread_mutex_unlock(&m->mutex);
356 pthread_mutex_destroy(&m->mutex);
358 } /* __PHYSFS_platformDestroyMutex */
361 int __PHYSFS_platformGrabMutex(void *mutex)
363 PthreadMutex *m = (PthreadMutex *) mutex;
364 pthread_t tid = pthread_self();
367 if (pthread_mutex_lock(&m->mutex) != 0)
374 } /* __PHYSFS_platformGrabMutex */
377 void __PHYSFS_platformReleaseMutex(void *mutex)
379 PthreadMutex *m = (PthreadMutex *) mutex;
380 if (m->owner == pthread_self())
384 m->owner = (pthread_t) 0xDEADBEEF;
385 pthread_mutex_unlock(&m->mutex);
388 } /* __PHYSFS_platformReleaseMutex */
390 #endif /* !PHYSFS_NO_PTHREADS_SUPPORT */
392 #endif /* PHYSFS_PLATFORM_UNIX */
394 /* end of unix.c ... */