X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgsettingsschema.c;h=85cad60cb4dd0c50bd9ec2b3b3fcf7682ad357e4;hb=2e5bd8cf47f9e1559ccc44823a2f321b8ff8c1ea;hp=8ee2ad09ecf2cec1a31ed9a120d2655acd2c1007;hpb=96a053e025f4b5a7286844df3d691ebb508953ab;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c index 8ee2ad0..85cad60 100644 --- a/gio/gsettingsschema.c +++ b/gio/gsettingsschema.c @@ -13,9 +13,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library; if not, see . */ #include "config.h" @@ -32,8 +30,9 @@ /** * SECTION:gsettingsschema - * @short_description: Introspecting and controlling the loading of - * GSettings schemas + * @short_description: Introspecting and controlling the loading + * of GSettings schemas + * @include: gio/gio.h * * The #GSettingsSchemaSource and #GSettingsSchema APIs provide a * mechanism for advanced control over the loading of schemas and a @@ -53,7 +52,7 @@ * * Consider the following example: * - * |[ + * |[ * typedef struct * { * ... @@ -110,7 +109,7 @@ * ships a gschemas.compiled file as part of itself, and then simply do * the following: * - * |[ + * |[ * { * GSettings *settings; * gint some_value; @@ -146,6 +145,8 @@ struct _GSettingsSchema GvdbTable *table; gchar *id; + GSettingsSchema *extends; + gint ref_count; }; @@ -249,16 +250,14 @@ g_settings_schema_source_unref (GSettingsSchemaSource *source) * This function is not required for normal uses of #GSettings but it * may be useful to authors of plugin management systems. * - * The directory should contain a file called - * gschemas.compiled as produced by - * glib-compile-schemas. + * The directory should contain a file called `gschemas.compiled` as + * produced by the [glib-compile-schemas][glib-compile-schemas] tool. * - * If @trusted is %TRUE then gschemas.compiled is - * trusted not to be corrupted. This assumption has a performance - * advantage, but can result in crashes or inconsistent behaviour in the - * case of a corrupted file. Generally, you should set @trusted to - * %TRUE for files installed by the system and to %FALSE for files in - * the home directory. + * If @trusted is %TRUE then `gschemas.compiled` is trusted not to be + * corrupted. This assumption has a performance advantage, but can result + * in crashes or inconsistent behaviour in the case of a corrupted file. + * Generally, you should set @trusted to %TRUE for files installed by the + * system and to %FALSE for files in the home directory. * * If @parent is non-%NULL then there are two effects. * @@ -267,8 +266,8 @@ g_settings_schema_source_unref (GSettingsSchemaSource *source) * source, the lookup will recurse to the parent. * * Second, any references to other schemas specified within this - * source (ie: child or extends) - * references may be resolved from the @parent. + * source (ie: `child` or `extends`) references may be resolved + * from the @parent. * * For this second reason, except in very unusual situations, the * @parent should probably be given as the default schema source, as @@ -362,9 +361,8 @@ initialise_schema_sources (void) * * The returned source may actually consist of multiple schema sources * from different directories, depending on which directories were given - * in XDG_DATA_DIRS and - * GSETTINGS_SCHEMA_DIR. For this reason, all lookups - * performed against the default source should probably be done + * in `XDG_DATA_DIRS` and `GSETTINGS_SCHEMA_DIR`. For this reason, all + * lookups performed against the default source should probably be done * recursively. * * Returns: (transfer none): the default schema source @@ -396,7 +394,7 @@ g_settings_schema_source_get_default (void) * * If the schema isn't found, %NULL is returned. * - * Returns: (transfer full): a new #GSettingsSchema + * Returns: (nullable) (transfer full): a new #GSettingsSchema * * Since: 2.32 **/ @@ -407,6 +405,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 +431,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; } @@ -666,8 +673,9 @@ parse_into_text_tables (const gchar *directory, GMarkupParseContext *context; context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &info, NULL); + /* Ignore errors here, this is best effort only. */ if (g_markup_parse_context_parse (context, contents, size, NULL)) - g_markup_parse_context_end_parse (context, NULL); + (void) g_markup_parse_context_end_parse (context, NULL); g_markup_parse_context_free (context); /* Clean up dangling stuff in case there was an error. */ @@ -690,6 +698,8 @@ parse_into_text_tables (const gchar *directory, g_free (filename); } + + g_dir_close (dir); } static GHashTable ** @@ -712,82 +722,112 @@ g_settings_schema_source_get_text_tables (GSettingsSchemaSource *source) return source->text_tables; } -static gboolean -steal_item (gpointer key, - gpointer value, - gpointer user_data) +/** + * g_settings_schema_source_list_schemas: + * @source: a #GSettingsSchemaSource + * @recursive: if we should recurse + * @non_relocatable: (out) (transfer full) (array zero-terminated=1): the + * list of non-relocatable schemas + * @relocatable: (out) (transfer full) (array zero-terminated=1): the list + * of relocatable schemas + * + * Lists the schemas in a given source. + * + * If @recursive is %TRUE then include parent sources. If %FALSE then + * only include the schemas from one source (ie: one directory). You + * probably want %TRUE. + * + * Non-relocatable schemas are those for which you can call + * g_settings_new(). Relocatable schemas are those for which you must + * use g_settings_new_with_path(). + * + * Do not call this function from normal programs. This is designed for + * use by database editors, commandline tools, etc. + * + * Since: 2.40 + **/ +void +g_settings_schema_source_list_schemas (GSettingsSchemaSource *source, + gboolean recursive, + gchar ***non_relocatable, + gchar ***relocatable) { - gchar ***ptr = user_data; + GHashTable *single, *reloc; + GSettingsSchemaSource *s; - *(*ptr)++ = (gchar *) key; - - return TRUE; -} - -static const gchar * const *non_relocatable_schema_list; -static const gchar * const *relocatable_schema_list; -static gsize schema_lists_initialised; + /* 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); -static void -ensure_schema_lists (void) -{ - if (g_once_init_enter (&schema_lists_initialised)) + for (s = source; s; s = s->parent) { - GSettingsSchemaSource *source; - GHashTable *single, *reloc; - const gchar **ptr; gchar **list; gint i; - initialise_schema_sources (); + list = gvdb_table_list (s->table, ""); - /* 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); + /* empty schema cache file? */ + if (list == NULL) + continue; - for (source = schema_sources; source; source = source->parent) + for (i = 0; list[i]; i++) { - list = gvdb_table_list (source->table, ""); - - /* empty schema cache file? */ - if (list == NULL) - continue; - - for (i = 0; list[i]; i++) + if (!g_hash_table_lookup (single, list[i]) && + !g_hash_table_lookup (reloc, list[i])) { - if (!g_hash_table_lookup (single, list[i]) && - !g_hash_table_lookup (reloc, list[i])) - { - GvdbTable *table; + GvdbTable *table; - table = gvdb_table_get_table (source->table, list[i]); - g_assert (table != NULL); + table = gvdb_table_get_table (s->table, 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); + 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); - gvdb_table_unref (table); - } + gvdb_table_unref (table); } - - g_strfreev (list); } - 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_strfreev (list); + + /* Only the first source if recursive not requested */ + if (!recursive) + break; + } + + if (non_relocatable) + { + *non_relocatable = (gchar **) g_hash_table_get_keys_as_array (single, NULL); + g_hash_table_steal_all (single); + } - 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; + if (relocatable) + { + *relocatable = (gchar **) g_hash_table_get_keys_as_array (reloc, NULL); + g_hash_table_steal_all (reloc); + } + + g_hash_table_unref (single); + g_hash_table_unref (reloc); +} + +static gchar **non_relocatable_schema_list; +static gchar **relocatable_schema_list; +static gsize schema_lists_initialised; + +static void +ensure_schema_lists (void) +{ + if (g_once_init_enter (&schema_lists_initialised)) + { + initialise_schema_sources (); + + g_settings_schema_source_list_schemas (schema_sources, TRUE, + &non_relocatable_schema_list, + &relocatable_schema_list); g_once_init_leave (&schema_lists_initialised, TRUE); } @@ -796,53 +836,42 @@ ensure_schema_lists (void) /** * 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. * * Since: 2.26 + * + * Deprecated:2.40: Use g_settings_schema_source_list_schemas() instead. + * If you used g_settings_list_schemas() to check for the presence of + * a particular schema, use g_settings_schema_source_lookup() instead + * of your whole loop. **/ const gchar * const * g_settings_list_schemas (void) { ensure_schema_lists (); - return non_relocatable_schema_list; + return (const gchar **) 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, taken 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. * * Since: 2.28 + * + * Deprecated:2.40: Use g_settings_schema_source_list_schemas() instead **/ const gchar * const * g_settings_list_relocatable_schemas (void) { ensure_schema_lists (); - return relocatable_schema_list; + return (const gchar **) relocatable_schema_list; } /** @@ -876,6 +905,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); @@ -905,10 +937,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 = s->extends) + if ((value = gvdb_table_get_raw_value (s->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); @@ -949,6 +986,17 @@ g_settings_schema_get_gettext_domain (GSettingsSchema *schema) return schema->gettext_domain; } +/** + * g_settings_schema_has_key: + * @schema: a #GSettingsSchema + * @name: the name of a key + * + * Checks if @schema has a key named @name. + * + * Returns: %TRUE if such a key exists + * + * Since: 2.40 + **/ gboolean g_settings_schema_has_key (GSettingsSchema *schema, const gchar *key) @@ -960,79 +1008,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; - list = gvdb_table_list (schema->table, ""); - len = list ? g_strv_length (list) : 0; + items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - schema->items = g_new (GQuark, len); - j = 0; + for (s = schema; s; s = s->extends) + { + gchar **list; + + list = gvdb_table_list (s->table, ""); + + if (list) + { + for (i = 0; list[i]; i++) + g_hash_table_add (items, list[i]); /* transfer ownership */ + + g_free (list); /* free container only */ + } + } - for (i = 0; i < len; i++) - if (list[i][0] != '.') + /* 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); + 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); + schema->n_items = i; + g_assert (i == len); + + g_hash_table_unref (items); } *n_items = schema->n_items; @@ -1148,39 +1217,6 @@ g_settings_schema_key_type_check (GSettingsSchemaKey *key, return g_variant_is_of_type (value, key->type); } -gboolean -g_settings_schema_key_range_check (GSettingsSchemaKey *key, - GVariant *value) -{ - if (key->minimum == NULL && key->strinfo == NULL) - return TRUE; - - if (g_variant_is_container (value)) - { - gboolean ok = TRUE; - GVariantIter iter; - GVariant *child; - - g_variant_iter_init (&iter, value); - while (ok && (child = g_variant_iter_next_value (&iter))) - { - ok = g_settings_schema_key_range_check (key, child); - g_variant_unref (child); - } - - return ok; - } - - if (key->minimum) - { - return g_variant_compare (key->minimum, value) <= 0 && - g_variant_compare (value, key->maximum) <= 0; - } - - return strinfo_is_string_valid (key->strinfo, key->strinfo_length, - g_variant_get_string (value, NULL)); -} - GVariant * g_settings_schema_key_range_fixup (GSettingsSchemaKey *key, GVariant *value) @@ -1227,7 +1263,6 @@ g_settings_schema_key_range_fixup (GSettingsSchemaKey *key, return target ? g_variant_ref_sink (g_variant_new_string (target)) : NULL; } - GVariant * g_settings_schema_key_get_translated_default (GSettingsSchemaKey *key) { @@ -1257,7 +1292,7 @@ g_settings_schema_key_get_translated_default (GSettingsSchemaKey *key) if (value == NULL) { g_warning ("Failed to parse translated string '%s' for " - "key '%s' in schema '%s': %s", key->unparsed, key->name, + "key '%s' in schema '%s': %s", translated, key->name, g_settings_schema_get_id (key->schema), error->message); g_warning ("Using untranslated default instead."); g_error_free (error); @@ -1504,3 +1539,167 @@ g_settings_schema_key_get_description (GSettingsSchemaKey *key) return descriptions ? g_hash_table_lookup (descriptions, key->name) : NULL; } + +/** + * g_settings_schema_key_get_value_type: + * @key: a #GSettingsSchemaKey + * + * Gets the #GVariantType of @key. + * + * Returns: (transfer none): the type of @key + * + * Since: 2.40 + **/ +const GVariantType * +g_settings_schema_key_get_value_type (GSettingsSchemaKey *key) +{ + g_return_val_if_fail (key, NULL); + + return key->type; +} + +/** + * g_settings_schema_key_get_default_value: + * @key: a #GSettingsSchemaKey + * + * Gets the default value for @key. + * + * Note that this is the default value according to the schema. System + * administrator defaults and lockdown are not visible via this API. + * + * Returns: (transfer full): the default value for the key + * + * Since: 2.40 + **/ +GVariant * +g_settings_schema_key_get_default_value (GSettingsSchemaKey *key) +{ + GVariant *value; + + g_return_val_if_fail (key, NULL); + + value = g_settings_schema_key_get_translated_default (key); + + if (!value) + value = g_variant_ref (key->default_value); + + return value; +} + +/** + * g_settings_schema_key_get_range: + * @key: a #GSettingsSchemaKey + * + * Queries the range of a key. + * + * This function will return a #GVariant that fully describes the range + * of values that are valid for @key. + * + * The type of #GVariant returned is `(sv)`. The string describes + * the type of range restriction in effect. The type and meaning of + * the value contained in the variant depends on the string. + * + * If the string is `'type'` then the variant contains an empty array. + * The element type of that empty array is the expected type of value + * and all values of that type are valid. + * + * If the string is `'enum'` then the variant contains an array + * enumerating the possible values. Each item in the array is + * a possible valid value and no other values are valid. + * + * If the string is `'flags'` then the variant contains an array. Each + * item in the array is a value that may appear zero or one times in an + * array to be used as the value for this key. For example, if the + * variant contained the array `['x', 'y']` then the valid values for + * the key would be `[]`, `['x']`, `['y']`, `['x', 'y']` and + * `['y', 'x']`. + * + * Finally, if the string is `'range'` then the variant contains a pair + * of like-typed values -- the minimum and maximum permissible values + * for this key. + * + * This information should not be used by normal programs. It is + * considered to be a hint for introspection purposes. Normal programs + * should already know what is permitted by their own schema. The + * format may change in any way in the future -- but particularly, new + * forms may be added to the possibilities described above. + * + * You should free the returned value with g_variant_unref() when it is + * no longer needed. + * + * Returns: (transfer full): a #GVariant describing the range + * + * Since: 2.40 + **/ +GVariant * +g_settings_schema_key_get_range (GSettingsSchemaKey *key) +{ + const gchar *type; + GVariant *range; + + if (key->minimum) + { + range = g_variant_new ("(**)", key->minimum, key->maximum); + type = "range"; + } + else if (key->strinfo) + { + range = strinfo_enumerate (key->strinfo, key->strinfo_length); + type = key->is_flags ? "flags" : "enum"; + } + else + { + range = g_variant_new_array (key->type, NULL, 0); + type = "type"; + } + + return g_variant_ref_sink (g_variant_new ("(sv)", type, range)); +} + +/** + * g_settings_schema_key_range_check: + * @key: a #GSettingsSchemaKey + * @value: the value to check + * + * Checks if the given @value is of the correct type and within the + * permitted range for @key. + * + * It is a programmer error if @value is not of the correct type -- you + * must check for this first. + * + * Returns: %TRUE if @value is valid for @key + * + * Since: 2.40 + **/ +gboolean +g_settings_schema_key_range_check (GSettingsSchemaKey *key, + GVariant *value) +{ + if (key->minimum == NULL && key->strinfo == NULL) + return TRUE; + + if (g_variant_is_container (value)) + { + gboolean ok = TRUE; + GVariantIter iter; + GVariant *child; + + g_variant_iter_init (&iter, value); + while (ok && (child = g_variant_iter_next_value (&iter))) + { + ok = g_settings_schema_key_range_check (key, child); + g_variant_unref (child); + } + + return ok; + } + + if (key->minimum) + { + return g_variant_compare (key->minimum, value) <= 0 && + g_variant_compare (value, key->maximum) <= 0; + } + + return strinfo_is_string_valid (key->strinfo, key->strinfo_length, + g_variant_get_string (value, NULL)); +}