Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

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