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)