|
@@ -12,8 +12,6 @@
|
|
|
#include "physfs_internal.h"
|
|
|
|
|
|
#if defined(_MSC_VER)
|
|
|
-#include <stdarg.h>
|
|
|
-
|
|
|
/* this code came from https://stackoverflow.com/a/8712996 */
|
|
|
int __PHYSFS_msvc_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
|
|
|
{
|
|
@@ -105,8 +103,8 @@ static inline int __PHYSFS_atomicAdd(int *ptrval, const int val)
|
|
|
{
|
|
|
int retval;
|
|
|
__PHYSFS_platformGrabMutex(stateLock);
|
|
|
+ *ptrval += val;
|
|
|
retval = *ptrval;
|
|
|
- *ptrval = retval + val;
|
|
|
__PHYSFS_platformReleaseMutex(stateLock);
|
|
|
return retval;
|
|
|
} /* __PHYSFS_atomicAdd */
|
|
@@ -925,12 +923,12 @@ static DirHandle *openDirectory(PHYSFS_Io *io, const char *d, int forWriting)
|
|
|
retval = tryOpenDir(io, *i, d, forWriting, &claimed);
|
|
|
} /* else */
|
|
|
|
|
|
- errcode = currentErrorCode();
|
|
|
+ errcode = claimed ? currentErrorCode() : PHYSFS_ERR_UNSUPPORTED;
|
|
|
|
|
|
if ((!retval) && (created_io))
|
|
|
io->destroy(io);
|
|
|
|
|
|
- BAIL_IF(!retval, claimed ? errcode : PHYSFS_ERR_UNSUPPORTED, NULL);
|
|
|
+ BAIL_IF(!retval, errcode, NULL);
|
|
|
return retval;
|
|
|
} /* openDirectory */
|
|
|
|
|
@@ -1097,6 +1095,8 @@ static int freeDirHandle(DirHandle *dh, FileHandle *openList)
|
|
|
BAIL_IF(i->dirHandle == dh, PHYSFS_ERR_FILES_STILL_OPEN, 0);
|
|
|
|
|
|
dh->funcs->closeArchive(dh->opaque);
|
|
|
+
|
|
|
+ if (dh->root) allocator.Free(dh->root);
|
|
|
allocator.Free(dh->dirName);
|
|
|
allocator.Free(dh->mountPoint);
|
|
|
allocator.Free(dh);
|
|
@@ -1252,7 +1252,9 @@ int PHYSFS_init(const char *argv0)
|
|
|
if (!userDir) goto initFailed;
|
|
|
|
|
|
/* Platform layer is required to append a dirsep. */
|
|
|
+ #ifndef __ANDROID__ /* it's an APK file, not a directory, on Android. */
|
|
|
assert(baseDir[strlen(baseDir) - 1] == __PHYSFS_platformDirSeparator);
|
|
|
+ #endif
|
|
|
assert(userDir[strlen(userDir) - 1] == __PHYSFS_platformDirSeparator);
|
|
|
|
|
|
if (!initStaticArchivers()) goto initFailed;
|
|
@@ -1452,15 +1454,60 @@ char *__PHYSFS_strdup(const char *str)
|
|
|
} /* __PHYSFS_strdup */
|
|
|
|
|
|
|
|
|
-PHYSFS_uint32 __PHYSFS_hashString(const char *str, size_t len)
|
|
|
+PHYSFS_uint32 __PHYSFS_hashString(const char *str)
|
|
|
{
|
|
|
PHYSFS_uint32 hash = 5381;
|
|
|
- while (len--)
|
|
|
- hash = ((hash << 5) + hash) ^ *(str++);
|
|
|
+ while (1)
|
|
|
+ {
|
|
|
+ const char ch = *(str++);
|
|
|
+ if (ch == 0)
|
|
|
+ break;
|
|
|
+ hash = ((hash << 5) + hash) ^ ch;
|
|
|
+ } /* while */
|
|
|
return hash;
|
|
|
} /* __PHYSFS_hashString */
|
|
|
|
|
|
|
|
|
+PHYSFS_uint32 __PHYSFS_hashStringCaseFold(const char *str)
|
|
|
+{
|
|
|
+ PHYSFS_uint32 hash = 5381;
|
|
|
+ while (1)
|
|
|
+ {
|
|
|
+ const PHYSFS_uint32 cp = __PHYSFS_utf8codepoint(&str);
|
|
|
+ if (cp == 0)
|
|
|
+ break;
|
|
|
+ else
|
|
|
+ {
|
|
|
+ PHYSFS_uint32 folded[3];
|
|
|
+ const int numbytes = (int) (PHYSFS_caseFold(cp, folded) * sizeof (PHYSFS_uint32));
|
|
|
+ const char *bytes = (const char *) folded;
|
|
|
+ int i;
|
|
|
+ for (i = 0; i < numbytes; i++)
|
|
|
+ hash = ((hash << 5) + hash) ^ *(bytes++);
|
|
|
+ } /* else */
|
|
|
+ } /* while */
|
|
|
+
|
|
|
+ return hash;
|
|
|
+} /* __PHYSFS_hashStringCaseFold */
|
|
|
+
|
|
|
+
|
|
|
+PHYSFS_uint32 __PHYSFS_hashStringCaseFoldUSAscii(const char *str)
|
|
|
+{
|
|
|
+ PHYSFS_uint32 hash = 5381;
|
|
|
+ while (1)
|
|
|
+ {
|
|
|
+ char ch = *(str++);
|
|
|
+ if (ch == 0)
|
|
|
+ break;
|
|
|
+ else if ((ch >= 'A') && (ch <= 'Z'))
|
|
|
+ ch -= ('A' - 'a');
|
|
|
+
|
|
|
+ hash = ((hash << 5) + hash) ^ ch;
|
|
|
+ } /* while */
|
|
|
+ return hash;
|
|
|
+} /* __PHYSFS_hashStringCaseFoldUSAscii */
|
|
|
+
|
|
|
+
|
|
|
/* MAKE SURE you hold stateLock before calling this! */
|
|
|
static int doRegisterArchiver(const PHYSFS_Archiver *_archiver)
|
|
|
{
|
|
@@ -1752,10 +1799,10 @@ int PHYSFS_setRoot(const char *archive, const char *subdir)
|
|
|
if (i->root)
|
|
|
allocator.Free(i->root);
|
|
|
i->root = ptr;
|
|
|
- i->rootlen = len;
|
|
|
+ i->rootlen = strlen(i->root); /* in case sanitizePlatformIndependentPath changed subdir */
|
|
|
|
|
|
- if (longest_root < len)
|
|
|
- longest_root = len;
|
|
|
+ if (longest_root < i->rootlen)
|
|
|
+ longest_root = i->rootlen;
|
|
|
} /* else */
|
|
|
|
|
|
break;
|
|
@@ -2157,10 +2204,10 @@ static int verifyPath(DirHandle *h, char **_fname, int allowMissing)
|
|
|
if (h->root)
|
|
|
{
|
|
|
const int isempty = (*fname == '\0');
|
|
|
- fname -= h->rootlen - 1;
|
|
|
+ fname -= h->rootlen + (isempty ? 0 : 1);
|
|
|
strcpy(fname, h->root);
|
|
|
if (!isempty)
|
|
|
- fname[h->rootlen - 2] = '/';
|
|
|
+ fname[h->rootlen] = '/';
|
|
|
*_fname = fname;
|
|
|
} /* if */
|
|
|
|
|
@@ -2303,7 +2350,12 @@ static int doMkdir(const char *_dname, char *dname, DirHandle *h)
|
|
|
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));
|
|
|
+ /* verifyPath made sure that (dname) doesn't have symlinks if they aren't
|
|
|
+ allowed, but it's possible the mounted writeDir itself has symlinks in it,
|
|
|
+ (for example "/var" on iOS is a symlink, and the prefpath will be somewhere
|
|
|
+ under that)...if we mounted that writeDir, we must allow those symlinks here
|
|
|
+ unconditionally. */
|
|
|
+ retval = ( (rc) && ((statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY) || (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK)) );
|
|
|
} /* if */
|
|
|
|
|
|
if (!exists)
|
|
@@ -2384,10 +2436,10 @@ static DirHandle *getRealDirHandle(const char *_fname)
|
|
|
BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
|
|
|
|
|
|
__PHYSFS_platformGrabMutex(stateLock);
|
|
|
- len = strlen(_fname) + longest_root + 1;
|
|
|
+ len = strlen(_fname) + longest_root + 2;
|
|
|
allocated_fname = __PHYSFS_smallAlloc(len);
|
|
|
BAIL_IF_MUTEX(!allocated_fname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, NULL);
|
|
|
- fname = allocated_fname + longest_root;
|
|
|
+ fname = allocated_fname + longest_root + 1;
|
|
|
if (sanitizePlatformIndependentPath(_fname, fname))
|
|
|
{
|
|
|
DirHandle *i;
|
|
@@ -2615,10 +2667,10 @@ int PHYSFS_enumerate(const char *_fn, PHYSFS_EnumerateCallback cb, void *data)
|
|
|
|
|
|
__PHYSFS_platformGrabMutex(stateLock);
|
|
|
|
|
|
- len = strlen(_fn) + longest_root + 1;
|
|
|
+ len = strlen(_fn) + longest_root + 2;
|
|
|
allocated_fname = (char *) __PHYSFS_smallAlloc(len);
|
|
|
BAIL_IF_MUTEX(!allocated_fname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, 0);
|
|
|
- fname = allocated_fname + longest_root;
|
|
|
+ fname = allocated_fname + longest_root + 1;
|
|
|
if (!sanitizePlatformIndependentPath(_fn, fname))
|
|
|
retval = PHYSFS_ENUM_STOP;
|
|
|
else
|
|
@@ -2821,10 +2873,10 @@ PHYSFS_File *PHYSFS_openRead(const char *_fname)
|
|
|
|
|
|
BAIL_IF_MUTEX(!searchPath, PHYSFS_ERR_NOT_FOUND, stateLock, 0);
|
|
|
|
|
|
- len = strlen(_fname) + longest_root + 1;
|
|
|
+ len = strlen(_fname) + longest_root + 2;
|
|
|
allocated_fname = (char *) __PHYSFS_smallAlloc(len);
|
|
|
BAIL_IF_MUTEX(!allocated_fname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, 0);
|
|
|
- fname = allocated_fname + longest_root;
|
|
|
+ fname = allocated_fname + longest_root + 1;
|
|
|
|
|
|
if (sanitizePlatformIndependentPath(_fname, fname))
|
|
|
{
|
|
@@ -2850,13 +2902,15 @@ PHYSFS_File *PHYSFS_openRead(const char *_fname)
|
|
|
io->destroy(io);
|
|
|
PHYSFS_setErrorCode(PHYSFS_ERR_OUT_OF_MEMORY);
|
|
|
} /* if */
|
|
|
-
|
|
|
- memset(fh, '\0', sizeof (FileHandle));
|
|
|
- fh->io = io;
|
|
|
- fh->forReading = 1;
|
|
|
- fh->dirHandle = i;
|
|
|
- fh->next = openReadList;
|
|
|
- openReadList = fh;
|
|
|
+ else
|
|
|
+ {
|
|
|
+ memset(fh, '\0', sizeof (FileHandle));
|
|
|
+ fh->io = io;
|
|
|
+ fh->forReading = 1;
|
|
|
+ fh->dirHandle = i;
|
|
|
+ fh->next = openReadList;
|
|
|
+ openReadList = fh;
|
|
|
+ } /* else */
|
|
|
} /* if */
|
|
|
} /* if */
|
|
|
|
|
@@ -3210,10 +3264,10 @@ int PHYSFS_stat(const char *_fname, PHYSFS_Stat *stat)
|
|
|
stat->readonly = 1;
|
|
|
|
|
|
__PHYSFS_platformGrabMutex(stateLock);
|
|
|
- len = strlen(_fname) + longest_root + 1;
|
|
|
+ len = strlen(_fname) + longest_root + 2;
|
|
|
allocated_fname = (char *) __PHYSFS_smallAlloc(len);
|
|
|
BAIL_IF_MUTEX(!allocated_fname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, 0);
|
|
|
- fname = allocated_fname + longest_root;
|
|
|
+ fname = allocated_fname + longest_root + 1;
|
|
|
|
|
|
if (sanitizePlatformIndependentPath(_fname, fname))
|
|
|
{
|
|
@@ -3346,7 +3400,7 @@ static void setDefaultAllocator(void)
|
|
|
} /* setDefaultAllocator */
|
|
|
|
|
|
|
|
|
-int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen)
|
|
|
+int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen, const int case_sensitive, const int only_usascii)
|
|
|
{
|
|
|
static char rootpath[2] = { '/', '\0' };
|
|
|
size_t alloclen;
|
|
@@ -3354,6 +3408,8 @@ int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen)
|
|
|
assert(entrylen >= sizeof (__PHYSFS_DirTreeEntry));
|
|
|
|
|
|
memset(dt, '\0', sizeof (*dt));
|
|
|
+ dt->case_sensitive = case_sensitive;
|
|
|
+ dt->only_usascii = only_usascii;
|
|
|
|
|
|
dt->root = (__PHYSFS_DirTreeEntry *) allocator.Malloc(entrylen);
|
|
|
BAIL_IF(!dt->root, PHYSFS_ERR_OUT_OF_MEMORY, 0);
|
|
@@ -3374,9 +3430,10 @@ int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen)
|
|
|
} /* __PHYSFS_DirTreeInit */
|
|
|
|
|
|
|
|
|
-static inline PHYSFS_uint32 hashPathName(__PHYSFS_DirTree *dt, const char *name)
|
|
|
+static PHYSFS_uint32 hashPathName(__PHYSFS_DirTree *dt, const char *name)
|
|
|
{
|
|
|
- return __PHYSFS_hashString(name, strlen(name)) % dt->hashBuckets;
|
|
|
+ const PHYSFS_uint32 hashval = dt->case_sensitive ? __PHYSFS_hashString(name) : dt->only_usascii ? __PHYSFS_hashStringCaseFoldUSAscii(name) : __PHYSFS_hashStringCaseFold(name);
|
|
|
+ return hashval % dt->hashBuckets;
|
|
|
} /* hashPathName */
|
|
|
|
|
|
|
|
@@ -3437,6 +3494,7 @@ void *__PHYSFS_DirTreeAdd(__PHYSFS_DirTree *dt, char *name, const int isdir)
|
|
|
/* Find the __PHYSFS_DirTreeEntry for a path in platform-independent notation. */
|
|
|
void *__PHYSFS_DirTreeFind(__PHYSFS_DirTree *dt, const char *path)
|
|
|
{
|
|
|
+ const int cs = dt->case_sensitive;
|
|
|
PHYSFS_uint32 hashval;
|
|
|
__PHYSFS_DirTreeEntry *prev = NULL;
|
|
|
__PHYSFS_DirTreeEntry *retval;
|
|
@@ -3447,7 +3505,8 @@ void *__PHYSFS_DirTreeFind(__PHYSFS_DirTree *dt, const char *path)
|
|
|
hashval = hashPathName(dt, path);
|
|
|
for (retval = dt->hash[hashval]; retval; retval = retval->hashnext)
|
|
|
{
|
|
|
- if (strcmp(retval->name, path) == 0)
|
|
|
+ const int cmp = cs ? strcmp(retval->name, path) : PHYSFS_utf8stricmp(retval->name, path);
|
|
|
+ if (cmp == 0)
|
|
|
{
|
|
|
if (prev != NULL) /* move this to the front of the list */
|
|
|
{
|