[retro-gtk/wip/aplazas/c-port: 20/34] Port ModuleIterator to C



commit 0b830ef83c61af772e36b059e1ec3ef1807d86c8
Author: Adrien Plazas <kekun plazas laposte net>
Date:   Fri Sep 15 13:27:42 2017 +0200

    Port ModuleIterator to C

 retro-gtk/Makefile.am                |    5 +-
 retro-gtk/retro-module-iterator.c    |  353 ++++++++++++++++++++++++++++++++++
 retro-gtk/retro-module-iterator.h    |   29 +++
 retro-gtk/retro-module-iterator.vala |  125 ------------
 retro-gtk/retro-module-query.c       |    7 +-
 retro-gtk/retro-module-query.h       |    4 +-
 6 files changed, 390 insertions(+), 133 deletions(-)
---
diff --git a/retro-gtk/Makefile.am b/retro-gtk/Makefile.am
index 00d15ef..c31c60d 100644
--- a/retro-gtk/Makefile.am
+++ b/retro-gtk/Makefile.am
@@ -41,6 +41,7 @@ retro_gtk_public_h_sources = \
        retro-lightgun-id.h \
        retro-log.h \
        retro-memory-type.h \
+       retro-module-iterator.h \
        retro-module-query.h \
        retro-mouse-id.h \
        retro-pixel-format.h \
@@ -94,8 +95,8 @@ libretro_gtk_la_SOURCES = \
        retro-log.c \
        retro-memory-type.c \
        retro-module.c \
+       retro-module-iterator.c \
        retro-module-query.c \
-       retro-module-iterator.vala \
        retro-mouse-id.c \
        retro-option.c \
        retro-options.c \
@@ -124,6 +125,8 @@ retro-log.c: retro-gtk-internal.h
 
 retro-module.c: retro-gtk-internal.h
 
+retro-module-iterator.c: retro-gtk-internal.h
+
 retro-module-query.c: retro-gtk-internal.h
 
 retro-pa-player.c: retro-gtk-internal.h
diff --git a/retro-gtk/retro-module-iterator.c b/retro-gtk/retro-module-iterator.c
new file mode 100644
index 0000000..0ecf7fb
--- /dev/null
+++ b/retro-gtk/retro-module-iterator.c
@@ -0,0 +1,353 @@
+// This file is part of retro-gtk. License: GPL-3.0+.
+
+#include "retro-gtk-internal.h"
+
+#include "retro-module-iterator.h"
+
+struct _RetroModuleIterator
+{
+  GObject parent_instance;
+  gchar **directories;
+  gboolean recursive;
+  gint current_directory;
+  GFileEnumerator *file_enumerator;
+  RetroCoreDescriptor *core_descriptor;
+  RetroModuleIterator *sub_directory;
+  GHashTable *visited;
+};
+
+G_DEFINE_TYPE (RetroModuleIterator, retro_module_iterator, G_TYPE_OBJECT)
+
+/* Private */
+
+static void
+retro_module_iterator_finalize (GObject *object)
+{
+  RetroModuleIterator *self = RETRO_MODULE_ITERATOR (object);
+
+  g_strfreev (self->directories);
+  g_clear_object (&self->file_enumerator);
+  g_clear_object (&self->core_descriptor);
+  g_clear_object (&self->sub_directory);
+  g_hash_table_unref (self->visited);
+
+  G_OBJECT_CLASS (retro_module_iterator_parent_class)->finalize (object);
+}
+
+static void
+retro_module_iterator_class_init (RetroModuleIteratorClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = retro_module_iterator_finalize;
+}
+
+static void
+retro_module_iterator_init (RetroModuleIterator *self)
+{
+}
+
+static RetroModuleIterator *
+retro_module_iterator_new_for_subdirectory (const gchar *lookup_path,
+                                            GHashTable  *visited_paths)
+{
+  RetroModuleIterator *self;
+
+  g_return_val_if_fail (lookup_path != NULL, NULL);
+  g_return_val_if_fail (visited_paths != NULL, NULL);
+
+  self = g_object_new (RETRO_TYPE_MODULE_ITERATOR, NULL);
+  self->directories = g_new0 (gchar *, 2);
+  self->directories[0] = g_strdup (lookup_path);
+  self->recursive = TRUE;
+  g_set_object (&self->visited, visited_paths);
+
+  return self;
+}
+
+static gboolean
+retro_module_iterator_was_current_directory_visited (RetroModuleIterator *self)
+{
+  GFile *current_directory_file;
+  gchar *current_directory_path;
+  gboolean result;
+
+  g_return_val_if_fail (RETRO_IS_MODULE_ITERATOR (self), FALSE);
+
+  current_directory_file = g_file_new_for_path (self->directories[self->current_directory]);
+  current_directory_path = g_file_get_path (current_directory_file);
+  g_object_unref (current_directory_file);
+  result = g_hash_table_contains (self->visited, current_directory_path);
+  g_free (current_directory_path);
+
+  return result;
+}
+
+static void
+retro_module_iterator_set_current_directory_as_visited (RetroModuleIterator *self)
+{
+  GFile *current_directory_file;
+  gchar *current_directory_path;
+
+  g_return_if_fail (RETRO_IS_MODULE_ITERATOR (self));
+
+  current_directory_file = g_file_new_for_path (self->directories[self->current_directory]);
+  current_directory_path = g_file_get_path (current_directory_file);
+  g_object_unref (current_directory_file);
+  g_hash_table_add (self->visited, current_directory_path);
+}
+
+static gboolean
+retro_module_iterator_next_in_sub_directory (RetroModuleIterator *self)
+{
+  g_return_val_if_fail (RETRO_IS_MODULE_ITERATOR (self), FALSE);
+
+  if (retro_module_iterator_next (self->sub_directory)) {
+    if (self->core_descriptor != NULL)
+      g_object_unref (self->core_descriptor);
+
+    self->core_descriptor = retro_module_iterator_get (self->sub_directory);
+
+    return TRUE;
+  }
+
+  if (self->sub_directory != NULL) {
+    g_object_unref (self->sub_directory);
+    self->sub_directory = NULL;
+  }
+
+  return FALSE;
+}
+
+static gboolean
+retro_module_iterator_iterate_next_in_current_path (RetroModuleIterator  *self,
+                                                    GFile                *directory,
+                                                    GFileInfo            *info,
+                                                    GError              **error)
+{
+  const gchar *sub_directory_basename;
+  GFile *sub_directory_file;
+  gchar *sub_directory_path;
+  const gchar *core_descriptor_basename;
+  GFile *core_descriptor_file;
+  gchar *core_descriptor_path;
+  RetroCoreDescriptor *core_descriptor;
+  GError *tmp_error = NULL;
+
+  g_return_val_if_fail (RETRO_IS_MODULE_ITERATOR (self), FALSE);
+  g_return_val_if_fail (G_IS_FILE (directory), FALSE);
+  g_return_val_if_fail (G_IS_FILE_INFO (info), FALSE);
+
+  if (self->recursive &&
+      g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY &&
+      self->sub_directory == NULL) {
+    sub_directory_basename = g_file_info_get_name (info);
+    sub_directory_file = g_file_get_child (directory, sub_directory_basename);
+    sub_directory_path = g_file_get_path (sub_directory_file);
+      g_object_unref (sub_directory_file);
+
+    if (g_hash_table_contains (self->visited, sub_directory_path)) {
+      g_free (sub_directory_path);
+
+      return FALSE;
+    }
+
+    self->sub_directory = retro_module_iterator_new_for_subdirectory (sub_directory_path, self->visited);
+    g_free (sub_directory_path);
+
+    return retro_module_iterator_next_in_sub_directory (self);
+  }
+
+  core_descriptor_basename = g_file_info_get_name (info);
+  if (!g_str_has_suffix (core_descriptor_basename, ".libretro"))
+    return FALSE;
+
+  core_descriptor_file = g_file_get_child (directory, core_descriptor_basename);
+  core_descriptor_path = g_file_get_path (core_descriptor_file);
+  g_object_unref (core_descriptor_file);
+  core_descriptor = retro_core_descriptor_new (core_descriptor_path, &tmp_error);
+  if (G_UNLIKELY (tmp_error != NULL)) {
+    g_debug ("%s", tmp_error->message);
+
+    g_error_free (tmp_error);
+    g_free (core_descriptor_path);
+
+    return FALSE;
+  }
+
+  g_free (core_descriptor_path);
+  g_clear_object (&self->core_descriptor);
+  self->core_descriptor = core_descriptor;
+
+  return TRUE;
+}
+
+static gboolean
+retro_module_iterator_next_in_current_path (RetroModuleIterator  *self,
+                                            GError              **error)
+{
+  GFile *directory = NULL;
+  GFileInfo *info = NULL;
+  gboolean found = FALSE;
+
+  GError *tmp_error = NULL;
+
+  g_return_val_if_fail (RETRO_IS_MODULE_ITERATOR (self), FALSE);
+
+  if (self->sub_directory != NULL &&
+      retro_module_iterator_next_in_sub_directory (self))
+    return TRUE;
+
+  directory = g_file_new_for_path (self->directories[self->current_directory]);
+
+  if (self->file_enumerator == NULL) {
+    self->file_enumerator =
+      g_file_enumerate_children (directory,
+                                 "",
+                                 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                 NULL,
+                                 &tmp_error);
+    if (G_UNLIKELY (tmp_error != NULL)) {
+      g_propagate_error (error, tmp_error);
+      g_object_unref (directory);
+      g_clear_object (&self->file_enumerator);
+
+      return FALSE;
+    }
+  }
+
+  if (self->file_enumerator == NULL) {
+    g_object_unref (directory);
+
+    return FALSE;
+  }
+
+  while (TRUE) {
+    if (info != NULL)
+      g_object_unref (info);
+
+    info = g_file_enumerator_next_file (self->file_enumerator, NULL, &tmp_error);
+    if (G_UNLIKELY (tmp_error != NULL)) {
+      g_propagate_error (error, tmp_error);
+      g_clear_object (&info);
+      g_object_unref (directory);
+
+      return FALSE;
+    }
+
+    if (info == NULL)
+      break;
+
+    found = retro_module_iterator_iterate_next_in_current_path (self, directory, info, &tmp_error);
+    if (G_UNLIKELY (tmp_error != NULL)) {
+      g_propagate_error (error, tmp_error);
+      g_object_unref (info);
+      g_object_unref (directory);
+
+      return FALSE;
+    }
+
+    if (found) {
+      g_object_unref (info);
+      g_object_unref (directory);
+
+      return TRUE;
+    }
+  }
+
+  g_clear_object (&self->file_enumerator);
+  g_object_unref (directory);
+
+  return FALSE;
+}
+
+/* Public */
+
+/**
+ * retro_module_iterator_get:
+ * @self: a #RetroModuleIterator
+ *
+ * Gets the last #RetroCoreDescriptor fetched by retro_module_iterator_next(),
+ * or %NULL if the end was reached.
+ *
+ * Returns: (nullable) (transfer full): a #RetroCoreDescriptor, or %NULL
+ */
+RetroCoreDescriptor *
+retro_module_iterator_get (RetroModuleIterator *self)
+{
+  g_return_val_if_fail (RETRO_IS_MODULE_ITERATOR (self), NULL);
+
+  return self->core_descriptor != NULL ?
+    g_object_ref (self->core_descriptor) :
+    NULL;
+}
+
+/**
+ * retro_module_iterator_next:
+ * @self: a #RetroModuleIterator
+ *
+ * Fetch the next #RetroModuleIterator.
+ *
+ * Returns: %FALSE if it reached the end, %TRUE otherwise
+ */
+gboolean
+retro_module_iterator_next (RetroModuleIterator *self)
+{
+  gboolean next_in_current_path;
+  GError *tmp_error = NULL;
+
+  g_return_val_if_fail (RETRO_IS_MODULE_ITERATOR (self), FALSE);
+
+  while (self->directories[self->current_directory] != NULL) {
+    retro_module_iterator_set_current_directory_as_visited (self);
+
+    next_in_current_path = retro_module_iterator_next_in_current_path (self, &tmp_error);
+    if (G_UNLIKELY (tmp_error != NULL)) {
+      g_debug ("%s", tmp_error->message);
+      g_clear_error (&tmp_error);
+      next_in_current_path = FALSE;
+    }
+
+    if (next_in_current_path)
+      return TRUE;
+
+    while (self->directories[self->current_directory] != NULL &&
+           retro_module_iterator_was_current_directory_visited (self))
+      self->current_directory++;
+  }
+
+  g_clear_object (&self->file_enumerator);
+  g_clear_object (&self->core_descriptor);
+  if (self->sub_directory != NULL) {
+    g_object_unref (self->sub_directory);
+    self->sub_directory = NULL;
+  }
+
+  return FALSE;
+}
+
+/**
+ * retro_module_iterator_new:
+ * @lookup_paths: (array zero-terminated=1): paths where to look for Libretro
+ * cores
+ * @recursive: whether to run the query in sub-directories
+ *
+ * Creates a new #RetroModuleIterator.
+ *
+ * Returns: (transfer full): a new #RetroModuleIterator
+ */
+RetroModuleIterator *
+retro_module_iterator_new (const gchar * const *lookup_paths,
+                           gboolean             recursive)
+{
+  RetroModuleIterator *self;
+
+  g_return_val_if_fail (lookup_paths != NULL, NULL);
+
+  self = g_object_new (RETRO_TYPE_MODULE_ITERATOR, NULL);
+  self->directories = g_strdupv ((gchar **) lookup_paths);
+  self->recursive = recursive;
+  self->visited = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
+
+  return self;
+}
diff --git a/retro-gtk/retro-module-iterator.h b/retro-gtk/retro-module-iterator.h
new file mode 100644
index 0000000..300c04f
--- /dev/null
+++ b/retro-gtk/retro-module-iterator.h
@@ -0,0 +1,29 @@
+// This file is part of retro-gtk. License: GPL-3.0+.
+
+#ifndef RETRO_MODULE_ITERATOR_H
+#define RETRO_MODULE_ITERATOR_H
+
+#if !defined(__RETRO_GTK_INSIDE__) && !defined(RETRO_GTK_COMPILATION)
+# error "Only <retro-gtk.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+// FIXME Remove as soon as possible.
+typedef struct _RetroCoreDescriptor RetroCoreDescriptor;
+
+#define RETRO_TYPE_MODULE_ITERATOR (retro_module_iterator_get_type())
+
+G_DECLARE_FINAL_TYPE (RetroModuleIterator, retro_module_iterator, RETRO, MODULE_ITERATOR, GObject)
+
+RetroModuleIterator *retro_module_iterator_new (const gchar * const *lookup_paths,
+                                                gboolean             recursive);
+RetroCoreDescriptor *retro_module_iterator_get (RetroModuleIterator *self);
+gboolean retro_module_iterator_next (RetroModuleIterator *self);
+
+G_END_DECLS
+
+#endif /* RETRO_MODULE_ITERATOR_H */
+
diff --git a/retro-gtk/retro-module-query.c b/retro-gtk/retro-module-query.c
index 942a14e..5336b47 100644
--- a/retro-gtk/retro-module-query.c
+++ b/retro-gtk/retro-module-query.c
@@ -19,8 +19,6 @@ G_DEFINE_TYPE (RetroModuleQuery, retro_module_query, G_TYPE_OBJECT)
 static void
 retro_module_query_finalize (GObject *object)
 {
-  RetroModuleQuery *self = RETRO_MODULE_QUERY (object);
-
   G_OBJECT_CLASS (retro_module_query_parent_class)->finalize (object);
 }
 
@@ -41,7 +39,7 @@ static gchar **
 retro_module_query_get_plugin_lookup_paths ()
 {
   gchar **envp;
-  gchar *env_plugin_path;
+  const gchar *env_plugin_path;
   gchar *full_plugin_path;
   gchar **result;
 
@@ -80,7 +78,8 @@ retro_module_query_iterator (RetroModuleQuery *self)
   g_return_val_if_fail (RETRO_IS_MODULE_ITERATOR (self), NULL);
 
   paths = retro_module_query_get_plugin_lookup_paths ();
-  result = retro_module_iterator_new (paths, g_strv_length (paths), self->recursive);
+  result = retro_module_iterator_new ((const gchar * const *) paths,
+                                      self->recursive);
   g_strfreev (paths);
 
   return result;
diff --git a/retro-gtk/retro-module-query.h b/retro-gtk/retro-module-query.h
index 1c6e445..e3db695 100644
--- a/retro-gtk/retro-module-query.h
+++ b/retro-gtk/retro-module-query.h
@@ -8,12 +8,10 @@
 #endif
 
 #include <glib-object.h>
+#include "retro-module-iterator.h"
 
 G_BEGIN_DECLS
 
-// FIXME Remove as soon as possible.
-typedef struct _RetroModuleIterator RetroModuleIterator;
-
 #define RETRO_TYPE_MODULE_QUERY (retro_module_query_get_type())
 
 G_DECLARE_FINAL_TYPE (RetroModuleQuery, retro_module_query, RETRO, MODULE_QUERY, GObject)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]