shithub: choc

Download patch

ref: 419776fc7241e8337fe84e8777fad095e4b13945
parent: 7f03c58cbf63a63bb5a105e06f9f675bf60adfc7
author: Simon Howard <fraggle@soulsphere.org>
date: Sat Oct 27 13:50:14 EDT 2018

Add system-specific globbing API.

Part of #1051.

--- a/configure.ac
+++ b/configure.ac
@@ -77,7 +77,7 @@
 LDFLAGS="$LDFLAGS $SDL_LIBS ${SAMPLERATE_LIBS:-} ${PNG_LIBS:-}"
 AC_CHECK_LIB(m, log)
 
-AC_CHECK_HEADERS([linux/kd.h dev/isa/spkrio.h dev/speaker/speaker.h])
+AC_CHECK_HEADERS([dirent.h linux/kd.h dev/isa/spkrio.h dev/speaker/speaker.h])
 AC_CHECK_FUNCS(mmap ioperm)
 AC_CHECK_DECLS([strcasecmp, strncasecmp], [], [], [[#include <strings.h>]])
 
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -65,6 +65,7 @@
 gusconf.c            gusconf.h             \
 i_cdmus.c            i_cdmus.h             \
 i_endoom.c           i_endoom.h            \
+i_glob.c             i_glob.h              \
 i_input.c            i_input.h             \
 i_joystick.c         i_joystick.h          \
                      i_swap.h              \
--- /dev/null
+++ b/src/i_glob.c
@@ -1,0 +1,148 @@
+//
+// Copyright(C) 2018 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+//
+// File globbing API. This allows the contents of the filesystem
+// to be interrogated.
+//
+
+// TODO: Merge win_opendir.[ch] into this file for MSVC implementation.
+
+#include <stdlib.h>
+
+#include "i_glob.h"
+#include "m_misc.h"
+#include "config.h"
+
+#ifdef HAVE_DIRENT_H
+#include "dirent.h"
+
+struct glob_s
+{
+    char *glob;
+    DIR *dir;
+    char *directory;
+    char *last_filename;
+};
+
+glob_t *I_StartGlob(const char *directory, const char *glob)
+{
+    glob_t *result;
+
+    result = malloc(sizeof(glob_t));
+    if (result == NULL)
+    {
+        return NULL;
+    }
+
+    result->dir = opendir(directory);
+    if (result->dir == NULL)
+    {
+        free(result);
+        return NULL;
+    }
+
+    result->directory = M_StringDuplicate(directory);
+    result->glob = M_StringDuplicate(glob);
+    result->last_filename = NULL;
+    return result;
+}
+
+void I_EndGlob(glob_t *glob)
+{
+    if (glob == NULL)
+    {
+        return;
+    }
+
+    free(glob->directory);
+    free(glob->last_filename);
+    free(glob->glob);
+    (void) closedir(glob->dir);
+    free(glob);
+}
+
+static boolean MatchesGlob(const char *name, const char *glob)
+{
+    while (*glob != '\0')
+    {
+        if (*glob == '*')
+        {
+            // To handle *-matching we skip past the * and recurse
+            // to check each subsequent character in turn. If none
+            // match then the whole match is a failure.
+            while (*name != '\0')
+            {
+                if (MatchesGlob(name, glob + 1))
+                {
+                    return true;
+                }
+                ++name;
+            }
+            return false;
+        }
+        else if (*glob != '?' && *name != *glob)
+        {
+            // For normal characters the name must match the glob,
+            // but for ? we don't care what the character is.
+            return false;
+        }
+
+        ++name;
+        ++glob;
+    }
+
+    // Match successful when glob and name end at the same time.
+    return *name == '\0';
+}
+
+const char *I_NextGlob(glob_t *glob)
+{
+    struct dirent *de;
+
+    do
+    {
+        de = readdir(glob->dir);
+        if (de == NULL)
+        {
+            return NULL;
+        }
+    } while (de->d_type != DT_REG || !MatchesGlob(de->d_name, glob->glob));
+
+    // Return the fully-qualified path, not just the bare filename.
+    free(glob->last_filename);
+    glob->last_filename = M_StringJoin(glob->directory, DIR_SEPARATOR_S,
+                                       de->d_name, NULL);
+    return glob->last_filename;
+}
+
+#else /* #ifndef HAVE_DIRENT_H */
+
+#warning No native implementation of file globbing.
+
+glob_t *I_StartGlob(const char *directory, const char *glob)
+{
+    return NULL;
+}
+
+void I_EndGlob(glob_t *glob)
+{
+}
+
+const char *I_NextGlob(glob_t *glob)
+{
+    return "";
+}
+
+#endif /* #ifndef HAVE_DIRENT_H */
+
--- /dev/null
+++ b/src/i_glob.h
@@ -1,0 +1,36 @@
+//
+// Copyright(C) 2018 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// DESCRIPTION:
+//	System specific file globbing interface.
+//
+
+
+#ifndef __I_GLOB__
+#define __I_GLOB__
+
+typedef struct glob_s glob_t;
+
+// Start reading a list of file paths from the given directory which match
+// the given glob pattern. I_EndGlob() must be called on completion.
+glob_t *I_StartGlob(const char *directory, const char *glob);
+
+// Finish reading file list.
+void I_EndGlob(glob_t *glob);
+
+// Read the name of the next globbed filename. NULL is returned if there
+// are no more found.
+const char *I_NextGlob(glob_t *glob);
+
+#endif
+