[glib] Clean up g_settings_list_schemas()



commit d2c06994402ee30e989efb9e912d3abd16ca0e10
Author: Ryan Lortie <desrt desrt ca>
Date:   Sat Oct 2 22:42:02 2010 -0400

    Clean up g_settings_list_schemas()
    
    In its previous form, g_settings_list_schemas() was not useful as a tool
    to prevent aborts due to using g_settings_new() with an invalid schema
    name.  This is because g_settings_list_scheams() also listed relocatable
    schemas, and calling g_settings_new() for those would abort just the
    same as if you called it for a non-existent schema.
    
    Modify g_settings_list_schemas() so that it only returns schemas for
    which it is safe to call g_settings_new().  Add another call for sake of
    completeness: g_settings_list_relocatable_schemas().

 gio/gio.symbols       |    1 +
 gio/gsettings.h       |    1 +
 gio/gsettingsschema.c |  126 +++++++++++++++++++++++++++++++++++++------------
 gio/tests/gsettings.c |    7 ++-
 4 files changed, 103 insertions(+), 32 deletions(-)
---
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 0e33f61..29378f4 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1520,6 +1520,7 @@ g_keyfile_settings_backend_new
 #if IN_HEADER(__G_SETTINGS_H__)
 #if IN_FILE(__G_SETTINGS_SCHEMA_C__)
 g_settings_list_schemas
+g_settings_list_relocatable_schemas
 #endif
 
 #if IN_FILE(__G_SETTINGS_C__)
diff --git a/gio/gsettings.h b/gio/gsettings.h
index 0ee4399..2b4e2f6 100644
--- a/gio/gsettings.h
+++ b/gio/gsettings.h
@@ -71,6 +71,7 @@ struct _GSettings
 GType                   g_settings_get_type                             (void);
 
 const gchar * const *   g_settings_list_schemas                         (void);
+const gchar * const *   g_settings_list_relocatable_schemas             (void);
 GSettings *             g_settings_new                                  (const gchar        *schema);
 GSettings *             g_settings_new_with_path                        (const gchar        *schema,
                                                                          const gchar        *path);
diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c
index 053b94e..6411f4f 100644
--- a/gio/gsettingsschema.c
+++ b/gio/gsettingsschema.c
@@ -83,65 +83,129 @@ initialise_schema_sources (void)
     }
 }
 
-static void
-add_item (gpointer key,
-          gpointer value,
-          gpointer user_data)
+static gboolean
+steal_item (gpointer key,
+            gpointer value,
+            gpointer user_data)
 {
   gchar ***ptr = user_data;
 
   *(*ptr)++ = (gchar *) key;
+
+  return TRUE;
 }
 
-/**
- * g_settings_list_schemas:
- * @returns: a list of the schemas installed on the system
- *
- * Returns: (element-type utf8) (transfer none):  a list of GSettings schemas that are available.  The list
- * must not be modified or freed.
- **/
-const gchar * const *
-g_settings_list_schemas (void)
-{
-  static gsize schema_list;
+static const gchar * const *non_relocatable_schema_list;
+static const gchar * const *relocatable_schema_list;
+static gsize schema_lists_initialised;
 
-  if (g_once_init_enter (&schema_list))
+static void
+ensure_schema_lists (void)
+{
+  if (g_once_init_enter (&schema_lists_initialised))
     {
-      GHashTable *builder;
+      GHashTable *single, *reloc;
+      const gchar **ptr;
       GSList *source;
       gchar **list;
-      gchar **ptr;
       gint i;
 
       initialise_schema_sources ();
 
-      builder = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+      /* We use hash tables to avoid duplicate listings for schemas that
+       * appear in more than one file.
+       */
+      single = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+      reloc = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
       for (source = schema_sources; source; source = source->next)
         {
           list = gvdb_table_list (source->data, "");
 
-          if (list)
-            {
-              for (i = 0; list[i]; i++)
-                g_hash_table_insert (builder, list[i], NULL);
+          g_assert (list != NULL);
 
-              /* not strfreev: we stole the strings into the hashtable */
-              g_free (list);
+          for (i = 0; list[i]; i++)
+            {
+              if (!g_hash_table_lookup (single, list[i]) &&
+                  !g_hash_table_lookup (reloc, list[i]))
+                {
+                  GvdbTable *table;
+
+                  table = gvdb_table_get_table (source->data, list[i]);
+                  g_assert (table != NULL);
+
+                  if (gvdb_table_has_value (table, ".path"))
+                    g_hash_table_insert (single, g_strdup (list[i]), NULL);
+                  else
+                    g_hash_table_insert (reloc, g_strdup (list[i]), NULL);
+                }
             }
+
+          g_strfreev (list);
         }
 
-      ptr = list = g_new (gchar *, g_hash_table_size (builder) + 1);
-      g_hash_table_foreach (builder, add_item, &ptr);
+      ptr = g_new (const gchar *, g_hash_table_size (single) + 1);
+      non_relocatable_schema_list = ptr;
+      g_hash_table_foreach_steal (single, steal_item, &ptr);
+      g_hash_table_unref (single);
       *ptr = NULL;
 
-      g_hash_table_steal_all (builder);
-      g_hash_table_unref (builder);
+      ptr = g_new (const gchar *, g_hash_table_size (reloc) + 1);
+      relocatable_schema_list = ptr;
+      g_hash_table_foreach_steal (reloc, steal_item, &ptr);
+      g_hash_table_unref (reloc);
+      *ptr = NULL;
 
-      g_once_init_leave (&schema_list, (gsize) list);
+      g_once_init_leave (&schema_lists_initialised, TRUE);
     }
+}
+
+/**
+ * g_settings_list_schemas:
+ *
+ * Gets a list of the #GSettings schemas installed on the system.  The
+ * returned list is exactly the list of schemas for which you may call
+ * g_settings_new() without adverse effects.
+ *
+ * This function does not list the schemas that do not provide their own
+ * paths (ie: schemas for which you must use
+ * g_settings_new_with_path()).  See
+ * g_settings_list_relocatable_schemas() for that.
+ *
+ * Returns: (element-type utf8) (transfer none):  a list of #GSettings
+ *   schemas that are available.  The list must not be modified or
+ *   freed.
+ **/
+const gchar * const *
+g_settings_list_schemas (void)
+{
+  ensure_schema_lists ();
+
+  return non_relocatable_schema_list;
+}
+
+/**
+ * g_settings_list_relocatable_schemas:
+ *
+ * Gets a list of the relocatable #GSettings schemas installed on the
+ * system.  These are schemas that do not provide their own path.  It is
+ * usual to instantiate these schemas directly, but if you want to you
+ * can use g_settings_new_with_path() to specify the path.
+ *
+ * The output of this function, tTaken together with the output of
+ * g_settings_list_schemas() represents the complete list of all
+ * installed schemas.
+ *
+ * Returns: (element-type utf8) (transfer none): a list of relocatable
+ *   #GSettings schemas that are available.  The list must not be
+ *   modified or freed.
+ **/
+const gchar * const *
+g_settings_list_relocatable_schemas (void)
+{
+  ensure_schema_lists ();
 
-  return (const gchar **) schema_list;
+  return relocatable_schema_list;
 }
 
 static void
diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c
index fdadf96..3313d38 100644
--- a/gio/tests/gsettings.c
+++ b/gio/tests/gsettings.c
@@ -1765,12 +1765,17 @@ static void
 test_list_schemas (void)
 {
   const gchar * const *schemas;
+  const gchar * const *relocs;
 
+  relocs = g_settings_list_relocatable_schemas ();
   schemas = g_settings_list_schemas ();
 
+  g_assert (strv_set_equal ((gchar **)relocs,
+                            "org.gtk.test.no-path",
+                            NULL));
+
   g_assert (strv_set_equal ((gchar **)schemas,
                             "org.gtk.test",
-                            "org.gtk.test.no-path",
                             "org.gtk.test.basic-types",
                             "org.gtk.test.complex-types",
                             "org.gtk.test.localized",



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