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