misc/libphysfs/physfs.c
changeset 12213 bb5522e88ab2
parent 10017 de822cd3df3a
child 12453 89423b1db329
--- a/misc/libphysfs/physfs.c	Mon Apr 10 09:05:16 2017 -0400
+++ b/misc/libphysfs/physfs.c	Mon Apr 10 12:06:43 2017 -0400
@@ -46,48 +46,6 @@
 } ErrState;
 
 
-/* The various i/o drivers...some of these may not be compiled in. */
-extern const PHYSFS_Archiver __PHYSFS_Archiver_ZIP;
-extern const PHYSFS_Archiver __PHYSFS_Archiver_LZMA;
-extern const PHYSFS_Archiver __PHYSFS_Archiver_GRP;
-extern const PHYSFS_Archiver __PHYSFS_Archiver_QPAK;
-extern const PHYSFS_Archiver __PHYSFS_Archiver_HOG;
-extern const PHYSFS_Archiver __PHYSFS_Archiver_MVL;
-extern const PHYSFS_Archiver __PHYSFS_Archiver_WAD;
-extern const PHYSFS_Archiver __PHYSFS_Archiver_DIR;
-extern const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660;
-
-static const PHYSFS_Archiver *staticArchivers[] =
-{
-#if PHYSFS_SUPPORTS_ZIP
-    &__PHYSFS_Archiver_ZIP,
-#endif
-#if PHYSFS_SUPPORTS_7Z
-    &__PHYSFS_Archiver_LZMA,
-#endif
-#if PHYSFS_SUPPORTS_GRP
-    &__PHYSFS_Archiver_GRP,
-#endif
-#if PHYSFS_SUPPORTS_QPAK
-    &__PHYSFS_Archiver_QPAK,
-#endif
-#if PHYSFS_SUPPORTS_HOG
-    &__PHYSFS_Archiver_HOG,
-#endif
-#if PHYSFS_SUPPORTS_MVL
-    &__PHYSFS_Archiver_MVL,
-#endif
-#if PHYSFS_SUPPORTS_WAD
-    &__PHYSFS_Archiver_WAD,
-#endif
-#if PHYSFS_SUPPORTS_ISO9660
-    &__PHYSFS_Archiver_ISO9660,
-#endif
-    NULL
-};
-
-
-
 /* General PhysicsFS state ... */
 static int initialized = 0;
 static ErrState *errorStates = NULL;
@@ -101,6 +59,7 @@
 static int allowSymLinks = 0;
 static const PHYSFS_Archiver **archivers = NULL;
 static const PHYSFS_ArchiveInfo **archiveInfo = NULL;
+static volatile size_t numArchivers = 0;
 
 /* mutexes ... */
 static void *errorLock = NULL;     /* protects error message list.        */
@@ -486,7 +445,7 @@
     memcpy(retval, io, sizeof (PHYSFS_Io));
     retval->opaque = newfh;
     return retval;
-
+    
 handleIo_dupe_failed:
     if (newfh)
     {
@@ -580,7 +539,7 @@
 
     if (ecd.errcode)
     {
-        __PHYSFS_setError(ecd.errcode);
+        PHYSFS_setErrorCode(ecd.errcode);
         return NULL;
     } /* if */
 
@@ -655,7 +614,7 @@
 {
     /*
      * Quicksort w/ Bubblesort fallback algorithm inspired by code from here:
-     *   http://www.cs.ubc.ca/spider/harrison/Java/sorting-demo.html
+     *   https://www.cs.ubc.ca/spider/harrison/Java/sorting-demo.html
      */
     if (max > 0)
         __PHYSFS_quick_sort(entries, 0, max - 1, cmpfn, swapfn);
@@ -692,7 +651,64 @@
 } /* findErrorForCurrentThread */
 
 
-void __PHYSFS_setError(const PHYSFS_ErrorCode errcode)
+/* this doesn't reset the error state. */
+static inline PHYSFS_ErrorCode currentErrorCode(void)
+{
+    const ErrState *err = findErrorForCurrentThread();
+    return err ? err->code : PHYSFS_ERR_OK;
+} /* currentErrorCode */
+
+
+PHYSFS_ErrorCode PHYSFS_getLastErrorCode(void)
+{
+    ErrState *err = findErrorForCurrentThread();
+    const PHYSFS_ErrorCode retval = (err) ? err->code : PHYSFS_ERR_OK;
+    if (err)
+        err->code = PHYSFS_ERR_OK;
+    return retval;
+} /* PHYSFS_getLastErrorCode */
+
+
+PHYSFS_DECL const char *PHYSFS_getErrorByCode(PHYSFS_ErrorCode code)
+{
+    switch (code)
+    {
+        case PHYSFS_ERR_OK: return "no error";
+        case PHYSFS_ERR_OTHER_ERROR: return "unknown error";
+        case PHYSFS_ERR_OUT_OF_MEMORY: return "out of memory";
+        case PHYSFS_ERR_NOT_INITIALIZED: return "not initialized";
+        case PHYSFS_ERR_IS_INITIALIZED: return "already initialized";
+        case PHYSFS_ERR_ARGV0_IS_NULL: return "argv[0] is NULL";
+        case PHYSFS_ERR_UNSUPPORTED: return "unsupported";
+        case PHYSFS_ERR_PAST_EOF: return "past end of file";
+        case PHYSFS_ERR_FILES_STILL_OPEN: return "files still open";
+        case PHYSFS_ERR_INVALID_ARGUMENT: return "invalid argument";
+        case PHYSFS_ERR_NOT_MOUNTED: return "not mounted";
+        case PHYSFS_ERR_NOT_FOUND: return "not found";
+        case PHYSFS_ERR_SYMLINK_FORBIDDEN: return "symlinks are forbidden";
+        case PHYSFS_ERR_NO_WRITE_DIR: return "write directory is not set";
+        case PHYSFS_ERR_OPEN_FOR_READING: return "file open for reading";
+        case PHYSFS_ERR_OPEN_FOR_WRITING: return "file open for writing";
+        case PHYSFS_ERR_NOT_A_FILE: return "not a file";
+        case PHYSFS_ERR_READ_ONLY: return "read-only filesystem";
+        case PHYSFS_ERR_CORRUPT: return "corrupted";
+        case PHYSFS_ERR_SYMLINK_LOOP: return "infinite symbolic link loop";
+        case PHYSFS_ERR_IO: return "i/o error";
+        case PHYSFS_ERR_PERMISSION: return "permission denied";
+        case PHYSFS_ERR_NO_SPACE: return "no space available for writing";
+        case PHYSFS_ERR_BAD_FILENAME: return "filename is illegal or insecure";
+        case PHYSFS_ERR_BUSY: return "tried to modify a file the OS needs";
+        case PHYSFS_ERR_DIR_NOT_EMPTY: return "directory isn't empty";
+        case PHYSFS_ERR_OS_ERROR: return "OS reported an error";
+        case PHYSFS_ERR_DUPLICATE: return "duplicate resource";
+        case PHYSFS_ERR_BAD_PASSWORD: return "bad password";
+    } /* switch */
+
+    return NULL;  /* don't know this error code. */
+} /* PHYSFS_getErrorByCode */
+
+
+void PHYSFS_setErrorCode(PHYSFS_ErrorCode errcode)
 {
     ErrState *err;
 
@@ -720,59 +736,6 @@
     } /* if */
 
     err->code = errcode;
-} /* __PHYSFS_setError */
-
-
-PHYSFS_ErrorCode PHYSFS_getLastErrorCode(void)
-{
-    ErrState *err = findErrorForCurrentThread();
-    const PHYSFS_ErrorCode retval = (err) ? err->code : PHYSFS_ERR_OK;
-    if (err)
-        err->code = PHYSFS_ERR_OK;
-    return retval;
-} /* PHYSFS_getLastErrorCode */
-
-
-PHYSFS_DECL const char *PHYSFS_getErrorByCode(PHYSFS_ErrorCode code)
-{
-    switch (code)
-    {
-        case PHYSFS_ERR_OK: return "no error";
-        case PHYSFS_ERR_OTHER_ERROR: return "unknown error";
-        case PHYSFS_ERR_OUT_OF_MEMORY: return "out of memory";
-        case PHYSFS_ERR_NOT_INITIALIZED: return "not initialized";
-        case PHYSFS_ERR_IS_INITIALIZED: return "already initialized";
-        case PHYSFS_ERR_ARGV0_IS_NULL: return "argv[0] is NULL";
-        case PHYSFS_ERR_UNSUPPORTED: return "unsupported";
-        case PHYSFS_ERR_PAST_EOF: return "past end of file";
-        case PHYSFS_ERR_FILES_STILL_OPEN: return "files still open";
-        case PHYSFS_ERR_INVALID_ARGUMENT: return "invalid argument";
-        case PHYSFS_ERR_NOT_MOUNTED: return "not mounted";
-        case PHYSFS_ERR_NO_SUCH_PATH: return "no such path";
-        case PHYSFS_ERR_SYMLINK_FORBIDDEN: return "symlinks are forbidden";
-        case PHYSFS_ERR_NO_WRITE_DIR: return "write directory is not set";
-        case PHYSFS_ERR_OPEN_FOR_READING: return "file open for reading";
-        case PHYSFS_ERR_OPEN_FOR_WRITING: return "file open for writing";
-        case PHYSFS_ERR_NOT_A_FILE: return "not a file";
-        case PHYSFS_ERR_READ_ONLY: return "read-only filesystem";
-        case PHYSFS_ERR_CORRUPT: return "corrupted";
-        case PHYSFS_ERR_SYMLINK_LOOP: return "infinite symbolic link loop";
-        case PHYSFS_ERR_IO: return "i/o error";
-        case PHYSFS_ERR_PERMISSION: return "permission denied";
-        case PHYSFS_ERR_NO_SPACE: return "no space available for writing";
-        case PHYSFS_ERR_BAD_FILENAME: return "filename is illegal or insecure";
-        case PHYSFS_ERR_BUSY: return "tried to modify a file the OS needs";
-        case PHYSFS_ERR_DIR_NOT_EMPTY: return "directory isn't empty";
-        case PHYSFS_ERR_OS_ERROR: return "OS reported an error";
-    } /* switch */
-
-    return NULL;  /* don't know this error code. */
-} /* PHYSFS_getErrorByCode */
-
-
-void PHYSFS_setErrorCode(PHYSFS_ErrorCode code)
-{
-    __PHYSFS_setError(code);
 } /* PHYSFS_setErrorCode */
 
 
@@ -866,18 +829,21 @@
     DirHandle *retval = NULL;
     const PHYSFS_Archiver **i;
     const char *ext;
+    int created_io = 0;
 
     assert((io != NULL) || (d != NULL));
 
     if (io == NULL)
     {
         /* DIR gets first shot (unlike the rest, it doesn't deal with files). */
+        extern const PHYSFS_Archiver __PHYSFS_Archiver_DIR;
         retval = tryOpenDir(io, &__PHYSFS_Archiver_DIR, d, forWriting);
         if (retval != NULL)
             return retval;
 
         io = __PHYSFS_createNativeIo(d, forWriting ? 'w' : 'r');
         BAIL_IF_MACRO(!io, ERRPASS, 0);
+        created_io = 1;
     } /* if */
 
     ext = find_filename_extension(d);
@@ -886,14 +852,14 @@
         /* Look for archivers with matching file extensions first... */
         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
         {
-            if (__PHYSFS_stricmpASCII(ext, (*i)->info.extension) == 0)
+            if (__PHYSFS_utf8stricmp(ext, (*i)->info.extension) == 0)
                 retval = tryOpenDir(io, *i, d, forWriting);
         } /* for */
 
         /* failing an exact file extension match, try all the others... */
         for (i = archivers; (*i != NULL) && (retval == NULL); i++)
         {
-            if (__PHYSFS_stricmpASCII(ext, (*i)->info.extension) != 0)
+            if (__PHYSFS_utf8stricmp(ext, (*i)->info.extension) != 0)
                 retval = tryOpenDir(io, *i, d, forWriting);
         } /* for */
     } /* if */
@@ -904,6 +870,9 @@
             retval = tryOpenDir(io, *i, d, forWriting);
     } /* else */
 
+    if ((!retval) && (created_io))
+        io->destroy(io);
+
     BAIL_IF_MACRO(!retval, PHYSFS_ERR_UNSUPPORTED, NULL);
     return retval;
 } /* openDirectory */
@@ -1121,32 +1090,52 @@
 } /* initializeMutexes */
 
 
-static void setDefaultAllocator(void);
+static int doRegisterArchiver(const PHYSFS_Archiver *_archiver);
 
 static int initStaticArchivers(void)
 {
-    const size_t numStaticArchivers = __PHYSFS_ARRAYLEN(staticArchivers);
-    const size_t len = numStaticArchivers * sizeof (void *);
-    size_t i;
-
-    assert(numStaticArchivers > 0);  /* seriously, none at all?! */
-    assert(staticArchivers[numStaticArchivers - 1] == NULL);
-
-    archiveInfo = (const PHYSFS_ArchiveInfo **) allocator.Malloc(len);
-    BAIL_IF_MACRO(!archiveInfo, PHYSFS_ERR_OUT_OF_MEMORY, 0);
-    archivers = (const PHYSFS_Archiver **) allocator.Malloc(len);
-    BAIL_IF_MACRO(!archivers, PHYSFS_ERR_OUT_OF_MEMORY, 0);
-
-    for (i = 0; i < numStaticArchivers - 1; i++)
-        archiveInfo[i] = &staticArchivers[i]->info;
-    archiveInfo[numStaticArchivers - 1] = NULL;
-
-    memcpy(archivers, staticArchivers, len);
+    #define REGISTER_STATIC_ARCHIVER(arc) { \
+        extern const PHYSFS_Archiver __PHYSFS_Archiver_##arc; \
+        if (!doRegisterArchiver(&__PHYSFS_Archiver_##arc)) { \
+            return 0; \
+        } \
+    }
+
+    #if PHYSFS_SUPPORTS_ZIP
+        REGISTER_STATIC_ARCHIVER(ZIP);
+    #endif
+    #if PHYSFS_SUPPORTS_7Z
+        REGISTER_STATIC_ARCHIVER(LZMA);
+    #endif
+    #if PHYSFS_SUPPORTS_GRP
+        REGISTER_STATIC_ARCHIVER(GRP);
+    #endif
+    #if PHYSFS_SUPPORTS_QPAK
+        REGISTER_STATIC_ARCHIVER(QPAK);
+    #endif
+    #if PHYSFS_SUPPORTS_HOG
+        REGISTER_STATIC_ARCHIVER(HOG);
+    #endif
+    #if PHYSFS_SUPPORTS_MVL
+        REGISTER_STATIC_ARCHIVER(MVL);
+    #endif
+    #if PHYSFS_SUPPORTS_WAD
+        REGISTER_STATIC_ARCHIVER(WAD);
+    #endif
+    #if PHYSFS_SUPPORTS_SLB
+        REGISTER_STATIC_ARCHIVER(SLB);
+    #endif
+    #if PHYSFS_SUPPORTS_ISO9660
+        REGISTER_STATIC_ARCHIVER(ISO9660);
+    #endif
+
+    #undef REGISTER_STATIC_ARCHIVER
 
     return 1;
 } /* initStaticArchivers */
 
 
+static void setDefaultAllocator(void);
 static int doDeinit(void);
 
 int PHYSFS_init(const char *argv0)
@@ -1183,7 +1172,7 @@
     initialized = 1;
 
     /* This makes sure that the error subsystem is initialized. */
-    __PHYSFS_setError(PHYSFS_getLastErrorCode());
+    PHYSFS_setErrorCode(PHYSFS_getLastErrorCode());
 
     return 1;
 
@@ -1204,7 +1193,7 @@
         PHYSFS_Io *io = i->io;
         next = i->next;
 
-        if (!io->flush(io))
+        if (io->flush && !io->flush(io))
         {
             *list = i;
             return 0;
@@ -1239,14 +1228,70 @@
 } /* freeSearchPath */
 
 
+/* MAKE SURE you hold stateLock before calling this! */
+static int archiverInUse(const PHYSFS_Archiver *arc, const DirHandle *list)
+{
+    const DirHandle *i;
+    for (i = list; i != NULL; i = i->next)
+    {
+        if (i->funcs == arc)
+            return 1;
+    } /* for */
+
+    return 0;  /* not in use */
+} /* archiverInUse */
+
+
+/* MAKE SURE you hold stateLock before calling this! */
+static int doDeregisterArchiver(const size_t idx)
+{
+    const size_t len = (numArchivers - idx) * sizeof (void *);
+    const PHYSFS_ArchiveInfo *info = archiveInfo[idx];
+    const PHYSFS_Archiver *arc = archivers[idx];
+
+    /* make sure nothing is still using this archiver */
+    if (archiverInUse(arc, searchPath) || archiverInUse(arc, writeDir))
+        BAIL_MACRO(PHYSFS_ERR_FILES_STILL_OPEN, 0);
+
+    allocator.Free((void *) info->extension);
+    allocator.Free((void *) info->description);
+    allocator.Free((void *) info->author);
+    allocator.Free((void *) info->url);
+    allocator.Free((void *) arc);
+
+    memmove(&archiveInfo[idx], &archiveInfo[idx+1], len);
+    memmove(&archivers[idx], &archivers[idx+1], len);
+
+    assert(numArchivers > 0);
+    numArchivers--;
+
+    return 1;
+} /* doDeregisterArchiver */
+
+
+/* Does NOT hold the state lock; we're shutting down. */
+static void freeArchivers(void)
+{
+    while (numArchivers > 0)
+    {
+        if (!doDeregisterArchiver(numArchivers - 1))
+            assert(!"nothing should be mounted during shutdown.");
+    } /* while */
+
+    allocator.Free(archivers);
+    allocator.Free(archiveInfo);
+    archivers = NULL;
+    archiveInfo = NULL;
+} /* freeArchivers */
+
+
 static int doDeinit(void)
 {
-    BAIL_IF_MACRO(!__PHYSFS_platformDeinit(), ERRPASS, 0);
-
     closeFileHandleList(&openWriteList);
     BAIL_IF_MACRO(!PHYSFS_setWriteDir(NULL), PHYSFS_ERR_FILES_STILL_OPEN, 0);
 
     freeSearchPath();
+    freeArchivers();
     freeErrorStates();
 
     if (baseDir != NULL)
@@ -1289,6 +1334,10 @@
         allocator.Deinit();
 
     errorLock = stateLock = NULL;
+
+    /* !!! FIXME: what on earth are you supposed to do if this fails? */
+    BAIL_IF_MACRO(!__PHYSFS_platformDeinit(), ERRPASS, 0);
+
     return 1;
 } /* doDeinit */
 
@@ -1306,6 +1355,143 @@
 } /* PHYSFS_isInit */
 
 
+char *__PHYSFS_strdup(const char *str)
+{
+    char *retval = (char *) allocator.Malloc(strlen(str) + 1);
+    if (retval)
+        strcpy(retval, str);
+    return retval;
+} /* __PHYSFS_strdup */
+
+
+PHYSFS_uint32 __PHYSFS_hashString(const char *str, size_t len)
+{
+    PHYSFS_uint32 hash = 5381;
+    while (len--)
+        hash = ((hash << 5) + hash) ^ *(str++);
+    return hash;
+} /* __PHYSFS_hashString */
+
+
+/* MAKE SURE you hold stateLock before calling this! */
+static int doRegisterArchiver(const PHYSFS_Archiver *_archiver)
+{
+    const PHYSFS_uint32 maxver = CURRENT_PHYSFS_ARCHIVER_API_VERSION;
+    const size_t len = (numArchivers + 2) * sizeof (void *);
+    PHYSFS_Archiver *archiver = NULL;
+    PHYSFS_ArchiveInfo *info = NULL;
+    const char *ext = NULL;
+    void *ptr = NULL;
+    size_t i;
+
+    BAIL_IF_MACRO(!_archiver, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(_archiver->version > maxver, PHYSFS_ERR_UNSUPPORTED, 0);
+    BAIL_IF_MACRO(!_archiver->info.extension, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->info.description, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->info.author, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->info.url, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->openArchive, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->enumerateFiles, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->openRead, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->openWrite, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->openAppend, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->remove, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->mkdir, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->closeArchive, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!_archiver->stat, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+
+    ext = _archiver->info.extension;
+    for (i = 0; i < numArchivers; i++)
+    {
+        if (__PHYSFS_utf8stricmp(archiveInfo[i]->extension, ext) == 0)
+            BAIL_MACRO(PHYSFS_ERR_DUPLICATE, 0);  /* !!! FIXME: better error? ERR_IN_USE? */
+    } /* for */
+
+    /* make a copy of the data. */
+    archiver = (PHYSFS_Archiver *) allocator.Malloc(sizeof (*archiver));
+    GOTO_IF_MACRO(!archiver, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
+
+    /* Must copy sizeof (OLD_VERSION_OF_STRUCT) when version changes! */
+    memcpy(archiver, _archiver, sizeof (*archiver));
+
+    info = (PHYSFS_ArchiveInfo *) &archiver->info;
+    memset(info, '\0', sizeof (*info));  /* NULL in case an alloc fails. */
+    #define CPYSTR(item) \
+        info->item = __PHYSFS_strdup(_archiver->info.item); \
+        GOTO_IF_MACRO(!info->item, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
+    CPYSTR(extension);
+    CPYSTR(description);
+    CPYSTR(author);
+    CPYSTR(url);
+    info->supportsSymlinks = _archiver->info.supportsSymlinks;
+    #undef CPYSTR
+
+    ptr = allocator.Realloc(archiveInfo, len);
+    GOTO_IF_MACRO(!ptr, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
+    archiveInfo = (const PHYSFS_ArchiveInfo **) ptr;
+
+    ptr = allocator.Realloc(archivers, len);
+    GOTO_IF_MACRO(!ptr, PHYSFS_ERR_OUT_OF_MEMORY, regfailed);
+    archivers = (const PHYSFS_Archiver **) ptr;
+
+    archiveInfo[numArchivers] = info;
+    archiveInfo[numArchivers + 1] = NULL;
+
+    archivers[numArchivers] = archiver;
+    archivers[numArchivers + 1] = NULL;
+
+    numArchivers++;
+
+    return 1;
+
+regfailed:
+    if (info != NULL)
+    {
+        allocator.Free((void *) info->extension);
+        allocator.Free((void *) info->description);
+        allocator.Free((void *) info->author);
+        allocator.Free((void *) info->url);
+    } /* if */
+    allocator.Free(archiver);
+
+    return 0;
+} /* doRegisterArchiver */
+
+
+int PHYSFS_registerArchiver(const PHYSFS_Archiver *archiver)
+{
+    int retval;
+    BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
+    __PHYSFS_platformGrabMutex(stateLock);
+    retval = doRegisterArchiver(archiver);
+    __PHYSFS_platformReleaseMutex(stateLock);
+    return retval;
+} /* PHYSFS_registerArchiver */
+
+
+int PHYSFS_deregisterArchiver(const char *ext)
+{
+    size_t i;
+
+    BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
+    BAIL_IF_MACRO(!ext, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+
+    __PHYSFS_platformGrabMutex(stateLock);
+    for (i = 0; i < numArchivers; i++)
+    {
+        if (__PHYSFS_utf8stricmp(archiveInfo[i]->extension, ext) == 0)
+        {
+            const int retval = doDeregisterArchiver(i);
+            __PHYSFS_platformReleaseMutex(stateLock);
+            return retval;
+        } /* if */
+    } /* for */
+    __PHYSFS_platformReleaseMutex(stateLock);
+
+    BAIL_MACRO(PHYSFS_ERR_NOT_FOUND, 0);
+} /* PHYSFS_deregisterArchiver */
+
+
 const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void)
 {
     BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, NULL);
@@ -1351,7 +1537,6 @@
     PHYSFS_Stat statbuf;
     char *ptr = NULL;
     char *endstr = NULL;
-    int exists = 0;
 
     BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
     BAIL_IF_MACRO(!org, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
@@ -1368,7 +1553,7 @@
     assert(*endstr == dirsep);
     *endstr = '\0';  /* mask out the final dirsep for now. */
 
-    if (!__PHYSFS_platformStat(prefDir, &exists, &statbuf))
+    if (!__PHYSFS_platformStat(prefDir, &statbuf))
     {
         for (ptr = strchr(prefDir, dirsep); ptr; ptr = strchr(ptr+1, dirsep))
         {
@@ -1698,7 +1883,7 @@
             if ((l > extlen) && ((*i)[l - extlen - 1] == '.'))
             {
                 ext = (*i) + (l - extlen);
-                if (__PHYSFS_stricmpASCII(ext, archiveExt) == 0)
+                if (__PHYSFS_utf8stricmp(ext, archiveExt) == 0)
                     setSaneCfgAddPath(*i, l, dirsep, archivesFirst);
             } /* if */
         } /* for */
@@ -1759,12 +1944,12 @@
         size_t len = strlen(fname);
         assert(mntpntlen > 1); /* root mount points should be NULL. */
         /* not under the mountpoint, so skip this archive. */
-        BAIL_IF_MACRO(len < mntpntlen-1, PHYSFS_ERR_NO_SUCH_PATH, 0);
+        BAIL_IF_MACRO(len < mntpntlen-1, PHYSFS_ERR_NOT_FOUND, 0);
         /* !!! FIXME: Case insensitive? */
         retval = strncmp(h->mountPoint, fname, mntpntlen-1);
-        BAIL_IF_MACRO(retval != 0, PHYSFS_ERR_NO_SUCH_PATH, 0);
+        BAIL_IF_MACRO(retval != 0, PHYSFS_ERR_NOT_FOUND, 0);
         if (len > mntpntlen-1)  /* corner case... */
-            BAIL_IF_MACRO(fname[mntpntlen-1]!='/', PHYSFS_ERR_NO_SUCH_PATH, 0);
+            BAIL_IF_MACRO(fname[mntpntlen-1]!='/', PHYSFS_ERR_NOT_FOUND, 0);
         fname += mntpntlen-1;  /* move to start of actual archive path. */
         if (*fname == '/')
             fname++;
@@ -1782,9 +1967,12 @@
             end = strchr(start, '/');
 
             if (end != NULL) *end = '\0';
-            rc = h->funcs->stat(h->opaque, fname, &retval, &statbuf);
+            rc = h->funcs->stat(h->opaque, fname, &statbuf);
             if (rc)
                 rc = (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK);
+            else if (currentErrorCode() == PHYSFS_ERR_NOT_FOUND)
+                retval = 0;
+
             if (end != NULL) *end = '/';
 
             /* insecure path (has a disallowed symlink in it)? */
@@ -1840,7 +2028,9 @@
         if (exists)
         {
             PHYSFS_Stat statbuf;
-            const int rc = h->funcs->stat(h->opaque, dname, &exists, &statbuf);
+            const int rc = h->funcs->stat(h->opaque, dname, &statbuf);
+            if ((!rc) && (currentErrorCode() == PHYSFS_ERR_NOT_FOUND))
+                exists = 0;
             retval = ((rc) && (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY));
         } /* if */
 
@@ -1937,11 +2127,9 @@
             else if (verifyPath(i, &arcfname, 0))
             {
                 PHYSFS_Stat statbuf;
-                int exists = 0;
-                if (i->funcs->stat(i->opaque, arcfname, &exists, &statbuf))
+                if (i->funcs->stat(i->opaque, arcfname, &statbuf))
                 {
-                    if (exists)
-                        retval = i->dirName;
+                    retval = i->dirName;
                     break;
                 } /* if */
             } /* if */
@@ -2060,6 +2248,40 @@
 } /* enumerateFromMountPoint */
 
 
+typedef struct SymlinkFilterData
+{
+    PHYSFS_EnumFilesCallback callback;
+    void *callbackData;
+    DirHandle *dirhandle;
+} SymlinkFilterData;
+
+/* !!! FIXME: broken if in a virtual mountpoint (stat call fails). */
+static void enumCallbackFilterSymLinks(void *_data, const char *origdir,
+                                       const char *fname)
+{
+    const char *trimmedDir = (*origdir == '/') ? (origdir+1) : origdir;
+    const size_t slen = strlen(trimmedDir) + strlen(fname) + 2;
+    char *path = (char *) __PHYSFS_smallAlloc(slen);
+
+    if (path != NULL)
+    {
+        SymlinkFilterData *data = (SymlinkFilterData *) _data;
+        const DirHandle *dh = data->dirhandle;
+        PHYSFS_Stat statbuf;
+
+        sprintf(path, "%s%s%s", trimmedDir, *trimmedDir ? "/" : "", fname);
+        if (dh->funcs->stat(dh->opaque, path, &statbuf))
+        {
+            /* Pass it on to the application if it's not a symlink. */
+            if (statbuf.filetype != PHYSFS_FILETYPE_SYMLINK)
+                data->callback(data->callbackData, origdir, fname);
+        } /* if */
+
+        __PHYSFS_smallFree(path);
+    } /* if */
+} /* enumCallbackFilterSymLinks */
+
+
 /* !!! FIXME: this should report error conditions. */
 void PHYSFS_enumerateFilesCallback(const char *_fname,
                                    PHYSFS_EnumFilesCallback callback,
@@ -2078,10 +2300,17 @@
     if (sanitizePlatformIndependentPath(_fname, fname))
     {
         DirHandle *i;
-        int noSyms;
+        SymlinkFilterData filterdata;
 
         __PHYSFS_platformGrabMutex(stateLock);
-        noSyms = !allowSymLinks;
+
+        if (!allowSymLinks)
+        {
+            memset(&filterdata, '\0', sizeof (filterdata));
+            filterdata.callback = callback;
+            filterdata.callbackData = data;
+        } /* if */
+
         for (i = searchPath; i != NULL; i = i->next)
         {
             char *arcfname = fname;
@@ -2090,8 +2319,18 @@
 
             else if (verifyPath(i, &arcfname, 0))
             {
-                i->funcs->enumerateFiles(i->opaque, arcfname, noSyms,
-                                         callback, _fname, data);
+                if ((!allowSymLinks) && (i->funcs->info.supportsSymlinks))
+                {
+                    filterdata.dirhandle = i;
+                    i->funcs->enumerateFiles(i->opaque, arcfname,
+                                             enumCallbackFilterSymLinks,
+                                             _fname, &filterdata);
+                } /* if */
+                else
+                {
+                    i->funcs->enumerateFiles(i->opaque, arcfname,
+                                             callback, _fname, data);
+                } /* else */
             } /* else if */
         } /* for */
         __PHYSFS_platformReleaseMutex(stateLock);
@@ -2212,20 +2451,19 @@
 
     if (sanitizePlatformIndependentPath(_fname, fname))
     {
-        int fileExists = 0;
         DirHandle *i = NULL;
         PHYSFS_Io *io = NULL;
 
         __PHYSFS_platformGrabMutex(stateLock);
 
-        GOTO_IF_MACRO(!searchPath, PHYSFS_ERR_NO_SUCH_PATH, openReadEnd);
-
-        for (i = searchPath; (i != NULL) && (!fileExists); i = i->next)
+        GOTO_IF_MACRO(!searchPath, PHYSFS_ERR_NOT_FOUND, openReadEnd);
+
+        for (i = searchPath; i != NULL; i = i->next)
         {
             char *arcfname = fname;
             if (verifyPath(i, &arcfname, 0))
             {
-                io = i->funcs->openRead(i->opaque, arcfname, &fileExists);
+                io = i->funcs->openRead(i->opaque, arcfname);
                 if (io)
                     break;
             } /* if */
@@ -2330,20 +2568,21 @@
         memcpy(buffer, fh->buffer + fh->bufpos, (size_t) len);
         fh->bufpos += (PHYSFS_uint32) len;
         return (PHYSFS_sint64) len;
-    } /* else if */
-
-    if (buffered > 0) /* partially in the buffer... */
+    } /* if */
+
+    else if (buffered > 0) /* partially in the buffer... */
     {
         memcpy(buffer, fh->buffer + fh->bufpos, (size_t) buffered);
         buffer = ((PHYSFS_uint8 *) buffer) + buffered;
         len -= buffered;
         retval = buffered;
-        fh->buffill = fh->bufpos = 0;
     } /* if */
 
     /* if you got here, the buffer is drained and we still need bytes. */
     assert(len > 0);
 
+    fh->buffill = fh->bufpos = 0;
+
     io = fh->io;
     if (len >= fh->bufsize)  /* need more than the buffer takes. */
     {
@@ -2582,7 +2821,7 @@
     rc = io->write(io, fh->buffer + fh->bufpos, fh->buffill - fh->bufpos);
     BAIL_IF_MACRO(rc <= 0, ERRPASS, 0);
     fh->bufpos = fh->buffill = 0;
-    return io->flush(io);
+    return io->flush ? io->flush(io) : 1;
 } /* PHYSFS_flush */
 
 
@@ -2592,11 +2831,11 @@
     char *fname;
     size_t len;
 
-    BAIL_IF_MACRO(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, -1);
-    BAIL_IF_MACRO(!stat, PHYSFS_ERR_INVALID_ARGUMENT, -1);
+    BAIL_IF_MACRO(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0);
+    BAIL_IF_MACRO(!stat, PHYSFS_ERR_INVALID_ARGUMENT, 0);
     len = strlen(_fname) + 1;
     fname = (char *) __PHYSFS_smallAlloc(len);
-    BAIL_IF_MACRO(!fname, PHYSFS_ERR_OUT_OF_MEMORY, -1);
+    BAIL_IF_MACRO(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);
 
     /* set some sane defaults... */
     stat->filesize = -1;
@@ -2634,7 +2873,9 @@
                     /* !!! FIXME: this test is wrong and should be elsewhere. */
                     stat->readonly = !(writeDir &&
                                  (strcmp(writeDir->dirName, i->dirName) == 0));
-                    retval = i->funcs->stat(i->opaque, arcfname, &exists, stat);
+                    retval = i->funcs->stat(i->opaque, arcfname, stat);
+                    if ((retval) || (currentErrorCode() != PHYSFS_ERR_NOT_FOUND))
+                        exists = 1;
                 } /* else if */
             } /* for */
             __PHYSFS_platformReleaseMutex(stateLock);