From ee9687bca9a5cb3a39384bc02e91b82e84d6195e Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 12 Aug 2017 02:19:22 -0400 Subject: [PATCH] Reworked enumeration to be more powerful. Now callbacks can stop further enumeration and report errors, if they had a catastrophic issue or just found what they needed and don't need to process any more items. Also, the actual enumerators can report errors instead of silently dropping items. This led to several other fixes as all these implementations got audited and reworked. The original, non-callback enumerator now returns NULL if it can't produce a complete list instead of dropping items and returning a partial list. --- src/physfs.c | 175 +++++++++++++++++++++--------- src/physfs.h | 194 +++++++++++++++++++++++++--------- src/physfs_archiver_7z.c | 2 +- src/physfs_archiver_dir.c | 21 ++-- src/physfs_archiver_grp.c | 2 +- src/physfs_archiver_hog.c | 2 +- src/physfs_archiver_iso9660.c | 2 +- src/physfs_archiver_mvl.c | 2 +- src/physfs_archiver_qpak.c | 2 +- src/physfs_archiver_slb.c | 2 +- src/physfs_archiver_vdf.c | 2 +- src/physfs_archiver_wad.c | 2 +- src/physfs_archiver_zip.c | 2 +- src/physfs_internal.h | 27 +++-- src/physfs_platform_os2.c | 34 +++--- src/physfs_platform_posix.c | 37 ++++--- src/physfs_platform_windows.c | 45 ++++---- 17 files changed, 370 insertions(+), 183 deletions(-) diff --git a/src/physfs.c b/src/physfs.c index a5c30b95..8630f59b 100644 --- a/src/physfs.c +++ b/src/physfs.c @@ -742,6 +742,7 @@ PHYSFS_DECL const char *PHYSFS_getErrorByCode(PHYSFS_ErrorCode code) 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"; + case PHYSFS_ERR_APP_CALLBACK: return "app callback reported error"; } /* switch */ return NULL; /* don't know this error code. */ @@ -1430,7 +1431,7 @@ static int doRegisterArchiver(const PHYSFS_Archiver *_archiver) BAIL_IF(!_archiver->info.author, PHYSFS_ERR_INVALID_ARGUMENT, 0); BAIL_IF(!_archiver->info.url, PHYSFS_ERR_INVALID_ARGUMENT, 0); BAIL_IF(!_archiver->openArchive, PHYSFS_ERR_INVALID_ARGUMENT, 0); - BAIL_IF(!_archiver->enumerateFiles, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!_archiver->enumerate, PHYSFS_ERR_INVALID_ARGUMENT, 0); BAIL_IF(!_archiver->openRead, PHYSFS_ERR_INVALID_ARGUMENT, 0); BAIL_IF(!_archiver->openWrite, PHYSFS_ERR_INVALID_ARGUMENT, 0); BAIL_IF(!_archiver->openAppend, PHYSFS_ERR_INVALID_ARGUMENT, 0); @@ -1910,6 +1911,7 @@ int PHYSFS_setSaneConfig(const char *organization, const char *appName, /* Root out archives, and add them to search path... */ if (archiveExt != NULL) { + /* !!! FIXME-3.0: turn this into a callback */ char **rc = PHYSFS_enumerateFiles("/"); char **i; size_t extlen = strlen(archiveExt); @@ -2212,7 +2214,7 @@ static int locateInStringList(const char *str, } /* locateInStringList */ -static void enumFilesCallback(void *data, const char *origdir, const char *str) +static int enumFilesCallback(void *data, const char *origdir, const char *str) { PHYSFS_uint32 pos; void *ptr; @@ -2225,7 +2227,7 @@ static void enumFilesCallback(void *data, const char *origdir, const char *str) */ pos = pecd->size; if (locateInStringList(str, pecd->list, &pos)) - return; /* already in the list. */ + return 1; /* already in the list, but keep going. */ ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *)); newstr = (char *) allocator.Malloc(strlen(str) + 1); @@ -2233,7 +2235,13 @@ static void enumFilesCallback(void *data, const char *origdir, const char *str) pecd->list = (char **) ptr; if ((ptr == NULL) || (newstr == NULL)) - return; /* better luck next time. */ + { + if (newstr) + allocator.Free(newstr); + + pecd->errcode = PHYSFS_ERR_OUT_OF_MEMORY; + return -1; /* better luck next time. */ + } /* if */ strcpy(newstr, str); @@ -2245,6 +2253,8 @@ static void enumFilesCallback(void *data, const char *origdir, const char *str) pecd->list[pos] = newstr; pecd->size++; + + return 1; } /* enumFilesCallback */ @@ -2254,7 +2264,17 @@ char **PHYSFS_enumerateFiles(const char *path) memset(&ecd, '\0', sizeof (ecd)); ecd.list = (char **) allocator.Malloc(sizeof (char *)); BAIL_IF(!ecd.list, PHYSFS_ERR_OUT_OF_MEMORY, NULL); - PHYSFS_enumerateFilesCallback(path, enumFilesCallback, &ecd); + if (!PHYSFS_enumerate(path, enumFilesCallback, &ecd)) + { + const PHYSFS_ErrorCode errcode = currentErrorCode(); + PHYSFS_uint32 i; + for (i = 0; i < ecd.size; i++) + allocator.Free(ecd.list[i]); + allocator.Free(ecd.list); + BAIL_IF(errcode == PHYSFS_ERR_APP_CALLBACK, ecd.errcode, NULL); + return NULL; + } /* if */ + ecd.list[ecd.size] = NULL; return ecd.list; } /* PHYSFS_enumerateFiles */ @@ -2263,8 +2283,8 @@ char **PHYSFS_enumerateFiles(const char *path) /* * Broke out to seperate function so we can use stack allocation gratuitously. */ -static void enumerateFromMountPoint(DirHandle *i, const char *arcfname, - PHYSFS_EnumFilesCallback callback, +static int enumerateFromMountPoint(DirHandle *i, const char *arcfname, + PHYSFS_EnumerateCallback callback, const char *_fname, void *data) { const size_t len = strlen(arcfname); @@ -2272,70 +2292,89 @@ static void enumerateFromMountPoint(DirHandle *i, const char *arcfname, char *end = NULL; const size_t slen = strlen(i->mountPoint) + 1; char *mountPoint = (char *) __PHYSFS_smallAlloc(slen); + int rc; - if (mountPoint == NULL) - return; /* oh well. */ + BAIL_IF(!mountPoint, PHYSFS_ERR_OUT_OF_MEMORY, -1); strcpy(mountPoint, i->mountPoint); ptr = mountPoint + ((len) ? len + 1 : 0); end = strchr(ptr, '/'); assert(end); /* should always find a terminating '/'. */ *end = '\0'; - callback(data, _fname, ptr); + rc = callback(data, _fname, ptr); __PHYSFS_smallFree(mountPoint); + + BAIL_IF(rc == -1, PHYSFS_ERR_APP_CALLBACK, -1); + return rc; } /* enumerateFromMountPoint */ typedef struct SymlinkFilterData { - PHYSFS_EnumFilesCallback callback; + PHYSFS_EnumerateCallback callback; void *callbackData; DirHandle *dirhandle; + PHYSFS_ErrorCode errcode; } SymlinkFilterData; /* !!! FIXME-3.0: broken if in a virtual mountpoint (stat call fails). */ -static void enumCallbackFilterSymLinks(void *_data, const char *origdir, - const char *fname) +static int enumCallbackFilterSymLinks(void *_data, const char *origdir, + const char *fname) { + SymlinkFilterData *data = (SymlinkFilterData *) _data; + const DirHandle *dh = data->dirhandle; + PHYSFS_Stat statbuf; const char *trimmedDir = (*origdir == '/') ? (origdir+1) : origdir; const size_t slen = strlen(trimmedDir) + strlen(fname) + 2; char *path = (char *) __PHYSFS_smallAlloc(slen); + int retval = 1; - if (path != NULL) + if (path == NULL) { - SymlinkFilterData *data = (SymlinkFilterData *) _data; - const DirHandle *dh = data->dirhandle; - PHYSFS_Stat statbuf; + data->errcode = PHYSFS_ERR_OUT_OF_MEMORY; + return -1; + } /* if */ - snprintf(path, slen, "%s%s%s", trimmedDir, *trimmedDir ? "/" : "", fname); - if (dh->funcs->stat(dh->opaque, path, &statbuf)) + snprintf(path, slen, "%s%s%s", trimmedDir, *trimmedDir ? "/" : "", fname); + + if (!dh->funcs->stat(dh->opaque, path, &statbuf)) + { + data->errcode = currentErrorCode(); + retval = -1; + } /* if */ + else + { + /* Pass it on to the application if it's not a symlink. */ + if (statbuf.filetype != PHYSFS_FILETYPE_SYMLINK) { - /* Pass it on to the application if it's not a symlink. */ - if (statbuf.filetype != PHYSFS_FILETYPE_SYMLINK) - data->callback(data->callbackData, origdir, fname); + retval = data->callback(data->callbackData, origdir, fname); + if (retval == -1) + data->errcode = PHYSFS_ERR_APP_CALLBACK; } /* if */ + } /* else */ - __PHYSFS_smallFree(path); - } /* if */ + __PHYSFS_smallFree(path); + + return retval; } /* enumCallbackFilterSymLinks */ -/* !!! FIXME-3.0: this should report error conditions. */ -void PHYSFS_enumerateFilesCallback(const char *_fname, - PHYSFS_EnumFilesCallback callback, - void *data) +int PHYSFS_enumerate(const char *_fn, PHYSFS_EnumerateCallback cb, void *data) { + int retval = 1; size_t len; char *fname; - BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, ) /*0*/; - BAIL_IF(!callback, PHYSFS_ERR_INVALID_ARGUMENT, ) /*0*/; + BAIL_IF(!_fn, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!cb, PHYSFS_ERR_INVALID_ARGUMENT, 0); - len = strlen(_fname) + 1; + len = strlen(_fn) + 1; fname = (char *) __PHYSFS_smallAlloc(len); - BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, ) /*0*/; + BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0); - if (sanitizePlatformIndependentPath(_fname, fname)) + if (!sanitizePlatformIndependentPath(_fn, fname)) + retval = 0; + else { DirHandle *i; SymlinkFilterData filterdata; @@ -2345,36 +2384,70 @@ void PHYSFS_enumerateFilesCallback(const char *_fname, if (!allowSymLinks) { memset(&filterdata, '\0', sizeof (filterdata)); - filterdata.callback = callback; + filterdata.callback = cb; filterdata.callbackData = data; } /* if */ - for (i = searchPath; i != NULL; i = i->next) + for (i = searchPath; (retval > 0) && (i != NULL); i = i->next) { char *arcfname = fname; + if (partOfMountPoint(i, arcfname)) - enumerateFromMountPoint(i, arcfname, callback, _fname, data); + retval = enumerateFromMountPoint(i, arcfname, cb, _fn, data); else if (verifyPath(i, &arcfname, 0)) { if ((!allowSymLinks) && (i->funcs->info.supportsSymlinks)) { filterdata.dirhandle = i; - i->funcs->enumerateFiles(i->opaque, arcfname, - enumCallbackFilterSymLinks, - _fname, &filterdata); + filterdata.errcode = PHYSFS_ERR_OK; + retval = i->funcs->enumerate(i->opaque, arcfname, + enumCallbackFilterSymLinks, + _fn, &filterdata); + if (retval == -1) + { + if (currentErrorCode() == PHYSFS_ERR_APP_CALLBACK) + PHYSFS_setErrorCode(filterdata.errcode); + } /* if */ } /* if */ else { - i->funcs->enumerateFiles(i->opaque, arcfname, - callback, _fname, data); + retval = i->funcs->enumerate(i->opaque, arcfname, + cb, _fn, data); } /* else */ } /* else if */ } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); } /* if */ __PHYSFS_smallFree(fname); + + return (retval < 0) ? 0 : 1; +} /* PHYSFS_enumerate */ + + +typedef struct +{ + /* can't use the typedef because it might trigger deprecation warnings. */ + void (*callback)(void *data, const char *origdir, const char *fname); + void *data; +} LegacyEnumFilesCallbackData; + +static int enumFilesCallbackAlwaysSucceed(void *data, const char *origdir, + const char *fname) +{ + LegacyEnumFilesCallbackData *cbdata = (LegacyEnumFilesCallbackData *) data; + cbdata->callback(cbdata->data, origdir, fname); + return 1; +} /* enumFilesCallbackAlwaysSucceed */ + +void PHYSFS_enumerateFilesCallback(const char *fname, + PHYSFS_EnumFilesCallback callback, + void *data) +{ + LegacyEnumFilesCallbackData cbdata = { callback, data }; + (void) PHYSFS_enumerate(fname, enumFilesCallbackAlwaysSucceed, &cbdata); } /* PHYSFS_enumerateFilesCallback */ @@ -3138,21 +3211,27 @@ void *__PHYSFS_DirTreeFind(__PHYSFS_DirTree *dt, const char *path) BAIL(PHYSFS_ERR_NOT_FOUND, NULL); } /* __PHYSFS_DirTreeFind */ -void __PHYSFS_DirTreeEnumerateFiles(void *opaque, const char *dname, - PHYSFS_EnumFilesCallback cb, - const char *origdir, void *callbackdata) +int __PHYSFS_DirTreeEnumerate(void *opaque, const char *dname, + PHYSFS_EnumerateCallback cb, + const char *origdir, void *callbackdata) { - __PHYSFS_DirTree *tree = ((__PHYSFS_DirTree *) opaque); + __PHYSFS_DirTree *tree = (__PHYSFS_DirTree *) opaque; const __PHYSFS_DirTreeEntry *entry = __PHYSFS_DirTreeFind(tree, dname); if (entry && entry->isdir) { for (entry = entry->children; entry; entry = entry->sibling) { - const char *ptr = strrchr(entry->name, '/'); - cb(callbackdata, origdir, ptr ? ptr + 1 : entry->name); + const char *name = entry->name; + const char *ptr = strrchr(name, '/'); + const int rc = cb(callbackdata, origdir, ptr ? ptr + 1 : name); + BAIL_IF(rc == -1, PHYSFS_ERR_APP_CALLBACK, -1); + if (rc == 0) + return 0; } /* for */ } /* if */ -} /* __PHYSFS_DirTreeEnumerateFiles */ + + return 1; +} /* __PHYSFS_DirTreeEnumerate */ void __PHYSFS_DirTreeDeinit(__PHYSFS_DirTree *dt) diff --git a/src/physfs.h b/src/physfs.h index 2f2b0457..2863388c 100644 --- a/src/physfs.h +++ b/src/physfs.h @@ -1066,6 +1066,17 @@ PHYSFS_DECL const char *PHYSFS_getRealDir(const char *filename); * \fn char **PHYSFS_enumerateFiles(const char *dir) * \brief Get a file listing of a search path's directory. * + * \warning In PhysicsFS versions prior to 2.1, this function would return + * as many items as it could in the face of a failure condition + * (out of memory, disk i/o error, etc). Since this meant apps + * couldn't distinguish between complete success and partial failure, + * and since the function could always return NULL to report + * catastrophic failures anyway, in PhysicsFS 2.1 this function's + * policy changed: it will either return a list of complete results + * or it will return NULL for any failure of any kind, so we can + * guarantee that the enumeration ran to completion and has no gaps + * in its results. + * * Matching directories are interpolated. That is, if "C:\mydir" is in the * search path and contains a directory "savegames" that contains "x.sav", * "y.sav", and "z.sav", and there is also a "C:\userdir" in the search path @@ -1097,9 +1108,10 @@ PHYSFS_DECL const char *PHYSFS_getRealDir(const char *filename); * function when you are done with it. * * \param dir directory in platform-independent notation to enumerate. - * \return Null-terminated array of null-terminated strings. + * \return Null-terminated array of null-terminated strings, or NULL for + * failure cases. * - * \sa PHYSFS_enumerateFilesCallback + * \sa PHYSFS_enumerate */ PHYSFS_DECL char **PHYSFS_enumerateFiles(const char *dir); @@ -2240,6 +2252,9 @@ typedef void (*PHYSFS_StringCallback)(void *data, const char *str); * \typedef PHYSFS_EnumFilesCallback * \brief Function signature for callbacks that enumerate files. * + * \deprecated As of PhysicsFS 2.1, Use PHYSFS_EnumerateCallback with + * PHYSFS_enumerate() instead; it gives you more control over the process. + * * These are used to report a list of directory entries to an original caller, * one file/dir/symlink per callback. All strings are UTF-8 encoded. * Functions should not try to modify or free any string's memory. @@ -2249,9 +2264,10 @@ typedef void (*PHYSFS_StringCallback)(void *data, const char *str); * PHYSFS_freeList(). The callback means that the library doesn't need to * allocate an entire list and all the strings up front. * - * Be aware that promises data ordering in the list versions are not + * Be aware that promised data ordering in the list versions are not * necessarily so in the callback versions. Check the documentation on - * specific APIs, but strings may not be sorted as you expect. + * specific APIs, but strings may not be sorted as you expect and you might + * get duplicate strings. * * \param data User-defined data pointer, passed through from the API * that eventually called the callback. @@ -2268,7 +2284,7 @@ typedef void (*PHYSFS_StringCallback)(void *data, const char *str); * \sa PHYSFS_enumerateFilesCallback */ typedef void (*PHYSFS_EnumFilesCallback)(void *data, const char *origdir, - const char *fname); + const char *fname) PHYSFS_DEPRECATED; /** @@ -2345,48 +2361,22 @@ PHYSFS_DECL void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback c, void *d); * \fn void PHYSFS_enumerateFilesCallback(const char *dir, PHYSFS_EnumFilesCallback c, void *d) * \brief Get a file listing of a search path's directory, using an application-defined callback. * - * Internally, PHYSFS_enumerateFiles() just calls this function and then builds - * a list before returning to the application, so functionality is identical - * except for how the information is represented to the application. - * - * Unlike PHYSFS_enumerateFiles(), this function does not return an array. - * Rather, it calls a function specified by the application once per - * element of the search path: - * - * \code - * - * static void printDir(void *data, const char *origdir, const char *fname) - * { - * printf(" * We've got [%s] in [%s].\n", fname, origdir); - * } + * \deprecated As of PhysicsFS 2.1, use PHYSFS_enumerate() instead. This + * function has no way to report errors (or to have the callback signal an + * error or request a stop), so if data will be lost, your callback has no + * way to direct the process, and your calling app has no way to know. * - * // ... - * PHYSFS_enumerateFilesCallback("/some/path", printDir, NULL); - * \endcode - * - * !!! FIXME-3.0: enumerateFiles() does not promise alphabetical sorting by - * !!! FIXME: case-sensitivity in the code, and doesn't promise sorting at - * !!! FIXME: all in the above docs. + * As of PhysicsFS 2.1, this function just wraps PHYSFS_enumerate() and + * ignores errors. Consider using PHYSFS_enumerate() or + * PHYSFS_enumerateFiles() instead. * - * Items sent to the callback are not guaranteed to be in any order whatsoever. - * There is no sorting done at this level, and if you need that, you should - * probably use PHYSFS_enumerateFiles() instead, which guarantees - * alphabetical sorting. This form reports whatever is discovered in each - * archive before moving on to the next. Even within one archive, we can't - * guarantee what order it will discover data. Any sorting you find in - * these callbacks is just pure luck. Do not rely on it. As this walks - * the entire list of archives, you may receive duplicate filenames. - * - * \param dir Directory, in platform-independent notation, to enumerate. - * \param c Callback function to notify about search path elements. - * \param d Application-defined data passed to callback. Can be NULL. - * - * \sa PHYSFS_EnumFilesCallback + * \sa PHYSFS_enumerate * \sa PHYSFS_enumerateFiles + * \sa PHYSFS_EnumFilesCallback */ PHYSFS_DECL void PHYSFS_enumerateFilesCallback(const char *dir, PHYSFS_EnumFilesCallback c, - void *d); + void *d) PHYSFS_DEPRECATED; /** * \fn void PHYSFS_utf8FromUcs4(const PHYSFS_uint32 *src, char *dst, PHYSFS_uint64 len) @@ -2544,6 +2534,99 @@ PHYSFS_DECL void PHYSFS_utf8FromLatin1(const char *src, char *dst, */ PHYSFS_DECL int PHYSFS_utf8stricmp(const char *str1, const char *str2); +/** + * \typedef PHYSFS_EnumerateCallback + * \brief Function signature for callbacks that enumerate and return results. + * + * This is the same thing as PHYSFS_EnumFilesCallback from PhysicsFS 2.0, + * except it can return a result from the callback: namely: if you're looking + * for something specific, once you find it, you can tell PhysicsFS to stop + * enumerating further. This is used with PHYSFS_enumerate(), which we + * hopefully got right this time. :) + * + * \param data User-defined data pointer, passed through from the API + * that eventually called the callback. + * \param origdir A string containing the full path, in platform-independent + * notation, of the directory containing this file. In most + * cases, this is the directory on which you requested + * enumeration, passed in the callback for your convenience. + * \param fname The filename that is being enumerated. It may not be in + * alphabetical order compared to other callbacks that have + * fired, and it will not contain the full path. You can + * recreate the fullpath with $origdir/$fname ... The file + * can be a subdirectory, a file, a symlink, etc. + * \return 1 to keep enumerating, 0 to stop (no error), -1 to stop (error). + * All other values are (currently) undefined; don't use them. + * + * \sa PHYSFS_enumerate + */ +typedef int (*PHYSFS_EnumerateCallback)(void *data, const char *origdir, + const char *fname); + +/** + * \fn int PHYSFS_enumerate(const char *dir, PHYSFS_EnumerateCallback c, void *d) + * \brief Get a file listing of a search path's directory, using an application-defined callback, with errors reported. + * + * Internally, PHYSFS_enumerateFiles() just calls this function and then builds + * a list before returning to the application, so functionality is identical + * except for how the information is represented to the application. + * + * Unlike PHYSFS_enumerateFiles(), this function does not return an array. + * Rather, it calls a function specified by the application once per + * element of the search path: + * + * \code + * + * static int printDir(void *data, const char *origdir, const char *fname) + * { + * printf(" * We've got [%s] in [%s].\n", fname, origdir); + * return 1; // give me more data, please. + * } + * + * // ... + * PHYSFS_enumerate("/some/path", printDir, NULL); + * \endcode + * + * !!! FIXME-3.0: enumerateFiles() does not promise alphabetical sorting by + * !!! FIXME: case-sensitivity in the code, and doesn't promise sorting at + * !!! FIXME: all in the above docs. + * + * Items sent to the callback are not guaranteed to be in any order whatsoever. + * There is no sorting done at this level, and if you need that, you should + * probably use PHYSFS_enumerateFiles() instead, which guarantees + * alphabetical sorting. This form reports whatever is discovered in each + * archive before moving on to the next. Even within one archive, we can't + * guarantee what order it will discover data. Any sorting you find in + * these callbacks is just pure luck. Do not rely on it. As this walks + * the entire list of archives, you may receive duplicate filenames. + * + * This API and the callbacks themselves are capable of reporting errors. + * Prior to this API, callbacks had to accept every enumerated item, even if + * they were only looking for a specific thing and wanted to stop after that, + * or had a serious error and couldn't alert anyone. Furthermore, if + * PhysicsFS itself had a problem (disk error or whatnot), it couldn't report + * it to the calling app, it would just have to skip items or stop + * enumerating outright, and the caller wouldn't know it had lost some data + * along the way. + * + * Now the caller can be sure it got a complete data set, and its callback has + * control if it wants enumeration to stop early. See the documentation for + * PHYSFS_EnumerateCallback for details on how your callback should behave. + * + * \param dir Directory, in platform-independent notation, to enumerate. + * \param c Callback function to notify about search path elements. + * \param d Application-defined data passed to callback. Can be NULL. + * \return non-zero on success, zero on failure. Specifics of the error can + * be gleaned from PHYSFS_getLastError(). If the callback returns + * zero to stop early, this will considered success. Callbacks + * returning -1 will result in PHYSFS_ERR_APP_CALLBACK. + * + * \sa PHYSFS_EnumerateCallback + * \sa PHYSFS_enumerateFiles + */ +PHYSFS_DECL int PHYSFS_enumerate(const char *dir, PHYSFS_EnumerateCallback c, + void *d); + /** * \fn int PHYSFS_unmount(const char *oldDir) @@ -3157,7 +3240,8 @@ typedef enum PHYSFS_ErrorCode PHYSFS_ERR_DIR_NOT_EMPTY, /**< Tried to delete dir with files in it. */ PHYSFS_ERR_OS_ERROR, /**< Unspecified OS-level error. */ PHYSFS_ERR_DUPLICATE, /**< Duplicate entry. */ - PHYSFS_ERR_BAD_PASSWORD /**< Bad password. */ + PHYSFS_ERR_BAD_PASSWORD, /**< Bad password. */ + PHYSFS_ERR_APP_CALLBACK /**< Application callback reported error. */ } PHYSFS_ErrorCode; @@ -3417,14 +3501,28 @@ typedef struct PHYSFS_Archiver /** * List all files in (dirname). Each file is passed to (cb), - * where a copy is made if appropriate, so you should dispose of - * it properly upon return from the callback. - * If you have a failure, report as much as you can. + * where a copy is made if appropriate, so you can dispose of + * it, if appropriate, upon return from the callback. * (dirname) is in platform-independent notation. + * If you have a failure, call PHYSFS_SetErrorCode() with whatever code + * seem appropriate and return -1. + * If the callback returns -1, please call + * PHYSFS_SetErrorCode(PHYSFS_ERR_APP_CALLBACK) and then return -1. + * If the callback returns 0, stop enumerating and return 0. Don't call + * the callback again in any circumstances. Don't set an error code in + * this case. + * Callbacks are (currently) only supposed to return -1, 0, or 1. Any + * other result has undefined behavior. + * As long as the callback returned 1 and you haven't experienced any + * errors of your own, keep enumerating until you're done and then return + * 1 without setting an error code. + * + * \warning PHYSFS_enumerate returns zero or non-zero (success or failure), + * so be aware this function pointer returns different values! */ - void (*enumerateFiles)(void *opaque, const char *dirname, - PHYSFS_EnumFilesCallback cb, - const char *origdir, void *callbackdata); + int (*enumerate)(void *opaque, const char *dirname, + PHYSFS_EnumerateCallback cb, + const char *origdir, void *callbackdata); /** * Open file for reading. diff --git a/src/physfs_archiver_7z.c b/src/physfs_archiver_7z.c index 57e6df42..3099f355 100644 --- a/src/physfs_archiver_7z.c +++ b/src/physfs_archiver_7z.c @@ -398,7 +398,7 @@ const PHYSFS_Archiver __PHYSFS_Archiver_7Z = 0, /* supportsSymlinks */ }, SZIP_openArchive, - __PHYSFS_DirTreeEnumerateFiles, + __PHYSFS_DirTreeEnumerate, SZIP_openRead, SZIP_openWrite, SZIP_openAppend, diff --git a/src/physfs_archiver_dir.c b/src/physfs_archiver_dir.c index 0597eecc..a0aac085 100644 --- a/src/physfs_archiver_dir.c +++ b/src/physfs_archiver_dir.c @@ -66,19 +66,18 @@ static void *DIR_openArchive(PHYSFS_Io *io, const char *name, int forWriting) } /* DIR_openArchive */ -static void DIR_enumerateFiles(void *opaque, const char *dname, - PHYSFS_EnumFilesCallback cb, - const char *origdir, void *callbackdata) +static int DIR_enumerate(void *opaque, const char *dname, + PHYSFS_EnumerateCallback cb, + const char *origdir, void *callbackdata) { char *d; - + int retval; CVT_TO_DEPENDENT(d, opaque, dname); - if (d != NULL) - { - __PHYSFS_platformEnumerateFiles(d, cb, origdir, callbackdata); - __PHYSFS_smallFree(d); - } /* if */ -} /* DIR_enumerateFiles */ + BAIL_IF_ERRPASS(!d, -1); + retval = __PHYSFS_platformEnumerate(d, cb, origdir, callbackdata); + __PHYSFS_smallFree(d); + return retval; +} /* DIR_enumerate */ static PHYSFS_Io *doOpen(void *opaque, const char *name, const int mode) @@ -178,7 +177,7 @@ const PHYSFS_Archiver __PHYSFS_Archiver_DIR = 1, /* supportsSymlinks */ }, DIR_openArchive, - DIR_enumerateFiles, + DIR_enumerate, DIR_openRead, DIR_openWrite, DIR_openAppend, diff --git a/src/physfs_archiver_grp.c b/src/physfs_archiver_grp.c index 43b247d2..dceeabc4 100644 --- a/src/physfs_archiver_grp.c +++ b/src/physfs_archiver_grp.c @@ -97,7 +97,7 @@ const PHYSFS_Archiver __PHYSFS_Archiver_GRP = 0, /* supportsSymlinks */ }, GRP_openArchive, - UNPK_enumerateFiles, + UNPK_enumerate, UNPK_openRead, UNPK_openWrite, UNPK_openAppend, diff --git a/src/physfs_archiver_hog.c b/src/physfs_archiver_hog.c index eec7bad6..bead0bad 100644 --- a/src/physfs_archiver_hog.c +++ b/src/physfs_archiver_hog.c @@ -95,7 +95,7 @@ const PHYSFS_Archiver __PHYSFS_Archiver_HOG = 0, /* supportsSymlinks */ }, HOG_openArchive, - UNPK_enumerateFiles, + UNPK_enumerate, UNPK_openRead, UNPK_openWrite, UNPK_openAppend, diff --git a/src/physfs_archiver_iso9660.c b/src/physfs_archiver_iso9660.c index 7c0413bc..f58a15f0 100644 --- a/src/physfs_archiver_iso9660.c +++ b/src/physfs_archiver_iso9660.c @@ -349,7 +349,7 @@ const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660 = 0, /* supportsSymlinks */ }, ISO9660_openArchive, - UNPK_enumerateFiles, + UNPK_enumerate, UNPK_openRead, UNPK_openWrite, UNPK_openAppend, diff --git a/src/physfs_archiver_mvl.c b/src/physfs_archiver_mvl.c index 70d46865..64077d33 100644 --- a/src/physfs_archiver_mvl.c +++ b/src/physfs_archiver_mvl.c @@ -91,7 +91,7 @@ const PHYSFS_Archiver __PHYSFS_Archiver_MVL = 0, /* supportsSymlinks */ }, MVL_openArchive, - UNPK_enumerateFiles, + UNPK_enumerate, UNPK_openRead, UNPK_openWrite, UNPK_openAppend, diff --git a/src/physfs_archiver_qpak.c b/src/physfs_archiver_qpak.c index 344156fd..3d09f380 100644 --- a/src/physfs_archiver_qpak.c +++ b/src/physfs_archiver_qpak.c @@ -107,7 +107,7 @@ const PHYSFS_Archiver __PHYSFS_Archiver_QPAK = 0, /* supportsSymlinks */ }, QPAK_openArchive, - UNPK_enumerateFiles, + UNPK_enumerate, UNPK_openRead, UNPK_openWrite, UNPK_openAppend, diff --git a/src/physfs_archiver_slb.c b/src/physfs_archiver_slb.c index 28ee09f6..9c649ed8 100644 --- a/src/physfs_archiver_slb.c +++ b/src/physfs_archiver_slb.c @@ -117,7 +117,7 @@ const PHYSFS_Archiver __PHYSFS_Archiver_SLB = 0, /* supportsSymlinks */ }, SLB_openArchive, - UNPK_enumerateFiles, + UNPK_enumerate, UNPK_openRead, UNPK_openWrite, UNPK_openAppend, diff --git a/src/physfs_archiver_vdf.c b/src/physfs_archiver_vdf.c index 1889de53..9547537e 100644 --- a/src/physfs_archiver_vdf.c +++ b/src/physfs_archiver_vdf.c @@ -144,7 +144,7 @@ const PHYSFS_Archiver __PHYSFS_Archiver_VDF = 0, /* supportsSymlinks */ }, VDF_openArchive, - UNPK_enumerateFiles, + UNPK_enumerate, UNPK_openRead, UNPK_openWrite, UNPK_openAppend, diff --git a/src/physfs_archiver_wad.c b/src/physfs_archiver_wad.c index f604bd94..91e0adc0 100644 --- a/src/physfs_archiver_wad.c +++ b/src/physfs_archiver_wad.c @@ -116,7 +116,7 @@ const PHYSFS_Archiver __PHYSFS_Archiver_WAD = 0, /* supportsSymlinks */ }, WAD_openArchive, - UNPK_enumerateFiles, + UNPK_enumerate, UNPK_openRead, UNPK_openWrite, UNPK_openAppend, diff --git a/src/physfs_archiver_zip.c b/src/physfs_archiver_zip.c index c7941ef9..4edfe1af 100644 --- a/src/physfs_archiver_zip.c +++ b/src/physfs_archiver_zip.c @@ -1678,7 +1678,7 @@ const PHYSFS_Archiver __PHYSFS_Archiver_ZIP = 1, /* supportsSymlinks */ }, ZIP_openArchive, - __PHYSFS_DirTreeEnumerateFiles, + __PHYSFS_DirTreeEnumerate, ZIP_openRead, ZIP_openWrite, ZIP_openAppend, diff --git a/src/physfs_internal.h b/src/physfs_internal.h index d0585d4a..636b0590 100644 --- a/src/physfs_internal.h +++ b/src/physfs_internal.h @@ -346,7 +346,7 @@ PHYSFS_Io *UNPK_openAppend(void *opaque, const char *name); int UNPK_remove(void *opaque, const char *name); int UNPK_mkdir(void *opaque, const char *name); int UNPK_stat(void *opaque, const char *fn, PHYSFS_Stat *st); -#define UNPK_enumerateFiles __PHYSFS_DirTreeEnumerateFiles +#define UNPK_enumerate __PHYSFS_DirTreeEnumerate @@ -374,9 +374,9 @@ typedef struct __PHYSFS_DirTree int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen); void *__PHYSFS_DirTreeAdd(__PHYSFS_DirTree *dt, char *name, const int isdir); void *__PHYSFS_DirTreeFind(__PHYSFS_DirTree *dt, const char *path); -void __PHYSFS_DirTreeEnumerateFiles(void *opaque, const char *dname, - PHYSFS_EnumFilesCallback cb, - const char *origdir, void *callbackdata); +int __PHYSFS_DirTreeEnumerate(void *opaque, const char *dname, + PHYSFS_EnumerateCallback cb, + const char *origdir, void *callbackdata); void __PHYSFS_DirTreeDeinit(__PHYSFS_DirTree *dt); @@ -615,16 +615,15 @@ void *__PHYSFS_platformGetThreadID(void); /* * Enumerate a directory of files. This follows the rules for the - * PHYSFS_Archiver::enumerateFiles() method, except that the - * (dirName) that is passed to this function is converted to - * platform-DEPENDENT notation by the caller. The PHYSFS_Archiver version - * uses platform-independent notation. Note that ".", "..", and other - * meta-entries should always be ignored. - */ -void __PHYSFS_platformEnumerateFiles(const char *dirname, - PHYSFS_EnumFilesCallback callback, - const char *origdir, - void *callbackdata); + * PHYSFS_Archiver::enumerate() method, except that the (dirName) that is + * passed to this function is converted to platform-DEPENDENT notation by + * the caller. The PHYSFS_Archiver version uses platform-independent + * notation. Note that ".", "..", and other meta-entries should always + * be ignored. + */ +int __PHYSFS_platformEnumerate(const char *dirname, + PHYSFS_EnumerateCallback callback, + const char *origdir, void *callbackdata); /* * Make a directory in the actual filesystem. (path) is specified in diff --git a/src/physfs_platform_os2.c b/src/physfs_platform_os2.c index dbbb20ed..44bd140b 100644 --- a/src/physfs_platform_os2.c +++ b/src/physfs_platform_os2.c @@ -390,10 +390,9 @@ char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app) return __PHYSFS_platformCalcBaseDir(NULL); /* !!! FIXME-3.0: ? */ } /* __PHYSFS_platformCalcPrefDir */ -void __PHYSFS_platformEnumerateFiles(const char *dirname, - PHYSFS_EnumFilesCallback callback, - const char *origdir, - void *callbackdata) +int __PHYSFS_platformEnumerate(const char *dirname, + PHYSFS_EnumerateCallback callback, + const char *origdir, void *callbackdata) { size_t utf8len = strlen(dirname); char *utf8 = (char *) __PHYSFS_smallAlloc(utf8len + 5); @@ -402,8 +401,10 @@ void __PHYSFS_platformEnumerateFiles(const char *dirname, HDIR hdir = HDIR_CREATE; ULONG count = 1; APIRET rc; + int cbrc; + int retval = 1; - BAIL_IF(!utf8, PHYSFS_ERR_OUT_OF_MEMORY,); + BAIL_IF(!utf8, PHYSFS_ERR_OUT_OF_MEMORY, -1); strcpy(utf8, dirname); if (utf8[utf8len - 1] != '\\') @@ -413,8 +414,7 @@ void __PHYSFS_platformEnumerateFiles(const char *dirname, cpspec = cvtUtf8ToCodepage(utf8); __PHYSFS_smallFree(utf8); - if (!cpspec) - return; + BAIL_IF_ERRPASS(!cpspec, -1); rc = DosFindFirst((unsigned char *) cpspec, &hdir, FILE_DIRECTORY | FILE_ARCHIVED | @@ -422,24 +422,34 @@ void __PHYSFS_platformEnumerateFiles(const char *dirname, &fb, sizeof (fb), &count, FIL_STANDARD); allocator.Free(cpspec); - BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc),); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), -1); while (count == 1) { if ((strcmp(fb.achName, ".") != 0) && (strcmp(fb.achName, "..") != 0)) { utf8 = cvtCodepageToUtf8(fb.achName); - if (utf8) + if (!utf8) + retval = -1; + else { - callback(callbackdata, origdir, utf8); + retval = callback(callbackdata, origdir, utf8); allocator.Free(utf8); - } /* if */ + if (retval == -1) + PHYSFS_SetErrorCode(PHYSFS_ERR_APP_CALLBACK); + } /* else */ } /* if */ + + if (retval != 1) + break; + DosFindNext(hdir, &fb, sizeof (fb), &count); } /* while */ DosFindClose(hdir); -} /* __PHYSFS_platformEnumerateFiles */ + + return retval; +} /* __PHYSFS_platformEnumerate */ char *__PHYSFS_platformCurrentDir(void) diff --git a/src/physfs_platform_posix.c b/src/physfs_platform_posix.c index 3a76e333..99f548c9 100644 --- a/src/physfs_platform_posix.c +++ b/src/physfs_platform_posix.c @@ -118,36 +118,35 @@ char *__PHYSFS_platformCalcUserDir(void) } /* __PHYSFS_platformCalcUserDir */ -void __PHYSFS_platformEnumerateFiles(const char *dirname, - PHYSFS_EnumFilesCallback callback, - const char *origdir, - void *callbackdata) +int __PHYSFS_platformEnumerate(const char *dirname, + PHYSFS_EnumerateCallback callback, + const char *origdir, void *callbackdata) { DIR *dir; struct dirent *ent; - char *buf = NULL; + int retval = 1; - errno = 0; dir = opendir(dirname); - if (dir == NULL) - { - allocator.Free(buf); - return; - } /* if */ + BAIL_IF(dir == NULL, errcodeFromErrno(), -1); - while ((ent = readdir(dir)) != NULL) + while ((retval == 1) && ((ent = readdir(dir)) != NULL)) { - if (strcmp(ent->d_name, ".") == 0) - continue; - else if (strcmp(ent->d_name, "..") == 0) - continue; + const char *name = ent->d_name; + if (name[0] == '.') /* ignore "." and ".." */ + { + if ((name[1] == '\0') || ((name[1] == '.') && (name[2] == '\0'))) + continue; + } /* if */ - callback(callbackdata, origdir, ent->d_name); + retval = callback(callbackdata, origdir, name); + if (retval == -1) + PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK); } /* while */ - allocator.Free(buf); closedir(dir); -} /* __PHYSFS_platformEnumerateFiles */ + + return retval; +} /* __PHYSFS_platformEnumerate */ int __PHYSFS_platformMkDir(const char *path) diff --git a/src/physfs_platform_windows.c b/src/physfs_platform_windows.c index 1593ffa5..485f652c 100644 --- a/src/physfs_platform_windows.c +++ b/src/physfs_platform_windows.c @@ -621,21 +621,20 @@ void *__PHYSFS_platformGetThreadID(void) } /* __PHYSFS_platformGetThreadID */ -void __PHYSFS_platformEnumerateFiles(const char *dirname, - PHYSFS_EnumFilesCallback callback, - const char *origdir, - void *callbackdata) +void __PHYSFS_platformEnumerate(const char *dirname, + PHYSFS_EnumerateCallback callback, + const char *origdir, void *callbackdata) { HANDLE dir = INVALID_HANDLE_VALUE; WIN32_FIND_DATAW entw; size_t len = strlen(dirname); char *searchPath = NULL; WCHAR *wSearchPath = NULL; + int retval = 1; /* Allocate a new string for path, maybe '\\', "*", and NULL terminator */ searchPath = (char *) __PHYSFS_smallAlloc(len + 3); - if (searchPath == NULL) - return; + BAIL_IF(!searchPath, PHYSFS_ERR_OUT_OF_MEMORY, -1); /* Copy current dirname */ strcpy(searchPath, dirname); @@ -651,36 +650,40 @@ void __PHYSFS_platformEnumerateFiles(const char *dirname, strcat(searchPath, "*"); UTF8_TO_UNICODE_STACK(wSearchPath, searchPath); - if (!wSearchPath) - return; /* oh well. */ + __PHYSFS_smallFree(searchPath); + BAIL_IF_ERRPASS(!wSearchPath, -1); dir = winFindFirstFileW(wSearchPath, &entw); - __PHYSFS_smallFree(wSearchPath); - __PHYSFS_smallFree(searchPath); - if (dir == INVALID_HANDLE_VALUE) - return; + BAIL_IF(dir == INVALID_HANDLE_VALUE, errcodeFromWinApi(), -1); do { const WCHAR *fn = entw.cFileName; char *utf8; - if ((fn[0] == '.') && (fn[1] == '\0')) - continue; - if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0')) - continue; + if (fn[0] == '.') /* ignore "." and ".." */ + { + if ((fn[1] == '\0') || ((fn[1] == '.') && (fn[2] == '\0'))) + continue; + } /* if */ utf8 = unicodeToUtf8Heap(fn); - if (utf8 != NULL) + if (utf8 == NULL) + retval = -1; + else { - callback(callbackdata, origdir, utf8); + retval = callback(callbackdata, origdir, utf8); allocator.Free(utf8); - } /* if */ - } while (FindNextFileW(dir, &entw) != 0); + if (retval == -1) + PHYSFS_SetErrorCode(PHYSFS_ERR_APP_CALLBACK); + } /* else */ + } while ((retval == 1) && (FindNextFileW(dir, &entw) != 0)); FindClose(dir); -} /* __PHYSFS_platformEnumerateFiles */ + + return retval; +} /* __PHYSFS_platformEnumerate */ int __PHYSFS_platformMkDir(const char *path)