misc/physfs/src/physfs.c
branchwebgl
changeset 8833 c13ebed437cb
parent 8450 404ddce27b23
parent 8830 343d3f0d6a86
child 8836 7a474fcc343d
equal deleted inserted replaced
8450:404ddce27b23 8833:c13ebed437cb
     1 /**
       
     2  * PhysicsFS; a portable, flexible file i/o abstraction.
       
     3  *
       
     4  * Documentation is in physfs.h. It's verbose, honest.  :)
       
     5  *
       
     6  * Please see the file LICENSE.txt in the source's root directory.
       
     7  *
       
     8  *  This file written by Ryan C. Gordon.
       
     9  */
       
    10 
       
    11 /* !!! FIXME: ERR_PAST_EOF shouldn't trigger for reads. Just return zero. */
       
    12 /* !!! FIXME: use snprintf(), not sprintf(). */
       
    13 
       
    14 #define __PHYSICSFS_INTERNAL__
       
    15 #include "physfs_internal.h"
       
    16 
       
    17 
       
    18 typedef struct __PHYSFS_DIRHANDLE__
       
    19 {
       
    20     void *opaque;  /* Instance data unique to the archiver. */
       
    21     char *dirName;  /* Path to archive in platform-dependent notation. */
       
    22     char *mountPoint; /* Mountpoint in virtual file tree. */
       
    23     const PHYSFS_Archiver *funcs;  /* Ptr to archiver info for this handle. */
       
    24     struct __PHYSFS_DIRHANDLE__ *next;  /* linked list stuff. */
       
    25 } DirHandle;
       
    26 
       
    27 
       
    28 typedef struct __PHYSFS_FILEHANDLE__
       
    29 {
       
    30     PHYSFS_Io *io;  /* Instance data unique to the archiver for this file. */
       
    31     PHYSFS_uint8 forReading; /* Non-zero if reading, zero if write/append */
       
    32     const DirHandle *dirHandle;  /* Archiver instance that created this */
       
    33     PHYSFS_uint8 *buffer;  /* Buffer, if set (NULL otherwise). Don't touch! */
       
    34     PHYSFS_uint32 bufsize;  /* Bufsize, if set (0 otherwise). Don't touch! */
       
    35     PHYSFS_uint32 buffill;  /* Buffer fill size. Don't touch! */
       
    36     PHYSFS_uint32 bufpos;  /* Buffer position. Don't touch! */
       
    37     struct __PHYSFS_FILEHANDLE__ *next;  /* linked list stuff. */
       
    38 } FileHandle;
       
    39 
       
    40 
       
    41 typedef struct __PHYSFS_ERRSTATETYPE__
       
    42 {
       
    43     void *tid;
       
    44     PHYSFS_ErrorCode code;
       
    45     struct __PHYSFS_ERRSTATETYPE__ *next;
       
    46 } ErrState;
       
    47 
       
    48 
       
    49 /* The various i/o drivers...some of these may not be compiled in. */
       
    50 extern const PHYSFS_Archiver __PHYSFS_Archiver_ZIP;
       
    51 extern const PHYSFS_Archiver __PHYSFS_Archiver_LZMA;
       
    52 extern const PHYSFS_Archiver __PHYSFS_Archiver_GRP;
       
    53 extern const PHYSFS_Archiver __PHYSFS_Archiver_QPAK;
       
    54 extern const PHYSFS_Archiver __PHYSFS_Archiver_HOG;
       
    55 extern const PHYSFS_Archiver __PHYSFS_Archiver_MVL;
       
    56 extern const PHYSFS_Archiver __PHYSFS_Archiver_WAD;
       
    57 extern const PHYSFS_Archiver __PHYSFS_Archiver_DIR;
       
    58 extern const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660;
       
    59 
       
    60 static const PHYSFS_Archiver *staticArchivers[] =
       
    61 {
       
    62 #if PHYSFS_SUPPORTS_ZIP
       
    63     &__PHYSFS_Archiver_ZIP,
       
    64 #endif
       
    65 #if PHYSFS_SUPPORTS_7Z
       
    66     &__PHYSFS_Archiver_LZMA,
       
    67 #endif
       
    68 #if PHYSFS_SUPPORTS_GRP
       
    69     &__PHYSFS_Archiver_GRP,
       
    70 #endif
       
    71 #if PHYSFS_SUPPORTS_QPAK
       
    72     &__PHYSFS_Archiver_QPAK,
       
    73 #endif
       
    74 #if PHYSFS_SUPPORTS_HOG
       
    75     &__PHYSFS_Archiver_HOG,
       
    76 #endif
       
    77 #if PHYSFS_SUPPORTS_MVL
       
    78     &__PHYSFS_Archiver_MVL,
       
    79 #endif
       
    80 #if PHYSFS_SUPPORTS_WAD
       
    81     &__PHYSFS_Archiver_WAD,
       
    82 #endif
       
    83 #if PHYSFS_SUPPORTS_ISO9660
       
    84     &__PHYSFS_Archiver_ISO9660,
       
    85 #endif
       
    86     NULL
       
    87 };
       
    88 
       
    89 
       
    90 
       
    91 /* General PhysicsFS state ... */
       
    92 static int initialized = 0;
       
    93 static ErrState *errorStates = NULL;
       
    94 static DirHandle *searchPath = NULL;
       
    95 static DirHandle *writeDir = NULL;
       
    96 static FileHandle *openWriteList = NULL;
       
    97 static FileHandle *openReadList = NULL;
       
    98 static char *baseDir = NULL;
       
    99 static char *userDir = NULL;
       
   100 static char *prefDir = NULL;
       
   101 static int allowSymLinks = 0;
       
   102 static const PHYSFS_Archiver **archivers = NULL;
       
   103 static const PHYSFS_ArchiveInfo **archiveInfo = NULL;
       
   104 
       
   105 /* mutexes ... */
       
   106 static void *errorLock = NULL;     /* protects error message list.        */
       
   107 static void *stateLock = NULL;     /* protects other PhysFS static state. */
       
   108 
       
   109 /* allocator ... */
       
   110 static int externalAllocator = 0;
       
   111 PHYSFS_Allocator allocator;
       
   112 
       
   113 
       
   114 /* PHYSFS_Io implementation for i/o to physical filesystem... */
       
   115 
       
   116 /* !!! FIXME: maybe refcount the paths in a string pool? */
       
   117 typedef struct __PHYSFS_NativeIoInfo
       
   118 {
       
   119     void *handle;
       
   120     const char *path;
       
   121     int mode;   /* 'r', 'w', or 'a' */
       
   122 } NativeIoInfo;
       
   123 
       
   124 static PHYSFS_sint64 nativeIo_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
       
   125 {
       
   126     NativeIoInfo *info = (NativeIoInfo *) io->opaque;
       
   127     return __PHYSFS_platformRead(info->handle, buf, len);
       
   128 } /* nativeIo_read */
       
   129 
       
   130 static PHYSFS_sint64 nativeIo_write(PHYSFS_Io *io, const void *buffer,
       
   131                                     PHYSFS_uint64 len)
       
   132 {
       
   133     NativeIoInfo *info = (NativeIoInfo *) io->opaque;
       
   134     return __PHYSFS_platformWrite(info->handle, buffer, len);
       
   135 } /* nativeIo_write */
       
   136 
       
   137 static int nativeIo_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
       
   138 {
       
   139     NativeIoInfo *info = (NativeIoInfo *) io->opaque;
       
   140     return __PHYSFS_platformSeek(info->handle, offset);
       
   141 } /* nativeIo_seek */
       
   142 
       
   143 static PHYSFS_sint64 nativeIo_tell(PHYSFS_Io *io)
       
   144 {
       
   145     NativeIoInfo *info = (NativeIoInfo *) io->opaque;
       
   146     return __PHYSFS_platformTell(info->handle);
       
   147 } /* nativeIo_tell */
       
   148 
       
   149 static PHYSFS_sint64 nativeIo_length(PHYSFS_Io *io)
       
   150 {
       
   151     NativeIoInfo *info = (NativeIoInfo *) io->opaque;
       
   152     return __PHYSFS_platformFileLength(info->handle);
       
   153 } /* nativeIo_length */
       
   154 
       
   155 static PHYSFS_Io *nativeIo_duplicate(PHYSFS_Io *io)
       
   156 {
       
   157     NativeIoInfo *info = (NativeIoInfo *) io->opaque;
       
   158     return __PHYSFS_createNativeIo(info->path, info->mode);
       
   159 } /* nativeIo_duplicate */
       
   160 
       
   161 static int nativeIo_flush(PHYSFS_Io *io)
       
   162 {
       
   163     return __PHYSFS_platformFlush(io->opaque);
       
   164 } /* nativeIo_flush */
       
   165 
       
   166 static void nativeIo_destroy(PHYSFS_Io *io)
       
   167 {
       
   168     NativeIoInfo *info = (NativeIoInfo *) io->opaque;
       
   169     __PHYSFS_platformClose(info->handle);
       
   170     allocator.Free((void *) info->path);
       
   171     allocator.Free(info);
       
   172     allocator.Free(io);
       
   173 } /* nativeIo_destroy */
       
   174 
       
   175 static const PHYSFS_Io __PHYSFS_nativeIoInterface =
       
   176 {
       
   177     CURRENT_PHYSFS_IO_API_VERSION, NULL,
       
   178     nativeIo_read,
       
   179     nativeIo_write,
       
   180     nativeIo_seek,
       
   181     nativeIo_tell,
       
   182     nativeIo_length,
       
   183     nativeIo_duplicate,
       
   184     nativeIo_flush,
       
   185     nativeIo_destroy
       
   186 };
       
   187 
       
   188 PHYSFS_Io *__PHYSFS_createNativeIo(const char *path, const int mode)
       
   189 {
       
   190     PHYSFS_Io *io = NULL;
       
   191     NativeIoInfo *info = NULL;
       
   192     void *handle = NULL;
       
   193     char *pathdup = NULL;
       
   194 
       
   195     assert((mode == 'r') || (mode == 'w') || (mode == 'a'));
       
   196 
       
   197     io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
       
   198     GOTO_IF_MACRO(!io, PHYSFS_ERR_OUT_OF_MEMORY, createNativeIo_failed);
       
   199     info = (NativeIoInfo *) allocator.Malloc(sizeof (NativeIoInfo));
       
   200     GOTO_IF_MACRO(!info, PHYSFS_ERR_OUT_OF_MEMORY, createNativeIo_failed);
       
   201     pathdup = (char *) allocator.Malloc(strlen(path) + 1);
       
   202     GOTO_IF_MACRO(!pathdup, PHYSFS_ERR_OUT_OF_MEMORY, createNativeIo_failed);
       
   203 
       
   204     if (mode == 'r')
       
   205         handle = __PHYSFS_platformOpenRead(path);
       
   206     else if (mode == 'w')
       
   207         handle = __PHYSFS_platformOpenWrite(path);
       
   208     else if (mode == 'a')
       
   209         handle = __PHYSFS_platformOpenAppend(path);
       
   210 
       
   211     GOTO_IF_MACRO(!handle, ERRPASS, createNativeIo_failed);
       
   212 
       
   213     strcpy(pathdup, path);
       
   214     info->handle = handle;
       
   215     info->path = pathdup;
       
   216     info->mode = mode;
       
   217     memcpy(io, &__PHYSFS_nativeIoInterface, sizeof (*io));
       
   218     io->opaque = info;
       
   219     return io;
       
   220 
       
   221 createNativeIo_failed:
       
   222     if (handle != NULL) __PHYSFS_platformClose(handle);
       
   223     if (pathdup != NULL) allocator.Free(pathdup);
       
   224     if (info != NULL) allocator.Free(info);
       
   225     if (io != NULL) allocator.Free(io);
       
   226     return NULL;
       
   227 } /* __PHYSFS_createNativeIo */
       
   228 
       
   229 
       
   230 /* PHYSFS_Io implementation for i/o to a memory buffer... */
       
   231 
       
   232 typedef struct __PHYSFS_MemoryIoInfo
       
   233 {
       
   234     const PHYSFS_uint8 *buf;
       
   235     PHYSFS_uint64 len;
       
   236     PHYSFS_uint64 pos;
       
   237     PHYSFS_Io *parent;
       
   238     volatile PHYSFS_uint32 refcount;
       
   239     void (*destruct)(void *);
       
   240 } MemoryIoInfo;
       
   241 
       
   242 static PHYSFS_sint64 memoryIo_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
       
   243 {
       
   244     MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
       
   245     const PHYSFS_uint64 avail = info->len - info->pos;
       
   246     assert(avail <= info->len);
       
   247 
       
   248     if (avail == 0)
       
   249         return 0;  /* we're at EOF; nothing to do. */
       
   250 
       
   251     if (len > avail)
       
   252         len = avail;
       
   253 
       
   254     memcpy(buf, info->buf + info->pos, (size_t) len);
       
   255     info->pos += len;
       
   256     return len;
       
   257 } /* memoryIo_read */
       
   258 
       
   259 static PHYSFS_sint64 memoryIo_write(PHYSFS_Io *io, const void *buffer,
       
   260                                     PHYSFS_uint64 len)
       
   261 {
       
   262     BAIL_MACRO(PHYSFS_ERR_OPEN_FOR_READING, -1);
       
   263 } /* memoryIo_write */
       
   264 
       
   265 static int memoryIo_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
       
   266 {
       
   267     MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
       
   268     BAIL_IF_MACRO(offset > info->len, PHYSFS_ERR_PAST_EOF, 0);
       
   269     info->pos = offset;
       
   270     return 1;
       
   271 } /* memoryIo_seek */
       
   272 
       
   273 static PHYSFS_sint64 memoryIo_tell(PHYSFS_Io *io)
       
   274 {
       
   275     const MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
       
   276     return (PHYSFS_sint64) info->pos;
       
   277 } /* memoryIo_tell */
       
   278 
       
   279 static PHYSFS_sint64 memoryIo_length(PHYSFS_Io *io)
       
   280 {
       
   281     const MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
       
   282     return (PHYSFS_sint64) info->len;
       
   283 } /* memoryIo_length */
       
   284 
       
   285 static PHYSFS_Io *memoryIo_duplicate(PHYSFS_Io *io)
       
   286 {
       
   287     MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
       
   288     MemoryIoInfo *newinfo = NULL;
       
   289     PHYSFS_Io *parent = info->parent;
       
   290     PHYSFS_Io *retval = NULL;
       
   291 
       
   292     /* avoid deep copies. */
       
   293     assert((!parent) || (!((MemoryIoInfo *) parent->opaque)->parent) );
       
   294 
       
   295     /* share the buffer between duplicates. */
       
   296     if (parent != NULL)  /* dup the parent, increment its refcount. */
       
   297         return parent->duplicate(parent);
       
   298 
       
   299     /* we're the parent. */
       
   300 
       
   301     retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
       
   302     BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   303     newinfo = (MemoryIoInfo *) allocator.Malloc(sizeof (MemoryIoInfo));
       
   304     if (!newinfo)
       
   305     {
       
   306         allocator.Free(retval);
       
   307         BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   308     } /* if */
       
   309 
       
   310     /* !!! FIXME: want lockless atomic increment. */
       
   311     __PHYSFS_platformGrabMutex(stateLock);
       
   312     info->refcount++;
       
   313     __PHYSFS_platformReleaseMutex(stateLock);
       
   314 
       
   315     memset(newinfo, '\0', sizeof (*info));
       
   316     newinfo->buf = info->buf;
       
   317     newinfo->len = info->len;
       
   318     newinfo->pos = 0;
       
   319     newinfo->parent = io;
       
   320     newinfo->refcount = 0;
       
   321     newinfo->destruct = NULL;
       
   322 
       
   323     memcpy(retval, io, sizeof (*retval));
       
   324     retval->opaque = newinfo;
       
   325     return retval;
       
   326 } /* memoryIo_duplicate */
       
   327 
       
   328 static int memoryIo_flush(PHYSFS_Io *io) { return 1;  /* it's read-only. */ }
       
   329 
       
   330 static void memoryIo_destroy(PHYSFS_Io *io)
       
   331 {
       
   332     MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
       
   333     PHYSFS_Io *parent = info->parent;
       
   334     int should_die = 0;
       
   335 
       
   336     if (parent != NULL)
       
   337     {
       
   338         assert(info->buf == ((MemoryIoInfo *) info->parent->opaque)->buf);
       
   339         assert(info->len == ((MemoryIoInfo *) info->parent->opaque)->len);
       
   340         assert(info->refcount == 0);
       
   341         assert(info->destruct == NULL);
       
   342         allocator.Free(info);
       
   343         allocator.Free(io);
       
   344         parent->destroy(parent);  /* decrements refcount. */
       
   345         return;
       
   346     } /* if */
       
   347 
       
   348     /* we _are_ the parent. */
       
   349     assert(info->refcount > 0);  /* even in a race, we hold a reference. */
       
   350 
       
   351     /* !!! FIXME: want lockless atomic decrement. */
       
   352     __PHYSFS_platformGrabMutex(stateLock);
       
   353     info->refcount--;
       
   354     should_die = (info->refcount == 0);
       
   355     __PHYSFS_platformReleaseMutex(stateLock);
       
   356 
       
   357     if (should_die)
       
   358     {
       
   359         void (*destruct)(void *) = info->destruct;
       
   360         void *buf = (void *) info->buf;
       
   361         io->opaque = NULL;  /* kill this here in case of race. */
       
   362         allocator.Free(info);
       
   363         allocator.Free(io);
       
   364         if (destruct != NULL)
       
   365             destruct(buf);
       
   366     } /* if */
       
   367 } /* memoryIo_destroy */
       
   368 
       
   369 
       
   370 static const PHYSFS_Io __PHYSFS_memoryIoInterface =
       
   371 {
       
   372     CURRENT_PHYSFS_IO_API_VERSION, NULL,
       
   373     memoryIo_read,
       
   374     memoryIo_write,
       
   375     memoryIo_seek,
       
   376     memoryIo_tell,
       
   377     memoryIo_length,
       
   378     memoryIo_duplicate,
       
   379     memoryIo_flush,
       
   380     memoryIo_destroy
       
   381 };
       
   382 
       
   383 PHYSFS_Io *__PHYSFS_createMemoryIo(const void *buf, PHYSFS_uint64 len,
       
   384                                    void (*destruct)(void *))
       
   385 {
       
   386     PHYSFS_Io *io = NULL;
       
   387     MemoryIoInfo *info = NULL;
       
   388 
       
   389     io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
       
   390     GOTO_IF_MACRO(!io, PHYSFS_ERR_OUT_OF_MEMORY, createMemoryIo_failed);
       
   391     info = (MemoryIoInfo *) allocator.Malloc(sizeof (MemoryIoInfo));
       
   392     GOTO_IF_MACRO(!info, PHYSFS_ERR_OUT_OF_MEMORY, createMemoryIo_failed);
       
   393 
       
   394     memset(info, '\0', sizeof (*info));
       
   395     info->buf = (const PHYSFS_uint8 *) buf;
       
   396     info->len = len;
       
   397     info->pos = 0;
       
   398     info->parent = NULL;
       
   399     info->refcount = 1;
       
   400     info->destruct = destruct;
       
   401 
       
   402     memcpy(io, &__PHYSFS_memoryIoInterface, sizeof (*io));
       
   403     io->opaque = info;
       
   404     return io;
       
   405 
       
   406 createMemoryIo_failed:
       
   407     if (info != NULL) allocator.Free(info);
       
   408     if (io != NULL) allocator.Free(io);
       
   409     return NULL;
       
   410 } /* __PHYSFS_createMemoryIo */
       
   411 
       
   412 
       
   413 /* PHYSFS_Io implementation for i/o to a PHYSFS_File... */
       
   414 
       
   415 static PHYSFS_sint64 handleIo_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
       
   416 {
       
   417     return PHYSFS_readBytes((PHYSFS_File *) io->opaque, buf, len);
       
   418 } /* handleIo_read */
       
   419 
       
   420 static PHYSFS_sint64 handleIo_write(PHYSFS_Io *io, const void *buffer,
       
   421                                     PHYSFS_uint64 len)
       
   422 {
       
   423     return PHYSFS_writeBytes((PHYSFS_File *) io->opaque, buffer, len);
       
   424 } /* handleIo_write */
       
   425 
       
   426 static int handleIo_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
       
   427 {
       
   428     return PHYSFS_seek((PHYSFS_File *) io->opaque, offset);
       
   429 } /* handleIo_seek */
       
   430 
       
   431 static PHYSFS_sint64 handleIo_tell(PHYSFS_Io *io)
       
   432 {
       
   433     return PHYSFS_tell((PHYSFS_File *) io->opaque);
       
   434 } /* handleIo_tell */
       
   435 
       
   436 static PHYSFS_sint64 handleIo_length(PHYSFS_Io *io)
       
   437 {
       
   438     return PHYSFS_fileLength((PHYSFS_File *) io->opaque);
       
   439 } /* handleIo_length */
       
   440 
       
   441 static PHYSFS_Io *handleIo_duplicate(PHYSFS_Io *io)
       
   442 {
       
   443     /*
       
   444      * There's no duplicate at the PHYSFS_File level, so we break the
       
   445      *  abstraction. We're allowed to: we're physfs.c!
       
   446      */
       
   447     FileHandle *origfh = (FileHandle *) io->opaque;
       
   448     FileHandle *newfh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
       
   449     PHYSFS_Io *retval = NULL;
       
   450 
       
   451     GOTO_IF_MACRO(!newfh, PHYSFS_ERR_OUT_OF_MEMORY, handleIo_dupe_failed);
       
   452     memset(newfh, '\0', sizeof (*newfh));
       
   453 
       
   454     retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
       
   455     GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, handleIo_dupe_failed);
       
   456 
       
   457 #if 0  /* we don't buffer the duplicate, at least not at the moment. */
       
   458     if (origfh->buffer != NULL)
       
   459     {
       
   460         newfh->buffer = (PHYSFS_uint8 *) allocator.Malloc(origfh->bufsize);
       
   461         if (!newfh->buffer)
       
   462             GOTO_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, handleIo_dupe_failed);
       
   463         newfh->bufsize = origfh->bufsize;
       
   464     } /* if */
       
   465 #endif
       
   466 
       
   467     newfh->io = origfh->io->duplicate(origfh->io);
       
   468     GOTO_IF_MACRO(!newfh->io, ERRPASS, handleIo_dupe_failed);
       
   469 
       
   470     newfh->forReading = origfh->forReading;
       
   471     newfh->dirHandle = origfh->dirHandle;
       
   472 
       
   473     __PHYSFS_platformGrabMutex(stateLock);
       
   474     if (newfh->forReading)
       
   475     {
       
   476         newfh->next = openReadList;
       
   477         openReadList = newfh;
       
   478     } /* if */
       
   479     else
       
   480     {
       
   481         newfh->next = openWriteList;
       
   482         openWriteList = newfh;
       
   483     } /* else */
       
   484     __PHYSFS_platformReleaseMutex(stateLock);
       
   485 
       
   486     memcpy(retval, io, sizeof (PHYSFS_Io));
       
   487     retval->opaque = newfh;
       
   488     return retval;
       
   489     
       
   490 handleIo_dupe_failed:
       
   491     if (newfh)
       
   492     {
       
   493         if (newfh->io != NULL) newfh->io->destroy(newfh->io);
       
   494         if (newfh->buffer != NULL) allocator.Free(newfh->buffer);
       
   495         allocator.Free(newfh);
       
   496     } /* if */
       
   497 
       
   498     return NULL;
       
   499 } /* handleIo_duplicate */
       
   500 
       
   501 static int handleIo_flush(PHYSFS_Io *io)
       
   502 {
       
   503     return PHYSFS_flush((PHYSFS_File *) io->opaque);
       
   504 } /* handleIo_flush */
       
   505 
       
   506 static void handleIo_destroy(PHYSFS_Io *io)
       
   507 {
       
   508     if (io->opaque != NULL)
       
   509         PHYSFS_close((PHYSFS_File *) io->opaque);
       
   510     allocator.Free(io);
       
   511 } /* handleIo_destroy */
       
   512 
       
   513 static const PHYSFS_Io __PHYSFS_handleIoInterface =
       
   514 {
       
   515     CURRENT_PHYSFS_IO_API_VERSION, NULL,
       
   516     handleIo_read,
       
   517     handleIo_write,
       
   518     handleIo_seek,
       
   519     handleIo_tell,
       
   520     handleIo_length,
       
   521     handleIo_duplicate,
       
   522     handleIo_flush,
       
   523     handleIo_destroy
       
   524 };
       
   525 
       
   526 static PHYSFS_Io *__PHYSFS_createHandleIo(PHYSFS_File *f)
       
   527 {
       
   528     PHYSFS_Io *io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
       
   529     BAIL_IF_MACRO(!io, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   530     memcpy(io, &__PHYSFS_handleIoInterface, sizeof (*io));
       
   531     io->opaque = f;
       
   532     return io;
       
   533 } /* __PHYSFS_createHandleIo */
       
   534 
       
   535 
       
   536 /* functions ... */
       
   537 
       
   538 typedef struct
       
   539 {
       
   540     char **list;
       
   541     PHYSFS_uint32 size;
       
   542     PHYSFS_ErrorCode errcode;
       
   543 } EnumStringListCallbackData;
       
   544 
       
   545 static void enumStringListCallback(void *data, const char *str)
       
   546 {
       
   547     void *ptr;
       
   548     char *newstr;
       
   549     EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data;
       
   550 
       
   551     if (pecd->errcode)
       
   552         return;
       
   553 
       
   554     ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *));
       
   555     newstr = (char *) allocator.Malloc(strlen(str) + 1);
       
   556     if (ptr != NULL)
       
   557         pecd->list = (char **) ptr;
       
   558 
       
   559     if ((ptr == NULL) || (newstr == NULL))
       
   560     {
       
   561         pecd->errcode = PHYSFS_ERR_OUT_OF_MEMORY;
       
   562         pecd->list[pecd->size] = NULL;
       
   563         PHYSFS_freeList(pecd->list);
       
   564         return;
       
   565     } /* if */
       
   566 
       
   567     strcpy(newstr, str);
       
   568     pecd->list[pecd->size] = newstr;
       
   569     pecd->size++;
       
   570 } /* enumStringListCallback */
       
   571 
       
   572 
       
   573 static char **doEnumStringList(void (*func)(PHYSFS_StringCallback, void *))
       
   574 {
       
   575     EnumStringListCallbackData ecd;
       
   576     memset(&ecd, '\0', sizeof (ecd));
       
   577     ecd.list = (char **) allocator.Malloc(sizeof (char *));
       
   578     BAIL_IF_MACRO(!ecd.list, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   579     func(enumStringListCallback, &ecd);
       
   580 
       
   581     if (ecd.errcode)
       
   582     {
       
   583         __PHYSFS_setError(ecd.errcode);
       
   584         return NULL;
       
   585     } /* if */
       
   586 
       
   587     ecd.list[ecd.size] = NULL;
       
   588     return ecd.list;
       
   589 } /* doEnumStringList */
       
   590 
       
   591 
       
   592 static void __PHYSFS_bubble_sort(void *a, size_t lo, size_t hi,
       
   593                                  int (*cmpfn)(void *, size_t, size_t),
       
   594                                  void (*swapfn)(void *, size_t, size_t))
       
   595 {
       
   596     size_t i;
       
   597     int sorted;
       
   598 
       
   599     do
       
   600     {
       
   601         sorted = 1;
       
   602         for (i = lo; i < hi; i++)
       
   603         {
       
   604             if (cmpfn(a, i, i + 1) > 0)
       
   605             {
       
   606                 swapfn(a, i, i + 1);
       
   607                 sorted = 0;
       
   608             } /* if */
       
   609         } /* for */
       
   610     } while (!sorted);
       
   611 } /* __PHYSFS_bubble_sort */
       
   612 
       
   613 
       
   614 static void __PHYSFS_quick_sort(void *a, size_t lo, size_t hi,
       
   615                          int (*cmpfn)(void *, size_t, size_t),
       
   616                          void (*swapfn)(void *, size_t, size_t))
       
   617 {
       
   618     size_t i;
       
   619     size_t j;
       
   620     size_t v;
       
   621 
       
   622     if ((hi - lo) <= PHYSFS_QUICKSORT_THRESHOLD)
       
   623         __PHYSFS_bubble_sort(a, lo, hi, cmpfn, swapfn);
       
   624     else
       
   625     {
       
   626         i = (hi + lo) / 2;
       
   627 
       
   628         if (cmpfn(a, lo, i) > 0) swapfn(a, lo, i);
       
   629         if (cmpfn(a, lo, hi) > 0) swapfn(a, lo, hi);
       
   630         if (cmpfn(a, i, hi) > 0) swapfn(a, i, hi);
       
   631 
       
   632         j = hi - 1;
       
   633         swapfn(a, i, j);
       
   634         i = lo;
       
   635         v = j;
       
   636         while (1)
       
   637         {
       
   638             while(cmpfn(a, ++i, v) < 0) { /* do nothing */ }
       
   639             while(cmpfn(a, --j, v) > 0) { /* do nothing */ }
       
   640             if (j < i)
       
   641                 break;
       
   642             swapfn(a, i, j);
       
   643         } /* while */
       
   644         if (i != (hi-1))
       
   645             swapfn(a, i, hi-1);
       
   646         __PHYSFS_quick_sort(a, lo, j, cmpfn, swapfn);
       
   647         __PHYSFS_quick_sort(a, i+1, hi, cmpfn, swapfn);
       
   648     } /* else */
       
   649 } /* __PHYSFS_quick_sort */
       
   650 
       
   651 
       
   652 void __PHYSFS_sort(void *entries, size_t max,
       
   653                    int (*cmpfn)(void *, size_t, size_t),
       
   654                    void (*swapfn)(void *, size_t, size_t))
       
   655 {
       
   656     /*
       
   657      * Quicksort w/ Bubblesort fallback algorithm inspired by code from here:
       
   658      *   http://www.cs.ubc.ca/spider/harrison/Java/sorting-demo.html
       
   659      */
       
   660     if (max > 0)
       
   661         __PHYSFS_quick_sort(entries, 0, max - 1, cmpfn, swapfn);
       
   662 } /* __PHYSFS_sort */
       
   663 
       
   664 
       
   665 static ErrState *findErrorForCurrentThread(void)
       
   666 {
       
   667     ErrState *i;
       
   668     void *tid;
       
   669 
       
   670     if (errorLock != NULL)
       
   671         __PHYSFS_platformGrabMutex(errorLock);
       
   672 
       
   673     if (errorStates != NULL)
       
   674     {
       
   675         tid = __PHYSFS_platformGetThreadID();
       
   676 
       
   677         for (i = errorStates; i != NULL; i = i->next)
       
   678         {
       
   679             if (i->tid == tid)
       
   680             {
       
   681                 if (errorLock != NULL)
       
   682                     __PHYSFS_platformReleaseMutex(errorLock);
       
   683                 return i;
       
   684             } /* if */
       
   685         } /* for */
       
   686     } /* if */
       
   687 
       
   688     if (errorLock != NULL)
       
   689         __PHYSFS_platformReleaseMutex(errorLock);
       
   690 
       
   691     return NULL;   /* no error available. */
       
   692 } /* findErrorForCurrentThread */
       
   693 
       
   694 
       
   695 void __PHYSFS_setError(const PHYSFS_ErrorCode errcode)
       
   696 {
       
   697     ErrState *err;
       
   698 
       
   699     if (!errcode)
       
   700         return;
       
   701 
       
   702     err = findErrorForCurrentThread();
       
   703     if (err == NULL)
       
   704     {
       
   705         err = (ErrState *) allocator.Malloc(sizeof (ErrState));
       
   706         if (err == NULL)
       
   707             return;   /* uhh...? */
       
   708 
       
   709         memset(err, '\0', sizeof (ErrState));
       
   710         err->tid = __PHYSFS_platformGetThreadID();
       
   711 
       
   712         if (errorLock != NULL)
       
   713             __PHYSFS_platformGrabMutex(errorLock);
       
   714 
       
   715         err->next = errorStates;
       
   716         errorStates = err;
       
   717 
       
   718         if (errorLock != NULL)
       
   719             __PHYSFS_platformReleaseMutex(errorLock);
       
   720     } /* if */
       
   721 
       
   722     err->code = errcode;
       
   723 } /* __PHYSFS_setError */
       
   724 
       
   725 
       
   726 PHYSFS_ErrorCode PHYSFS_getLastErrorCode(void)
       
   727 {
       
   728     ErrState *err = findErrorForCurrentThread();
       
   729     const PHYSFS_ErrorCode retval = (err) ? err->code : PHYSFS_ERR_OK;
       
   730     if (err)
       
   731         err->code = PHYSFS_ERR_OK;
       
   732     return retval;
       
   733 } /* PHYSFS_getLastErrorCode */
       
   734 
       
   735 
       
   736 PHYSFS_DECL const char *PHYSFS_getErrorByCode(PHYSFS_ErrorCode code)
       
   737 {
       
   738     switch (code)
       
   739     {
       
   740         case PHYSFS_ERR_OK: return "no error";
       
   741         case PHYSFS_ERR_OTHER_ERROR: return "unknown error";
       
   742         case PHYSFS_ERR_OUT_OF_MEMORY: return "out of memory";
       
   743         case PHYSFS_ERR_NOT_INITIALIZED: return "not initialized";
       
   744         case PHYSFS_ERR_IS_INITIALIZED: return "already initialized";
       
   745         case PHYSFS_ERR_ARGV0_IS_NULL: return "argv[0] is NULL";
       
   746         case PHYSFS_ERR_UNSUPPORTED: return "unsupported";
       
   747         case PHYSFS_ERR_PAST_EOF: return "past end of file";
       
   748         case PHYSFS_ERR_FILES_STILL_OPEN: return "files still open";
       
   749         case PHYSFS_ERR_INVALID_ARGUMENT: return "invalid argument";
       
   750         case PHYSFS_ERR_NOT_MOUNTED: return "not mounted";
       
   751         case PHYSFS_ERR_NO_SUCH_PATH: return "no such path";
       
   752         case PHYSFS_ERR_SYMLINK_FORBIDDEN: return "symlinks are forbidden";
       
   753         case PHYSFS_ERR_NO_WRITE_DIR: return "write directory is not set";
       
   754         case PHYSFS_ERR_OPEN_FOR_READING: return "file open for reading";
       
   755         case PHYSFS_ERR_OPEN_FOR_WRITING: return "file open for writing";
       
   756         case PHYSFS_ERR_NOT_A_FILE: return "not a file";
       
   757         case PHYSFS_ERR_READ_ONLY: return "read-only filesystem";
       
   758         case PHYSFS_ERR_CORRUPT: return "corrupted";
       
   759         case PHYSFS_ERR_SYMLINK_LOOP: return "infinite symbolic link loop";
       
   760         case PHYSFS_ERR_IO: return "i/o error";
       
   761         case PHYSFS_ERR_PERMISSION: return "permission denied";
       
   762         case PHYSFS_ERR_NO_SPACE: return "no space available for writing";
       
   763         case PHYSFS_ERR_BAD_FILENAME: return "filename is illegal or insecure";
       
   764         case PHYSFS_ERR_BUSY: return "tried to modify a file the OS needs";
       
   765         case PHYSFS_ERR_DIR_NOT_EMPTY: return "directory isn't empty";
       
   766         case PHYSFS_ERR_OS_ERROR: return "OS reported an error";
       
   767     } /* switch */
       
   768 
       
   769     return NULL;  /* don't know this error code. */
       
   770 } /* PHYSFS_getErrorByCode */
       
   771 
       
   772 
       
   773 void PHYSFS_setErrorCode(PHYSFS_ErrorCode code)
       
   774 {
       
   775     __PHYSFS_setError(code);
       
   776 } /* PHYSFS_setErrorCode */
       
   777 
       
   778 
       
   779 const char *PHYSFS_getLastError(void)
       
   780 {
       
   781     const PHYSFS_ErrorCode err = PHYSFS_getLastErrorCode();
       
   782     return (err) ? PHYSFS_getErrorByCode(err) : NULL;
       
   783 } /* PHYSFS_getLastError */
       
   784 
       
   785 
       
   786 /* MAKE SURE that errorLock is held before calling this! */
       
   787 static void freeErrorStates(void)
       
   788 {
       
   789     ErrState *i;
       
   790     ErrState *next;
       
   791 
       
   792     for (i = errorStates; i != NULL; i = next)
       
   793     {
       
   794         next = i->next;
       
   795         allocator.Free(i);
       
   796     } /* for */
       
   797 
       
   798     errorStates = NULL;
       
   799 } /* freeErrorStates */
       
   800 
       
   801 
       
   802 void PHYSFS_getLinkedVersion(PHYSFS_Version *ver)
       
   803 {
       
   804     if (ver != NULL)
       
   805     {
       
   806         ver->major = PHYSFS_VER_MAJOR;
       
   807         ver->minor = PHYSFS_VER_MINOR;
       
   808         ver->patch = PHYSFS_VER_PATCH;
       
   809     } /* if */
       
   810 } /* PHYSFS_getLinkedVersion */
       
   811 
       
   812 
       
   813 static const char *find_filename_extension(const char *fname)
       
   814 {
       
   815     const char *retval = NULL;
       
   816     if (fname != NULL)
       
   817     {
       
   818         const char *p = strchr(fname, '.');
       
   819         retval = p;
       
   820 
       
   821         while (p != NULL)
       
   822         {
       
   823             p = strchr(p + 1, '.');
       
   824             if (p != NULL)
       
   825                 retval = p;
       
   826         } /* while */
       
   827 
       
   828         if (retval != NULL)
       
   829             retval++;  /* skip '.' */
       
   830     } /* if */
       
   831 
       
   832     return retval;
       
   833 } /* find_filename_extension */
       
   834 
       
   835 
       
   836 static DirHandle *tryOpenDir(PHYSFS_Io *io, const PHYSFS_Archiver *funcs,
       
   837                              const char *d, int forWriting)
       
   838 {
       
   839     DirHandle *retval = NULL;
       
   840     void *opaque = NULL;
       
   841 
       
   842     if (io != NULL)
       
   843         BAIL_IF_MACRO(!io->seek(io, 0), ERRPASS, NULL);
       
   844 
       
   845     opaque = funcs->openArchive(io, d, forWriting);
       
   846     if (opaque != NULL)
       
   847     {
       
   848         retval = (DirHandle *) allocator.Malloc(sizeof (DirHandle));
       
   849         if (retval == NULL)
       
   850             funcs->closeArchive(opaque);
       
   851         else
       
   852         {
       
   853             memset(retval, '\0', sizeof (DirHandle));
       
   854             retval->mountPoint = NULL;
       
   855             retval->funcs = funcs;
       
   856             retval->opaque = opaque;
       
   857         } /* else */
       
   858     } /* if */
       
   859 
       
   860     return retval;
       
   861 } /* tryOpenDir */
       
   862 
       
   863 
       
   864 static DirHandle *openDirectory(PHYSFS_Io *io, const char *d, int forWriting)
       
   865 {
       
   866     DirHandle *retval = NULL;
       
   867     const PHYSFS_Archiver **i;
       
   868     const char *ext;
       
   869 
       
   870     assert((io != NULL) || (d != NULL));
       
   871 
       
   872     if (io == NULL)
       
   873     {
       
   874         /* DIR gets first shot (unlike the rest, it doesn't deal with files). */
       
   875         retval = tryOpenDir(io, &__PHYSFS_Archiver_DIR, d, forWriting);
       
   876         if (retval != NULL)
       
   877             return retval;
       
   878 
       
   879         io = __PHYSFS_createNativeIo(d, forWriting ? 'w' : 'r');
       
   880         BAIL_IF_MACRO(!io, ERRPASS, 0);
       
   881     } /* if */
       
   882 
       
   883     ext = find_filename_extension(d);
       
   884     if (ext != NULL)
       
   885     {
       
   886         /* Look for archivers with matching file extensions first... */
       
   887         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
       
   888         {
       
   889             if (__PHYSFS_stricmpASCII(ext, (*i)->info.extension) == 0)
       
   890                 retval = tryOpenDir(io, *i, d, forWriting);
       
   891         } /* for */
       
   892 
       
   893         /* failing an exact file extension match, try all the others... */
       
   894         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
       
   895         {
       
   896             if (__PHYSFS_stricmpASCII(ext, (*i)->info.extension) != 0)
       
   897                 retval = tryOpenDir(io, *i, d, forWriting);
       
   898         } /* for */
       
   899     } /* if */
       
   900 
       
   901     else  /* no extension? Try them all. */
       
   902     {
       
   903         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
       
   904             retval = tryOpenDir(io, *i, d, forWriting);
       
   905     } /* else */
       
   906 
       
   907     BAIL_IF_MACRO(!retval, PHYSFS_ERR_UNSUPPORTED, NULL);
       
   908     return retval;
       
   909 } /* openDirectory */
       
   910 
       
   911 
       
   912 /*
       
   913  * Make a platform-independent path string sane. Doesn't actually check the
       
   914  *  file hierarchy, it just cleans up the string.
       
   915  *  (dst) must be a buffer at least as big as (src), as this is where the
       
   916  *  cleaned up string is deposited.
       
   917  * If there are illegal bits in the path (".." entries, etc) then we
       
   918  *  return zero and (dst) is undefined. Non-zero if the path was sanitized.
       
   919  */
       
   920 static int sanitizePlatformIndependentPath(const char *src, char *dst)
       
   921 {
       
   922     char *prev;
       
   923     char ch;
       
   924 
       
   925     while (*src == '/')  /* skip initial '/' chars... */
       
   926         src++;
       
   927 
       
   928     prev = dst;
       
   929     do
       
   930     {
       
   931         ch = *(src++);
       
   932 
       
   933         if ((ch == ':') || (ch == '\\'))  /* illegal chars in a physfs path. */
       
   934             BAIL_MACRO(PHYSFS_ERR_BAD_FILENAME, 0);
       
   935 
       
   936         if (ch == '/')   /* path separator. */
       
   937         {
       
   938             *dst = '\0';  /* "." and ".." are illegal pathnames. */
       
   939             if ((strcmp(prev, ".") == 0) || (strcmp(prev, "..") == 0))
       
   940                 BAIL_MACRO(PHYSFS_ERR_BAD_FILENAME, 0);
       
   941 
       
   942             while (*src == '/')   /* chop out doubles... */
       
   943                 src++;
       
   944 
       
   945             if (*src == '\0') /* ends with a pathsep? */
       
   946                 break;  /* we're done, don't add final pathsep to dst. */
       
   947 
       
   948             prev = dst + 1;
       
   949         } /* if */
       
   950 
       
   951         *(dst++) = ch;
       
   952     } while (ch != '\0');
       
   953 
       
   954     return 1;
       
   955 } /* sanitizePlatformIndependentPath */
       
   956 
       
   957 
       
   958 /*
       
   959  * Figure out if (fname) is part of (h)'s mountpoint. (fname) must be an
       
   960  *  output from sanitizePlatformIndependentPath(), so that it is in a known
       
   961  *  state.
       
   962  *
       
   963  * This only finds legitimate segments of a mountpoint. If the mountpoint is
       
   964  *  "/a/b/c" and (fname) is "/a/b/c", "/", or "/a/b/c/d", then the results are
       
   965  *  all zero. "/a/b" will succeed, though.
       
   966  */
       
   967 static int partOfMountPoint(DirHandle *h, char *fname)
       
   968 {
       
   969     /* !!! FIXME: This code feels gross. */
       
   970     int rc;
       
   971     size_t len, mntpntlen;
       
   972 
       
   973     if (h->mountPoint == NULL)
       
   974         return 0;
       
   975     else if (*fname == '\0')
       
   976         return 1;
       
   977 
       
   978     len = strlen(fname);
       
   979     mntpntlen = strlen(h->mountPoint);
       
   980     if (len > mntpntlen)  /* can't be a subset of mountpoint. */
       
   981         return 0;
       
   982 
       
   983     /* if true, must be not a match or a complete match, but not a subset. */
       
   984     if ((len + 1) == mntpntlen)
       
   985         return 0;
       
   986 
       
   987     rc = strncmp(fname, h->mountPoint, len); /* !!! FIXME: case insensitive? */
       
   988     if (rc != 0)
       
   989         return 0;  /* not a match. */
       
   990 
       
   991     /* make sure /a/b matches /a/b/ and not /a/bc ... */
       
   992     return h->mountPoint[len] == '/';
       
   993 } /* partOfMountPoint */
       
   994 
       
   995 
       
   996 static DirHandle *createDirHandle(PHYSFS_Io *io, const char *newDir,
       
   997                                   const char *mountPoint, int forWriting)
       
   998 {
       
   999     DirHandle *dirHandle = NULL;
       
  1000     char *tmpmntpnt = NULL;
       
  1001 
       
  1002     if (mountPoint != NULL)
       
  1003     {
       
  1004         const size_t len = strlen(mountPoint) + 1;
       
  1005         tmpmntpnt = (char *) __PHYSFS_smallAlloc(len);
       
  1006         GOTO_IF_MACRO(!tmpmntpnt, PHYSFS_ERR_OUT_OF_MEMORY, badDirHandle);
       
  1007         if (!sanitizePlatformIndependentPath(mountPoint, tmpmntpnt))
       
  1008             goto badDirHandle;
       
  1009         mountPoint = tmpmntpnt;  /* sanitized version. */
       
  1010     } /* if */
       
  1011 
       
  1012     dirHandle = openDirectory(io, newDir, forWriting);
       
  1013     GOTO_IF_MACRO(!dirHandle, ERRPASS, badDirHandle);
       
  1014 
       
  1015     if (newDir == NULL)
       
  1016         dirHandle->dirName = NULL;
       
  1017     else
       
  1018     {
       
  1019         dirHandle->dirName = (char *) allocator.Malloc(strlen(newDir) + 1);
       
  1020         if (!dirHandle->dirName)
       
  1021             GOTO_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, badDirHandle);
       
  1022         strcpy(dirHandle->dirName, newDir);
       
  1023     } /* else */
       
  1024 
       
  1025     if ((mountPoint != NULL) && (*mountPoint != '\0'))
       
  1026     {
       
  1027         dirHandle->mountPoint = (char *)allocator.Malloc(strlen(mountPoint)+2);
       
  1028         if (!dirHandle->mountPoint)
       
  1029             GOTO_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, badDirHandle);
       
  1030         strcpy(dirHandle->mountPoint, mountPoint);
       
  1031         strcat(dirHandle->mountPoint, "/");
       
  1032     } /* if */
       
  1033 
       
  1034     __PHYSFS_smallFree(tmpmntpnt);
       
  1035     return dirHandle;
       
  1036 
       
  1037 badDirHandle:
       
  1038     if (dirHandle != NULL)
       
  1039     {
       
  1040         dirHandle->funcs->closeArchive(dirHandle->opaque);
       
  1041         allocator.Free(dirHandle->dirName);
       
  1042         allocator.Free(dirHandle->mountPoint);
       
  1043         allocator.Free(dirHandle);
       
  1044     } /* if */
       
  1045 
       
  1046     __PHYSFS_smallFree(tmpmntpnt);
       
  1047     return NULL;
       
  1048 } /* createDirHandle */
       
  1049 
       
  1050 
       
  1051 /* MAKE SURE you've got the stateLock held before calling this! */
       
  1052 static int freeDirHandle(DirHandle *dh, FileHandle *openList)
       
  1053 {
       
  1054     FileHandle *i;
       
  1055 
       
  1056     if (dh == NULL)
       
  1057         return 1;
       
  1058 
       
  1059     for (i = openList; i != NULL; i = i->next)
       
  1060         BAIL_IF_MACRO(i->dirHandle == dh, PHYSFS_ERR_FILES_STILL_OPEN, 0);
       
  1061 
       
  1062     dh->funcs->closeArchive(dh->opaque);
       
  1063     allocator.Free(dh->dirName);
       
  1064     allocator.Free(dh->mountPoint);
       
  1065     allocator.Free(dh);
       
  1066     return 1;
       
  1067 } /* freeDirHandle */
       
  1068 
       
  1069 
       
  1070 static char *calculateBaseDir(const char *argv0)
       
  1071 {
       
  1072     const char dirsep = __PHYSFS_platformDirSeparator;
       
  1073     char *retval = NULL;
       
  1074     char *ptr = NULL;
       
  1075 
       
  1076     /* Give the platform layer first shot at this. */
       
  1077     retval = __PHYSFS_platformCalcBaseDir(argv0);
       
  1078     if (retval != NULL)
       
  1079         return retval;
       
  1080 
       
  1081     /* We need argv0 to go on. */
       
  1082     BAIL_IF_MACRO(argv0 == NULL, PHYSFS_ERR_ARGV0_IS_NULL, NULL);
       
  1083 
       
  1084     ptr = strrchr(argv0, dirsep);
       
  1085     if (ptr != NULL)
       
  1086     {
       
  1087         const size_t size = ((size_t) (ptr - argv0)) + 1;
       
  1088         retval = (char *) allocator.Malloc(size + 1);
       
  1089         BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
  1090         memcpy(retval, argv0, size);
       
  1091         retval[size] = '\0';
       
  1092         return retval;
       
  1093     } /* if */
       
  1094 
       
  1095     /* argv0 wasn't helpful. */
       
  1096     BAIL_MACRO(PHYSFS_ERR_INVALID_ARGUMENT, NULL);
       
  1097 } /* calculateBaseDir */
       
  1098 
       
  1099 
       
  1100 static int initializeMutexes(void)
       
  1101 {
       
  1102     errorLock = __PHYSFS_platformCreateMutex();
       
  1103     if (errorLock == NULL)
       
  1104         goto initializeMutexes_failed;
       
  1105 
       
  1106     stateLock = __PHYSFS_platformCreateMutex();
       
  1107     if (stateLock == NULL)
       
  1108         goto initializeMutexes_failed;
       
  1109 
       
  1110     return 1;  /* success. */
       
  1111 
       
  1112 initializeMutexes_failed:
       
  1113     if (errorLock != NULL)
       
  1114         __PHYSFS_platformDestroyMutex(errorLock);
       
  1115 
       
  1116     if (stateLock != NULL)
       
  1117         __PHYSFS_platformDestroyMutex(stateLock);
       
  1118 
       
  1119     errorLock = stateLock = NULL;
       
  1120     return 0;  /* failed. */
       
  1121 } /* initializeMutexes */
       
  1122 
       
  1123 
       
  1124 static void setDefaultAllocator(void);
       
  1125 
       
  1126 static int initStaticArchivers(void)
       
  1127 {
       
  1128     const size_t numStaticArchivers = __PHYSFS_ARRAYLEN(staticArchivers);
       
  1129     const size_t len = numStaticArchivers * sizeof (void *);
       
  1130     size_t i;
       
  1131 
       
  1132     assert(numStaticArchivers > 0);  /* seriously, none at all?! */
       
  1133     assert(staticArchivers[numStaticArchivers - 1] == NULL);
       
  1134 
       
  1135     archiveInfo = (const PHYSFS_ArchiveInfo **) allocator.Malloc(len);
       
  1136     BAIL_IF_MACRO(!archiveInfo, PHYSFS_ERR_OUT_OF_MEMORY, 0);
       
  1137     archivers = (const PHYSFS_Archiver **) allocator.Malloc(len);
       
  1138     BAIL_IF_MACRO(!archivers, PHYSFS_ERR_OUT_OF_MEMORY, 0);
       
  1139 
       
  1140     for (i = 0; i < numStaticArchivers - 1; i++)
       
  1141         archiveInfo[i] = &staticArchivers[i]->info;
       
  1142     archiveInfo[numStaticArchivers - 1] = NULL;
       
  1143 
       
  1144     memcpy(archivers, staticArchivers, len);
       
  1145 
       
  1146     return 1;
       
  1147 } /* initStaticArchivers */
       
  1148 
       
  1149 
       
  1150 static int doDeinit(void);
       
  1151 
       
  1152 int PHYSFS_init(const char *argv0)
       
  1153 {
       
  1154     BAIL_IF_MACRO(initialized, PHYSFS_ERR_IS_INITIALIZED, 0);
       
  1155 
       
  1156     if (!externalAllocator)
       
  1157         setDefaultAllocator();
       
  1158 
       
  1159     if ((allocator.Init != NULL) && (!allocator.Init())) return 0;
       
  1160 
       
  1161     if (!__PHYSFS_platformInit())
       
  1162     {
       
  1163         if (allocator.Deinit != NULL) allocator.Deinit();
       
  1164         return 0;
       
  1165     } /* if */
       
  1166 
       
  1167     /* everything below here can be cleaned up safely by doDeinit(). */
       
  1168 
       
  1169     if (!initializeMutexes()) goto initFailed;
       
  1170 
       
  1171     baseDir = calculateBaseDir(argv0);
       
  1172     if (!baseDir) goto initFailed;
       
  1173 
       
  1174     userDir = __PHYSFS_platformCalcUserDir();
       
  1175     if (!userDir) goto initFailed;
       
  1176 
       
  1177     /* Platform layer is required to append a dirsep. */
       
  1178     assert(baseDir[strlen(baseDir) - 1] == __PHYSFS_platformDirSeparator);
       
  1179     assert(userDir[strlen(userDir) - 1] == __PHYSFS_platformDirSeparator);
       
  1180 
       
  1181     if (!initStaticArchivers()) goto initFailed;
       
  1182 
       
  1183     initialized = 1;
       
  1184 
       
  1185     /* This makes sure that the error subsystem is initialized. */
       
  1186     __PHYSFS_setError(PHYSFS_getLastErrorCode());
       
  1187 
       
  1188     return 1;
       
  1189 
       
  1190 initFailed:
       
  1191     doDeinit();
       
  1192     return 0;
       
  1193 } /* PHYSFS_init */
       
  1194 
       
  1195 
       
  1196 /* MAKE SURE you hold stateLock before calling this! */
       
  1197 static int closeFileHandleList(FileHandle **list)
       
  1198 {
       
  1199     FileHandle *i;
       
  1200     FileHandle *next = NULL;
       
  1201 
       
  1202     for (i = *list; i != NULL; i = next)
       
  1203     {
       
  1204         PHYSFS_Io *io = i->io;
       
  1205         next = i->next;
       
  1206 
       
  1207         if (!io->flush(io))
       
  1208         {
       
  1209             *list = i;
       
  1210             return 0;
       
  1211         } /* if */
       
  1212 
       
  1213         io->destroy(io);
       
  1214         allocator.Free(i);
       
  1215     } /* for */
       
  1216 
       
  1217     *list = NULL;
       
  1218     return 1;
       
  1219 } /* closeFileHandleList */
       
  1220 
       
  1221 
       
  1222 /* MAKE SURE you hold the stateLock before calling this! */
       
  1223 static void freeSearchPath(void)
       
  1224 {
       
  1225     DirHandle *i;
       
  1226     DirHandle *next = NULL;
       
  1227 
       
  1228     closeFileHandleList(&openReadList);
       
  1229 
       
  1230     if (searchPath != NULL)
       
  1231     {
       
  1232         for (i = searchPath; i != NULL; i = next)
       
  1233         {
       
  1234             next = i->next;
       
  1235             freeDirHandle(i, openReadList);
       
  1236         } /* for */
       
  1237         searchPath = NULL;
       
  1238     } /* if */
       
  1239 } /* freeSearchPath */
       
  1240 
       
  1241 
       
  1242 static int doDeinit(void)
       
  1243 {
       
  1244     BAIL_IF_MACRO(!__PHYSFS_platformDeinit(), ERRPASS, 0);
       
  1245 
       
  1246     closeFileHandleList(&openWriteList);
       
  1247     BAIL_IF_MACRO(!PHYSFS_setWriteDir(NULL), PHYSFS_ERR_FILES_STILL_OPEN, 0);
       
  1248 
       
  1249     freeSearchPath();
       
  1250     freeErrorStates();
       
  1251 
       
  1252     if (baseDir != NULL)
       
  1253     {
       
  1254         allocator.Free(baseDir);
       
  1255         baseDir = NULL;
       
  1256     } /* if */
       
  1257 
       
  1258     if (userDir != NULL)
       
  1259     {
       
  1260         allocator.Free(userDir);
       
  1261         userDir = NULL;
       
  1262     } /* if */
       
  1263 
       
  1264     if (prefDir != NULL)
       
  1265     {
       
  1266         allocator.Free(prefDir);
       
  1267         prefDir = NULL;
       
  1268     } /* if */
       
  1269 
       
  1270     if (archiveInfo != NULL)
       
  1271     {
       
  1272         allocator.Free(archiveInfo);
       
  1273         archiveInfo = NULL;
       
  1274     } /* if */
       
  1275 
       
  1276     if (archivers != NULL)
       
  1277     {
       
  1278         allocator.Free(archivers);
       
  1279         archivers = NULL;
       
  1280     } /* if */
       
  1281 
       
  1282     allowSymLinks = 0;
       
  1283     initialized = 0;
       
  1284 
       
  1285     if (errorLock) __PHYSFS_platformDestroyMutex(errorLock);
       
  1286     if (stateLock) __PHYSFS_platformDestroyMutex(stateLock);
       
  1287 
       
  1288     if (allocator.Deinit != NULL)
       
  1289         allocator.Deinit();
       
  1290 
       
  1291     errorLock = stateLock = NULL;
       
  1292     return 1;
       
  1293 } /* doDeinit */
       
  1294 
       
  1295 
       
  1296 int PHYSFS_deinit(void)
       
  1297 {
       
  1298     BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
       
  1299     return doDeinit();
       
  1300 } /* PHYSFS_deinit */
       
  1301 
       
  1302 
       
  1303 int PHYSFS_isInit(void)
       
  1304 {
       
  1305     return initialized;
       
  1306 } /* PHYSFS_isInit */
       
  1307 
       
  1308 
       
  1309 const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void)
       
  1310 {
       
  1311     BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, NULL);
       
  1312     return archiveInfo;
       
  1313 } /* PHYSFS_supportedArchiveTypes */
       
  1314 
       
  1315 
       
  1316 void PHYSFS_freeList(void *list)
       
  1317 {
       
  1318     void **i;
       
  1319     if (list != NULL)
       
  1320     {
       
  1321         for (i = (void **) list; *i != NULL; i++)
       
  1322             allocator.Free(*i);
       
  1323 
       
  1324         allocator.Free(list);
       
  1325     } /* if */
       
  1326 } /* PHYSFS_freeList */
       
  1327 
       
  1328 
       
  1329 const char *PHYSFS_getDirSeparator(void)
       
  1330 {
       
  1331     static char retval[2] = { __PHYSFS_platformDirSeparator, '\0' };
       
  1332     return retval;
       
  1333 } /* PHYSFS_getDirSeparator */
       
  1334 
       
  1335 
       
  1336 char **PHYSFS_getCdRomDirs(void)
       
  1337 {
       
  1338     return doEnumStringList(__PHYSFS_platformDetectAvailableCDs);
       
  1339 } /* PHYSFS_getCdRomDirs */
       
  1340 
       
  1341 
       
  1342 void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback callback, void *data)
       
  1343 {
       
  1344     __PHYSFS_platformDetectAvailableCDs(callback, data);
       
  1345 } /* PHYSFS_getCdRomDirsCallback */
       
  1346 
       
  1347 
       
  1348 const char *PHYSFS_getPrefDir(const char *org, const char *app)
       
  1349 {
       
  1350     const char dirsep = __PHYSFS_platformDirSeparator;
       
  1351     PHYSFS_Stat statbuf;
       
  1352     char *ptr = NULL;
       
  1353     char *endstr = NULL;
       
  1354     int exists = 0;
       
  1355 
       
  1356     BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
       
  1357     BAIL_IF_MACRO(!org, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
       
  1358     BAIL_IF_MACRO(*org == '\0', PHYSFS_ERR_INVALID_ARGUMENT, NULL);
       
  1359     BAIL_IF_MACRO(!app, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
       
  1360     BAIL_IF_MACRO(*app == '\0', PHYSFS_ERR_INVALID_ARGUMENT, NULL);
       
  1361 
       
  1362     allocator.Free(prefDir);
       
  1363     prefDir = __PHYSFS_platformCalcPrefDir(org, app);
       
  1364     BAIL_IF_MACRO(!prefDir, ERRPASS, NULL);
       
  1365 
       
  1366     assert(strlen(prefDir) > 0);
       
  1367     endstr = prefDir + (strlen(prefDir) - 1);
       
  1368     assert(*endstr == dirsep);
       
  1369     *endstr = '\0';  /* mask out the final dirsep for now. */
       
  1370 
       
  1371     if (!__PHYSFS_platformStat(prefDir, &exists, &statbuf))
       
  1372     {
       
  1373         for (ptr = strchr(prefDir, dirsep); ptr; ptr = strchr(ptr+1, dirsep))
       
  1374         {
       
  1375             *ptr = '\0';
       
  1376             __PHYSFS_platformMkDir(prefDir);
       
  1377             *ptr = dirsep;
       
  1378         } /* for */
       
  1379 
       
  1380         if (!__PHYSFS_platformMkDir(prefDir))
       
  1381         {
       
  1382             allocator.Free(prefDir);
       
  1383             prefDir = NULL;
       
  1384         } /* if */
       
  1385     } /* if */
       
  1386 
       
  1387     *endstr = dirsep;  /* readd the final dirsep. */
       
  1388 
       
  1389     return prefDir;
       
  1390 } /* PHYSFS_getPrefDir */
       
  1391 
       
  1392 
       
  1393 const char *PHYSFS_getBaseDir(void)
       
  1394 {
       
  1395     return baseDir;   /* this is calculated in PHYSFS_init()... */
       
  1396 } /* PHYSFS_getBaseDir */
       
  1397 
       
  1398 
       
  1399 const char *__PHYSFS_getUserDir(void)  /* not deprecated internal version. */
       
  1400 {
       
  1401     return userDir;   /* this is calculated in PHYSFS_init()... */
       
  1402 } /* __PHYSFS_getUserDir */
       
  1403 
       
  1404 
       
  1405 const char *PHYSFS_getUserDir(void)
       
  1406 {
       
  1407     return __PHYSFS_getUserDir();
       
  1408 } /* PHYSFS_getUserDir */
       
  1409 
       
  1410 
       
  1411 const char *PHYSFS_getWriteDir(void)
       
  1412 {
       
  1413     const char *retval = NULL;
       
  1414 
       
  1415     __PHYSFS_platformGrabMutex(stateLock);
       
  1416     if (writeDir != NULL)
       
  1417         retval = writeDir->dirName;
       
  1418     __PHYSFS_platformReleaseMutex(stateLock);
       
  1419 
       
  1420     return retval;
       
  1421 } /* PHYSFS_getWriteDir */
       
  1422 
       
  1423 
       
  1424 int PHYSFS_setWriteDir(const char *newDir)
       
  1425 {
       
  1426     int retval = 1;
       
  1427 
       
  1428     __PHYSFS_platformGrabMutex(stateLock);
       
  1429 
       
  1430     if (writeDir != NULL)
       
  1431     {
       
  1432         BAIL_IF_MACRO_MUTEX(!freeDirHandle(writeDir, openWriteList), ERRPASS,
       
  1433                             stateLock, 0);
       
  1434         writeDir = NULL;
       
  1435     } /* if */
       
  1436 
       
  1437     if (newDir != NULL)
       
  1438     {
       
  1439         /* !!! FIXME: PHYSFS_Io shouldn't be NULL */
       
  1440         writeDir = createDirHandle(NULL, newDir, NULL, 1);
       
  1441         retval = (writeDir != NULL);
       
  1442     } /* if */
       
  1443 
       
  1444     __PHYSFS_platformReleaseMutex(stateLock);
       
  1445 
       
  1446     return retval;
       
  1447 } /* PHYSFS_setWriteDir */
       
  1448 
       
  1449 
       
  1450 static int doMount(PHYSFS_Io *io, const char *fname,
       
  1451                    const char *mountPoint, int appendToPath)
       
  1452 {
       
  1453     DirHandle *dh;
       
  1454     DirHandle *prev = NULL;
       
  1455     DirHandle *i;
       
  1456 
       
  1457     if (mountPoint == NULL)
       
  1458         mountPoint = "/";
       
  1459 
       
  1460     __PHYSFS_platformGrabMutex(stateLock);
       
  1461 
       
  1462     if (fname != NULL)
       
  1463     {
       
  1464         for (i = searchPath; i != NULL; i = i->next)
       
  1465         {
       
  1466             /* already in search path? */
       
  1467             if ((i->dirName != NULL) && (strcmp(fname, i->dirName) == 0))
       
  1468                 BAIL_MACRO_MUTEX(ERRPASS, stateLock, 1);
       
  1469             prev = i;
       
  1470         } /* for */
       
  1471     } /* if */
       
  1472 
       
  1473     dh = createDirHandle(io, fname, mountPoint, 0);
       
  1474     BAIL_IF_MACRO_MUTEX(!dh, ERRPASS, stateLock, 0);
       
  1475 
       
  1476     if (appendToPath)
       
  1477     {
       
  1478         if (prev == NULL)
       
  1479             searchPath = dh;
       
  1480         else
       
  1481             prev->next = dh;
       
  1482     } /* if */
       
  1483     else
       
  1484     {
       
  1485         dh->next = searchPath;
       
  1486         searchPath = dh;
       
  1487     } /* else */
       
  1488 
       
  1489     __PHYSFS_platformReleaseMutex(stateLock);
       
  1490     return 1;
       
  1491 } /* doMount */
       
  1492 
       
  1493 
       
  1494 int PHYSFS_mountIo(PHYSFS_Io *io, const char *fname,
       
  1495                    const char *mountPoint, int appendToPath)
       
  1496 {
       
  1497     BAIL_IF_MACRO(!io, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1498     BAIL_IF_MACRO(io->version != 0, PHYSFS_ERR_UNSUPPORTED, 0);
       
  1499     return doMount(io, fname, mountPoint, appendToPath);
       
  1500 } /* PHYSFS_mountIo */
       
  1501 
       
  1502 
       
  1503 int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len, void (*del)(void *),
       
  1504                        const char *fname, const char *mountPoint,
       
  1505                        int appendToPath)
       
  1506 {
       
  1507     int retval = 0;
       
  1508     PHYSFS_Io *io = NULL;
       
  1509 
       
  1510     BAIL_IF_MACRO(!buf, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1511 
       
  1512     io = __PHYSFS_createMemoryIo(buf, len, del);
       
  1513     BAIL_IF_MACRO(!io, ERRPASS, 0);
       
  1514     retval = doMount(io, fname, mountPoint, appendToPath);
       
  1515     if (!retval)
       
  1516     {
       
  1517         /* docs say not to call (del) in case of failure, so cheat. */
       
  1518         MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
       
  1519         info->destruct = NULL;
       
  1520         io->destroy(io);
       
  1521     } /* if */
       
  1522 
       
  1523     return retval;
       
  1524 } /* PHYSFS_mountMemory */
       
  1525 
       
  1526 
       
  1527 int PHYSFS_mountHandle(PHYSFS_File *file, const char *fname,
       
  1528                        const char *mountPoint, int appendToPath)
       
  1529 {
       
  1530     int retval = 0;
       
  1531     PHYSFS_Io *io = NULL;
       
  1532 
       
  1533     BAIL_IF_MACRO(file == NULL, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1534 
       
  1535     io = __PHYSFS_createHandleIo(file);
       
  1536     BAIL_IF_MACRO(!io, ERRPASS, 0);
       
  1537     retval = doMount(io, fname, mountPoint, appendToPath);
       
  1538     if (!retval)
       
  1539     {
       
  1540         /* docs say not to destruct in case of failure, so cheat. */
       
  1541         io->opaque = NULL;
       
  1542         io->destroy(io);
       
  1543     } /* if */
       
  1544 
       
  1545     return retval;
       
  1546 } /* PHYSFS_mountHandle */
       
  1547 
       
  1548 
       
  1549 int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath)
       
  1550 {
       
  1551     BAIL_IF_MACRO(!newDir, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1552     return doMount(NULL, newDir, mountPoint, appendToPath);
       
  1553 } /* PHYSFS_mount */
       
  1554 
       
  1555 
       
  1556 int PHYSFS_addToSearchPath(const char *newDir, int appendToPath)
       
  1557 {
       
  1558     return doMount(NULL, newDir, NULL, appendToPath);
       
  1559 } /* PHYSFS_addToSearchPath */
       
  1560 
       
  1561 
       
  1562 int PHYSFS_removeFromSearchPath(const char *oldDir)
       
  1563 {
       
  1564     return PHYSFS_unmount(oldDir);
       
  1565 } /* PHYSFS_removeFromSearchPath */
       
  1566 
       
  1567 
       
  1568 int PHYSFS_unmount(const char *oldDir)
       
  1569 {
       
  1570     DirHandle *i;
       
  1571     DirHandle *prev = NULL;
       
  1572     DirHandle *next = NULL;
       
  1573 
       
  1574     BAIL_IF_MACRO(oldDir == NULL, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1575 
       
  1576     __PHYSFS_platformGrabMutex(stateLock);
       
  1577     for (i = searchPath; i != NULL; i = i->next)
       
  1578     {
       
  1579         if (strcmp(i->dirName, oldDir) == 0)
       
  1580         {
       
  1581             next = i->next;
       
  1582             BAIL_IF_MACRO_MUTEX(!freeDirHandle(i, openReadList), ERRPASS,
       
  1583                                 stateLock, 0);
       
  1584 
       
  1585             if (prev == NULL)
       
  1586                 searchPath = next;
       
  1587             else
       
  1588                 prev->next = next;
       
  1589 
       
  1590             BAIL_MACRO_MUTEX(ERRPASS, stateLock, 1);
       
  1591         } /* if */
       
  1592         prev = i;
       
  1593     } /* for */
       
  1594 
       
  1595     BAIL_MACRO_MUTEX(PHYSFS_ERR_NOT_MOUNTED, stateLock, 0);
       
  1596 } /* PHYSFS_unmount */
       
  1597 
       
  1598 
       
  1599 char **PHYSFS_getSearchPath(void)
       
  1600 {
       
  1601     return doEnumStringList(PHYSFS_getSearchPathCallback);
       
  1602 } /* PHYSFS_getSearchPath */
       
  1603 
       
  1604 
       
  1605 const char *PHYSFS_getMountPoint(const char *dir)
       
  1606 {
       
  1607     DirHandle *i;
       
  1608     __PHYSFS_platformGrabMutex(stateLock);
       
  1609     for (i = searchPath; i != NULL; i = i->next)
       
  1610     {
       
  1611         if (strcmp(i->dirName, dir) == 0)
       
  1612         {
       
  1613             const char *retval = ((i->mountPoint) ? i->mountPoint : "/");
       
  1614             __PHYSFS_platformReleaseMutex(stateLock);
       
  1615             return retval;
       
  1616         } /* if */
       
  1617     } /* for */
       
  1618     __PHYSFS_platformReleaseMutex(stateLock);
       
  1619 
       
  1620     BAIL_MACRO(PHYSFS_ERR_NOT_MOUNTED, NULL);
       
  1621 } /* PHYSFS_getMountPoint */
       
  1622 
       
  1623 
       
  1624 void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback callback, void *data)
       
  1625 {
       
  1626     DirHandle *i;
       
  1627 
       
  1628     __PHYSFS_platformGrabMutex(stateLock);
       
  1629 
       
  1630     for (i = searchPath; i != NULL; i = i->next)
       
  1631         callback(data, i->dirName);
       
  1632 
       
  1633     __PHYSFS_platformReleaseMutex(stateLock);
       
  1634 } /* PHYSFS_getSearchPathCallback */
       
  1635 
       
  1636 
       
  1637 /* Split out to avoid stack allocation in a loop. */
       
  1638 static void setSaneCfgAddPath(const char *i, const size_t l, const char *dirsep,
       
  1639                               int archivesFirst)
       
  1640 {
       
  1641     const char *d = PHYSFS_getRealDir(i);
       
  1642     const size_t allocsize = strlen(d) + strlen(dirsep) + l + 1;
       
  1643     char *str = (char *) __PHYSFS_smallAlloc(allocsize);
       
  1644     if (str != NULL)
       
  1645     {
       
  1646         sprintf(str, "%s%s%s", d, dirsep, i);
       
  1647         PHYSFS_mount(str, NULL, archivesFirst == 0);
       
  1648         __PHYSFS_smallFree(str);
       
  1649     } /* if */
       
  1650 } /* setSaneCfgAddPath */
       
  1651 
       
  1652 
       
  1653 int PHYSFS_setSaneConfig(const char *organization, const char *appName,
       
  1654                          const char *archiveExt, int includeCdRoms,
       
  1655                          int archivesFirst)
       
  1656 {
       
  1657     const char *dirsep = PHYSFS_getDirSeparator();
       
  1658     const char *basedir;
       
  1659     const char *prefdir;
       
  1660 
       
  1661     BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
       
  1662 
       
  1663     prefdir = PHYSFS_getPrefDir(organization, appName);
       
  1664     BAIL_IF_MACRO(!prefdir, ERRPASS, 0);
       
  1665 
       
  1666     basedir = PHYSFS_getBaseDir();
       
  1667     BAIL_IF_MACRO(!basedir, ERRPASS, 0);
       
  1668 
       
  1669     BAIL_IF_MACRO(!PHYSFS_setWriteDir(prefdir), PHYSFS_ERR_NO_WRITE_DIR, 0);
       
  1670 
       
  1671     /* Put write dir first in search path... */
       
  1672     PHYSFS_mount(prefdir, NULL, 0);
       
  1673 
       
  1674     /* Put base path on search path... */
       
  1675     PHYSFS_mount(basedir, NULL, 1);
       
  1676 
       
  1677     /* handle CD-ROMs... */
       
  1678     if (includeCdRoms)
       
  1679     {
       
  1680         char **cds = PHYSFS_getCdRomDirs();
       
  1681         char **i;
       
  1682         for (i = cds; *i != NULL; i++)
       
  1683             PHYSFS_mount(*i, NULL, 1);
       
  1684         PHYSFS_freeList(cds);
       
  1685     } /* if */
       
  1686 
       
  1687     /* Root out archives, and add them to search path... */
       
  1688     if (archiveExt != NULL)
       
  1689     {
       
  1690         char **rc = PHYSFS_enumerateFiles("/");
       
  1691         char **i;
       
  1692         size_t extlen = strlen(archiveExt);
       
  1693         char *ext;
       
  1694 
       
  1695         for (i = rc; *i != NULL; i++)
       
  1696         {
       
  1697             size_t l = strlen(*i);
       
  1698             if ((l > extlen) && ((*i)[l - extlen - 1] == '.'))
       
  1699             {
       
  1700                 ext = (*i) + (l - extlen);
       
  1701                 if (__PHYSFS_stricmpASCII(ext, archiveExt) == 0)
       
  1702                     setSaneCfgAddPath(*i, l, dirsep, archivesFirst);
       
  1703             } /* if */
       
  1704         } /* for */
       
  1705 
       
  1706         PHYSFS_freeList(rc);
       
  1707     } /* if */
       
  1708 
       
  1709     return 1;
       
  1710 } /* PHYSFS_setSaneConfig */
       
  1711 
       
  1712 
       
  1713 void PHYSFS_permitSymbolicLinks(int allow)
       
  1714 {
       
  1715     allowSymLinks = allow;
       
  1716 } /* PHYSFS_permitSymbolicLinks */
       
  1717 
       
  1718 
       
  1719 int PHYSFS_symbolicLinksPermitted(void)
       
  1720 {
       
  1721     return allowSymLinks;
       
  1722 } /* PHYSFS_symbolicLinksPermitted */
       
  1723 
       
  1724 
       
  1725 /*
       
  1726  * Verify that (fname) (in platform-independent notation), in relation
       
  1727  *  to (h) is secure. That means that each element of fname is checked
       
  1728  *  for symlinks (if they aren't permitted). This also allows for quick
       
  1729  *  rejection of files that exist outside an archive's mountpoint.
       
  1730  *
       
  1731  * With some exceptions (like PHYSFS_mkdir(), which builds multiple subdirs
       
  1732  *  at a time), you should always pass zero for "allowMissing" for efficiency.
       
  1733  *
       
  1734  * (fname) must point to an output from sanitizePlatformIndependentPath(),
       
  1735  *  since it will make sure that path names are in the right format for
       
  1736  *  passing certain checks. It will also do checks for "insecure" pathnames
       
  1737  *  like ".." which should be done once instead of once per archive. This also
       
  1738  *  gives us license to treat (fname) as scratch space in this function.
       
  1739  *
       
  1740  * Returns non-zero if string is safe, zero if there's a security issue.
       
  1741  *  PHYSFS_getLastError() will specify what was wrong. (*fname) will be
       
  1742  *  updated to point past any mount point elements so it is prepared to
       
  1743  *  be used with the archiver directly.
       
  1744  */
       
  1745 static int verifyPath(DirHandle *h, char **_fname, int allowMissing)
       
  1746 {
       
  1747     char *fname = *_fname;
       
  1748     int retval = 1;
       
  1749     char *start;
       
  1750     char *end;
       
  1751 
       
  1752     if (*fname == '\0')  /* quick rejection. */
       
  1753         return 1;
       
  1754 
       
  1755     /* !!! FIXME: This codeblock sucks. */
       
  1756     if (h->mountPoint != NULL)  /* NULL mountpoint means "/". */
       
  1757     {
       
  1758         size_t mntpntlen = strlen(h->mountPoint);
       
  1759         size_t len = strlen(fname);
       
  1760         assert(mntpntlen > 1); /* root mount points should be NULL. */
       
  1761         /* not under the mountpoint, so skip this archive. */
       
  1762         BAIL_IF_MACRO(len < mntpntlen-1, PHYSFS_ERR_NO_SUCH_PATH, 0);
       
  1763         /* !!! FIXME: Case insensitive? */
       
  1764         retval = strncmp(h->mountPoint, fname, mntpntlen-1);
       
  1765         BAIL_IF_MACRO(retval != 0, PHYSFS_ERR_NO_SUCH_PATH, 0);
       
  1766         if (len > mntpntlen-1)  /* corner case... */
       
  1767             BAIL_IF_MACRO(fname[mntpntlen-1]!='/', PHYSFS_ERR_NO_SUCH_PATH, 0);
       
  1768         fname += mntpntlen-1;  /* move to start of actual archive path. */
       
  1769         if (*fname == '/')
       
  1770             fname++;
       
  1771         *_fname = fname;  /* skip mountpoint for later use. */
       
  1772         retval = 1;  /* may be reset, below. */
       
  1773     } /* if */
       
  1774 
       
  1775     start = fname;
       
  1776     if (!allowSymLinks)
       
  1777     {
       
  1778         while (1)
       
  1779         {
       
  1780             PHYSFS_Stat statbuf;
       
  1781             int rc = 0;
       
  1782             end = strchr(start, '/');
       
  1783 
       
  1784             if (end != NULL) *end = '\0';
       
  1785             rc = h->funcs->stat(h->opaque, fname, &retval, &statbuf);
       
  1786             if (rc)
       
  1787                 rc = (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK);
       
  1788             if (end != NULL) *end = '/';
       
  1789 
       
  1790             /* insecure path (has a disallowed symlink in it)? */
       
  1791             BAIL_IF_MACRO(rc, PHYSFS_ERR_SYMLINK_FORBIDDEN, 0);
       
  1792 
       
  1793             /* break out early if path element is missing. */
       
  1794             if (!retval)
       
  1795             {
       
  1796                 /*
       
  1797                  * We need to clear it if it's the last element of the path,
       
  1798                  *  since this might be a non-existant file we're opening
       
  1799                  *  for writing...
       
  1800                  */
       
  1801                 if ((end == NULL) || (allowMissing))
       
  1802                     retval = 1;
       
  1803                 break;
       
  1804             } /* if */
       
  1805 
       
  1806             if (end == NULL)
       
  1807                 break;
       
  1808 
       
  1809             start = end + 1;
       
  1810         } /* while */
       
  1811     } /* if */
       
  1812 
       
  1813     return retval;
       
  1814 } /* verifyPath */
       
  1815 
       
  1816 
       
  1817 static int doMkdir(const char *_dname, char *dname)
       
  1818 {
       
  1819     DirHandle *h;
       
  1820     char *start;
       
  1821     char *end;
       
  1822     int retval = 0;
       
  1823     int exists = 1;  /* force existance check on first path element. */
       
  1824 
       
  1825     BAIL_IF_MACRO(!sanitizePlatformIndependentPath(_dname, dname), ERRPASS, 0);
       
  1826 
       
  1827     __PHYSFS_platformGrabMutex(stateLock);
       
  1828     BAIL_IF_MACRO_MUTEX(!writeDir, PHYSFS_ERR_NO_WRITE_DIR, stateLock, 0);
       
  1829     h = writeDir;
       
  1830     BAIL_IF_MACRO_MUTEX(!verifyPath(h, &dname, 1), ERRPASS, stateLock, 0);
       
  1831 
       
  1832     start = dname;
       
  1833     while (1)
       
  1834     {
       
  1835         end = strchr(start, '/');
       
  1836         if (end != NULL)
       
  1837             *end = '\0';
       
  1838 
       
  1839         /* only check for existance if all parent dirs existed, too... */
       
  1840         if (exists)
       
  1841         {
       
  1842             PHYSFS_Stat statbuf;
       
  1843             const int rc = h->funcs->stat(h->opaque, dname, &exists, &statbuf);
       
  1844             retval = ((rc) && (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY));
       
  1845         } /* if */
       
  1846 
       
  1847         if (!exists)
       
  1848             retval = h->funcs->mkdir(h->opaque, dname);
       
  1849 
       
  1850         if (!retval)
       
  1851             break;
       
  1852 
       
  1853         if (end == NULL)
       
  1854             break;
       
  1855 
       
  1856         *end = '/';
       
  1857         start = end + 1;
       
  1858     } /* while */
       
  1859 
       
  1860     __PHYSFS_platformReleaseMutex(stateLock);
       
  1861     return retval;
       
  1862 } /* doMkdir */
       
  1863 
       
  1864 
       
  1865 int PHYSFS_mkdir(const char *_dname)
       
  1866 {
       
  1867     int retval = 0;
       
  1868     char *dname;
       
  1869     size_t len;
       
  1870 
       
  1871     BAIL_IF_MACRO(!_dname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1872     len = strlen(_dname) + 1;
       
  1873     dname = (char *) __PHYSFS_smallAlloc(len);
       
  1874     BAIL_IF_MACRO(!dname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
       
  1875     retval = doMkdir(_dname, dname);
       
  1876     __PHYSFS_smallFree(dname);
       
  1877     return retval;
       
  1878 } /* PHYSFS_mkdir */
       
  1879 
       
  1880 
       
  1881 static int doDelete(const char *_fname, char *fname)
       
  1882 {
       
  1883     int retval;
       
  1884     DirHandle *h;
       
  1885     BAIL_IF_MACRO(!sanitizePlatformIndependentPath(_fname, fname), ERRPASS, 0);
       
  1886 
       
  1887     __PHYSFS_platformGrabMutex(stateLock);
       
  1888 
       
  1889     BAIL_IF_MACRO_MUTEX(!writeDir, PHYSFS_ERR_NO_WRITE_DIR, stateLock, 0);
       
  1890     h = writeDir;
       
  1891     BAIL_IF_MACRO_MUTEX(!verifyPath(h, &fname, 0), ERRPASS, stateLock, 0);
       
  1892     retval = h->funcs->remove(h->opaque, fname);
       
  1893 
       
  1894     __PHYSFS_platformReleaseMutex(stateLock);
       
  1895     return retval;
       
  1896 } /* doDelete */
       
  1897 
       
  1898 
       
  1899 int PHYSFS_delete(const char *_fname)
       
  1900 {
       
  1901     int retval;
       
  1902     char *fname;
       
  1903     size_t len;
       
  1904 
       
  1905     BAIL_IF_MACRO(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  1906     len = strlen(_fname) + 1;
       
  1907     fname = (char *) __PHYSFS_smallAlloc(len);
       
  1908     BAIL_IF_MACRO(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
       
  1909     retval = doDelete(_fname, fname);
       
  1910     __PHYSFS_smallFree(fname);
       
  1911     return retval;
       
  1912 } /* PHYSFS_delete */
       
  1913 
       
  1914 
       
  1915 const char *PHYSFS_getRealDir(const char *_fname)
       
  1916 {
       
  1917     const char *retval = NULL;
       
  1918     char *fname = NULL;
       
  1919     size_t len;
       
  1920 
       
  1921     BAIL_IF_MACRO(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
       
  1922     len = strlen(_fname) + 1;
       
  1923     fname = __PHYSFS_smallAlloc(len);
       
  1924     BAIL_IF_MACRO(!fname, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
  1925     if (sanitizePlatformIndependentPath(_fname, fname))
       
  1926     {
       
  1927         DirHandle *i;
       
  1928         __PHYSFS_platformGrabMutex(stateLock);
       
  1929         for (i = searchPath; i != NULL; i = i->next)
       
  1930         {
       
  1931             char *arcfname = fname;
       
  1932             if (partOfMountPoint(i, arcfname))
       
  1933             {
       
  1934                 retval = i->dirName;
       
  1935                 break;
       
  1936             } /* if */
       
  1937             else if (verifyPath(i, &arcfname, 0))
       
  1938             {
       
  1939                 PHYSFS_Stat statbuf;
       
  1940                 int exists = 0;
       
  1941                 if (i->funcs->stat(i->opaque, arcfname, &exists, &statbuf))
       
  1942                 {
       
  1943                     if (exists)
       
  1944                         retval = i->dirName;
       
  1945                     break;
       
  1946                 } /* if */
       
  1947             } /* if */
       
  1948         } /* for */
       
  1949         __PHYSFS_platformReleaseMutex(stateLock);
       
  1950     } /* if */
       
  1951 
       
  1952     __PHYSFS_smallFree(fname);
       
  1953     return retval;
       
  1954 } /* PHYSFS_getRealDir */
       
  1955 
       
  1956 
       
  1957 static int locateInStringList(const char *str,
       
  1958                               char **list,
       
  1959                               PHYSFS_uint32 *pos)
       
  1960 {
       
  1961     PHYSFS_uint32 len = *pos;
       
  1962     PHYSFS_uint32 half_len;
       
  1963     PHYSFS_uint32 lo = 0;
       
  1964     PHYSFS_uint32 middle;
       
  1965     int cmp;
       
  1966 
       
  1967     while (len > 0)
       
  1968     {
       
  1969         half_len = len >> 1;
       
  1970         middle = lo + half_len;
       
  1971         cmp = strcmp(list[middle], str);
       
  1972 
       
  1973         if (cmp == 0)  /* it's in the list already. */
       
  1974             return 1;
       
  1975         else if (cmp > 0)
       
  1976             len = half_len;
       
  1977         else
       
  1978         {
       
  1979             lo = middle + 1;
       
  1980             len -= half_len + 1;
       
  1981         } /* else */
       
  1982     } /* while */
       
  1983 
       
  1984     *pos = lo;
       
  1985     return 0;
       
  1986 } /* locateInStringList */
       
  1987 
       
  1988 
       
  1989 static void enumFilesCallback(void *data, const char *origdir, const char *str)
       
  1990 {
       
  1991     PHYSFS_uint32 pos;
       
  1992     void *ptr;
       
  1993     char *newstr;
       
  1994     EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data;
       
  1995 
       
  1996     /*
       
  1997      * See if file is in the list already, and if not, insert it in there
       
  1998      *  alphabetically...
       
  1999      */
       
  2000     pos = pecd->size;
       
  2001     if (locateInStringList(str, pecd->list, &pos))
       
  2002         return;  /* already in the list. */
       
  2003 
       
  2004     ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *));
       
  2005     newstr = (char *) allocator.Malloc(strlen(str) + 1);
       
  2006     if (ptr != NULL)
       
  2007         pecd->list = (char **) ptr;
       
  2008 
       
  2009     if ((ptr == NULL) || (newstr == NULL))
       
  2010         return;  /* better luck next time. */
       
  2011 
       
  2012     strcpy(newstr, str);
       
  2013 
       
  2014     if (pos != pecd->size)
       
  2015     {
       
  2016         memmove(&pecd->list[pos+1], &pecd->list[pos],
       
  2017                  sizeof (char *) * ((pecd->size) - pos));
       
  2018     } /* if */
       
  2019 
       
  2020     pecd->list[pos] = newstr;
       
  2021     pecd->size++;
       
  2022 } /* enumFilesCallback */
       
  2023 
       
  2024 
       
  2025 char **PHYSFS_enumerateFiles(const char *path)
       
  2026 {
       
  2027     EnumStringListCallbackData ecd;
       
  2028     memset(&ecd, '\0', sizeof (ecd));
       
  2029     ecd.list = (char **) allocator.Malloc(sizeof (char *));
       
  2030     BAIL_IF_MACRO(!ecd.list, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
  2031     PHYSFS_enumerateFilesCallback(path, enumFilesCallback, &ecd);
       
  2032     ecd.list[ecd.size] = NULL;
       
  2033     return ecd.list;
       
  2034 } /* PHYSFS_enumerateFiles */
       
  2035 
       
  2036 
       
  2037 /*
       
  2038  * Broke out to seperate function so we can use stack allocation gratuitously.
       
  2039  */
       
  2040 static void enumerateFromMountPoint(DirHandle *i, const char *arcfname,
       
  2041                                     PHYSFS_EnumFilesCallback callback,
       
  2042                                     const char *_fname, void *data)
       
  2043 {
       
  2044     const size_t len = strlen(arcfname);
       
  2045     char *ptr = NULL;
       
  2046     char *end = NULL;
       
  2047     const size_t slen = strlen(i->mountPoint) + 1;
       
  2048     char *mountPoint = (char *) __PHYSFS_smallAlloc(slen);
       
  2049 
       
  2050     if (mountPoint == NULL)
       
  2051         return;  /* oh well. */
       
  2052 
       
  2053     strcpy(mountPoint, i->mountPoint);
       
  2054     ptr = mountPoint + ((len) ? len + 1 : 0);
       
  2055     end = strchr(ptr, '/');
       
  2056     assert(end);  /* should always find a terminating '/'. */
       
  2057     *end = '\0';
       
  2058     callback(data, _fname, ptr);
       
  2059     __PHYSFS_smallFree(mountPoint);
       
  2060 } /* enumerateFromMountPoint */
       
  2061 
       
  2062 
       
  2063 /* !!! FIXME: this should report error conditions. */
       
  2064 void PHYSFS_enumerateFilesCallback(const char *_fname,
       
  2065                                    PHYSFS_EnumFilesCallback callback,
       
  2066                                    void *data)
       
  2067 {
       
  2068     size_t len;
       
  2069     char *fname;
       
  2070 
       
  2071     BAIL_IF_MACRO(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, ) /*0*/;
       
  2072     BAIL_IF_MACRO(!callback, PHYSFS_ERR_INVALID_ARGUMENT, ) /*0*/;
       
  2073 
       
  2074     len = strlen(_fname) + 1;
       
  2075     fname = (char *) __PHYSFS_smallAlloc(len);
       
  2076     BAIL_IF_MACRO(!fname, PHYSFS_ERR_OUT_OF_MEMORY, ) /*0*/;
       
  2077 
       
  2078     if (sanitizePlatformIndependentPath(_fname, fname))
       
  2079     {
       
  2080         DirHandle *i;
       
  2081         int noSyms;
       
  2082 
       
  2083         __PHYSFS_platformGrabMutex(stateLock);
       
  2084         noSyms = !allowSymLinks;
       
  2085         for (i = searchPath; i != NULL; i = i->next)
       
  2086         {
       
  2087             char *arcfname = fname;
       
  2088             if (partOfMountPoint(i, arcfname))
       
  2089                 enumerateFromMountPoint(i, arcfname, callback, _fname, data);
       
  2090 
       
  2091             else if (verifyPath(i, &arcfname, 0))
       
  2092             {
       
  2093                 i->funcs->enumerateFiles(i->opaque, arcfname, noSyms,
       
  2094                                          callback, _fname, data);
       
  2095             } /* else if */
       
  2096         } /* for */
       
  2097         __PHYSFS_platformReleaseMutex(stateLock);
       
  2098     } /* if */
       
  2099 
       
  2100     __PHYSFS_smallFree(fname);
       
  2101 } /* PHYSFS_enumerateFilesCallback */
       
  2102 
       
  2103 
       
  2104 int PHYSFS_exists(const char *fname)
       
  2105 {
       
  2106     return (PHYSFS_getRealDir(fname) != NULL);
       
  2107 } /* PHYSFS_exists */
       
  2108 
       
  2109 
       
  2110 PHYSFS_sint64 PHYSFS_getLastModTime(const char *fname)
       
  2111 {
       
  2112     PHYSFS_Stat statbuf;
       
  2113     BAIL_IF_MACRO(!PHYSFS_stat(fname, &statbuf), ERRPASS, -1);
       
  2114     return statbuf.modtime;
       
  2115 } /* PHYSFS_getLastModTime */
       
  2116 
       
  2117 
       
  2118 int PHYSFS_isDirectory(const char *fname)
       
  2119 {
       
  2120     PHYSFS_Stat statbuf;
       
  2121     BAIL_IF_MACRO(!PHYSFS_stat(fname, &statbuf), ERRPASS, 0);
       
  2122     return (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY);
       
  2123 } /* PHYSFS_isDirectory */
       
  2124 
       
  2125 
       
  2126 int PHYSFS_isSymbolicLink(const char *fname)
       
  2127 {
       
  2128     PHYSFS_Stat statbuf;
       
  2129     BAIL_IF_MACRO(!PHYSFS_stat(fname, &statbuf), ERRPASS, 0);
       
  2130     return (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK);
       
  2131 } /* PHYSFS_isSymbolicLink */
       
  2132 
       
  2133 
       
  2134 static PHYSFS_File *doOpenWrite(const char *_fname, int appending)
       
  2135 {
       
  2136     FileHandle *fh = NULL;
       
  2137     size_t len;
       
  2138     char *fname;
       
  2139 
       
  2140     BAIL_IF_MACRO(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  2141     len = strlen(_fname) + 1;
       
  2142     fname = (char *) __PHYSFS_smallAlloc(len);
       
  2143     BAIL_IF_MACRO(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
       
  2144 
       
  2145     if (sanitizePlatformIndependentPath(_fname, fname))
       
  2146     {
       
  2147         PHYSFS_Io *io = NULL;
       
  2148         DirHandle *h = NULL;
       
  2149         const PHYSFS_Archiver *f;
       
  2150 
       
  2151         __PHYSFS_platformGrabMutex(stateLock);
       
  2152 
       
  2153         GOTO_IF_MACRO(!writeDir, PHYSFS_ERR_NO_WRITE_DIR, doOpenWriteEnd);
       
  2154 
       
  2155         h = writeDir;
       
  2156         GOTO_IF_MACRO(!verifyPath(h, &fname, 0), ERRPASS, doOpenWriteEnd);
       
  2157 
       
  2158         f = h->funcs;
       
  2159         if (appending)
       
  2160             io = f->openAppend(h->opaque, fname);
       
  2161         else
       
  2162             io = f->openWrite(h->opaque, fname);
       
  2163 
       
  2164         GOTO_IF_MACRO(!io, ERRPASS, doOpenWriteEnd);
       
  2165 
       
  2166         fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
       
  2167         if (fh == NULL)
       
  2168         {
       
  2169             io->destroy(io);
       
  2170             GOTO_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, doOpenWriteEnd);
       
  2171         } /* if */
       
  2172         else
       
  2173         {
       
  2174             memset(fh, '\0', sizeof (FileHandle));
       
  2175             fh->io = io;
       
  2176             fh->dirHandle = h;
       
  2177             fh->next = openWriteList;
       
  2178             openWriteList = fh;
       
  2179         } /* else */
       
  2180 
       
  2181         doOpenWriteEnd:
       
  2182         __PHYSFS_platformReleaseMutex(stateLock);
       
  2183     } /* if */
       
  2184 
       
  2185     __PHYSFS_smallFree(fname);
       
  2186     return ((PHYSFS_File *) fh);
       
  2187 } /* doOpenWrite */
       
  2188 
       
  2189 
       
  2190 PHYSFS_File *PHYSFS_openWrite(const char *filename)
       
  2191 {
       
  2192     return doOpenWrite(filename, 0);
       
  2193 } /* PHYSFS_openWrite */
       
  2194 
       
  2195 
       
  2196 PHYSFS_File *PHYSFS_openAppend(const char *filename)
       
  2197 {
       
  2198     return doOpenWrite(filename, 1);
       
  2199 } /* PHYSFS_openAppend */
       
  2200 
       
  2201 
       
  2202 PHYSFS_File *PHYSFS_openRead(const char *_fname)
       
  2203 {
       
  2204     FileHandle *fh = NULL;
       
  2205     char *fname;
       
  2206     size_t len;
       
  2207 
       
  2208     BAIL_IF_MACRO(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  2209     len = strlen(_fname) + 1;
       
  2210     fname = (char *) __PHYSFS_smallAlloc(len);
       
  2211     BAIL_IF_MACRO(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
       
  2212 
       
  2213     if (sanitizePlatformIndependentPath(_fname, fname))
       
  2214     {
       
  2215         int fileExists = 0;
       
  2216         DirHandle *i = NULL;
       
  2217         PHYSFS_Io *io = NULL;
       
  2218 
       
  2219         __PHYSFS_platformGrabMutex(stateLock);
       
  2220 
       
  2221         GOTO_IF_MACRO(!searchPath, PHYSFS_ERR_NO_SUCH_PATH, openReadEnd);
       
  2222 
       
  2223         for (i = searchPath; (i != NULL) && (!fileExists); i = i->next)
       
  2224         {
       
  2225             char *arcfname = fname;
       
  2226             if (verifyPath(i, &arcfname, 0))
       
  2227             {
       
  2228                 io = i->funcs->openRead(i->opaque, arcfname, &fileExists);
       
  2229                 if (io)
       
  2230                     break;
       
  2231             } /* if */
       
  2232         } /* for */
       
  2233 
       
  2234         GOTO_IF_MACRO(!io, ERRPASS, openReadEnd);
       
  2235 
       
  2236         fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
       
  2237         if (fh == NULL)
       
  2238         {
       
  2239             io->destroy(io);
       
  2240             GOTO_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, openReadEnd);
       
  2241         } /* if */
       
  2242 
       
  2243         memset(fh, '\0', sizeof (FileHandle));
       
  2244         fh->io = io;
       
  2245         fh->forReading = 1;
       
  2246         fh->dirHandle = i;
       
  2247         fh->next = openReadList;
       
  2248         openReadList = fh;
       
  2249 
       
  2250         openReadEnd:
       
  2251         __PHYSFS_platformReleaseMutex(stateLock);
       
  2252     } /* if */
       
  2253 
       
  2254     __PHYSFS_smallFree(fname);
       
  2255     return ((PHYSFS_File *) fh);
       
  2256 } /* PHYSFS_openRead */
       
  2257 
       
  2258 
       
  2259 static int closeHandleInOpenList(FileHandle **list, FileHandle *handle)
       
  2260 {
       
  2261     FileHandle *prev = NULL;
       
  2262     FileHandle *i;
       
  2263     int rc = 1;
       
  2264 
       
  2265     for (i = *list; i != NULL; i = i->next)
       
  2266     {
       
  2267         if (i == handle)  /* handle is in this list? */
       
  2268         {
       
  2269             PHYSFS_Io *io = handle->io;
       
  2270             PHYSFS_uint8 *tmp = handle->buffer;
       
  2271             rc = PHYSFS_flush((PHYSFS_File *) handle);
       
  2272             if (!rc)
       
  2273                 return -1;
       
  2274             io->destroy(io);
       
  2275 
       
  2276             if (tmp != NULL)  /* free any associated buffer. */
       
  2277                 allocator.Free(tmp);
       
  2278 
       
  2279             if (prev == NULL)
       
  2280                 *list = handle->next;
       
  2281             else
       
  2282                 prev->next = handle->next;
       
  2283 
       
  2284             allocator.Free(handle);
       
  2285             return 1;
       
  2286         } /* if */
       
  2287         prev = i;
       
  2288     } /* for */
       
  2289 
       
  2290     return 0;
       
  2291 } /* closeHandleInOpenList */
       
  2292 
       
  2293 
       
  2294 int PHYSFS_close(PHYSFS_File *_handle)
       
  2295 {
       
  2296     FileHandle *handle = (FileHandle *) _handle;
       
  2297     int rc;
       
  2298 
       
  2299     __PHYSFS_platformGrabMutex(stateLock);
       
  2300 
       
  2301     /* -1 == close failure. 0 == not found. 1 == success. */
       
  2302     rc = closeHandleInOpenList(&openReadList, handle);
       
  2303     BAIL_IF_MACRO_MUTEX(rc == -1, ERRPASS, stateLock, 0);
       
  2304     if (!rc)
       
  2305     {
       
  2306         rc = closeHandleInOpenList(&openWriteList, handle);
       
  2307         BAIL_IF_MACRO_MUTEX(rc == -1, ERRPASS, stateLock, 0);
       
  2308     } /* if */
       
  2309 
       
  2310     __PHYSFS_platformReleaseMutex(stateLock);
       
  2311     BAIL_IF_MACRO(!rc, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  2312     return 1;
       
  2313 } /* PHYSFS_close */
       
  2314 
       
  2315 
       
  2316 static PHYSFS_sint64 doBufferedRead(FileHandle *fh, void *buffer,
       
  2317                                     PHYSFS_uint64 len)
       
  2318 {
       
  2319     PHYSFS_Io *io = NULL;
       
  2320     PHYSFS_sint64 retval = 0;
       
  2321     PHYSFS_uint32 buffered = 0;
       
  2322     PHYSFS_sint64 rc = 0;
       
  2323 
       
  2324     if (len == 0)
       
  2325         return 0;
       
  2326 
       
  2327     buffered = fh->buffill - fh->bufpos;
       
  2328     if (buffered >= len)  /* totally in the buffer, just copy and return! */
       
  2329     {
       
  2330         memcpy(buffer, fh->buffer + fh->bufpos, (size_t) len);
       
  2331         fh->bufpos += (PHYSFS_uint32) len;
       
  2332         return (PHYSFS_sint64) len;
       
  2333     } /* else if */
       
  2334 
       
  2335     if (buffered > 0) /* partially in the buffer... */
       
  2336     {
       
  2337         memcpy(buffer, fh->buffer + fh->bufpos, (size_t) buffered);
       
  2338         buffer = ((PHYSFS_uint8 *) buffer) + buffered;
       
  2339         len -= buffered;
       
  2340         retval = buffered;
       
  2341         fh->buffill = fh->bufpos = 0;
       
  2342     } /* if */
       
  2343 
       
  2344     /* if you got here, the buffer is drained and we still need bytes. */
       
  2345     assert(len > 0);
       
  2346 
       
  2347     io = fh->io;
       
  2348     if (len >= fh->bufsize)  /* need more than the buffer takes. */
       
  2349     {
       
  2350         /* leave buffer empty, go right to output instead. */
       
  2351         rc = io->read(io, buffer, len);
       
  2352         if (rc < 0)
       
  2353             return ((retval == 0) ? rc : retval);
       
  2354         return retval + rc;
       
  2355     } /* if */
       
  2356 
       
  2357     /* need less than buffer can take. Fill buffer. */
       
  2358     rc = io->read(io, fh->buffer, fh->bufsize);
       
  2359     if (rc < 0)
       
  2360         return ((retval == 0) ? rc : retval);
       
  2361 
       
  2362     assert(fh->bufpos == 0);
       
  2363     fh->buffill = (PHYSFS_uint32) rc;
       
  2364     rc = doBufferedRead(fh, buffer, len);  /* go from the start, again. */
       
  2365     if (rc < 0)
       
  2366         return ((retval == 0) ? rc : retval);
       
  2367 
       
  2368     return retval + rc;
       
  2369 } /* doBufferedRead */
       
  2370 
       
  2371 
       
  2372 PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, void *buffer,
       
  2373                           PHYSFS_uint32 size, PHYSFS_uint32 count)
       
  2374 {
       
  2375     const PHYSFS_uint64 len = ((PHYSFS_uint64) size) * ((PHYSFS_uint64) count);
       
  2376     const PHYSFS_sint64 retval = PHYSFS_readBytes(handle, buffer, len);
       
  2377     return ( (retval <= 0) ? retval : (retval / ((PHYSFS_sint64) size)) );
       
  2378 } /* PHYSFS_read */
       
  2379 
       
  2380 
       
  2381 PHYSFS_sint64 PHYSFS_readBytes(PHYSFS_File *handle, void *buffer,
       
  2382                                PHYSFS_uint64 len)
       
  2383 {
       
  2384     FileHandle *fh = (FileHandle *) handle;
       
  2385 
       
  2386 #ifdef PHYSFS_NO_64BIT_SUPPORT
       
  2387     const PHYSFS_uint64 maxlen = __PHYSFS_UI64(0x7FFFFFFF);
       
  2388 #else
       
  2389     const PHYSFS_uint64 maxlen = __PHYSFS_UI64(0x7FFFFFFFFFFFFFFF);
       
  2390 #endif
       
  2391 
       
  2392     if (!__PHYSFS_ui64FitsAddressSpace(len))
       
  2393         BAIL_MACRO(PHYSFS_ERR_INVALID_ARGUMENT, -1);
       
  2394 
       
  2395     BAIL_IF_MACRO(len > maxlen, PHYSFS_ERR_INVALID_ARGUMENT, -1);
       
  2396     BAIL_IF_MACRO(!fh->forReading, PHYSFS_ERR_OPEN_FOR_WRITING, -1);
       
  2397     BAIL_IF_MACRO(len == 0, ERRPASS, 0);
       
  2398     if (fh->buffer)
       
  2399         return doBufferedRead(fh, buffer, len);
       
  2400 
       
  2401     return fh->io->read(fh->io, buffer, len);
       
  2402 } /* PHYSFS_readBytes */
       
  2403 
       
  2404 
       
  2405 static PHYSFS_sint64 doBufferedWrite(PHYSFS_File *handle, const void *buffer,
       
  2406                                      PHYSFS_uint64 len)
       
  2407 {
       
  2408     FileHandle *fh = (FileHandle *) handle;
       
  2409 
       
  2410     /* whole thing fits in the buffer? */
       
  2411     if ( (((PHYSFS_uint64) fh->buffill) + len) < fh->bufsize )
       
  2412     {
       
  2413         memcpy(fh->buffer + fh->buffill, buffer, (size_t) len);
       
  2414         fh->buffill += (PHYSFS_uint32) len;
       
  2415         return (PHYSFS_sint64) len;
       
  2416     } /* if */
       
  2417 
       
  2418     /* would overflow buffer. Flush and then write the new objects, too. */
       
  2419     BAIL_IF_MACRO(!PHYSFS_flush(handle), ERRPASS, -1);
       
  2420     return fh->io->write(fh->io, buffer, len);
       
  2421 } /* doBufferedWrite */
       
  2422 
       
  2423 
       
  2424 PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle, const void *buffer,
       
  2425                            PHYSFS_uint32 size, PHYSFS_uint32 count)
       
  2426 {
       
  2427     const PHYSFS_uint64 len = ((PHYSFS_uint64) size) * ((PHYSFS_uint64) count);
       
  2428     const PHYSFS_sint64 retval = PHYSFS_writeBytes(handle, buffer, len);
       
  2429     return ( (retval <= 0) ? retval : (retval / ((PHYSFS_sint64) size)) );
       
  2430 } /* PHYSFS_write */
       
  2431 
       
  2432 
       
  2433 PHYSFS_sint64 PHYSFS_writeBytes(PHYSFS_File *handle, const void *buffer,
       
  2434                                 PHYSFS_uint64 len)
       
  2435 {
       
  2436     FileHandle *fh = (FileHandle *) handle;
       
  2437 
       
  2438 #ifdef PHYSFS_NO_64BIT_SUPPORT
       
  2439     const PHYSFS_uint64 maxlen = __PHYSFS_UI64(0x7FFFFFFF);
       
  2440 #else
       
  2441     const PHYSFS_uint64 maxlen = __PHYSFS_UI64(0x7FFFFFFFFFFFFFFF);
       
  2442 #endif
       
  2443 
       
  2444     if (!__PHYSFS_ui64FitsAddressSpace(len))
       
  2445         BAIL_MACRO(PHYSFS_ERR_INVALID_ARGUMENT, -1);
       
  2446 
       
  2447     BAIL_IF_MACRO(len > maxlen, PHYSFS_ERR_INVALID_ARGUMENT, -1);
       
  2448     BAIL_IF_MACRO(fh->forReading, PHYSFS_ERR_OPEN_FOR_READING, -1);
       
  2449     BAIL_IF_MACRO(len == 0, ERRPASS, 0);
       
  2450     if (fh->buffer)
       
  2451         return doBufferedWrite(handle, buffer, len);
       
  2452 
       
  2453     return fh->io->write(fh->io, buffer, len);
       
  2454 } /* PHYSFS_write */
       
  2455 
       
  2456 
       
  2457 int PHYSFS_eof(PHYSFS_File *handle)
       
  2458 {
       
  2459     FileHandle *fh = (FileHandle *) handle;
       
  2460 
       
  2461     if (!fh->forReading)  /* never EOF on files opened for write/append. */
       
  2462         return 0;
       
  2463 
       
  2464     /* can't be eof if buffer isn't empty */
       
  2465     if (fh->bufpos == fh->buffill)
       
  2466     {
       
  2467         /* check the Io. */
       
  2468         PHYSFS_Io *io = fh->io;
       
  2469         const PHYSFS_sint64 pos = io->tell(io);
       
  2470         const PHYSFS_sint64 len = io->length(io);
       
  2471         if ((pos < 0) || (len < 0))
       
  2472             return 0;  /* beats me. */
       
  2473         return (pos >= len);
       
  2474     } /* if */
       
  2475 
       
  2476     return 0;
       
  2477 } /* PHYSFS_eof */
       
  2478 
       
  2479 
       
  2480 PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle)
       
  2481 {
       
  2482     FileHandle *fh = (FileHandle *) handle;
       
  2483     const PHYSFS_sint64 pos = fh->io->tell(fh->io);
       
  2484     const PHYSFS_sint64 retval = fh->forReading ?
       
  2485                                  (pos - fh->buffill) + fh->bufpos :
       
  2486                                  (pos + fh->buffill);
       
  2487     return retval;
       
  2488 } /* PHYSFS_tell */
       
  2489 
       
  2490 
       
  2491 int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos)
       
  2492 {
       
  2493     FileHandle *fh = (FileHandle *) handle;
       
  2494     BAIL_IF_MACRO(!PHYSFS_flush(handle), ERRPASS, 0);
       
  2495 
       
  2496     if (fh->buffer && fh->forReading)
       
  2497     {
       
  2498         /* avoid throwing away our precious buffer if seeking within it. */
       
  2499         PHYSFS_sint64 offset = pos - PHYSFS_tell(handle);
       
  2500         if ( /* seeking within the already-buffered range? */
       
  2501             ((offset >= 0) && (offset <= fh->buffill - fh->bufpos)) /* fwd */
       
  2502             || ((offset < 0) && (-offset <= fh->bufpos)) /* backward */ )
       
  2503         {
       
  2504             fh->bufpos += (PHYSFS_uint32) offset;
       
  2505             return 1; /* successful seek */
       
  2506         } /* if */
       
  2507     } /* if */
       
  2508 
       
  2509     /* we have to fall back to a 'raw' seek. */
       
  2510     fh->buffill = fh->bufpos = 0;
       
  2511     return fh->io->seek(fh->io, pos);
       
  2512 } /* PHYSFS_seek */
       
  2513 
       
  2514 
       
  2515 PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle)
       
  2516 {
       
  2517     PHYSFS_Io *io = ((FileHandle *) handle)->io;
       
  2518     return io->length(io);
       
  2519 } /* PHYSFS_filelength */
       
  2520 
       
  2521 
       
  2522 int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 _bufsize)
       
  2523 {
       
  2524     FileHandle *fh = (FileHandle *) handle;
       
  2525     PHYSFS_uint32 bufsize;
       
  2526 
       
  2527     /* !!! FIXME: actually, why use 32 bits here? */
       
  2528     /*BAIL_IF_MACRO(_bufsize > 0xFFFFFFFF, "buffer must fit in 32-bits", 0);*/
       
  2529     BAIL_IF_MACRO(_bufsize > 0xFFFFFFFF, PHYSFS_ERR_INVALID_ARGUMENT, 0);
       
  2530     bufsize = (PHYSFS_uint32) _bufsize;
       
  2531 
       
  2532     BAIL_IF_MACRO(!PHYSFS_flush(handle), ERRPASS, 0);
       
  2533 
       
  2534     /*
       
  2535      * For reads, we need to move the file pointer to where it would be
       
  2536      *  if we weren't buffering, so that the next read will get the
       
  2537      *  right chunk of stuff from the file. PHYSFS_flush() handles writes.
       
  2538      */
       
  2539     if ((fh->forReading) && (fh->buffill != fh->bufpos))
       
  2540     {
       
  2541         PHYSFS_uint64 pos;
       
  2542         const PHYSFS_sint64 curpos = fh->io->tell(fh->io);
       
  2543         BAIL_IF_MACRO(curpos == -1, ERRPASS, 0);
       
  2544         pos = ((curpos - fh->buffill) + fh->bufpos);
       
  2545         BAIL_IF_MACRO(!fh->io->seek(fh->io, pos), ERRPASS, 0);
       
  2546     } /* if */
       
  2547 
       
  2548     if (bufsize == 0)  /* delete existing buffer. */
       
  2549     {
       
  2550         if (fh->buffer)
       
  2551         {
       
  2552             allocator.Free(fh->buffer);
       
  2553             fh->buffer = NULL;
       
  2554         } /* if */
       
  2555     } /* if */
       
  2556 
       
  2557     else
       
  2558     {
       
  2559         PHYSFS_uint8 *newbuf;
       
  2560         newbuf = (PHYSFS_uint8 *) allocator.Realloc(fh->buffer, bufsize);
       
  2561         BAIL_IF_MACRO(!newbuf, PHYSFS_ERR_OUT_OF_MEMORY, 0);
       
  2562         fh->buffer = newbuf;
       
  2563     } /* else */
       
  2564 
       
  2565     fh->bufsize = bufsize;
       
  2566     fh->buffill = fh->bufpos = 0;
       
  2567     return 1;
       
  2568 } /* PHYSFS_setBuffer */
       
  2569 
       
  2570 
       
  2571 int PHYSFS_flush(PHYSFS_File *handle)
       
  2572 {
       
  2573     FileHandle *fh = (FileHandle *) handle;
       
  2574     PHYSFS_Io *io;
       
  2575     PHYSFS_sint64 rc;
       
  2576 
       
  2577     if ((fh->forReading) || (fh->bufpos == fh->buffill))
       
  2578         return 1;  /* open for read or buffer empty are successful no-ops. */
       
  2579 
       
  2580     /* dump buffer to disk. */
       
  2581     io = fh->io;
       
  2582     rc = io->write(io, fh->buffer + fh->bufpos, fh->buffill - fh->bufpos);
       
  2583     BAIL_IF_MACRO(rc <= 0, ERRPASS, 0);
       
  2584     fh->bufpos = fh->buffill = 0;
       
  2585     return io->flush(io);
       
  2586 } /* PHYSFS_flush */
       
  2587 
       
  2588 
       
  2589 int PHYSFS_stat(const char *_fname, PHYSFS_Stat *stat)
       
  2590 {
       
  2591     int retval = 0;
       
  2592     char *fname;
       
  2593     size_t len;
       
  2594 
       
  2595     BAIL_IF_MACRO(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, -1);
       
  2596     BAIL_IF_MACRO(!stat, PHYSFS_ERR_INVALID_ARGUMENT, -1);
       
  2597     len = strlen(_fname) + 1;
       
  2598     fname = (char *) __PHYSFS_smallAlloc(len);
       
  2599     BAIL_IF_MACRO(!fname, PHYSFS_ERR_OUT_OF_MEMORY, -1);
       
  2600 
       
  2601     /* set some sane defaults... */
       
  2602     stat->filesize = -1;
       
  2603     stat->modtime = -1;
       
  2604     stat->createtime = -1;
       
  2605     stat->accesstime = -1;
       
  2606     stat->filetype = PHYSFS_FILETYPE_OTHER;
       
  2607     stat->readonly = 1;  /* !!! FIXME */
       
  2608 
       
  2609     if (sanitizePlatformIndependentPath(_fname, fname))
       
  2610     {
       
  2611         if (*fname == '\0')
       
  2612         {
       
  2613             stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
       
  2614             stat->readonly = !writeDir; /* Writeable if we have a writeDir */
       
  2615             retval = 1;
       
  2616         } /* if */
       
  2617         else
       
  2618         {
       
  2619             DirHandle *i;
       
  2620             int exists = 0;
       
  2621             __PHYSFS_platformGrabMutex(stateLock);
       
  2622             for (i = searchPath; ((i != NULL) && (!exists)); i = i->next)
       
  2623             {
       
  2624                 char *arcfname = fname;
       
  2625                 exists = partOfMountPoint(i, arcfname);
       
  2626                 if (exists)
       
  2627                 {
       
  2628                     stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
       
  2629                     stat->readonly = 1;  /* !!! FIXME */
       
  2630                     retval = 1;
       
  2631                 } /* if */
       
  2632                 else if (verifyPath(i, &arcfname, 0))
       
  2633                 {
       
  2634                     /* !!! FIXME: this test is wrong and should be elsewhere. */
       
  2635                     stat->readonly = !(writeDir &&
       
  2636                                  (strcmp(writeDir->dirName, i->dirName) == 0));
       
  2637                     retval = i->funcs->stat(i->opaque, arcfname, &exists, stat);
       
  2638                 } /* else if */
       
  2639             } /* for */
       
  2640             __PHYSFS_platformReleaseMutex(stateLock);
       
  2641         } /* else */
       
  2642     } /* if */
       
  2643 
       
  2644     __PHYSFS_smallFree(fname);
       
  2645     return retval;
       
  2646 } /* PHYSFS_stat */
       
  2647 
       
  2648 
       
  2649 int __PHYSFS_readAll(PHYSFS_Io *io, void *buf, const PHYSFS_uint64 len)
       
  2650 {
       
  2651     return (io->read(io, buf, len) == len);
       
  2652 } /* __PHYSFS_readAll */
       
  2653 
       
  2654 
       
  2655 void *__PHYSFS_initSmallAlloc(void *ptr, PHYSFS_uint64 len)
       
  2656 {
       
  2657     void *useHeap = ((ptr == NULL) ? ((void *) 1) : ((void *) 0));
       
  2658     if (useHeap)  /* too large for stack allocation or alloca() failed. */
       
  2659         ptr = allocator.Malloc(len+sizeof (void *));
       
  2660 
       
  2661     if (ptr != NULL)
       
  2662     {
       
  2663         void **retval = (void **) ptr;
       
  2664         /*printf("%s alloc'd (%d) bytes at (%p).\n",
       
  2665                 useHeap ? "heap" : "stack", (int) len, ptr);*/
       
  2666         *retval = useHeap;
       
  2667         return retval + 1;
       
  2668     } /* if */
       
  2669 
       
  2670     return NULL;  /* allocation failed. */
       
  2671 } /* __PHYSFS_initSmallAlloc */
       
  2672 
       
  2673 
       
  2674 void __PHYSFS_smallFree(void *ptr)
       
  2675 {
       
  2676     if (ptr != NULL)
       
  2677     {
       
  2678         void **block = ((void **) ptr) - 1;
       
  2679         const int useHeap = (*block != 0);
       
  2680         if (useHeap)
       
  2681             allocator.Free(block);
       
  2682         /*printf("%s free'd (%p).\n", useHeap ? "heap" : "stack", block);*/
       
  2683     } /* if */
       
  2684 } /* __PHYSFS_smallFree */
       
  2685 
       
  2686 
       
  2687 int PHYSFS_setAllocator(const PHYSFS_Allocator *a)
       
  2688 {
       
  2689     BAIL_IF_MACRO(initialized, PHYSFS_ERR_IS_INITIALIZED, 0);
       
  2690     externalAllocator = (a != NULL);
       
  2691     if (externalAllocator)
       
  2692         memcpy(&allocator, a, sizeof (PHYSFS_Allocator));
       
  2693 
       
  2694     return 1;
       
  2695 } /* PHYSFS_setAllocator */
       
  2696 
       
  2697 
       
  2698 const PHYSFS_Allocator *PHYSFS_getAllocator(void)
       
  2699 {
       
  2700     BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, NULL);
       
  2701     return &allocator;
       
  2702 } /* PHYSFS_getAllocator */
       
  2703 
       
  2704 
       
  2705 static void *mallocAllocatorMalloc(PHYSFS_uint64 s)
       
  2706 {
       
  2707     if (!__PHYSFS_ui64FitsAddressSpace(s))
       
  2708         BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
  2709     #undef malloc
       
  2710     return malloc((size_t) s);
       
  2711 } /* mallocAllocatorMalloc */
       
  2712 
       
  2713 
       
  2714 static void *mallocAllocatorRealloc(void *ptr, PHYSFS_uint64 s)
       
  2715 {
       
  2716     if (!__PHYSFS_ui64FitsAddressSpace(s))
       
  2717         BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
  2718     #undef realloc
       
  2719     return realloc(ptr, (size_t) s);
       
  2720 } /* mallocAllocatorRealloc */
       
  2721 
       
  2722 
       
  2723 static void mallocAllocatorFree(void *ptr)
       
  2724 {
       
  2725     #undef free
       
  2726     free(ptr);
       
  2727 } /* mallocAllocatorFree */
       
  2728 
       
  2729 
       
  2730 static void setDefaultAllocator(void)
       
  2731 {
       
  2732     assert(!externalAllocator);
       
  2733     if (!__PHYSFS_platformSetDefaultAllocator(&allocator))
       
  2734     {
       
  2735         allocator.Init = NULL;
       
  2736         allocator.Deinit = NULL;
       
  2737         allocator.Malloc = mallocAllocatorMalloc;
       
  2738         allocator.Realloc = mallocAllocatorRealloc;
       
  2739         allocator.Free = mallocAllocatorFree;
       
  2740     } /* if */
       
  2741 } /* setDefaultAllocator */
       
  2742 
       
  2743 /* end of physfs.c ... */
       
  2744