2 * PhysicsFS; a portable, flexible file i/o abstraction.
4 * Documentation is in physfs.h. It's verbose, honest. :)
6 * Please see the file LICENSE.txt in the source's root directory.
8 * This file written by Ryan C. Gordon.
16 #define __PHYSICSFS_INTERNAL__
17 #include "physfs_internal.h"
20 typedef struct __PHYSFS_DIRHANDLE__
22 void *opaque; /* Instance data unique to the archiver. */
23 char *dirName; /* Path to archive in platform-dependent notation. */
24 char *mountPoint; /* Mountpoint in virtual file tree. */
25 const PHYSFS_Archiver *funcs; /* Ptr to archiver info for this handle. */
26 struct __PHYSFS_DIRHANDLE__ *next; /* linked list stuff. */
30 typedef struct __PHYSFS_FILEHANDLE__
32 void *opaque; /* Instance data unique to the archiver for this file. */
33 PHYSFS_uint8 forReading; /* Non-zero if reading, zero if write/append */
34 const DirHandle *dirHandle; /* Archiver instance that created this */
35 const PHYSFS_Archiver *funcs; /* Ptr to archiver info for this handle. */
36 PHYSFS_uint8 *buffer; /* Buffer, if set (NULL otherwise). Don't touch! */
37 PHYSFS_uint32 bufsize; /* Bufsize, if set (0 otherwise). Don't touch! */
38 PHYSFS_uint32 buffill; /* Buffer fill size. Don't touch! */
39 PHYSFS_uint32 bufpos; /* Buffer position. Don't touch! */
40 struct __PHYSFS_FILEHANDLE__ *next; /* linked list stuff. */
44 typedef struct __PHYSFS_ERRMSGTYPE__
49 struct __PHYSFS_ERRMSGTYPE__ *next;
53 /* The various i/o drivers...some of these may not be compiled in. */
54 extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP;
55 extern const PHYSFS_Archiver __PHYSFS_Archiver_ZIP;
56 extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_LZMA;
57 extern const PHYSFS_Archiver __PHYSFS_Archiver_LZMA;
58 extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP;
59 extern const PHYSFS_Archiver __PHYSFS_Archiver_GRP;
60 extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_QPAK;
61 extern const PHYSFS_Archiver __PHYSFS_Archiver_QPAK;
62 extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG;
63 extern const PHYSFS_Archiver __PHYSFS_Archiver_HOG;
64 extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL;
65 extern const PHYSFS_Archiver __PHYSFS_Archiver_MVL;
66 extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_WAD;
67 extern const PHYSFS_Archiver __PHYSFS_Archiver_WAD;
68 extern const PHYSFS_Archiver __PHYSFS_Archiver_DIR;
71 static const PHYSFS_ArchiveInfo *supported_types[] =
73 #if (defined PHYSFS_SUPPORTS_ZIP)
74 &__PHYSFS_ArchiveInfo_ZIP,
76 #if (defined PHYSFS_SUPPORTS_7Z)
77 &__PHYSFS_ArchiveInfo_LZMA,
79 #if (defined PHYSFS_SUPPORTS_GRP)
80 &__PHYSFS_ArchiveInfo_GRP,
82 #if (defined PHYSFS_SUPPORTS_QPAK)
83 &__PHYSFS_ArchiveInfo_QPAK,
85 #if (defined PHYSFS_SUPPORTS_HOG)
86 &__PHYSFS_ArchiveInfo_HOG,
88 #if (defined PHYSFS_SUPPORTS_MVL)
89 &__PHYSFS_ArchiveInfo_MVL,
91 #if (defined PHYSFS_SUPPORTS_WAD)
92 &__PHYSFS_ArchiveInfo_WAD,
97 static const PHYSFS_Archiver *archivers[] =
99 &__PHYSFS_Archiver_DIR,
100 #if (defined PHYSFS_SUPPORTS_ZIP)
101 &__PHYSFS_Archiver_ZIP,
103 #if (defined PHYSFS_SUPPORTS_7Z)
104 &__PHYSFS_Archiver_LZMA,
106 #if (defined PHYSFS_SUPPORTS_GRP)
107 &__PHYSFS_Archiver_GRP,
109 #if (defined PHYSFS_SUPPORTS_QPAK)
110 &__PHYSFS_Archiver_QPAK,
112 #if (defined PHYSFS_SUPPORTS_HOG)
113 &__PHYSFS_Archiver_HOG,
115 #if (defined PHYSFS_SUPPORTS_MVL)
116 &__PHYSFS_Archiver_MVL,
118 #if (defined PHYSFS_SUPPORTS_WAD)
119 &__PHYSFS_Archiver_WAD,
126 /* General PhysicsFS state ... */
127 static int initialized = 0;
128 static ErrMsg *errorMessages = NULL;
129 static DirHandle *searchPath = NULL;
130 static DirHandle *writeDir = NULL;
131 static FileHandle *openWriteList = NULL;
132 static FileHandle *openReadList = NULL;
133 static char *baseDir = NULL;
134 static char *userDir = NULL;
135 static int allowSymLinks = 0;
138 static void *errorLock = NULL; /* protects error message list. */
139 static void *stateLock = NULL; /* protects other PhysFS static state. */
142 static int externalAllocator = 0;
143 PHYSFS_Allocator allocator;
152 const char *errorstr;
153 } EnumStringListCallbackData;
155 static void enumStringListCallback(void *data, const char *str)
159 EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data;
164 ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *));
165 newstr = (char *) allocator.Malloc(strlen(str) + 1);
167 pecd->list = (char **) ptr;
169 if ((ptr == NULL) || (newstr == NULL))
171 pecd->errorstr = ERR_OUT_OF_MEMORY;
172 pecd->list[pecd->size] = NULL;
173 PHYSFS_freeList(pecd->list);
178 pecd->list[pecd->size] = newstr;
180 } /* enumStringListCallback */
183 static char **doEnumStringList(void (*func)(PHYSFS_StringCallback, void *))
185 EnumStringListCallbackData ecd;
186 memset(&ecd, '\0', sizeof (ecd));
187 ecd.list = (char **) allocator.Malloc(sizeof (char *));
188 BAIL_IF_MACRO(ecd.list == NULL, ERR_OUT_OF_MEMORY, NULL);
189 func(enumStringListCallback, &ecd);
190 BAIL_IF_MACRO(ecd.errorstr != NULL, ecd.errorstr, NULL);
191 ecd.list[ecd.size] = NULL;
193 } /* doEnumStringList */
196 static void __PHYSFS_bubble_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi,
197 int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32),
198 void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32))
206 for (i = lo; i < hi; i++)
208 if (cmpfn(a, i, i + 1) > 0)
215 } /* __PHYSFS_bubble_sort */
218 static void __PHYSFS_quick_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi,
219 int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32),
220 void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32))
226 if ((hi - lo) <= PHYSFS_QUICKSORT_THRESHOLD)
227 __PHYSFS_bubble_sort(a, lo, hi, cmpfn, swapfn);
232 if (cmpfn(a, lo, i) > 0) swapfn(a, lo, i);
233 if (cmpfn(a, lo, hi) > 0) swapfn(a, lo, hi);
234 if (cmpfn(a, i, hi) > 0) swapfn(a, i, hi);
242 while(cmpfn(a, ++i, v) < 0) { /* do nothing */ }
243 while(cmpfn(a, --j, v) > 0) { /* do nothing */ }
249 __PHYSFS_quick_sort(a, lo, j, cmpfn, swapfn);
250 __PHYSFS_quick_sort(a, i+1, hi, cmpfn, swapfn);
252 } /* __PHYSFS_quick_sort */
255 void __PHYSFS_sort(void *entries, PHYSFS_uint32 max,
256 int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32),
257 void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32))
260 * Quicksort w/ Bubblesort fallback algorithm inspired by code from here:
261 * http://www.cs.ubc.ca/spider/harrison/Java/sorting-demo.html
263 __PHYSFS_quick_sort(entries, 0, max - 1, cmpfn, swapfn);
264 } /* __PHYSFS_sort */
267 static ErrMsg *findErrorForCurrentThread(void)
272 if (errorLock != NULL)
273 __PHYSFS_platformGrabMutex(errorLock);
275 if (errorMessages != NULL)
277 tid = __PHYSFS_platformGetThreadID();
279 for (i = errorMessages; i != NULL; i = i->next)
283 if (errorLock != NULL)
284 __PHYSFS_platformReleaseMutex(errorLock);
290 if (errorLock != NULL)
291 __PHYSFS_platformReleaseMutex(errorLock);
293 return(NULL); /* no error available. */
294 } /* findErrorForCurrentThread */
297 void __PHYSFS_setError(const char *str)
304 err = findErrorForCurrentThread();
308 err = (ErrMsg *) allocator.Malloc(sizeof (ErrMsg));
310 return; /* uhh...? */
312 memset((void *) err, '\0', sizeof (ErrMsg));
313 err->tid = __PHYSFS_platformGetThreadID();
315 if (errorLock != NULL)
316 __PHYSFS_platformGrabMutex(errorLock);
318 err->next = errorMessages;
321 if (errorLock != NULL)
322 __PHYSFS_platformReleaseMutex(errorLock);
325 err->errorAvailable = 1;
326 strncpy(err->errorString, str, sizeof (err->errorString));
327 err->errorString[sizeof (err->errorString) - 1] = '\0';
328 } /* __PHYSFS_setError */
331 const char *PHYSFS_getLastError(void)
333 ErrMsg *err = findErrorForCurrentThread();
335 if ((err == NULL) || (!err->errorAvailable))
338 err->errorAvailable = 0;
339 return(err->errorString);
340 } /* PHYSFS_getLastError */
343 /* MAKE SURE that errorLock is held before calling this! */
344 static void freeErrorMessages(void)
349 for (i = errorMessages; i != NULL; i = next)
355 errorMessages = NULL;
356 } /* freeErrorMessages */
359 void PHYSFS_getLinkedVersion(PHYSFS_Version *ver)
363 ver->major = PHYSFS_VER_MAJOR;
364 ver->minor = PHYSFS_VER_MINOR;
365 ver->patch = PHYSFS_VER_PATCH;
367 } /* PHYSFS_getLinkedVersion */
370 static const char *find_filename_extension(const char *fname)
372 const char *retval = strchr(fname, '.');
373 const char *p = retval;
377 p = strchr(p + 1, '.');
383 retval++; /* skip '.' */
386 } /* find_filename_extension */
389 static DirHandle *tryOpenDir(const PHYSFS_Archiver *funcs,
390 const char *d, int forWriting)
392 DirHandle *retval = NULL;
393 if (funcs->isArchive(d, forWriting))
395 void *opaque = funcs->openArchive(d, forWriting);
398 retval = (DirHandle *) allocator.Malloc(sizeof (DirHandle));
400 funcs->dirClose(opaque);
403 memset(retval, '\0', sizeof (DirHandle));
404 retval->mountPoint = NULL;
405 retval->funcs = funcs;
406 retval->opaque = opaque;
415 static DirHandle *openDirectory(const char *d, int forWriting)
417 DirHandle *retval = NULL;
418 const PHYSFS_Archiver **i;
421 BAIL_IF_MACRO(!__PHYSFS_platformExists(d), ERR_NO_SUCH_FILE, NULL);
423 ext = find_filename_extension(d);
426 /* Look for archivers with matching file extensions first... */
427 for (i = archivers; (*i != NULL) && (retval == NULL); i++)
429 if (__PHYSFS_stricmpASCII(ext, (*i)->info->extension) == 0)
430 retval = tryOpenDir(*i, d, forWriting);
433 /* failing an exact file extension match, try all the others... */
434 for (i = archivers; (*i != NULL) && (retval == NULL); i++)
436 if (__PHYSFS_stricmpASCII(ext, (*i)->info->extension) != 0)
437 retval = tryOpenDir(*i, d, forWriting);
441 else /* no extension? Try them all. */
443 for (i = archivers; (*i != NULL) && (retval == NULL); i++)
444 retval = tryOpenDir(*i, d, forWriting);
447 BAIL_IF_MACRO(retval == NULL, ERR_UNSUPPORTED_ARCHIVE, NULL);
449 } /* openDirectory */
453 * Make a platform-independent path string sane. Doesn't actually check the
454 * file hierarchy, it just cleans up the string.
455 * (dst) must be a buffer at least as big as (src), as this is where the
456 * cleaned up string is deposited.
457 * If there are illegal bits in the path (".." entries, etc) then we
458 * return zero and (dst) is undefined. Non-zero if the path was sanitized.
460 static int sanitizePlatformIndependentPath(const char *src, char *dst)
465 while (*src == '/') /* skip initial '/' chars... */
473 if ((ch == ':') || (ch == '\\')) /* illegal chars in a physfs path. */
474 BAIL_MACRO(ERR_INSECURE_FNAME, 0);
476 if (ch == '/') /* path separator. */
478 *dst = '\0'; /* "." and ".." are illegal pathnames. */
479 if ((strcmp(prev, ".") == 0) || (strcmp(prev, "..") == 0))
480 BAIL_MACRO(ERR_INSECURE_FNAME, 0);
482 while (*src == '/') /* chop out doubles... */
485 if (*src == '\0') /* ends with a pathsep? */
486 break; /* we're done, don't add final pathsep to dst. */
492 } while (ch != '\0');
495 } /* sanitizePlatformIndependentPath */
499 * Figure out if (fname) is part of (h)'s mountpoint. (fname) must be an
500 * output from sanitizePlatformIndependentPath(), so that it is in a known
503 * This only finds legitimate segments of a mountpoint. If the mountpoint is
504 * "/a/b/c" and (fname) is "/a/b/c", "/", or "/a/b/c/d", then the results are
505 * all zero. "/a/b" will succeed, though.
507 static int partOfMountPoint(DirHandle *h, char *fname)
509 /* !!! FIXME: This code feels gross. */
511 size_t len, mntpntlen;
513 if (h->mountPoint == NULL)
515 else if (*fname == '\0')
519 mntpntlen = strlen(h->mountPoint);
520 if (len > mntpntlen) /* can't be a subset of mountpoint. */
523 /* if true, must be not a match or a complete match, but not a subset. */
524 if ((len + 1) == mntpntlen)
527 rc = strncmp(fname, h->mountPoint, len); /* !!! FIXME: case insensitive? */
529 return(0); /* not a match. */
531 /* make sure /a/b matches /a/b/ and not /a/bc ... */
532 return(h->mountPoint[len] == '/');
533 } /* partOfMountPoint */
536 static DirHandle *createDirHandle(const char *newDir,
537 const char *mountPoint,
540 DirHandle *dirHandle = NULL;
541 char *tmpmntpnt = NULL;
543 GOTO_IF_MACRO(!newDir, ERR_INVALID_ARGUMENT, badDirHandle);
544 if (mountPoint != NULL)
546 const size_t len = strlen(mountPoint) + 1;
547 tmpmntpnt = (char *) __PHYSFS_smallAlloc(len);
548 GOTO_IF_MACRO(!tmpmntpnt, ERR_OUT_OF_MEMORY, badDirHandle);
549 if (!sanitizePlatformIndependentPath(mountPoint, tmpmntpnt))
551 mountPoint = tmpmntpnt; /* sanitized version. */
554 dirHandle = openDirectory(newDir, forWriting);
555 GOTO_IF_MACRO(!dirHandle, NULL, badDirHandle);
557 dirHandle->dirName = (char *) allocator.Malloc(strlen(newDir) + 1);
558 GOTO_IF_MACRO(!dirHandle->dirName, ERR_OUT_OF_MEMORY, badDirHandle);
559 strcpy(dirHandle->dirName, newDir);
561 if ((mountPoint != NULL) && (*mountPoint != '\0'))
563 dirHandle->mountPoint = (char *)allocator.Malloc(strlen(mountPoint)+2);
564 GOTO_IF_MACRO(!dirHandle->mountPoint, ERR_OUT_OF_MEMORY, badDirHandle);
565 strcpy(dirHandle->mountPoint, mountPoint);
566 strcat(dirHandle->mountPoint, "/");
569 __PHYSFS_smallFree(tmpmntpnt);
573 if (dirHandle != NULL)
575 dirHandle->funcs->dirClose(dirHandle->opaque);
576 allocator.Free(dirHandle->dirName);
577 allocator.Free(dirHandle->mountPoint);
578 allocator.Free(dirHandle);
581 __PHYSFS_smallFree(tmpmntpnt);
583 } /* createDirHandle */
586 /* MAKE SURE you've got the stateLock held before calling this! */
587 static int freeDirHandle(DirHandle *dh, FileHandle *openList)
594 for (i = openList; i != NULL; i = i->next)
595 BAIL_IF_MACRO(i->dirHandle == dh, ERR_FILES_STILL_OPEN, 0);
597 dh->funcs->dirClose(dh->opaque);
598 allocator.Free(dh->dirName);
599 allocator.Free(dh->mountPoint);
602 } /* freeDirHandle */
605 static char *calculateUserDir(void)
608 const char *str = NULL;
610 str = __PHYSFS_platformGetUserDir();
612 retval = (char *) str;
615 const char *dirsep = PHYSFS_getDirSeparator();
616 const char *uname = __PHYSFS_platformGetUserName();
618 str = (uname != NULL) ? uname : "default";
619 retval = (char *) allocator.Malloc(strlen(baseDir) + strlen(str) +
623 __PHYSFS_setError(ERR_OUT_OF_MEMORY);
625 sprintf(retval, "%susers%s%s", baseDir, dirsep, str);
627 allocator.Free((void *) uname);
631 } /* calculateUserDir */
634 static int appendDirSep(char **dir)
636 const char *dirsep = PHYSFS_getDirSeparator();
639 if (strcmp((*dir + strlen(*dir)) - strlen(dirsep), dirsep) == 0)
642 ptr = (char *) allocator.Realloc(*dir, strlen(*dir) + strlen(dirsep) + 1);
645 allocator.Free(*dir);
655 static char *calculateBaseDir(const char *argv0)
658 const char *dirsep = NULL;
661 /* Give the platform layer first shot at this. */
662 retval = __PHYSFS_platformCalcBaseDir(argv0);
666 /* We need argv0 to go on. */
667 BAIL_IF_MACRO(argv0 == NULL, ERR_ARGV0_IS_NULL, NULL);
669 dirsep = PHYSFS_getDirSeparator();
670 if (strlen(dirsep) == 1) /* fast path. */
671 ptr = strrchr(argv0, *dirsep);
674 ptr = strstr(argv0, dirsep);
681 p = strstr(p + 1, dirsep);
688 size_t size = (size_t) (ptr - argv0);
689 retval = (char *) allocator.Malloc(size + 1);
690 BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
691 memcpy(retval, argv0, size);
696 /* argv0 wasn't helpful. */
697 BAIL_MACRO(ERR_INVALID_ARGUMENT, NULL);
699 } /* calculateBaseDir */
702 static int initializeMutexes(void)
704 errorLock = __PHYSFS_platformCreateMutex();
705 if (errorLock == NULL)
706 goto initializeMutexes_failed;
708 stateLock = __PHYSFS_platformCreateMutex();
709 if (stateLock == NULL)
710 goto initializeMutexes_failed;
712 return(1); /* success. */
714 initializeMutexes_failed:
715 if (errorLock != NULL)
716 __PHYSFS_platformDestroyMutex(errorLock);
718 if (stateLock != NULL)
719 __PHYSFS_platformDestroyMutex(stateLock);
721 errorLock = stateLock = NULL;
722 return(0); /* failed. */
723 } /* initializeMutexes */
726 static void setDefaultAllocator(void);
728 int PHYSFS_init(const char *argv0)
732 BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0);
734 if (!externalAllocator)
735 setDefaultAllocator();
737 if (allocator.Init != NULL)
738 BAIL_IF_MACRO(!allocator.Init(), NULL, 0);
740 BAIL_IF_MACRO(!__PHYSFS_platformInit(), NULL, 0);
742 BAIL_IF_MACRO(!initializeMutexes(), NULL, 0);
744 baseDir = calculateBaseDir(argv0);
745 BAIL_IF_MACRO(baseDir == NULL, NULL, 0);
747 /* !!! FIXME: only call this if we got this from argv0 (unreliable). */
748 ptr = __PHYSFS_platformRealPath(baseDir);
749 allocator.Free(baseDir);
750 BAIL_IF_MACRO(ptr == NULL, NULL, 0);
753 BAIL_IF_MACRO(!appendDirSep(&baseDir), NULL, 0);
755 userDir = calculateUserDir();
758 ptr = __PHYSFS_platformRealPath(userDir);
759 allocator.Free(userDir);
763 if ((userDir == NULL) || (!appendDirSep(&userDir)))
765 allocator.Free(baseDir);
772 /* This makes sure that the error subsystem is initialized. */
773 __PHYSFS_setError(PHYSFS_getLastError());
779 /* MAKE SURE you hold stateLock before calling this! */
780 static int closeFileHandleList(FileHandle **list)
783 FileHandle *next = NULL;
785 for (i = *list; i != NULL; i = next)
788 if (!i->funcs->fileClose(i->opaque))
799 } /* closeFileHandleList */
802 /* MAKE SURE you hold the stateLock before calling this! */
803 static void freeSearchPath(void)
806 DirHandle *next = NULL;
808 closeFileHandleList(&openReadList);
810 if (searchPath != NULL)
812 for (i = searchPath; i != NULL; i = next)
815 freeDirHandle(i, openReadList);
819 } /* freeSearchPath */
822 int PHYSFS_deinit(void)
824 BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
825 BAIL_IF_MACRO(!__PHYSFS_platformDeinit(), NULL, 0);
827 closeFileHandleList(&openWriteList);
828 BAIL_IF_MACRO(!PHYSFS_setWriteDir(NULL), ERR_FILES_STILL_OPEN, 0);
835 allocator.Free(baseDir);
841 allocator.Free(userDir);
848 __PHYSFS_platformDestroyMutex(errorLock);
849 __PHYSFS_platformDestroyMutex(stateLock);
851 if (allocator.Deinit != NULL)
854 errorLock = stateLock = NULL;
856 } /* PHYSFS_deinit */
859 int PHYSFS_isInit(void)
862 } /* PHYSFS_isInit */
865 const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void)
867 return(supported_types);
868 } /* PHYSFS_supportedArchiveTypes */
871 void PHYSFS_freeList(void *list)
874 for (i = (void **) list; *i != NULL; i++)
877 allocator.Free(list);
878 } /* PHYSFS_freeList */
881 const char *PHYSFS_getDirSeparator(void)
883 return(__PHYSFS_platformDirSeparator);
884 } /* PHYSFS_getDirSeparator */
887 char **PHYSFS_getCdRomDirs(void)
889 return(doEnumStringList(__PHYSFS_platformDetectAvailableCDs));
890 } /* PHYSFS_getCdRomDirs */
893 void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback callback, void *data)
895 __PHYSFS_platformDetectAvailableCDs(callback, data);
896 } /* PHYSFS_getCdRomDirsCallback */
899 const char *PHYSFS_getBaseDir(void)
901 return(baseDir); /* this is calculated in PHYSFS_init()... */
902 } /* PHYSFS_getBaseDir */
905 const char *PHYSFS_getUserDir(void)
907 return(userDir); /* this is calculated in PHYSFS_init()... */
908 } /* PHYSFS_getUserDir */
911 const char *PHYSFS_getWriteDir(void)
913 const char *retval = NULL;
915 __PHYSFS_platformGrabMutex(stateLock);
916 if (writeDir != NULL)
917 retval = writeDir->dirName;
918 __PHYSFS_platformReleaseMutex(stateLock);
921 } /* PHYSFS_getWriteDir */
924 int PHYSFS_setWriteDir(const char *newDir)
928 __PHYSFS_platformGrabMutex(stateLock);
930 if (writeDir != NULL)
932 BAIL_IF_MACRO_MUTEX(!freeDirHandle(writeDir, openWriteList), NULL,
939 writeDir = createDirHandle(newDir, NULL, 1);
940 retval = (writeDir != NULL);
943 __PHYSFS_platformReleaseMutex(stateLock);
946 } /* PHYSFS_setWriteDir */
949 int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath)
952 DirHandle *prev = NULL;
955 BAIL_IF_MACRO(newDir == NULL, ERR_INVALID_ARGUMENT, 0);
957 if (mountPoint == NULL)
960 __PHYSFS_platformGrabMutex(stateLock);
962 for (i = searchPath; i != NULL; i = i->next)
964 /* already in search path? */
965 BAIL_IF_MACRO_MUTEX(strcmp(newDir, i->dirName)==0, NULL, stateLock, 1);
969 dh = createDirHandle(newDir, mountPoint, 0);
970 BAIL_IF_MACRO_MUTEX(dh == NULL, NULL, stateLock, 0);
981 dh->next = searchPath;
985 __PHYSFS_platformReleaseMutex(stateLock);
990 int PHYSFS_addToSearchPath(const char *newDir, int appendToPath)
992 return(PHYSFS_mount(newDir, NULL, appendToPath));
993 } /* PHYSFS_addToSearchPath */
996 int PHYSFS_removeFromSearchPath(const char *oldDir)
999 DirHandle *prev = NULL;
1000 DirHandle *next = NULL;
1002 BAIL_IF_MACRO(oldDir == NULL, ERR_INVALID_ARGUMENT, 0);
1004 __PHYSFS_platformGrabMutex(stateLock);
1005 for (i = searchPath; i != NULL; i = i->next)
1007 if (strcmp(i->dirName, oldDir) == 0)
1010 BAIL_IF_MACRO_MUTEX(!freeDirHandle(i, openReadList), NULL,
1018 BAIL_MACRO_MUTEX(NULL, stateLock, 1);
1023 BAIL_MACRO_MUTEX(ERR_NOT_IN_SEARCH_PATH, stateLock, 0);
1024 } /* PHYSFS_removeFromSearchPath */
1027 char **PHYSFS_getSearchPath(void)
1029 return(doEnumStringList(PHYSFS_getSearchPathCallback));
1030 } /* PHYSFS_getSearchPath */
1033 const char *PHYSFS_getMountPoint(const char *dir)
1036 __PHYSFS_platformGrabMutex(stateLock);
1037 for (i = searchPath; i != NULL; i = i->next)
1039 if (strcmp(i->dirName, dir) == 0)
1041 const char *retval = ((i->mountPoint) ? i->mountPoint : "/");
1042 __PHYSFS_platformReleaseMutex(stateLock);
1046 __PHYSFS_platformReleaseMutex(stateLock);
1048 BAIL_MACRO(ERR_NOT_IN_SEARCH_PATH, NULL);
1049 } /* PHYSFS_getMountPoint */
1052 void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback callback, void *data)
1056 __PHYSFS_platformGrabMutex(stateLock);
1058 for (i = searchPath; i != NULL; i = i->next)
1059 callback(data, i->dirName);
1061 __PHYSFS_platformReleaseMutex(stateLock);
1062 } /* PHYSFS_getSearchPathCallback */
1065 /* Split out to avoid stack allocation in a loop. */
1066 static void setSaneCfgAddPath(const char *i, const size_t l, const char *dirsep,
1069 const char *d = PHYSFS_getRealDir(i);
1070 const size_t allocsize = strlen(d) + strlen(dirsep) + l + 1;
1071 char *str = (char *) __PHYSFS_smallAlloc(allocsize);
1074 sprintf(str, "%s%s%s", d, dirsep, i);
1075 PHYSFS_addToSearchPath(str, archivesFirst == 0);
1076 __PHYSFS_smallFree(str);
1078 } /* setSaneCfgAddPath */
1081 int PHYSFS_setSaneConfig(const char *organization, const char *appName,
1082 const char *archiveExt, int includeCdRoms,
1085 const char *basedir = PHYSFS_getBaseDir();
1086 const char *userdir = PHYSFS_getUserDir();
1087 const char *dirsep = PHYSFS_getDirSeparator();
1088 PHYSFS_uint64 len = 0;
1091 BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
1093 /* set write dir... */
1094 len = (strlen(userdir) + (strlen(organization) * 2) +
1095 (strlen(appName) * 2) + (strlen(dirsep) * 3) + 2);
1097 str = (char *) __PHYSFS_smallAlloc(len);
1099 BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0);
1100 sprintf(str, "%s.%s%s%s", userdir, organization, dirsep, appName);
1102 if (!PHYSFS_setWriteDir(str))
1105 sprintf(str, ".%s/%s", organization, appName);
1106 if ( (PHYSFS_setWriteDir(userdir)) &&
1107 (PHYSFS_mkdir(str)) )
1109 sprintf(str, "%s.%s%s%s", userdir, organization, dirsep, appName);
1110 if (!PHYSFS_setWriteDir(str))
1120 PHYSFS_setWriteDir(NULL); /* just in case. */
1121 __PHYSFS_smallFree(str);
1122 BAIL_MACRO(ERR_CANT_SET_WRITE_DIR, 0);
1126 /* Put write dir first in search path... */
1127 PHYSFS_addToSearchPath(str, 0);
1128 __PHYSFS_smallFree(str);
1130 /* Put base path on search path... */
1131 PHYSFS_addToSearchPath(basedir, 1);
1133 /* handle CD-ROMs... */
1136 char **cds = PHYSFS_getCdRomDirs();
1138 for (i = cds; *i != NULL; i++)
1139 PHYSFS_addToSearchPath(*i, 1);
1141 PHYSFS_freeList(cds);
1144 /* Root out archives, and add them to search path... */
1145 if (archiveExt != NULL)
1147 char **rc = PHYSFS_enumerateFiles("/");
1149 size_t extlen = strlen(archiveExt);
1152 for (i = rc; *i != NULL; i++)
1154 size_t l = strlen(*i);
1155 if ((l > extlen) && ((*i)[l - extlen - 1] == '.'))
1157 ext = (*i) + (l - extlen);
1158 if (__PHYSFS_stricmpASCII(ext, archiveExt) == 0)
1159 setSaneCfgAddPath(*i, l, dirsep, archivesFirst);
1163 PHYSFS_freeList(rc);
1167 } /* PHYSFS_setSaneConfig */
1170 void PHYSFS_permitSymbolicLinks(int allow)
1172 allowSymLinks = allow;
1173 } /* PHYSFS_permitSymbolicLinks */
1176 int PHYSFS_symbolicLinksPermitted(void)
1178 return(allowSymLinks);
1179 } /* PHYSFS_symbolicLinksPermitted */
1182 /* string manipulation in C makes my ass itch. */
1183 char *__PHYSFS_convertToDependent(const char *prepend,
1184 const char *dirName,
1187 const char *dirsep = __PHYSFS_platformDirSeparator;
1188 size_t sepsize = strlen(dirsep);
1194 while (*dirName == '/') /* !!! FIXME: pass through sanitize function. */
1197 allocSize = strlen(dirName) + 1;
1198 if (prepend != NULL)
1199 allocSize += strlen(prepend) + sepsize;
1201 allocSize += strlen(append) + sepsize;
1203 /* make sure there's enough space if the dir separator is bigger. */
1206 str = (char *) dirName;
1209 str = strchr(str, '/');
1212 allocSize += (sepsize - 1);
1215 } while (str != NULL);
1218 str = (char *) allocator.Malloc(allocSize);
1219 BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, NULL);
1221 if (prepend == NULL)
1225 strcpy(str, prepend);
1226 strcat(str, dirsep);
1229 for (i1 = (char *) dirName, i2 = str + strlen(str); *i1; i1++, i2++)
1245 strcat(str, dirsep);
1246 strcat(str, append);
1250 } /* __PHYSFS_convertToDependent */
1254 * Verify that (fname) (in platform-independent notation), in relation
1255 * to (h) is secure. That means that each element of fname is checked
1256 * for symlinks (if they aren't permitted). This also allows for quick
1257 * rejection of files that exist outside an archive's mountpoint.
1259 * With some exceptions (like PHYSFS_mkdir(), which builds multiple subdirs
1260 * at a time), you should always pass zero for "allowMissing" for efficiency.
1262 * (fname) must point to an output from sanitizePlatformIndependentPath(),
1263 * since it will make sure that path names are in the right format for
1264 * passing certain checks. It will also do checks for "insecure" pathnames
1265 * like ".." which should be done once instead of once per archive. This also
1266 * gives us license to treat (fname) as scratch space in this function.
1268 * Returns non-zero if string is safe, zero if there's a security issue.
1269 * PHYSFS_getLastError() will specify what was wrong. (*fname) will be
1270 * updated to point past any mount point elements so it is prepared to
1271 * be used with the archiver directly.
1273 static int verifyPath(DirHandle *h, char **_fname, int allowMissing)
1275 char *fname = *_fname;
1280 if (*fname == '\0') /* quick rejection. */
1283 /* !!! FIXME: This codeblock sucks. */
1284 if (h->mountPoint != NULL) /* NULL mountpoint means "/". */
1286 size_t mntpntlen = strlen(h->mountPoint);
1287 size_t len = strlen(fname);
1288 assert(mntpntlen > 1); /* root mount points should be NULL. */
1289 /* not under the mountpoint, so skip this archive. */
1290 BAIL_IF_MACRO(len < mntpntlen-1, ERR_NO_SUCH_PATH, 0);
1291 /* !!! FIXME: Case insensitive? */
1292 retval = strncmp(h->mountPoint, fname, mntpntlen-1);
1293 BAIL_IF_MACRO(retval != 0, ERR_NO_SUCH_PATH, 0);
1294 if (len > mntpntlen-1) /* corner case... */
1295 BAIL_IF_MACRO(fname[mntpntlen-1] != '/', ERR_NO_SUCH_PATH, 0);
1296 fname += mntpntlen-1; /* move to start of actual archive path. */
1299 *_fname = fname; /* skip mountpoint for later use. */
1300 retval = 1; /* may be reset, below. */
1309 end = strchr(start, '/');
1311 if (end != NULL) *end = '\0';
1312 rc = h->funcs->isSymLink(h->opaque, fname, &retval);
1313 if (end != NULL) *end = '/';
1315 BAIL_IF_MACRO(rc, ERR_SYMLINK_DISALLOWED, 0); /* insecure. */
1317 /* break out early if path element is missing. */
1321 * We need to clear it if it's the last element of the path,
1322 * since this might be a non-existant file we're opening
1325 if ((end == NULL) || (allowMissing))
1341 static int doMkdir(const char *_dname, char *dname)
1347 int exists = 1; /* force existance check on first path element. */
1349 BAIL_IF_MACRO(!sanitizePlatformIndependentPath(_dname, dname), NULL, 0);
1351 __PHYSFS_platformGrabMutex(stateLock);
1352 BAIL_IF_MACRO_MUTEX(writeDir == NULL, ERR_NO_WRITE_DIR, stateLock, 0);
1354 BAIL_IF_MACRO_MUTEX(!verifyPath(h, &dname, 1), NULL, stateLock, 0);
1359 end = strchr(start, '/');
1363 /* only check for existance if all parent dirs existed, too... */
1365 retval = h->funcs->isDirectory(h->opaque, dname, &exists);
1368 retval = h->funcs->mkdir(h->opaque, dname);
1380 __PHYSFS_platformReleaseMutex(stateLock);
1385 int PHYSFS_mkdir(const char *_dname)
1391 BAIL_IF_MACRO(_dname == NULL, ERR_INVALID_ARGUMENT, 0);
1392 len = strlen(_dname) + 1;
1393 dname = (char *) __PHYSFS_smallAlloc(len);
1394 BAIL_IF_MACRO(dname == NULL, ERR_OUT_OF_MEMORY, 0);
1395 retval = doMkdir(_dname, dname);
1396 __PHYSFS_smallFree(dname);
1398 } /* PHYSFS_mkdir */
1401 static int doDelete(const char *_fname, char *fname)
1405 BAIL_IF_MACRO(!sanitizePlatformIndependentPath(_fname, fname), NULL, 0);
1407 __PHYSFS_platformGrabMutex(stateLock);
1409 BAIL_IF_MACRO_MUTEX(writeDir == NULL, ERR_NO_WRITE_DIR, stateLock, 0);
1411 BAIL_IF_MACRO_MUTEX(!verifyPath(h, &fname, 0), NULL, stateLock, 0);
1412 retval = h->funcs->remove(h->opaque, fname);
1414 __PHYSFS_platformReleaseMutex(stateLock);
1419 int PHYSFS_delete(const char *_fname)
1425 BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0);
1426 len = strlen(_fname) + 1;
1427 fname = (char *) __PHYSFS_smallAlloc(len);
1428 BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0);
1429 retval = doDelete(_fname, fname);
1430 __PHYSFS_smallFree(fname);
1432 } /* PHYSFS_delete */
1435 const char *PHYSFS_getRealDir(const char *_fname)
1437 const char *retval = NULL;
1441 BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, NULL);
1442 len = strlen(_fname) + 1;
1443 fname = __PHYSFS_smallAlloc(len);
1444 BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, NULL);
1445 if (sanitizePlatformIndependentPath(_fname, fname))
1448 __PHYSFS_platformGrabMutex(stateLock);
1449 for (i = searchPath; ((i != NULL) && (retval == NULL)); i = i->next)
1451 char *arcfname = fname;
1452 if (partOfMountPoint(i, arcfname))
1453 retval = i->dirName;
1454 else if (verifyPath(i, &arcfname, 0))
1456 if (i->funcs->exists(i->opaque, arcfname))
1457 retval = i->dirName;
1460 __PHYSFS_platformReleaseMutex(stateLock);
1463 __PHYSFS_smallFree(fname);
1465 } /* PHYSFS_getRealDir */
1468 static int locateInStringList(const char *str,
1472 PHYSFS_uint32 len = *pos;
1473 PHYSFS_uint32 half_len;
1474 PHYSFS_uint32 lo = 0;
1475 PHYSFS_uint32 middle;
1480 half_len = len >> 1;
1481 middle = lo + half_len;
1482 cmp = strcmp(list[middle], str);
1484 if (cmp == 0) /* it's in the list already. */
1491 len -= half_len + 1;
1497 } /* locateInStringList */
1500 static void enumFilesCallback(void *data, const char *origdir, const char *str)
1505 EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data;
1508 * See if file is in the list already, and if not, insert it in there
1512 if (locateInStringList(str, pecd->list, &pos))
1513 return; /* already in the list. */
1515 ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *));
1516 newstr = (char *) allocator.Malloc(strlen(str) + 1);
1518 pecd->list = (char **) ptr;
1520 if ((ptr == NULL) || (newstr == NULL))
1521 return; /* better luck next time. */
1523 strcpy(newstr, str);
1525 if (pos != pecd->size)
1527 memmove(&pecd->list[pos+1], &pecd->list[pos],
1528 sizeof (char *) * ((pecd->size) - pos));
1531 pecd->list[pos] = newstr;
1533 } /* enumFilesCallback */
1536 char **PHYSFS_enumerateFiles(const char *path)
1538 EnumStringListCallbackData ecd;
1539 memset(&ecd, '\0', sizeof (ecd));
1540 ecd.list = (char **) allocator.Malloc(sizeof (char *));
1541 BAIL_IF_MACRO(ecd.list == NULL, ERR_OUT_OF_MEMORY, NULL);
1542 PHYSFS_enumerateFilesCallback(path, enumFilesCallback, &ecd);
1543 ecd.list[ecd.size] = NULL;
1545 } /* PHYSFS_enumerateFiles */
1549 * Broke out to seperate function so we can use stack allocation gratuitously.
1551 static void enumerateFromMountPoint(DirHandle *i, const char *arcfname,
1552 PHYSFS_EnumFilesCallback callback,
1553 const char *_fname, void *data)
1555 const size_t len = strlen(arcfname);
1558 const size_t slen = strlen(i->mountPoint) + 1;
1559 char *mountPoint = (char *) __PHYSFS_smallAlloc(slen);
1561 if (mountPoint == NULL)
1562 return; /* oh well. */
1564 strcpy(mountPoint, i->mountPoint);
1565 ptr = mountPoint + ((len) ? len + 1 : 0);
1566 end = strchr(ptr, '/');
1567 assert(end); /* should always find a terminating '/'. */
1569 callback(data, _fname, ptr);
1570 __PHYSFS_smallFree(mountPoint);
1571 } /* enumerateFromMountPoint */
1574 /* !!! FIXME: this should report error conditions. */
1575 void PHYSFS_enumerateFilesCallback(const char *_fname,
1576 PHYSFS_EnumFilesCallback callback,
1582 BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, ) /*0*/;
1583 BAIL_IF_MACRO(callback == NULL, ERR_INVALID_ARGUMENT, ) /*0*/;
1585 len = strlen(_fname) + 1;
1586 fname = (char *) __PHYSFS_smallAlloc(len);
1587 BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, ) /*0*/;
1589 if (sanitizePlatformIndependentPath(_fname, fname))
1594 __PHYSFS_platformGrabMutex(stateLock);
1595 noSyms = !allowSymLinks;
1596 for (i = searchPath; i != NULL; i = i->next)
1598 char *arcfname = fname;
1599 if (partOfMountPoint(i, arcfname))
1600 enumerateFromMountPoint(i, arcfname, callback, _fname, data);
1602 else if (verifyPath(i, &arcfname, 0))
1604 i->funcs->enumerateFiles(i->opaque, arcfname, noSyms,
1605 callback, _fname, data);
1608 __PHYSFS_platformReleaseMutex(stateLock);
1611 __PHYSFS_smallFree(fname);
1612 } /* PHYSFS_enumerateFilesCallback */
1615 int PHYSFS_exists(const char *fname)
1617 return(PHYSFS_getRealDir(fname) != NULL);
1618 } /* PHYSFS_exists */
1621 PHYSFS_sint64 PHYSFS_getLastModTime(const char *_fname)
1623 PHYSFS_sint64 retval = -1;
1627 BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, -1);
1628 len = strlen(_fname) + 1;
1629 fname = (char *) __PHYSFS_smallAlloc(len);
1630 BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, -1);
1632 if (sanitizePlatformIndependentPath(_fname, fname))
1634 if (*fname == '\0') /* eh...punt if it's the root dir. */
1635 retval = 1; /* !!! FIXME: Maybe this should be an error? */
1640 __PHYSFS_platformGrabMutex(stateLock);
1641 for (i = searchPath; ((i != NULL) && (!exists)); i = i->next)
1643 char *arcfname = fname;
1644 exists = partOfMountPoint(i, arcfname);
1646 retval = 1; /* !!! FIXME: What's the right value? */
1647 else if (verifyPath(i, &arcfname, 0))
1649 retval = i->funcs->getLastModTime(i->opaque, arcfname,
1653 __PHYSFS_platformReleaseMutex(stateLock);
1657 __PHYSFS_smallFree(fname);
1659 } /* PHYSFS_getLastModTime */
1662 int PHYSFS_isDirectory(const char *_fname)
1668 BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0);
1669 len = strlen(_fname) + 1;
1670 fname = (char *) __PHYSFS_smallAlloc(len);
1671 BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0);
1673 if (!sanitizePlatformIndependentPath(_fname, fname))
1676 else if (*fname == '\0')
1677 retval = 1; /* Root is always a dir. :) */
1684 __PHYSFS_platformGrabMutex(stateLock);
1685 for (i = searchPath; ((i != NULL) && (!exists)); i = i->next)
1687 char *arcfname = fname;
1688 if ((exists = partOfMountPoint(i, arcfname)) != 0)
1690 else if (verifyPath(i, &arcfname, 0))
1691 retval = i->funcs->isDirectory(i->opaque, arcfname, &exists);
1693 __PHYSFS_platformReleaseMutex(stateLock);
1696 __PHYSFS_smallFree(fname);
1698 } /* PHYSFS_isDirectory */
1701 int PHYSFS_isSymbolicLink(const char *_fname)
1707 BAIL_IF_MACRO(!allowSymLinks, ERR_SYMLINK_DISALLOWED, 0);
1709 BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0);
1710 len = strlen(_fname) + 1;
1711 fname = (char *) __PHYSFS_smallAlloc(len);
1712 BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0);
1714 if (!sanitizePlatformIndependentPath(_fname, fname))
1717 else if (*fname == '\0')
1718 retval = 1; /* Root is never a symlink. */
1725 __PHYSFS_platformGrabMutex(stateLock);
1726 for (i = searchPath; ((i != NULL) && (!fileExists)); i = i->next)
1728 char *arcfname = fname;
1729 if ((fileExists = partOfMountPoint(i, arcfname)) != 0)
1730 retval = 0; /* virtual dir...not a symlink. */
1731 else if (verifyPath(i, &arcfname, 0))
1732 retval = i->funcs->isSymLink(i->opaque, arcfname, &fileExists);
1734 __PHYSFS_platformReleaseMutex(stateLock);
1737 __PHYSFS_smallFree(fname);
1739 } /* PHYSFS_isSymbolicLink */
1742 static PHYSFS_File *doOpenWrite(const char *_fname, int appending)
1744 FileHandle *fh = NULL;
1748 BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0);
1749 len = strlen(_fname) + 1;
1750 fname = (char *) __PHYSFS_smallAlloc(len);
1751 BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0);
1753 if (sanitizePlatformIndependentPath(_fname, fname))
1755 void *opaque = NULL;
1756 DirHandle *h = NULL;
1757 const PHYSFS_Archiver *f;
1759 __PHYSFS_platformGrabMutex(stateLock);
1761 GOTO_IF_MACRO(!writeDir, ERR_NO_WRITE_DIR, doOpenWriteEnd);
1764 GOTO_IF_MACRO(!verifyPath(h, &fname, 0), NULL, doOpenWriteEnd);
1768 opaque = f->openAppend(h->opaque, fname);
1770 opaque = f->openWrite(h->opaque, fname);
1772 GOTO_IF_MACRO(opaque == NULL, NULL, doOpenWriteEnd);
1774 fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
1777 f->fileClose(opaque);
1778 GOTO_MACRO(ERR_OUT_OF_MEMORY, doOpenWriteEnd);
1782 memset(fh, '\0', sizeof (FileHandle));
1783 fh->opaque = opaque;
1785 fh->funcs = h->funcs;
1786 fh->next = openWriteList;
1791 __PHYSFS_platformReleaseMutex(stateLock);
1794 __PHYSFS_smallFree(fname);
1795 return((PHYSFS_File *) fh);
1799 PHYSFS_File *PHYSFS_openWrite(const char *filename)
1801 return(doOpenWrite(filename, 0));
1802 } /* PHYSFS_openWrite */
1805 PHYSFS_File *PHYSFS_openAppend(const char *filename)
1807 return(doOpenWrite(filename, 1));
1808 } /* PHYSFS_openAppend */
1811 PHYSFS_File *PHYSFS_openRead(const char *_fname)
1813 FileHandle *fh = NULL;
1817 BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0);
1818 len = strlen(_fname) + 1;
1819 fname = (char *) __PHYSFS_smallAlloc(len);
1820 BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0);
1822 if (sanitizePlatformIndependentPath(_fname, fname))
1825 DirHandle *i = NULL;
1826 fvoid *opaque = NULL;
1828 __PHYSFS_platformGrabMutex(stateLock);
1830 GOTO_IF_MACRO(!searchPath, ERR_NO_SUCH_PATH, openReadEnd);
1832 /* !!! FIXME: Why aren't we using a for loop here? */
1837 char *arcfname = fname;
1838 if (verifyPath(i, &arcfname, 0))
1840 opaque = i->funcs->openRead(i->opaque, arcfname, &fileExists);
1845 } while ((i != NULL) && (!fileExists));
1847 /* !!! FIXME: may not set an error if openRead didn't fail. */
1848 GOTO_IF_MACRO(opaque == NULL, NULL, openReadEnd);
1850 fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
1853 i->funcs->fileClose(opaque);
1854 GOTO_MACRO(ERR_OUT_OF_MEMORY, openReadEnd);
1857 memset(fh, '\0', sizeof (FileHandle));
1858 fh->opaque = opaque;
1861 fh->funcs = i->funcs;
1862 fh->next = openReadList;
1866 __PHYSFS_platformReleaseMutex(stateLock);
1869 __PHYSFS_smallFree(fname);
1870 return((PHYSFS_File *) fh);
1871 } /* PHYSFS_openRead */
1874 static int closeHandleInOpenList(FileHandle **list, FileHandle *handle)
1876 FileHandle *prev = NULL;
1880 for (i = *list; i != NULL; i = i->next)
1882 if (i == handle) /* handle is in this list? */
1884 PHYSFS_uint8 *tmp = handle->buffer;
1885 rc = PHYSFS_flush((PHYSFS_File *) handle);
1887 rc = handle->funcs->fileClose(handle->opaque);
1891 if (tmp != NULL) /* free any associated buffer. */
1892 allocator.Free(tmp);
1895 *list = handle->next;
1897 prev->next = handle->next;
1899 allocator.Free(handle);
1906 } /* closeHandleInOpenList */
1909 int PHYSFS_close(PHYSFS_File *_handle)
1911 FileHandle *handle = (FileHandle *) _handle;
1914 __PHYSFS_platformGrabMutex(stateLock);
1916 /* -1 == close failure. 0 == not found. 1 == success. */
1917 rc = closeHandleInOpenList(&openReadList, handle);
1918 BAIL_IF_MACRO_MUTEX(rc == -1, NULL, stateLock, 0);
1921 rc = closeHandleInOpenList(&openWriteList, handle);
1922 BAIL_IF_MACRO_MUTEX(rc == -1, NULL, stateLock, 0);
1925 __PHYSFS_platformReleaseMutex(stateLock);
1926 BAIL_IF_MACRO(!rc, ERR_NOT_A_HANDLE, 0);
1928 } /* PHYSFS_close */
1931 static PHYSFS_sint64 doBufferedRead(FileHandle *fh, void *buffer,
1932 PHYSFS_uint32 objSize,
1933 PHYSFS_uint32 objCount)
1935 PHYSFS_sint64 retval = 0;
1936 PHYSFS_uint32 remainder = 0;
1938 while (objCount > 0)
1940 PHYSFS_uint32 buffered = fh->buffill - fh->bufpos;
1941 PHYSFS_uint64 mustread = (objSize * objCount) - remainder;
1942 PHYSFS_uint32 copied;
1944 if (buffered == 0) /* need to refill buffer? */
1946 PHYSFS_sint64 rc = fh->funcs->read(fh->opaque, fh->buffer,
1950 fh->bufpos -= remainder;
1951 return(((rc == -1) && (retval == 0)) ? -1 : retval);
1954 buffered = fh->buffill = (PHYSFS_uint32) rc;
1958 if (buffered > mustread)
1959 buffered = (PHYSFS_uint32) mustread;
1961 memcpy(buffer, fh->buffer + fh->bufpos, (size_t) buffered);
1962 buffer = ((PHYSFS_uint8 *) buffer) + buffered;
1963 fh->bufpos += buffered;
1964 buffered += remainder; /* take remainder into account. */
1965 copied = (buffered / objSize);
1966 remainder = (buffered % objSize);
1972 } /* doBufferedRead */
1975 PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, void *buffer,
1976 PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
1978 FileHandle *fh = (FileHandle *) handle;
1980 BAIL_IF_MACRO(!fh->forReading, ERR_FILE_ALREADY_OPEN_W, -1);
1981 if (fh->buffer != NULL)
1982 return(doBufferedRead(fh, buffer, objSize, objCount));
1984 return(fh->funcs->read(fh->opaque, buffer, objSize, objCount));
1988 static PHYSFS_sint64 doBufferedWrite(PHYSFS_File *handle, const void *buffer,
1989 PHYSFS_uint32 objSize,
1990 PHYSFS_uint32 objCount)
1992 FileHandle *fh = (FileHandle *) handle;
1994 /* whole thing fits in the buffer? */
1995 if (fh->buffill + (objSize * objCount) < fh->bufsize)
1997 memcpy(fh->buffer + fh->buffill, buffer, objSize * objCount);
1998 fh->buffill += (objSize * objCount);
2002 /* would overflow buffer. Flush and then write the new objects, too. */
2003 BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, -1);
2004 return(fh->funcs->write(fh->opaque, buffer, objSize, objCount));
2005 } /* doBufferedWrite */
2008 PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle, const void *buffer,
2009 PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
2011 FileHandle *fh = (FileHandle *) handle;
2013 BAIL_IF_MACRO(fh->forReading, ERR_FILE_ALREADY_OPEN_R, -1);
2014 if (fh->buffer != NULL)
2015 return(doBufferedWrite(handle, buffer, objSize, objCount));
2017 return(fh->funcs->write(fh->opaque, buffer, objSize, objCount));
2018 } /* PHYSFS_write */
2021 int PHYSFS_eof(PHYSFS_File *handle)
2023 FileHandle *fh = (FileHandle *) handle;
2025 if (!fh->forReading) /* never EOF on files opened for write/append. */
2028 /* eof if buffer is empty and archiver says so. */
2029 return((fh->bufpos == fh->buffill) && (fh->funcs->eof(fh->opaque)));
2033 PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle)
2035 FileHandle *fh = (FileHandle *) handle;
2036 PHYSFS_sint64 pos = fh->funcs->tell(fh->opaque);
2037 PHYSFS_sint64 retval = fh->forReading ?
2038 (pos - fh->buffill) + fh->bufpos :
2039 (pos + fh->buffill);
2044 int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos)
2046 FileHandle *fh = (FileHandle *) handle;
2047 BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, 0);
2049 if (fh->buffer && fh->forReading)
2051 /* avoid throwing away our precious buffer if seeking within it. */
2052 PHYSFS_sint64 offset = pos - PHYSFS_tell(handle);
2053 if ( /* seeking within the already-buffered range? */
2054 ((offset >= 0) && (offset <= fh->buffill - fh->bufpos)) /* fwd */
2055 || ((offset < 0) && (-offset <= fh->bufpos)) /* backward */ )
2057 fh->bufpos += (PHYSFS_uint32) offset;
2058 return(1); /* successful seek */
2062 /* we have to fall back to a 'raw' seek. */
2063 fh->buffill = fh->bufpos = 0;
2064 return(fh->funcs->seek(fh->opaque, pos));
2068 PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle)
2070 FileHandle *fh = (FileHandle *) handle;
2071 return(fh->funcs->fileLength(fh->opaque));
2072 } /* PHYSFS_filelength */
2075 int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 _bufsize)
2077 FileHandle *fh = (FileHandle *) handle;
2078 PHYSFS_uint32 bufsize;
2080 /* !!! FIXME: Unlocalized string. */
2081 BAIL_IF_MACRO(_bufsize > 0xFFFFFFFF, "buffer must fit in 32-bits", 0);
2082 bufsize = (PHYSFS_uint32) _bufsize;
2084 BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, 0);
2087 * For reads, we need to move the file pointer to where it would be
2088 * if we weren't buffering, so that the next read will get the
2089 * right chunk of stuff from the file. PHYSFS_flush() handles writes.
2091 if ((fh->forReading) && (fh->buffill != fh->bufpos))
2094 PHYSFS_sint64 curpos = fh->funcs->tell(fh->opaque);
2095 BAIL_IF_MACRO(curpos == -1, NULL, 0);
2096 pos = ((curpos - fh->buffill) + fh->bufpos);
2097 BAIL_IF_MACRO(!fh->funcs->seek(fh->opaque, pos), NULL, 0);
2100 if (bufsize == 0) /* delete existing buffer. */
2102 if (fh->buffer != NULL)
2104 allocator.Free(fh->buffer);
2111 PHYSFS_uint8 *newbuf;
2112 newbuf = (PHYSFS_uint8 *) allocator.Realloc(fh->buffer, bufsize);
2113 BAIL_IF_MACRO(newbuf == NULL, ERR_OUT_OF_MEMORY, 0);
2114 fh->buffer = newbuf;
2117 fh->bufsize = bufsize;
2118 fh->buffill = fh->bufpos = 0;
2120 } /* PHYSFS_setBuffer */
2123 int PHYSFS_flush(PHYSFS_File *handle)
2125 FileHandle *fh = (FileHandle *) handle;
2128 if ((fh->forReading) || (fh->bufpos == fh->buffill))
2129 return(1); /* open for read or buffer empty are successful no-ops. */
2131 /* dump buffer to disk. */
2132 rc = fh->funcs->write(fh->opaque, fh->buffer + fh->bufpos,
2133 fh->buffill - fh->bufpos, 1);
2134 BAIL_IF_MACRO(rc <= 0, NULL, 0);
2135 fh->bufpos = fh->buffill = 0;
2137 } /* PHYSFS_flush */
2140 int PHYSFS_setAllocator(const PHYSFS_Allocator *a)
2142 BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0);
2143 externalAllocator = (a != NULL);
2144 if (externalAllocator)
2145 memcpy(&allocator, a, sizeof (PHYSFS_Allocator));
2148 } /* PHYSFS_setAllocator */
2151 static void *mallocAllocatorMalloc(PHYSFS_uint64 s)
2153 BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL);
2155 return(malloc((size_t) s));
2156 } /* mallocAllocatorMalloc */
2159 static void *mallocAllocatorRealloc(void *ptr, PHYSFS_uint64 s)
2161 BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL);
2163 return(realloc(ptr, (size_t) s));
2164 } /* mallocAllocatorRealloc */
2167 static void mallocAllocatorFree(void *ptr)
2171 } /* mallocAllocatorFree */
2174 static void setDefaultAllocator(void)
2176 assert(!externalAllocator);
2177 if (!__PHYSFS_platformSetDefaultAllocator(&allocator))
2179 allocator.Init = NULL;
2180 allocator.Deinit = NULL;
2181 allocator.Malloc = mallocAllocatorMalloc;
2182 allocator.Realloc = mallocAllocatorRealloc;
2183 allocator.Free = mallocAllocatorFree;
2185 } /* setDefaultAllocator */
2188 void *__PHYSFS_initSmallAlloc(void *ptr, PHYSFS_uint64 len)
2190 const char useHeap = ((ptr == NULL) ? 1 : 0);
2191 if (useHeap) /* too large for stack allocation or alloca() failed. */
2192 ptr = allocator.Malloc(len+1);
2196 char *retval = (char *) ptr;
2197 /*printf("%s alloc'd (%d) bytes at (%p).\n",
2198 useHeap ? "heap" : "stack", (int) len, ptr);*/
2203 return(NULL); /* allocation failed. */
2204 } /* __PHYSFS_initSmallAlloc */
2207 void __PHYSFS_smallFree(void *ptr)
2211 char *block = ((char *) ptr) - 1;
2212 const char useHeap = *block;
2214 allocator.Free(block);
2215 /*printf("%s free'd (%p).\n", useHeap ? "heap" : "stack", block);*/
2217 } /* __PHYSFS_smallFree */
2219 /* end of physfs.c ... */