diff --git a/src/physfs.c b/src/physfs.c index a5c30b9..8630f59 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; - - snprintf(path, slen, "%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); + 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)) + { + 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) + { + retval = data->callback(data->callbackData, origdir, fname); + if (retval == -1) + data->errcode = PHYSFS_ERR_APP_CALLBACK; + } /* if */ + } /* else */ + + __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 2f2b045..2863388 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. + * \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. * - * 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: + * As of PhysicsFS 2.1, this function just wraps PHYSFS_enumerate() and + * ignores errors. Consider using PHYSFS_enumerate() or + * PHYSFS_enumerateFiles() instead. * - * \code - * - * static void printDir(void *data, const char *origdir, const char *fname) - * { - * printf(" * We've got [%s] in [%s].\n", fname, origdir); - * } - * - * // ... - * 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. - * - * 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 57e6df4..3099f35 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 0597eec..a0aac08 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 43b247d..dceeabc 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 eec7bad..bead0ba 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 7c0413b..f58a15f 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 70d4686..64077d3 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 344156f..3d09f38 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 28ee09f..9c649ed 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 1889de5..9547537 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 f604bd9..91e0adc 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 c7941ef..4edfe1a 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 d0585d4..636b059 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. + * 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. */ -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); /* * 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 dbbb20e..44bd140 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 3a76e33..99f548c 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 1593ffa..485f652 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)