From cbf8cf8598e527a0d3b895cbfedef6b728ab8b82 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Sun, 27 Oct 2013 17:18:10 -0700 Subject: [PATCH] GSettings: properly support 'extends' Support the 'extends' attribute that has been supported by the compiler for a long time by doing three things: - when creating a schema that extends another schema, lookup that other schema - when looking up keys and we can't find them in the schema, check (recursively) in the 'extends' schema - when listing all keys in a schema, also visit the extends schemas, but take care to avoid duplicates caused by overrides Extend the testsuite to verify that it works. https://bugzilla.gnome.org/show_bug.cgi?id=645453 --- gio/gsettingsschema.c | 152 +++++++++++++++++++++++++++++++------------------- 1 file changed, 96 insertions(+), 56 deletions(-) diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c index 80b0bb6..b79d91a 100644 --- a/gio/gsettingsschema.c +++ b/gio/gsettingsschema.c @@ -146,6 +146,8 @@ struct _GSettingsSchema GvdbTable *table; gchar *id; + GSettingsSchema *extends; + gint ref_count; }; @@ -407,6 +409,7 @@ g_settings_schema_source_lookup (GSettingsSchemaSource *source, { GSettingsSchema *schema; GvdbTable *table; + const gchar *extends; g_return_val_if_fail (source != NULL, NULL); g_return_val_if_fail (schema_id != NULL, NULL); @@ -432,6 +435,14 @@ g_settings_schema_source_lookup (GSettingsSchemaSource *source, if (schema->gettext_domain) bind_textdomain_codeset (schema->gettext_domain, "UTF-8"); + extends = g_settings_schema_get_string (schema, ".extends"); + if (extends) + { + schema->extends = g_settings_schema_source_lookup (source, extends, TRUE); + if (schema->extends == NULL) + g_warning ("Schema '%s' extends schema '%s' but we could not find it", schema_id, extends); + } + return schema; } @@ -892,6 +903,9 @@ g_settings_schema_unref (GSettingsSchema *schema) { if (g_atomic_int_dec_and_test (&schema->ref_count)) { + if (schema->extends) + g_settings_schema_unref (schema->extends); + g_settings_schema_source_unref (schema->source); gvdb_table_unref (schema->table); g_free (schema->items); @@ -921,10 +935,15 @@ GVariantIter * g_settings_schema_get_value (GSettingsSchema *schema, const gchar *key) { + GSettingsSchema *s = schema; GVariantIter *iter; GVariant *value; - value = gvdb_table_get_raw_value (schema->table, key); + g_return_val_if_fail (schema != NULL, NULL); + + for (s = schema; s; s = schema->extends) + if ((value = gvdb_table_get_raw_value (schema->table, key))) + break; if G_UNLIKELY (value == NULL || !g_variant_is_of_type (value, G_VARIANT_TYPE_TUPLE)) g_error ("Settings schema '%s' does not contain a key named '%s'", schema->id, key); @@ -976,79 +995,100 @@ const GQuark * g_settings_schema_list (GSettingsSchema *schema, gint *n_items) { - gint i, j; - if (schema->items == NULL) { - gchar **list; + GSettingsSchema *s; + GHashTableIter iter; + GHashTable *items; + gpointer name; gint len; + gint i; + + items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + for (s = schema; s; s = s->extends) + { + gchar **list; - list = gvdb_table_list (schema->table, ""); - len = list ? g_strv_length (list) : 0; + list = gvdb_table_list (s->table, ""); - schema->items = g_new (GQuark, len); - j = 0; + for (i = 0; list[i]; i++) + g_hash_table_add (items, list[i]); /* transfer ownership */ - for (i = 0; i < len; i++) - if (list[i][0] != '.') + g_free (list); /* free container only */ + } + + /* Do a first pass to eliminate child items that do not map to + * valid schemas (ie: ones that would crash us if we actually + * tried to create them). + */ + g_hash_table_iter_init (&iter, items); + while (g_hash_table_iter_next (&iter, &name, NULL)) + if (g_str_has_suffix (name, "/")) { - if (g_str_has_suffix (list[i], "/")) - { - /* This is a child. Check to make sure that - * instantiating the child would actually work before we - * return it from list() and cause a crash. - */ - GSettingsSchemaSource *source; - GVariant *child_schema; - GvdbTable *child_table; + GSettingsSchemaSource *source; + GVariant *child_schema; + GvdbTable *child_table; - child_schema = gvdb_table_get_raw_value (schema->table, list[i]); - if (!child_schema) - continue; + child_schema = gvdb_table_get_raw_value (schema->table, name); + if (!child_schema) + continue; - child_table = NULL; + child_table = NULL; - for (source = schema_sources; source; source = source->parent) - if ((child_table = gvdb_table_get_table (source->table, g_variant_get_string (child_schema, NULL)))) - break; + for (source = schema_sources; source; source = source->parent) + if ((child_table = gvdb_table_get_table (source->table, g_variant_get_string (child_schema, NULL)))) + break; - g_variant_unref (child_schema); + g_variant_unref (child_schema); - /* Schema is not found -> don't add it to the list */ - if (child_table == NULL) - continue; + /* Schema is not found -> remove it from the list */ + if (child_table == NULL) + { + g_hash_table_iter_remove (&iter); + continue; + } - /* Make sure the schema is relocatable or at the - * expected path + /* Make sure the schema is relocatable or at the + * expected path + */ + if (gvdb_table_has_value (child_table, ".path")) + { + GVariant *path; + gchar *expected; + gboolean same; + + path = gvdb_table_get_raw_value (child_table, ".path"); + expected = g_strconcat (schema->path, name, NULL); + same = g_str_equal (expected, g_variant_get_string (path, NULL)); + g_variant_unref (path); + g_free (expected); + + /* Schema is non-relocatable and did not have the + * expected path -> remove it from the list */ - if (gvdb_table_has_value (child_table, ".path")) - { - GVariant *path; - gchar *expected; - gboolean same; - - path = gvdb_table_get_raw_value (child_table, ".path"); - expected = g_strconcat (schema->path, list[i], NULL); - same = g_str_equal (expected, g_variant_get_string (path, NULL)); - g_variant_unref (path); - g_free (expected); - - if (!same) - { - gvdb_table_unref (child_table); - continue; - } - } - - gvdb_table_unref (child_table); - /* Else, it's good... */ + if (!same) + g_hash_table_iter_remove (&iter); } - schema->items[j++] = g_quark_from_string (list[i]); + gvdb_table_unref (child_table); } - schema->n_items = j; - g_strfreev (list); + /* Now create the list */ + len = g_hash_table_size (items); + schema->items = g_new (GQuark, len + 1); + i = 0; + g_hash_table_iter_init (&iter, items); + + while (g_hash_table_iter_next (&iter, &name, NULL)) + { + schema->items[i++] = g_quark_from_string (name); + g_hash_table_iter_steal (&iter); + } + schema->n_items = i; + g_assert (i == len); + + g_hash_table_unref (items); } *n_items = schema->n_items; -- 2.7.4