Tons of updates. Mostly implemented. Mostly compiling.
authorRyan C. Gordon <icculus@icculus.org>
Sat, 07 Jul 2001 03:52:43 +0000
changeset 15 418eacc97ac8
parent 14 7d822f0d5f57
child 16 702a5d1f34fd
Tons of updates. Mostly implemented. Mostly compiling.
physfs.c
physfs.h
physfs_internal.h
unix.c
--- a/physfs.c	Sat Jul 07 03:47:13 2001 +0000
+++ b/physfs.c	Sat Jul 07 03:52:43 2001 +0000
@@ -25,16 +25,16 @@
     struct __PHYSFS_ERRMSGTYPE__ *next;
 } ErrMsg;
 
-typedef struct __PHYSFS_SEARCHDIRINFO__
+typedef struct __PHYSFS_DIRINFO__
 {
     char *dirName;
-    DirReader *reader;
-    struct __PHYSFS_SEARCHDIRINFO__ *next;
-} SearchDirInfo;
+    DirHandle *dirHandle;
+    struct __PHYSFS_DIRINFO__ *next;
+} DirInfo;
 
 typedef struct __PHYSFS_FILEHANDLELIST__
 {
-    FileHandle *handle;
+    PHYSFS_file *handle;
     struct __PHYSFS_FILEHANDLELIST__ *next;
 } FileHandleList;
 
@@ -73,12 +73,12 @@
 
 static int initialized = 0;
 static ErrMsg *errorMessages = NULL;
-static SearchDirInfo *searchPath = NULL;
+static DirInfo *searchPath = NULL;
+static DirInfo *writeDir = NULL;
 static FileHandleList *openWriteList = NULL;
 static FileHandleList *openReadList = NULL;
 static char *baseDir = NULL;
 static char *userDir = NULL;
-static char *writeDir = NULL;
 static int allowSymLinks = 0;
 
 
@@ -107,7 +107,12 @@
 
 void __PHYSFS_setError(const char *str)
 {
-    ErrMsg *err = findErrorForCurrentThread();
+    ErrMsg *err;
+
+    if (str == NULL)
+        return;
+
+    err = findErrorForCurrentThread();
 
     if (err == NULL)
     {
@@ -126,6 +131,19 @@
 } /* __PHYSFS_setError */
 
 
+static void freeErrorMessages(void)
+{
+    ErrMsg *i;
+    ErrMsg *next;
+
+    for (i = errorMessages; i != NULL; i = next)
+    {
+        next = i;
+        free(i);
+    } /* for */
+} /* freeErrorMessages */
+
+
 const char *PHYSFS_getLastError(void)
 {
     ErrMsg *err = findErrorForCurrentThread();
@@ -149,22 +167,88 @@
 } /* PHYSFS_getLinkedVersion */
 
 
-static const char *calculateUserDir(void)
+static DirHandle *openDirectory(const char *d, int forWriting)
+{
+    const DirFunctions **i;
+
+    for (i = dirFunctions; *i != NULL; i++)
+    {
+        if ((*i)->isArchive(d, forWriting))
+            return( (*i)->openArchive(d, forWriting) );
+    } /* for */
+
+    __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
+    return(NULL);
+} /* openDirectory */
+
+
+static DirInfo *buildDirInfo(const char *newDir, int forWriting)
+{
+    DirHandle *dirHandle = NULL;
+    DirInfo *di = NULL;
+
+    BAIL_IF_MACRO(newDir == NULL, ERR_INVALID_ARGUMENT, 0);
+
+    dirHandle = openDirectory(newDir, forWriting);
+    BAIL_IF_MACRO(dirHandle == NULL, NULL, 0);
+
+    di = (DirInfo *) malloc(sizeof (DirInfo));
+    if (di == NULL)
+        dirHandle->funcs->close(dirHandle);
+    BAIL_IF_MACRO(di == NULL, ERR_OUT_OF_MEMORY, 0);
+
+    di->dirName = (char *) malloc(strlen(newDir) + 1);
+    if (di->dirName == NULL)
+    {
+        free(di);
+        dirHandle->funcs->close(dirHandle);
+        __PHYSFS_setError(ERR_OUT_OF_MEMORY);
+        return(0);
+    } /* if */
+
+    di->next = NULL;
+    di->dirHandle = dirHandle;
+    strcpy(di->dirName, newDir);
+    return(di);
+} /* buildDirInfo */
+
+
+static int freeDirInfo(DirInfo *di, FileHandleList *openList)
+{
+    FileHandleList *i;
+
+    if (di == NULL)
+        return(1);
+
+    for (i = openList; i != NULL; i = i->next)
+    {
+        const DirHandle *h = ((FileHandle *) i->handle->opaque)->dirHandle;
+        BAIL_IF_MACRO(h == di->dirHandle, ERR_FILES_STILL_OPEN, 0);
+    } /* for */
+
+    di->dirHandle->funcs->close(di->dirHandle);
+    free(di->dirName);
+    free(di);
+    return(1);
+} /* freeDirInfo */
+
+
+static char *calculateUserDir(void)
 {
     char *retval = NULL;
     const char *str = NULL;
 
     str = __PHYSFS_platformGetUserDir();
     if (str != NULL)
-        retval = str;
+        retval = (char *) str;
     else
     {
         const char *dirsep = PHYSFS_getDirSeparator();
         const char *uname = __PHYSFS_platformGetUserName();
 
         str = (uname != NULL) ? uname : "default";
-        retval = malloc(strlen(baseDir) + strlen(str) +
-                        (strlen(dirsep) * 2) + 6);
+        retval = (char *) malloc(strlen(baseDir) + strlen(str) +
+                                 (strlen(dirsep) * 2) + 6);
 
         if (retval == NULL)
             __PHYSFS_setError(ERR_OUT_OF_MEMORY);
@@ -172,21 +256,26 @@
             sprintf(retval, "%s%susers%s%s", baseDir, dirsep, dirsep, str);
 
         if (uname != NULL)
-            free(uname);
+            free((void *) uname);
     } /* else */
 
     return(retval);
 } /* calculateUserDir */
 
 
+static char *calculateBaseDir(const char *argv0)
+{
+assert(0); return(NULL);
+} /* calculateBaseDir */
+
+
 int PHYSFS_init(const char *argv0)
 {
     BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0);
     BAIL_IF_MACRO(argv0 == NULL, ERR_INVALID_ARGUMENT, 0);
 
     baseDir = calculateBaseDir(argv0);
-    if (baseDir == NULL)
-        return(0);
+    BAIL_IF_MACRO(baseDir == NULL, NULL, 0);
 
     userDir = calculateUserDir();
     if (userDir == NULL)
@@ -201,42 +290,35 @@
 } /* PHYSFS_init */
 
 
-static void freeSearchDir(SearchDirInfo *sdi)
-{
-    FileHandleList *i;
-
-    assert(sdi != NULL);
-    for (i = openReadList; i != NULL; i = i->next)
-    {
-        BAIL_IF_MACRO(i->handle->dirReader == sdi->reader,
-                        ERR_FILES_OPEN_READ, 0);
-    } /* for */
-
-    sdi->reader->close(sdi->reader);
-    free(sdi->dirName);
-    free(sdi);
-} /* freeSearchDir */
-
-
-static void closeFileHandleList(FileHandleList **list)
+static int closeFileHandleList(FileHandleList **list)
 {
     FileHandleList *i;
     FileHandleList *next = NULL;
+    FileHandle *h;
 
     for (i = *list; i != NULL; i = next)
     {
         next = i->next;
-        i->handle->close(i->handle);
+        h = (FileHandle *) (i->handle->opaque);
+        if (!h->funcs->close(i->handle->opaque))
+        {
+            *list = i;
+            return(0);
+        } /* if */
+
+        free(i->handle);
+        free(i);
     } /* for */
 
     *list = NULL;
-} /* closeAllFiles */
+    return(1);
+} /* closeFileHandleList */
 
 
 static void freeSearchPath(void)
 {
-    SearchDirInfo *i;
-    SearchDirInfo *next = NULL;
+    DirInfo *i;
+    DirInfo *next = NULL;
 
     closeFileHandleList(&openReadList);
 
@@ -245,19 +327,20 @@
         for (i = searchPath; i != NULL; i = next)
         {
             next = i;
-            freeSearchDir(i);
+            freeDirInfo(i, openReadList);
         } /* for */
         searchPath = NULL;
     } /* if */
 } /* freeSearchPath */
 
 
-void PHYSFS_deinit(void)
+int PHYSFS_deinit(void)
 {
     BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
 
     closeFileHandleList(&openWriteList);
-    PHYSFS_setWriteDir(NULL);
+    BAIL_IF_MACRO(!PHYSFS_setWriteDir(NULL), ERR_FILES_STILL_OPEN, 0);
+
     freeSearchPath();
     freeErrorMessages();
 
@@ -297,7 +380,7 @@
 
 const char *PHYSFS_getDirSeparator(void)
 {
-    return(__PHYSFS_pathSeparator);
+    return(__PHYSFS_platformDirSeparator);
 } /* PHYSFS_getDirSeparator */
 
 
@@ -321,96 +404,55 @@
 
 const char *PHYSFS_getWriteDir(void)
 {
-    return(writeDir);
+    if (writeDir == NULL)
+        return(NULL);
+
+    return(writeDir->dirName);
 } /* PHYSFS_getWriteDir */
 
 
 int PHYSFS_setWriteDir(const char *newDir)
 {
-    BAIL_IF_MACRO(openWriteList != NULL, ERR_FILES_OPEN_WRITE, 0);
-
     if (writeDir != NULL)
     {
-        free(writeDir);
+        BAIL_IF_MACRO(!freeDirInfo(writeDir, openWriteList), NULL, 0);
         writeDir = NULL;
     } /* if */
 
     if (newDir != NULL)
     {
-        BAIL_IF_MACRO(!createDirs_dependent(newDir), ERR_NO_DIR_CREATE, 0);
-
-        writeDir = malloc(strlen(newDir) + 1);
-        BAIL_IF_MACRO(writeDir == NULL, ERR_OUT_OF_MEMORY, 0);
-
-        strcpy(writeDir, newDir);
+        writeDir = buildDirInfo(newDir, 1);
+        return(writeDir != NULL);
     } /* if */
 
     return(1);
 } /* PHYSFS_setWriteDir */
 
 
-static DirReader *getDirReader(const char *d)
-{
-    DirFunctions **i;
-
-    for (i = dirFunctions; *i != NULL; i++)
-    {
-        if ((*i)->isArchive(d))
-            return( (*i)->openArchive(d) );
-    } /* for */
-
-    __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
-    return(NULL);
-} /* getDirReader */
-
-
 int PHYSFS_addToSearchPath(const char *newDir, int appendToPath)
 {
-    char *str = NULL;
-    SearchDirInfo *sdi = NULL;
-    DirReader *dirReader = NULL;
-
-    BAIL_IF_MACRO(newDir == NULL, ERR_INVALID_ARGUMENT, 0);
-
-    reader = getDirReader(newDir); /* This sets the error message. */
-    if (reader == NULL)
-        return(0);
+    DirInfo *di = buildDirInfo(newDir, 0);
 
-    sdi = (SearchDirInfo *) malloc(sizeof (SearchDirInfo));
-    if (sdi == NULL)
-        reader->close(reader);
-    BAIL_IF_MACRO(sdi == NULL, ERR_OUT_OF_MEMORY, 0);
-
-    sdi->dirName = (char *) malloc(strlen(newDir) + 1);
-    if (sdi->dirName == NULL)
-    {
-        free(sdi);
-        reader->close(reader);
-        __PHYSFS_setError(ERR_OUT_OF_MEMORY);
-        return(0);
-    } /* if */
-
-    sdi->dirReader = dirReader;
-    strcpy(sdi->dirName, newDir);
+    BAIL_IF_MACRO(di == NULL, NULL, 0);
 
     if (appendToPath)
     {
-        sdi->next = searchPath;
-        searchPath = sdi;
+        di->next = searchPath;
+        searchPath = di;
     } /* if */
     else
     {
-        SearchDirInfo *i = searchPath;
-        SearchDirInfo *prev = NULL;
+        DirInfo *i = searchPath;
+        DirInfo *prev = NULL;
 
-        sdi->next = NULL;
+        di->next = NULL;
         while (i != NULL)
             prev = i;
 
         if (prev == NULL)
-            searchPath = sdi;
+            searchPath = di;
         else
-            prev->next = sdi;
+            prev->next = di;
     } /* else */
 
     return(1);
@@ -419,9 +461,9 @@
 
 int PHYSFS_removeFromSearchPath(const char *oldDir)
 {
-    SearchDirInfo *i;
-    SearchDirInfo *prev = NULL;
-    SearchDirInfo *next = NULL;
+    DirInfo *i;
+    DirInfo *prev = NULL;
+    DirInfo *next = NULL;
 
     BAIL_IF_MACRO(oldDir == NULL, ERR_INVALID_ARGUMENT, 0);
 
@@ -430,8 +472,7 @@
         if (strcmp(i->dirName, oldDir) == 0)
         {
             next = i->next;
-            if (!freeSearchDir(i))
-                return(0);
+            BAIL_IF_MACRO(!freeDirInfo(i, openReadList), NULL, 0);
 
             if (prev == NULL)
                 searchPath = next;
@@ -452,7 +493,7 @@
 {
     int count = 1;
     int x;
-    SearchDirInfo *i;
+    DirInfo *i;
     char **retval;
 
     for (i = searchPath; i != NULL; i = i->next)
@@ -501,13 +542,12 @@
     BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0);
     sprintf(str, "%s%s.%s", userdir, dirsep, appName);
     rc = PHYSFS_setWriteDir(str);
-    if (!rc)
-        return(0);  /* error set by PHYSFS_setWriteDir() ... */
+    BAIL_IF_MACRO(!rc, NULL, 0);
 
         /* Put write dir related dirs on search path... */
     PHYSFS_addToSearchPath(str, 1);
     PHYSFS_mkdir(appName); /* don't care if this fails. */
-    strcat(str, dirSep);
+    strcat(str, dirsep);
     strcat(str, appName);
     PHYSFS_addToSearchPath(str, 1);
     free(str);
@@ -530,12 +570,12 @@
         char **i;
         for (i = cds; *i != NULL; i++)
         {
-            PHYSFS_addToSearchPath(*i);
+            PHYSFS_addToSearchPath(*i, 1);
             str = malloc(strlen(*i) + strlen(appName) + strlen(dirsep) + 1);
             if (str != NULL)
             {
                 sprintf(str, "%s%s%s", *i, dirsep, appName);
-                PHYSFS_addToSearchPath(str);
+                PHYSFS_addToSearchPath(str, 1);
                 free(str);
             } /* if */
         } /* for */
@@ -563,7 +603,7 @@
                     if (str != NULL)
                     {
                         sprintf(str, "%s%s%s", d, dirsep, *i);
-                        PHYSFS_addToSearchPath(d, str);
+                        PHYSFS_addToSearchPath(str, archivesFirst == 0);
                         free(str);
                     } /* if */
                 } /* if */
@@ -577,11 +617,16 @@
 } /* PHYSFS_setSaneConfig */
 
 
+void PHYSFS_permitSymbolicLinks(int allow)
+{
+    allowSymLinks = allow;
+} /* PHYSFS_permitSymbolicLinks */
+
+
 /* string manipulation in C makes my ass itch. */
-/*  be sure to free this crap after you're done with it. */
-static char *convertToDependentNotation(const char *prepend,
-                                        const char *dirName,
-                                        const char *append)
+char *__PHYSFS_convertToDependentNotation(const char *prepend,
+                                          const char *dirName,
+                                          const char *append)
 {
     const char *dirsep = PHYSFS_getDirSeparator();
     int sepsize = strlen(dirsep);
@@ -590,7 +635,7 @@
     char *i2;
     size_t allocSize;
 
-    allocSize = strlen(dirName) + strlen(writeDir) + sepsize + 1;
+    allocSize = strlen(dirName) + 1;
     if (prepend != NULL)
         allocSize += strlen(prepend) + sepsize;
     if (append != NULL)
@@ -599,11 +644,16 @@
         /* make sure there's enough space if the dir separator is bigger. */
     if (sepsize > 1)
     {
-        for (str = dirName; *str != '\0'; str++)
+        str = (char *) dirName;
+        do
         {
-            if (*str == '/')
+            str = strchr(str, '/');
+            if (str != NULL)
+            {
                 allocSize += (sepsize - 1);
-        } /* for */
+                str++;
+            } /* if */
+        } while (str != NULL);
     } /* if */
 
     str = (char *) malloc(allocSize);
@@ -616,7 +666,7 @@
         strcat(str, dirsep);
     } /* if */
 
-    for (i1 = dirName, i2 = str + strlen(str); *i1 != '\0'; i1++, i2++)
+    for (i1 = (char *) dirName, i2 = str + strlen(str); *i1; i1++, i2++)
     {
         if (*i1 == '/')
         {
@@ -637,78 +687,123 @@
     } /* if */
 
     return(str);
-} /* convertToDependentNotation */
+} /* __PHYSFS_convertToDependentNotation */
+
+
+int __PHYSFS_verifySecurity(DirHandle *h, const char *fname)
+{
+    int retval = 1;
+    char *start;
+    char *end;
+    char *str;
+
+    start = str = malloc(strlen(fname) + 1);
+    BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0);
+    strcpy(str, fname);
+
+    while (1)
+    {
+        end = strchr(start, '/');
+        if (end != NULL)
+            *end = '\0';
+
+        if ( (strcmp(start, ".") == 0) ||
+             (strcmp(start, "..") == 0) ||
+             (strchr(start, ':') != NULL) )
+        {
+            __PHYSFS_setError(ERR_INSECURE_FNAME);
+            retval = 0;
+            break;
+        } /* if */
+
+        if ((!allowSymLinks) && (h->funcs->isSymLink(h, str)))
+        {
+            __PHYSFS_setError(ERR_SYMLINK_DISALLOWED);
+            retval = 0;
+            break;
+        } /* if */
+
+        if (end == NULL)
+            break;
+
+        *end = '/';
+        start = end + 1;
+    } /* while */
+
+    free(str);
+    return(retval);
+} /* __PHYSFS_verifySecurity */
 
 
 int PHYSFS_mkdir(const char *dirName)
 {
+    DirHandle *h;
     char *str;
-    int rc;
+    char *start;
+    char *end;
+    int retval = 0;
 
-    BAIL_IF_MACRO(writeDir == NULL, ERR_NO_WRITE_DIR, NULL);
+    BAIL_IF_MACRO(writeDir == NULL, ERR_NO_WRITE_DIR, 0);
+    h = writeDir->dirHandle;
+    BAIL_IF_MACRO(h->funcs->mkdir == NULL, ERR_NOT_SUPPORTED, 0);
+    BAIL_IF_MACRO(!__PHYSFS_verifySecurity(h, dirName), NULL, 0);
+
+    start = str = malloc(strlen(dirName) + 1);
+    BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0);
+    strcpy(str, dirName);
 
-    str = convertToDependentNotation(writeDir, dirName, NULL);
-    if (str == NULL)  /* __PHYSFS_setError is called in convert call. */
-        return(0);
+    while (1)
+    {
+        end = strchr(start, '/');
+        if (end != NULL)
+            *end = '\0';
+
+        retval = h->funcs->mkdir(h, str);
+        if (!retval)
+            break;
 
-    rc = createDirs_dependent(str);
+        if (end == NULL)
+            break;
+
+        *end = '/';
+        start = end + 1;
+    } /* while */
+
     free(str);
-    return(rc);
+    return(retval);
 } /* PHYSFS_mkdir */
 
 
-int PHYSFS_delete(const char *filename)
+int PHYSFS_delete(const char *fname)
 {
-    char *str;
-    int rc;
-
-    BAIL_IF_MACRO(writeDir == NULL, ERR_NO_WRITE_DIR, NULL);
-
-    str = convertToDependentNotation(writeDir, fileName, NULL);
-    if (str == NULL)  /* __PHYSFS_setError is called in convert call. */
-        return(0);
-
-    rc = remove(str);
-    free(str);
-
-    rc = (rc == 0);
-    if (!rc)
-        __PHYSFS_setError(strerror(errno));
-
-    return(rc);
+    DirHandle *h;
+    BAIL_IF_MACRO(writeDir == NULL, ERR_NO_WRITE_DIR, 0);
+    h = writeDir->dirHandle;
+    BAIL_IF_MACRO(h->funcs->remove == NULL, ERR_NOT_SUPPORTED, 0);
+    BAIL_IF_MACRO(!__PHYSFS_verifySecurity(h, fname), NULL, 0);
+    return(h->funcs->remove(h, fname));
 } /* PHYSFS_delete */
 
 
-void PHYSFS_permitSymbolicLinks(int allow)
-{
-    allowSymLinks = allow;
-} /* PHYSFS_permitSymbolicLinks */
-
-
-/**
- * Figure out where in the search path a file resides. The file is specified
- *  in platform-independent notation. The returned filename will be the
- *  element of the search path where the file was found, which may be a
- *  directory, or an archive. Even if there are multiple matches in different
- *  parts of the search path, only the first one found is used, just like
- *  when opening a file.
- *
- * So, if you look for "maps/level1.map", and C:\mygame is in your search
- *  path and C:\mygame\maps\level1.map exists, then "C:\mygame" is returned.
- *
- * If a match is a symbolic link, and you've not explicitly permitted symlinks,
- *  then it will be ignored, and the search for a match will continue.
- *
- *     @param filename file to look for.
- *    @return READ ONLY string of element of search path containing the
- *             the file in question. NULL if not found.
- */
 const char *PHYSFS_getRealDir(const char *filename)
 {
+    DirInfo *i;
+
+    for (i = searchPath; i != NULL; i = i->next)
+    {
+        DirHandle *h = i->dirHandle;
+        if (__PHYSFS_verifySecurity(h, filename))
+        {
+            if (h->funcs->exists(h, filename))
+                return(i->dirName);
+        } /* if */
+    } /* for */
+
+    return(NULL);
 } /* PHYSFS_getRealDir */
 
 
-static void countList(LinkedStringList *list)
+static int countList(LinkedStringList *list)
 {
     int retval = 0;
     LinkedStringList *i;
@@ -797,16 +892,19 @@
 
 char **PHYSFS_enumerateFiles(const char *path)
 {
-    SearchDirInfo *i;
+    DirInfo *i;
     char **retval = NULL;
     LinkedStringList *rc;
     LinkedStringList *finalList = NULL;
 
     for (i = searchPath; i != NULL; i = i->next)
     {
-        assert(i->reader->funcs->enumerateFiles != NULL);
-        rc = i->reader->funcs->enumerateFiles(path);
-        interpolateStringLists(&finalList, rc);
+        DirHandle *h = i->dirHandle;
+        if (__PHYSFS_verifySecurity(h, path))
+        {
+            rc = h->funcs->enumerateFiles(h, path);
+            interpolateStringLists(&finalList, rc);
+        } /* if */
     } /* for */
 
     retval = convertStringListToPhysFSList(finalList);
@@ -814,6 +912,50 @@
 } /* PHYSFS_enumerateFiles */
 
 
+int PHYSFS_exists(const char *fname)
+{
+    return(PHYSFS_getRealDir(fname) != NULL);
+} /* PHYSFS_exists */
+
+
+int PHYSFS_isDirectory(const char *fname)
+{
+    DirInfo *i;
+
+    for (i = searchPath; i != NULL; i = i->next)
+    {
+        DirHandle *h = i->dirHandle;
+        if (__PHYSFS_verifySecurity(h, fname))
+        {
+            if (h->funcs->exists(h, fname))
+                return(h->funcs->isDirectory(h, fname));
+        } /* if */
+    } /* for */
+
+    return(0);
+} /* PHYSFS_isDirectory */
+
+
+int PHYSFS_isSymbolicLink(const char *fname)
+{
+    DirInfo *i;
+
+    if (!allowSymLinks)
+        return(0);
+
+    for (i = searchPath; i != NULL; i = i->next)
+    {
+        DirHandle *h = i->dirHandle;
+        if (__PHYSFS_verifySecurity(h, fname))
+        {
+            if (h->funcs->exists(h, fname))
+                return(h->funcs->isSymLink(h, fname));
+        } /* if */
+    } /* for */
+
+    return(0);
+} /* PHYSFS_isSymbolicLink */
+
 /**
  * Open a file for writing, in platform-independent notation and in relation
  *  to the write path as the root of the writable filesystem. The specified
@@ -826,6 +968,7 @@
  */
 PHYSFS_file *PHYSFS_openWrite(const char *filename)
 {
+return NULL;
 } /* PHYSFS_openWrite */
 
 
@@ -842,6 +985,7 @@
  */
 PHYSFS_file *PHYSFS_openAppend(const char *filename)
 {
+return NULL;
 } /* PHYSFS_openAppend */
 
 
@@ -857,44 +1001,31 @@
  */
 PHYSFS_file *PHYSFS_openRead(const char *filename)
 {
+return NULL;
 } /* PHYSFS_openRead */
 
 
-/**
- * Close a PhysicsFS filehandle. This call is capable of failing if the
- *  operating system was buffering writes to this file, and (now forced to
- *  write those changes to physical media) can not store the data for any
- *  reason. In such a case, the filehandle stays open. A well-written program
- *  should ALWAYS check the return value from the close call in addition to
- *  every writing call!
- *
- *   @param handle handle returned from PHYSFS_open*().
- *  @return nonzero on success, zero on error. Specifics of the error can be
- *          gleaned from PHYSFS_getLastError().
- */
 int PHYSFS_close(PHYSFS_file *handle)
 {
     FileHandle *h = (FileHandle *) handle->opaque;
     FileHandleList *i;
-    FileHandleList **lists[] = { &openWriteList, &openReadList, NULL };
+    FileHandleList *prev;
+    FileHandleList **_lists[] = { &openWriteList, &openReadList, NULL };
+    FileHandleList ***lists = _lists;  /* gay. */
     int rc;
 
-    assert(h != NULL);
-    assert(h->funcs != NULL);
-    assert(h->funcs->close != NULL);
-
     while (lists != NULL)
     {
-        for (i = *(*lists); i != NULL; i = i->next)
+        for (i = *(*lists), prev = NULL; i != NULL; prev = i, i = i->next)
         {
-            if (i->handle == h)
+            if (((FileHandle *) i->handle->opaque) == h)
             {
-                rc = h->close(h);
+                rc = h->funcs->close(h);
                 if (!rc)
                     return(0);
 
                 if (prev == NULL)
-                    *lists = i->next;
+                    *(*lists) = i->next;
                 else
                     prev->next = i->next;
                 free(i);
@@ -905,7 +1036,8 @@
         lists++;
     } /* while */
 
-    assert(0);  /* shouldn't EVER hit this. */
+    __PHYSFS_setError(ERR_NOT_A_HANDLE);
+    return(0);
 } /* PHYSFS_close */
 
 
@@ -960,5 +1092,6 @@
     return(h->funcs->seek(h, pos));
 } /* PHYSFS_seek */
 
+
 /* end of physfs.c ... */
 
--- a/physfs.h	Sat Jul 07 03:47:13 2001 +0000
+++ b/physfs.h	Sat Jul 07 03:52:43 2001 +0000
@@ -211,16 +211,22 @@
  * Shutdown PhysicsFS. This closes any files opened via PhysicsFS, blanks the
  *  search/write paths, frees memory, and invalidates all of your handles.
  *
- * Once deinitialized, PHYSFS_init() can be called again to restart the
- *  subsystem.
+ * Note that this call can FAIL if there's a file open for writing that
+ *  refuses to close (for example, the underlying operating system was
+ *  buffering writes to network filesystem, and the fileserver has crashed,
+ *  or a hard drive has failed, etc). It is usually best to close all write
+ *  handles yourself before calling this function, so that you can gracefully
+ *  handle a specific failure.
  *
- * This function can be used with atexit(), if you feel it's prudent to do so.
+ * Once successfully deinitialized, PHYSFS_init() can be called again to
+ *  restart the subsystem. All defaults API states are restored at this
+ *  point.
  *
  *  @return nonzero on success, zero on error. Specifics of the error can be
  *          gleaned from PHYSFS_getLastError(). If failure, state of PhysFS is
  *          undefined, and probably badly screwed up.
  */
-void PHYSFS_deinit(void);
+int PHYSFS_deinit(void);
 
 
 /**
@@ -287,6 +293,30 @@
 
 
 /**
+ * Enable symbolic links. Some physical filesystems and archives contain
+ *  files that are just pointers to other files. On the physical filesystem,
+ *  opening such a link will (transparently) open the file that is pointed to.
+ *
+ * By default, PhysicsFS will check if a file is really a symlink during open
+ *  calls and fail if it is. Otherwise, the link could take you outside the
+ *  write and search paths, and compromise security.
+ *
+ * If you want to take that risk, call this function with a non-zero parameter.
+ *  Note that this is more for sandboxing a program's scripting language, in
+ *  case untrusted scripts try to compromise the system. Generally speaking,
+ *  a user could very well have a legitimate reason to set up a symlink, so
+ *  unless you feel there's a specific danger in allowing them, you should
+ *  permit them.
+ *
+ * Symbolic link permission can be enabled or disabled at any time, and is
+ *  disabled by default.
+ *
+ *   @param allow nonzero to permit symlinks, zero to deny linking.
+ */
+void PHYSFS_permitSymbolicLinks(int allow);
+
+
+/**
  * Get an array of dirs to available CD-ROM drives.
  *
  * The dirs returned are platform-dependent ("D:\" on Win32, "/cdrom" or
@@ -529,30 +559,6 @@
 
 
 /**
- * Enable symbolic links. Some physical filesystems and archives contain
- *  files that are just pointers to other files. On the physical filesystem,
- *  opening such a link will (transparently) open the file that is pointed to.
- *
- * By default, PhysicsFS will check if a file is really a symlink during open
- *  calls and fail if it is. Otherwise, the link could take you outside the
- *  write and search paths, and compromise security.
- *
- * If you want to take that risk, call this function with a non-zero parameter.
- *  Note that this is more for sandboxing a program's scripting language, in
- *  case untrusted scripts try to compromise the system. Generally speaking,
- *  a user could very well have a legitimate reason to set up a symlink, so
- *  unless you feel there's a specific danger in allowing them, you should
- *  permit them.
- *
- * Symbolic link permission can be enabled or disabled at any time, and is
- *  disabled by default.
- *
- *   @param allow nonzero to permit symlinks, zero to deny linking.
- */
-void PHYSFS_permitSymbolicLinks(int allow);
-
-
-/**
  * Figure out where in the search path a file resides. The file is specified
  *  in platform-independent notation. The returned filename will be the
  *  element of the search path where the file was found, which may be a
@@ -563,8 +569,9 @@
  * So, if you look for "maps/level1.map", and C:\mygame is in your search
  *  path and C:\mygame\maps\level1.map exists, then "C:\mygame" is returned.
  *
- * If a match is a symbolic link, and you've not explicitly permitted symlinks,
- *  then it will be ignored, and the search for a match will continue.
+ * If a any part of a match is a symbolic link, and you've not explicitly
+ *  permitted symlinks, then it will be ignored, and the search for a match
+ *  will continue.
  *
  *     @param filename file to look for.
  *    @return READ ONLY string of element of search path containing the
@@ -613,11 +620,57 @@
 
 
 /**
+ * Determine if there is an entry anywhere in the search path by the
+ *  name of (fname).
+ *
+ * Note that entries that are symlinks are ignored if
+ *  PHYSFS_permitSymbolicLinks(1) hasn't been called, so you
+ *  might end up further down in the search path than expected.
+ *
+ *    @param fname filename in platform-independent notation.
+ *   @return non-zero if filename exists. zero otherwise.
+ */
+int PHYSFS_exists(const char *fname);
+
+
+/**
+ * Determine if the first occurence of (fname) in the search path is
+ *  really a directory entry.
+ *
+ * Note that entries that are symlinks are ignored if
+ *  PHYSFS_permitSymbolicLinks(1) hasn't been called, so you
+ *  might end up further down in the search path than expected.
+ *
+ *    @param fname filename in platform-independent notation.
+ *   @return non-zero if filename exists and is a directory.  zero otherwise.
+ */
+int PHYSFS_isDirectory(const char *fname);
+
+
+/**
+ * Determine if the first occurence of (fname) in the search path is
+ *  really a symbolic link.
+ *
+ * Note that entries that are symlinks are ignored if
+ *  PHYSFS_permitSymbolicLinks(1) hasn't been called, and as such,
+ *  this function will always return 0 in that case.
+ *
+ *    @param fname filename in platform-independent notation.
+ *   @return non-zero if filename exists and is a symlink.  zero otherwise.
+ */
+int PHYSFS_isSymbolicLink(const char *fname);
+
+
+/**
  * Open a file for writing, in platform-independent notation and in relation
  *  to the write dir as the root of the writable filesystem. The specified
  *  file is created if it doesn't exist. If it does exist, it is truncated to
  *  zero bytes, and the writing offset is set to the start.
  *
+ * Note that entries that are symlinks are ignored if
+ *  PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a
+ *  symlink with this function will fail in such a case.
+ *
  *   @param filename File to open.
  *  @return A valid PhysicsFS filehandle on success, NULL on error. Specifics
  *           of the error can be gleaned from PHYSFS_getLastError().
@@ -632,6 +685,10 @@
  *  is set to the end of the file, so the first write will be the byte after
  *  the end.
  *
+ * Note that entries that are symlinks are ignored if
+ *  PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a
+ *  symlink with this function will fail in such a case.
+ *
  *   @param filename File to open.
  *  @return A valid PhysicsFS filehandle on success, NULL on error. Specifics
  *           of the error can be gleaned from PHYSFS_getLastError().
@@ -645,6 +702,10 @@
  *  abstract filehandle is associated with it, and reading may be done.
  *  The reading offset is set to the first byte of the file.
  *
+ * Note that entries that are symlinks are ignored if
+ *  PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a
+ *  symlink with this function will fail in such a case.
+ *
  *   @param filename File to open.
  *  @return A valid PhysicsFS filehandle on success, NULL on error. Specifics
  *           of the error can be gleaned from PHYSFS_getLastError().
--- a/physfs_internal.h	Sat Jul 07 03:47:13 2001 +0000
+++ b/physfs_internal.h	Sat Jul 07 03:52:43 2001 +0000
@@ -14,7 +14,7 @@
 #error Do not include this header from your applications.
 #endif
 
-struct __PHYSFS_DIRREADER__;
+struct __PHYSFS_DIRHANDLE__;
 struct __PHYSFS_FILEFUNCTIONS__;
 
 typedef struct __PHYSFS_FILEHANDLE__
@@ -27,7 +27,7 @@
         /*
          * This should be the DirHandle that created this FileHandle.
          */
-    const struct __PHYSFS_DIRREADER__ *dirReader;
+    const struct __PHYSFS_DIRHANDLE__ *dirHandle;
 
         /*
          * Pointer to the file i/o functions for this filehandle.
@@ -40,6 +40,9 @@
 {
         /*
          * Read more from the file.
+         * Returns number of objects of (objSize) bytes read from file, -1
+         *  if complete failure.
+         * On failure, call __PHYSFS_setError().
          */
     int (*read)(FileHandle *handle, void *buffer,
                 unsigned int objSize, unsigned int objCount);
@@ -47,6 +50,9 @@
         /*
          * Write more to the file. Archives don't have to implement this.
          *  (Set it to NULL if not implemented).
+         * Returns number of objects of (objSize) bytes written to file, -1
+         *  if complete failure.
+         * On failure, call __PHYSFS_setError().
          */
     int (*write)(FileHandle *handle, void *buffer,
                  unsigned int objSize, unsigned int objCount);
@@ -64,17 +70,20 @@
         /*
          * Move read/write pointer to byte offset from start of file.
          *  Returns non-zero on success, zero on error.
+         * On failure, call __PHYSFS_setError().
          */
     int (*seek)(FileHandle *handle, int offset);
 
         /*
          * Close the file, and free the FileHandle structure (including "opaque").
+         *  returns non-zero on success, zero if can't close file.
+         * On failure, call __PHYSFS_setError().
          */
     int (*close)(FileHandle *handle);
 } FileFunctions;
 
 
-typedef struct __PHYSFS_DIRREADER__
+typedef struct __PHYSFS_DIRHANDLE__
 {
         /*
          * This is reserved for the driver to store information.
@@ -82,7 +91,7 @@
     void *opaque;
 
         /*
-         * Pointer to the directory i/o functions for this reader.
+         * Pointer to the directory i/o functions for this handle.
          */
     const struct __PHYSFS_DIRFUNCTIONS__ *funcs;
 } DirHandle;
@@ -104,16 +113,21 @@
         /*
          * Returns non-zero if (filename) is a valid archive that this
          *  driver can handle. This filename is in platform-dependent
-         *  notation.
+         *  notation. forWriting is non-zero if this is to be used for
+         *  the write directory, and zero if this is to be used for an
+         *  element of the search path.
          */
-    int (*isArchive)(const char *filename);
+    int (*isArchive)(const char *filename, int forWriting);
 
         /*
          * Return a DirHandle for dir/archive (name).
          *  This filename is in platform-dependent notation.
-         *  return (NULL) on error.
+         *  forWriting is non-zero if this is to be used for
+         *  the write directory, and zero if this is to be used for an
+         *  element of the search path.
+         * Returns NULL on failure, and calls __PHYSFS_setError().
          */
-    DirHandle *(*openArchive)(const char *name);
+    DirHandle *(*openArchive)(const char *name, int forWriting);
 
         /*
          * Returns a list of all files in dirname. Each element of this list
@@ -123,7 +137,13 @@
          * If you have a memory failure, return as much as you can.
          *  This dirname is in platform-independent notation.
          */
-    LinkedStringList **(*enumerateFiles)(DirHandle *r, const char *dirname);
+    LinkedStringList *(*enumerateFiles)(DirHandle *r, const char *dirname);
+
+        /*
+         * Returns non-zero if filename can be opened for reading.
+         *  This filename is in platform-independent notation.
+         */
+    int (*exists)(DirHandle *r, const char *name);
 
         /*
          * Returns non-zero if filename is really a directory.
@@ -138,14 +158,11 @@
     int (*isSymLink)(DirHandle *r, const char *name);
 
         /*
-         * Returns non-zero if filename can be opened for reading.
-         *  This filename is in platform-independent notation.
-         */
-    int (*isOpenable)(DirHandle *r, const char *name);
-
-        /*
          * Open file for reading, and return a FileHandle.
          *  This filename is in platform-independent notation.
+         * If you can't handle multiple opens of the same file,
+         *  you can opt to fail for the second call.
+         * Returns NULL on failure, and calls __PHYSFS_setError().
          */
     FileHandle *(*openRead)(DirHandle *r, const char *filename);
 
@@ -153,6 +170,9 @@
          * Open file for writing, and return a FileHandle.
          *  This filename is in platform-independent notation.
          *  This method may be NULL.
+         * If you can't handle multiple opens of the same file,
+         *  you can opt to fail for the second call.
+         * Returns NULL on failure, and calls __PHYSFS_setError().
          */
     FileHandle *(*openWrite)(DirHandle *r, const char *filename);
 
@@ -160,10 +180,34 @@
          * Open file for appending, and return a FileHandle.
          *  This filename is in platform-independent notation.
          *  This method may be NULL.
+         * If you can't handle multiple opens of the same file,
+         *  you can opt to fail for the second call.
+         * Returns NULL on failure, and calls __PHYSFS_setError().
          */
     FileHandle *(*openAppend)(DirHandle *r, const char *filename);
 
         /*
+         * Delete a file in the archive/directory.
+         *  Return non-zero on success, zero on failure.
+         *  This filename is in platform-independent notation.
+         *  This method may be NULL.
+         * On failure, call __PHYSFS_setError().
+         */
+    int (*remove)(DirHandle *r, const char *filename);
+
+        /*
+         * Create a directory in the archive/directory.
+         *  If the application is trying to make multiple dirs, PhysicsFS
+         *  will split them up into multiple calls before passing them to
+         *  your driver.
+         *  Return non-zero on success, zero on failure.
+         *  This filename is in platform-independent notation.
+         *  This method may be NULL.
+         * On failure, call __PHYSFS_setError().
+         */
+    int (*mkdir)(DirHandle *r, const char *filename);
+
+        /*
          * Close directories/archives, and free the handle, including
          *  the "opaque" entry. This should assume that it won't be called if
          *  there are still files open from this DirHandle.
@@ -176,25 +220,59 @@
 #define ERR_IS_INITIALIZED       "Already initialized"
 #define ERR_NOT_INITIALIZED      "Not initialized"
 #define ERR_INVALID_ARGUMENT     "Invalid argument"
-#define ERR_FILES_OPEN_READ      "Files still open for reading"
-#define ERR_FILES_OPEN_WRITE     "Files still open for writing"
+#define ERR_FILES_STILL_OPEN     "Files still open"
 #define ERR_NO_DIR_CREATE        "Failed to create directories"
 #define ERR_OUT_OF_MEMORY        "Out of memory"
 #define ERR_NOT_IN_SEARCH_PATH   "No such entry in search path"
 #define ERR_NOT_SUPPORTED        "Operation not supported"
 #define ERR_UNSUPPORTED_ARCHIVE  "Archive type unsupported"
+#define ERR_NOT_A_HANDLE         "Not a file handle"
+#define ERR_INSECURE_FNAME       "Insecure filename"
+#define ERR_SYMLINK_DISALLOWED   "Symbolic links are disabled"
+#define ERR_NO_WRITE_DIR         "Write directory is not set"
 
 
 /*
  * Call this to set the message returned by PHYSFS_getLastError().
  *  Please only use the ERR_* constants above, or add new constants to the
  *  above group, but I want these all in one place.
+ *
+ * Calling this with a NULL argument is a safe no-op.
  */
 void __PHYSFS_setError(const char *err);
 
 
+/*
+ * Convert (dirName) to platform-dependent notation, then prepend (prepend)
+ *  and append (append) to the converted string.
+ *
+ *  So, on Win32, calling:
+ *     __PHYSFS_convertToDependentNotation("C:\", "my/files", NULL);
+ *  ...will return the string "C:\my\files".
+ *
+ * This is a convenience function; you might want to hack something out that
+ *  is less generic (and therefore more efficient).
+ *
+ * Be sure to free() the return value when done with it.
+ */
+char *__PHYSFS_convertToDependentNotation(const char *prepend,
+                                          const char *dirName,
+                                          const char *append);
+
+/*
+ * Verify that (fname) (in platform-independent notation), in relation
+ *  to (h) is secure. That means that each element of fname is checked
+ *  for symlinks (if they aren't permitted). Also, elements such as
+ *  ".", "..", or ":" are flagged.
+ *
+ * Returns non-zero if string is safe, zero if there's a security issue.
+ *  PHYSFS_getLastError() will specify what was wrong.
+ */
+int __PHYSFS_verifySecurity(DirHandle *h, const char *fname);
+
+
 /* This gets used all over for lessening code clutter. */
-#define BAIL_IF_MACRO(c, e, r) if (c) { __PHYSFS_setError(e); return(r); }
+#define BAIL_IF_MACRO(c, e, r) if (c) { __PHYSFS_setError(e); return r; }
 
 
 
@@ -214,7 +292,7 @@
  * The dir separator; "/" on unix, "\\" on win32, ":" on MacOS, etc...
  *  Obviously, this isn't a function, but it IS a null-terminated string.
  */
-extern const char *__PHYSFS_PlatformDirSeparator;
+extern const char *__PHYSFS_platformDirSeparator;
 
 /*
  * Platform implementation of PHYSFS_getCdRomDirs()...
@@ -262,6 +340,11 @@
  */
 int __PHYSFS_platformIsSymlink(const char *fname);
 
+/*
+ * Return non-zero if filename (in platform-dependent notation) is a symlink.
+ */
+int __PHYSFS_platformIsDirectory(const char *fname);
+
 
 #ifdef __cplusplus
 extern "C" {
--- a/unix.c	Sat Jul 07 03:47:13 2001 +0000
+++ b/unix.c	Sat Jul 07 03:52:43 2001 +0000
@@ -14,7 +14,7 @@
 #include "physfs_internal.h"
 
 
-const char *__PHYSFS_PlatformDirSeparator = "/";
+const char *__PHYSFS_platformDirSeparator = "/";
 
 char **__PHYSFS_platformDetectAvailableCDs(void)
 {
@@ -27,6 +27,16 @@
 } /* __PHYSFS_platformCalcBaseDir */
 
 
+char *__PHYSFS_platformGetUserName(void)
+{
+} /* __PHYSFS_platformGetUserName */
+
+
+char *__PHYSFS_platformGetUserDir(void)
+{
+} /* __PHYSFS_platformGetUserDir */
+
+
 int __PHYSFS_platformGetThreadID(void)
 {
     return((int) pthread_self());
@@ -44,14 +54,9 @@
 } /* __PHYSFS_platformIsSymlink */
 
 
-char *__PHYSFS_platformGetUserName(void)
+int __PHYSFS_platformIsDirectory(const char *fname)
 {
-} /* __PHYSFS_platformGetUserName */
-
-
-char *__PHYSFS_platformGetUserDir(void);
-{
-} /* __PHYSFS_platformGetUserDir */
+} /* __PHYSFS_platformIsDirectory */
 
 
 /* end of unix.c ... */