Improved globbing extra.
authorRyan C. Gordon <icculus@icculus.org>
Sat, 28 Mar 2009 18:14:16 -0400
changeset 977 5a2f49a5f804
parent 976 372a5232bc18
child 978 4f631458a5a0
Improved globbing extra. Fixed FIXMEs, improved documentation, updated for 2.1 API.
extras/globbing.c
extras/globbing.h
--- a/extras/globbing.c	Sat Mar 28 17:51:10 2009 -0400
+++ b/extras/globbing.c	Sat Mar 28 18:14:16 2009 -0400
@@ -4,8 +4,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
+#include <assert.h>
 
-#include "physfs.h"
 #include "globbing.h"
 
 /**
@@ -22,7 +22,7 @@
  *  NO WARRANTY.
  *
  * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
- *  Please see LICENSE.txt in the source's "docs" directory.
+ *  Please see the file LICENSE.txt in the source's root directory.
  *
  *  \author Ryan C. Gordon.
  */
@@ -87,28 +87,108 @@
     return(*fnameptr == *wildptr);
 } /* matchesPattern */
 
+typedef struct
+{
+    const PHYSFS_Allocator *allocator;
+    const char *wildcard;
+    int caseSensitive;
+    PHYSFS_EnumFilesCallback callback;
+    void *origData;
+} WildcardCallbackData;
+
+
+/*
+ * This callback sits between the enumerator and the enduser callback,
+ *  filtering out files that don't match the wildcard pattern.
+ */
+static void wildcardCallback(void *_d, const char *origdir, const char *fname)
+{
+    const WildcardCallbackData *data = (const WildcardCallbackData *) _d;
+    if (matchesPattern(fname, data->wildcard, data->caseSensitive))
+        data->callback(data->origData, origdir, fname);
+} /* wildcardCallback */
+
+
+void PHYSFSEXT_enumerateFilesCallbackWildcard(const char *dir,
+                                              const char *wildcard,
+                                              int caseSensitive,
+                                              PHYSFS_EnumFilesCallback c,
+                                              void *d)
+{
+    WildcardCallbackData data;
+    data.allocator = PHYSFS_getAllocator();
+    data.wildcard = wildcard;
+    data.caseSensitive = caseSensitive;
+    data.callback = c;
+    data.origData = d;
+    PHYSFS_enumerateFilesCallback(dir, wildcardCallback, &data);
+} /* PHYSFSEXT_enumerateFilesCallbackWildcard */
+
+
+void PHYSFSEXT_freeEnumeration(char **list)
+{
+    const PHYSFS_Allocator *allocator = PHYSFS_getAllocator();
+    int i;
+    if (list != NULL)
+    {
+        for (i = 0; list[i] != NULL; i++)
+            allocator->Free(list[i]);
+        allocator->Free(list);
+    } /* if */
+} /* PHYSFSEXT_freeEnumeration */
+
 
 char **PHYSFSEXT_enumerateFilesWildcard(const char *dir, const char *wildcard,
                                         int caseSensitive)
 {
-    char **rc = PHYSFS_enumerateFiles(dir);
-    char **i = rc;
-    char **j;
+    const PHYSFS_Allocator *allocator = PHYSFS_getAllocator();
+    char **list = PHYSFS_enumerateFiles(dir);
+    char **retval = NULL;
+    int totalmatches = 0;
+    int matches = 0;
+    char **i;
 
-    while (*i != NULL)
+    for (i = list; *i != NULL; i++)
     {
+        #if 0
+        printf("matchesPattern: '%s' vs '%s' (%s) ... %s\n", *i, wildcard,
+               caseSensitive ? "case" : "nocase",
+               matchesPattern(*i, wildcard, caseSensitive) ? "true" : "false");
+        #endif
         if (matchesPattern(*i, wildcard, caseSensitive))
-            i++;
-        else
-        {
-            /* FIXME: This counts on physfs's allocation method not changing! */
-            free(*i);
-            for (j = i; *j != NULL; j++)
-                j[0] = j[1];
-        } /* else */
+            totalmatches++;
     } /* for */
 
-    return(rc);
+    retval = (char **) allocator->Malloc(sizeof (char *) * (totalmatches+1));
+    if (retval != NULL)
+    {
+        for (i = list; ((matches < totalmatches) && (*i != NULL)); i++)
+        {
+            if (matchesPattern(*i, wildcard, caseSensitive))
+            {
+                retval[matches] = (char *) allocator->Malloc(strlen(*i) + 1);
+                if (retval[matches] == NULL)
+                {
+                    while (matches--)
+                        allocator->Free(retval[matches]);
+                    allocator->Free(retval);
+                    retval = NULL;
+                    break;
+                } /* if */
+                strcpy(retval[matches], *i);
+                matches++;
+            } /* if */
+        } /* for */
+
+        if (retval != NULL)
+        {
+            assert(totalmatches == matches);
+            retval[matches] = NULL;
+        } /* if */
+    } /* if */
+
+    PHYSFS_freeList(list);
+    return(retval);
 } /* PHYSFSEXT_enumerateFilesWildcard */
 
 
@@ -148,7 +228,7 @@
     } /* for */
     printf("\n  total %d files.\n\n", rc);
 
-    PHYSFS_freeList(flist);
+    PHYSFSEXT_freeEnumeration(flist);
     PHYSFS_deinit();
 
     return(0);
--- a/extras/globbing.h	Sat Mar 28 17:51:10 2009 -0400
+++ b/extras/globbing.h	Sat Mar 28 18:14:16 2009 -0400
@@ -1,5 +1,7 @@
 /** \file globbing.h */
 
+#include "physfs.h"
+
 /**
  * \mainpage PhysicsFS globbing
  *
@@ -9,10 +11,10 @@
  *  locating matching entries.
  *
  * Usage: Set up PhysicsFS as you normally would, then use
- *  PHYSFSEXT_enumerateFilesPattern() when enumerating files. This is just
+ *  PHYSFSEXT_enumerateFilesWildcard() when enumerating files. This is just
  *  like PHYSFS_enumerateFiles(), but it returns a subset that matches your
- *  wildcard pattern. You must call PHYSFS_freeList() on the results, just
- *  like you would with PHYSFS_enumerateFiles().
+ *  wildcard pattern. You must call PHYSFSEXT_freeEnumeration() on the results,
+ *  just PHYSFS_enumerateFiles() would do with PHYSFS_freeList().
  *
  * License: this code is public domain. I make no warranty that it is useful,
  *  correct, harmless, or environmentally safe.
@@ -33,7 +35,7 @@
 
 /**
  * \fn char **PHYSFS_enumerateFilesWildcard(const char *dir, const char *wildcard, int caseSensitive)
- * \brief Get a file listing of a search path's directory.
+ * \brief Get a file listing of a search path's directory, filtered with a wildcard pattern.
  *
  * Matching directories are interpolated. That is, if "C:\mydir" is in the
  *  search path and contains a directory "savegames" that contains "x.sav",
@@ -63,15 +65,89 @@
  * Wildcard strings can use the '*' and '?' characters, currently.
  * Matches can be case-insensitive if you pass a zero for argument 3.
  *
- * Don't forget to call PHYSFS_freeList() with the return value from this
- *  function when you are done with it.
+ * Don't forget to call PHYSFSEXT_freeEnumerator() with the return value from
+ *  this function when you are done with it. As we use PhysicsFS's allocator
+ *  for this list, you must free it before calling PHYSFS_deinit().
+ *  Do not use PHYSFS_freeList() on the returned value!
  *
  *    \param dir directory in platform-independent notation to enumerate.
+ *    \param wildcard Wildcard pattern to use for filtering.
+ *    \param caseSensitive Zero for case-insensitive matching,
+ *                         non-zero for case-sensitive.
  *   \return Null-terminated array of null-terminated strings.
+ *
+ * \sa PHYSFSEXT_freeEnumeration
  */
 __EXPORT__ char **PHYSFSEXT_enumerateFilesWildcard(const char *dir,
                                                    const char *wildcard,
                                                    int caseSensitive);
 
+/**
+ * \fn void PHYSFSEXT_freeEnumeration(char **list)
+ * \brief Free data returned by PHYSFSEXT_enumerateFilesWildcard
+ *
+ * Conceptually, this works like PHYSFS_freeList(), but is used with data
+ *  returned by PHYSFSEXT_enumerateFilesWildcard() only. Be sure to call this
+ *  on any returned data from that function before
+ *
+ *    \param list Pointer previously returned by
+ *                PHYSFSEXT_enumerateFilesWildcard(). It is safe to pass a
+ *                NULL here.
+ *
+ * \sa PHYSFSEXT_enumerateFilesWildcard
+ */
+__EXPORT__ void PHYSFSEXT_freeEnumeration(char **list);
+
+
+/**
+ * \fn void PHYSFSEXT_enumerateFilesCallbackWildcard(const char *dir, const char *wildcard, int caseSensitive, PHYSFS_EnumFilesCallback c, void *d);
+ * \brief Get a file listing of a search path's directory, filtered with a wildcard pattern, using an application-defined callback.
+ *
+ * This function is equivalent to PHYSFSEXT_enumerateFilesWildcard(). It
+ *  reports file listings, filtered by a wildcard pattern.
+ *
+ * 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);
+ * }
+ *
+ * // ...
+ * PHYSFS_enumerateFilesCallbackWildcard("savegames","*.sav",0,printDir,NULL);
+ * \endcode
+ *
+ * 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_enumerateFilesWildcard() 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. <em>Any sorting you find in
+ *  these callbacks is just pure luck. Do not rely on it.</em> As this walks
+ *  the entire list of archives, you may receive duplicate filenames.
+ *
+ * Wildcard strings can use the '*' and '?' characters, currently.
+ * Matches can be case-insensitive if you pass a zero for argument 3.
+ *
+ *    \param dir Directory, in platform-independent notation, to enumerate.
+ *    \param wildcard Wildcard pattern to use for filtering.
+ *    \param caseSensitive Zero for case-insensitive matching,
+ *                         non-zero for case-sensitive.
+ *    \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_enumerateFiles
+ */
+__EXPORT__ void PHYSFSEXT_enumerateFilesCallbackWildcard(const char *dir,
+                                              const char *wildcard,
+                                              int caseSensitive,
+                                              PHYSFS_EnumFilesCallback c,
+                                              void *d);
+
 /* end of globbing.h ... */