diff -r ea891871f481 -r bb5522e88ab2 misc/libphysfs/physfs.c --- 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);