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().
This commit is contained in:
Ryan Lortie 2010-10-02 22:42:02 -04:00
parent 1fee36f72b
commit d2c0699440
4 changed files with 103 additions and 32 deletions

View File

@ -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__)

View File

@ -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);

View File

@ -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);
}
}
return (const gchar **) schema_list;
/**
* 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 relocatable_schema_list;
}
static void

View File

@ -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",