Make GSettingsSchemaKey public
[platform/upstream/glib.git] / gio / gsettingsschema.c
1 /*
2  * Copyright © 2010 Codethink Limited
3  * Copyright © 2011 Canonical Limited
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the licence, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include "gsettingsschema-internal.h"
24 #include "gsettings.h"
25
26 #include "gvdb/gvdb-reader.h"
27 #include "strinfo.c"
28
29 #include <glibintl.h>
30 #include <locale.h>
31 #include <string.h>
32
33 /**
34  * SECTION:gsettingsschema
35  * @short_description: Introspecting and controlling the loading of
36  *                     GSettings schemas
37  *
38  * The #GSettingsSchemaSource and #GSettingsSchema APIs provide a
39  * mechanism for advanced control over the loading of schemas and a
40  * mechanism for introspecting their content.
41  *
42  * Plugin loading systems that wish to provide plugins a way to access
43  * settings face the problem of how to make the schemas for these
44  * settings visible to GSettings.  Typically, a plugin will want to ship
45  * the schema along with itself and it won't be installed into the
46  * standard system directories for schemas.
47  *
48  * #GSettingsSchemaSource provides a mechanism for dealing with this by
49  * allowing the creation of a new 'schema source' from which schemas can
50  * be acquired.  This schema source can then become part of the metadata
51  * associated with the plugin and queried whenever the plugin requires
52  * access to some settings.
53  *
54  * Consider the following example:
55  *
56  * |[
57  * typedef struct
58  * {
59  *    ...
60  *    GSettingsSchemaSource *schema_source;
61  *    ...
62  * } Plugin;
63  *
64  * Plugin *
65  * initialise_plugin (const gchar *dir)
66  * {
67  *   Plugin *plugin;
68  *
69  *   ...
70  *
71  *   plugin->schema_source =
72  *     g_settings_new_schema_source_from_directory (dir,
73  *       g_settings_schema_source_get_default (), FALSE, NULL);
74  *
75  *   ...
76  *
77  *   return plugin;
78  * }
79  *
80  * ...
81  *
82  * GSettings *
83  * plugin_get_settings (Plugin      *plugin,
84  *                      const gchar *schema_id)
85  * {
86  *   GSettingsSchema *schema;
87  *
88  *   if (schema_id == NULL)
89  *     schema_id = plugin->identifier;
90  *
91  *   schema = g_settings_schema_source_lookup (plugin->schema_source,
92  *                                             schema_id, FALSE);
93  *
94  *   if (schema == NULL)
95  *     {
96  *       ... disable the plugin or abort, etc ...
97  *     }
98  *
99  *   return g_settings_new_full (schema, NULL, NULL);
100  * }
101  * ]|
102  *
103  * The code above shows how hooks should be added to the code that
104  * initialises (or enables) the plugin to create the schema source and
105  * how an API can be added to the plugin system to provide a convenient
106  * way for the plugin to access its settings, using the schemas that it
107  * ships.
108  *
109  * From the standpoint of the plugin, it would need to ensure that it
110  * ships a gschemas.compiled file as part of itself, and then simply do
111  * the following:
112  *
113  * |[
114  * {
115  *   GSettings *settings;
116  *   gint some_value;
117  *
118  *   settings = plugin_get_settings (self, NULL);
119  *   some_value = g_settings_get_int (settings, "some-value");
120  *   ...
121  * }
122  * ]|
123  *
124  * It's also possible that the plugin system expects the schema source
125  * files (ie: .gschema.xml files) instead of a gschemas.compiled file.
126  * In that case, the plugin loading system must compile the schemas for
127  * itself before attempting to create the settings source.
128  *
129  * Since: 2.32
130  **/
131
132 /**
133  * GSettingsSchema:
134  *
135  * This is an opaque structure type.  You may not access it directly.
136  *
137  * Since: 2.32
138  **/
139 struct _GSettingsSchema
140 {
141   GSettingsSchemaSource *source;
142   const gchar *gettext_domain;
143   const gchar *path;
144   GQuark *items;
145   gint n_items;
146   GvdbTable *table;
147   gchar *id;
148
149   gint ref_count;
150 };
151
152 /**
153  * G_TYPE_SETTINGS_SCHEMA_SOURCE:
154  *
155  * A boxed #GType corresponding to #GSettingsSchemaSource.
156  *
157  * Since: 2.32
158  **/
159 G_DEFINE_BOXED_TYPE (GSettingsSchemaSource, g_settings_schema_source, g_settings_schema_source_ref, g_settings_schema_source_unref)
160
161 /**
162  * G_TYPE_SETTINGS_SCHEMA:
163  *
164  * A boxed #GType corresponding to #GSettingsSchema.
165  *
166  * Since: 2.32
167  **/
168 G_DEFINE_BOXED_TYPE (GSettingsSchema, g_settings_schema, g_settings_schema_ref, g_settings_schema_unref)
169
170 /**
171  * GSettingsSchemaSource:
172  *
173  * This is an opaque structure type.  You may not access it directly.
174  *
175  * Since: 2.32
176  **/
177 struct _GSettingsSchemaSource
178 {
179   GSettingsSchemaSource *parent;
180   gchar *directory;
181   GvdbTable *table;
182
183   gint ref_count;
184 };
185
186 static GSettingsSchemaSource *schema_sources;
187
188 /**
189  * g_settings_schema_source_ref:
190  * @source: a #GSettingsSchemaSource
191  *
192  * Increase the reference count of @source, returning a new reference.
193  *
194  * Returns: a new reference to @source
195  *
196  * Since: 2.32
197  **/
198 GSettingsSchemaSource *
199 g_settings_schema_source_ref (GSettingsSchemaSource *source)
200 {
201   g_atomic_int_inc (&source->ref_count);
202
203   return source;
204 }
205
206 /**
207  * g_settings_schema_source_unref:
208  * @source: a #GSettingsSchemaSource
209  *
210  * Decrease the reference count of @source, possibly freeing it.
211  *
212  * Since: 2.32
213  **/
214 void
215 g_settings_schema_source_unref (GSettingsSchemaSource *source)
216 {
217   if (g_atomic_int_dec_and_test (&source->ref_count))
218     {
219       if (source == schema_sources)
220         g_error ("g_settings_schema_source_unref() called too many times on the default schema source");
221
222       if (source->parent)
223         g_settings_schema_source_unref (source->parent);
224       gvdb_table_unref (source->table);
225       g_free (source->directory);
226
227       g_slice_free (GSettingsSchemaSource, source);
228     }
229 }
230
231 /**
232  * g_settings_schema_source_new_from_directory:
233  * @directory: the filename of a directory
234  * @parent: (allow-none): a #GSettingsSchemaSource, or %NULL
235  * @trusted: %TRUE, if the directory is trusted
236  * @error: a pointer to a #GError pointer set to %NULL, or %NULL
237  *
238  * Attempts to create a new schema source corresponding to the contents
239  * of the given directory.
240  *
241  * This function is not required for normal uses of #GSettings but it
242  * may be useful to authors of plugin management systems.
243  *
244  * The directory should contain a file called
245  * <filename>gschemas.compiled</filename> as produced by
246  * <command>glib-compile-schemas</command>.
247  *
248  * If @trusted is %TRUE then <filename>gschemas.compiled</filename> is
249  * trusted not to be corrupted.  This assumption has a performance
250  * advantage, but can result in crashes or inconsistent behaviour in the
251  * case of a corrupted file.  Generally, you should set @trusted to
252  * %TRUE for files installed by the system and to %FALSE for files in
253  * the home directory.
254  *
255  * If @parent is non-%NULL then there are two effects.
256  *
257  * First, if g_settings_schema_source_lookup() is called with the
258  * @recursive flag set to %TRUE and the schema can not be found in the
259  * source, the lookup will recurse to the parent.
260  *
261  * Second, any references to other schemas specified within this
262  * source (ie: <literal>child</literal> or <literal>extends</literal>)
263  * references may be resolved from the @parent.
264  *
265  * For this second reason, except in very unusual situations, the
266  * @parent should probably be given as the default schema source, as
267  * returned by g_settings_schema_source_get_default().
268  *
269  * Since: 2.32
270  **/
271 GSettingsSchemaSource *
272 g_settings_schema_source_new_from_directory (const gchar            *directory,
273                                              GSettingsSchemaSource  *parent,
274                                              gboolean                trusted,
275                                              GError                **error)
276 {
277   GSettingsSchemaSource *source;
278   GvdbTable *table;
279   gchar *filename;
280
281   filename = g_build_filename (directory, "gschemas.compiled", NULL);
282   table = gvdb_table_new (filename, trusted, error);
283   g_free (filename);
284
285   if (table == NULL)
286     return NULL;
287
288   source = g_slice_new (GSettingsSchemaSource);
289   source->directory = g_strdup (directory);
290   source->parent = parent ? g_settings_schema_source_ref (parent) : NULL;
291   source->table = table;
292   source->ref_count = 1;
293
294   return source;
295 }
296
297 static void
298 try_prepend_dir (const gchar *directory)
299 {
300   GSettingsSchemaSource *source;
301
302   source = g_settings_schema_source_new_from_directory (directory, schema_sources, TRUE, NULL);
303
304   /* If we successfully created it then prepend it to the global list */
305   if (source != NULL)
306     schema_sources = source;
307 }
308
309 static void
310 initialise_schema_sources (void)
311 {
312   static gsize initialised;
313
314   /* need a separate variable because 'schema_sources' may legitimately
315    * be null if we have zero valid schema sources
316    */
317   if G_UNLIKELY (g_once_init_enter (&initialised))
318     {
319       const gchar * const *dirs;
320       const gchar *path;
321       gint i;
322
323       /* iterate in reverse: count up, then count down */
324       dirs = g_get_system_data_dirs ();
325       for (i = 0; dirs[i]; i++);
326
327       while (i--)
328         {
329           gchar *dirname;
330
331           dirname = g_build_filename (dirs[i], "glib-2.0", "schemas", NULL);
332           try_prepend_dir (dirname);
333           g_free (dirname);
334         }
335
336       if ((path = g_getenv ("GSETTINGS_SCHEMA_DIR")) != NULL)
337         try_prepend_dir (path);
338
339       g_once_init_leave (&initialised, TRUE);
340     }
341 }
342
343 /**
344  * g_settings_schema_source_get_default:
345  *
346  * Gets the default system schema source.
347  *
348  * This function is not required for normal uses of #GSettings but it
349  * may be useful to authors of plugin management systems or to those who
350  * want to introspect the content of schemas.
351  *
352  * If no schemas are installed, %NULL will be returned.
353  *
354  * The returned source may actually consist of multiple schema sources
355  * from different directories, depending on which directories were given
356  * in <envar>XDG_DATA_DIRS</envar> and
357  * <envar>GSETTINGS_SCHEMA_DIR</envar>.  For this reason, all lookups
358  * performed against the default source should probably be done
359  * recursively.
360  *
361  * Returns: (transfer none): the default schema source
362  *
363  * Since: 2.32
364  **/
365  GSettingsSchemaSource *
366 g_settings_schema_source_get_default (void)
367 {
368   initialise_schema_sources ();
369
370   return schema_sources;
371 }
372
373 /**
374  * g_settings_schema_source_lookup:
375  * @source: a #GSettingsSchemaSource
376  * @schema_id: a schema ID
377  * @recursive: %TRUE if the lookup should be recursive
378  *
379  * Looks up a schema with the identifier @schema_id in @source.
380  *
381  * This function is not required for normal uses of #GSettings but it
382  * may be useful to authors of plugin management systems or to those who
383  * want to introspect the content of schemas.
384  *
385  * If the schema isn't found directly in @source and @recursive is %TRUE
386  * then the parent sources will also be checked.
387  *
388  * If the schema isn't found, %NULL is returned.
389  *
390  * Returns: (transfer full): a new #GSettingsSchema
391  *
392  * Since: 2.32
393  **/
394 GSettingsSchema *
395 g_settings_schema_source_lookup (GSettingsSchemaSource *source,
396                                  const gchar           *schema_id,
397                                  gboolean               recursive)
398 {
399   GSettingsSchema *schema;
400   GvdbTable *table;
401
402   g_return_val_if_fail (source != NULL, NULL);
403   g_return_val_if_fail (schema_id != NULL, NULL);
404
405   table = gvdb_table_get_table (source->table, schema_id);
406
407   if (table == NULL && recursive)
408     for (source = source->parent; source; source = source->parent)
409       if ((table = gvdb_table_get_table (source->table, schema_id)))
410         break;
411
412   if (table == NULL)
413     return NULL;
414
415   schema = g_slice_new0 (GSettingsSchema);
416   schema->source = g_settings_schema_source_ref (source);
417   schema->ref_count = 1;
418   schema->id = g_strdup (schema_id);
419   schema->table = table;
420   schema->path = g_settings_schema_get_string (schema, ".path");
421   schema->gettext_domain = g_settings_schema_get_string (schema, ".gettext-domain");
422
423   if (schema->gettext_domain)
424     bind_textdomain_codeset (schema->gettext_domain, "UTF-8");
425
426   return schema;
427 }
428
429 static gboolean
430 steal_item (gpointer key,
431             gpointer value,
432             gpointer user_data)
433 {
434   gchar ***ptr = user_data;
435
436   *(*ptr)++ = (gchar *) key;
437
438   return TRUE;
439 }
440
441 static const gchar * const *non_relocatable_schema_list;
442 static const gchar * const *relocatable_schema_list;
443 static gsize schema_lists_initialised;
444
445 static void
446 ensure_schema_lists (void)
447 {
448   if (g_once_init_enter (&schema_lists_initialised))
449     {
450       GSettingsSchemaSource *source;
451       GHashTable *single, *reloc;
452       const gchar **ptr;
453       gchar **list;
454       gint i;
455
456       initialise_schema_sources ();
457
458       /* We use hash tables to avoid duplicate listings for schemas that
459        * appear in more than one file.
460        */
461       single = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
462       reloc = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
463
464       for (source = schema_sources; source; source = source->parent)
465         {
466           list = gvdb_table_list (source->table, "");
467
468           /* empty schema cache file? */
469           if (list == NULL)
470             continue;
471
472           for (i = 0; list[i]; i++)
473             {
474               if (!g_hash_table_lookup (single, list[i]) &&
475                   !g_hash_table_lookup (reloc, list[i]))
476                 {
477                   GvdbTable *table;
478
479                   table = gvdb_table_get_table (source->table, list[i]);
480                   g_assert (table != NULL);
481
482                   if (gvdb_table_has_value (table, ".path"))
483                     g_hash_table_insert (single, g_strdup (list[i]), NULL);
484                   else
485                     g_hash_table_insert (reloc, g_strdup (list[i]), NULL);
486
487                   gvdb_table_unref (table);
488                 }
489             }
490
491           g_strfreev (list);
492         }
493
494       ptr = g_new (const gchar *, g_hash_table_size (single) + 1);
495       non_relocatable_schema_list = ptr;
496       g_hash_table_foreach_steal (single, steal_item, &ptr);
497       g_hash_table_unref (single);
498       *ptr = NULL;
499
500       ptr = g_new (const gchar *, g_hash_table_size (reloc) + 1);
501       relocatable_schema_list = ptr;
502       g_hash_table_foreach_steal (reloc, steal_item, &ptr);
503       g_hash_table_unref (reloc);
504       *ptr = NULL;
505
506       g_once_init_leave (&schema_lists_initialised, TRUE);
507     }
508 }
509
510 /**
511  * g_settings_list_schemas:
512  *
513  * Gets a list of the #GSettings schemas installed on the system.  The
514  * returned list is exactly the list of schemas for which you may call
515  * g_settings_new() without adverse effects.
516  *
517  * This function does not list the schemas that do not provide their own
518  * paths (ie: schemas for which you must use
519  * g_settings_new_with_path()).  See
520  * g_settings_list_relocatable_schemas() for that.
521  *
522  * Returns: (element-type utf8) (transfer none):  a list of #GSettings
523  *   schemas that are available.  The list must not be modified or
524  *   freed.
525  *
526  * Since: 2.26
527  **/
528 const gchar * const *
529 g_settings_list_schemas (void)
530 {
531   ensure_schema_lists ();
532
533   return non_relocatable_schema_list;
534 }
535
536 /**
537  * g_settings_list_relocatable_schemas:
538  *
539  * Gets a list of the relocatable #GSettings schemas installed on the
540  * system.  These are schemas that do not provide their own path.  It is
541  * usual to instantiate these schemas directly, but if you want to you
542  * can use g_settings_new_with_path() to specify the path.
543  *
544  * The output of this function, taken together with the output of
545  * g_settings_list_schemas() represents the complete list of all
546  * installed schemas.
547  *
548  * Returns: (element-type utf8) (transfer none): a list of relocatable
549  *   #GSettings schemas that are available.  The list must not be
550  *   modified or freed.
551  *
552  * Since: 2.28
553  **/
554 const gchar * const *
555 g_settings_list_relocatable_schemas (void)
556 {
557   ensure_schema_lists ();
558
559   return relocatable_schema_list;
560 }
561
562 /**
563  * g_settings_schema_ref:
564  * @schema: a #GSettingsSchema
565  *
566  * Increase the reference count of @schema, returning a new reference.
567  *
568  * Returns: a new reference to @schema
569  *
570  * Since: 2.32
571  **/
572 GSettingsSchema *
573 g_settings_schema_ref (GSettingsSchema *schema)
574 {
575   g_atomic_int_inc (&schema->ref_count);
576
577   return schema;
578 }
579
580 /**
581  * g_settings_schema_unref:
582  * @schema: a #GSettingsSchema
583  *
584  * Decrease the reference count of @schema, possibly freeing it.
585  *
586  * Since: 2.32
587  **/
588 void
589 g_settings_schema_unref (GSettingsSchema *schema)
590 {
591   if (g_atomic_int_dec_and_test (&schema->ref_count))
592     {
593       g_settings_schema_source_unref (schema->source);
594       gvdb_table_unref (schema->table);
595       g_free (schema->items);
596       g_free (schema->id);
597
598       g_slice_free (GSettingsSchema, schema);
599     }
600 }
601
602 const gchar *
603 g_settings_schema_get_string (GSettingsSchema *schema,
604                               const gchar     *key)
605 {
606   const gchar *result = NULL;
607   GVariant *value;
608
609   if ((value = gvdb_table_get_raw_value (schema->table, key)))
610     {
611       result = g_variant_get_string (value, NULL);
612       g_variant_unref (value);
613     }
614
615   return result;
616 }
617
618 GVariantIter *
619 g_settings_schema_get_value (GSettingsSchema *schema,
620                              const gchar     *key)
621 {
622   GVariantIter *iter;
623   GVariant *value;
624
625   value = gvdb_table_get_raw_value (schema->table, key);
626
627   if G_UNLIKELY (value == NULL || !g_variant_is_of_type (value, G_VARIANT_TYPE_TUPLE))
628     g_error ("Settings schema '%s' does not contain a key named '%s'", schema->id, key);
629
630   iter = g_variant_iter_new (value);
631   g_variant_unref (value);
632
633   return iter;
634 }
635
636 /**
637  * g_settings_schema_get_path:
638  * @schema: a #GSettingsSchema
639  *
640  * Gets the path associated with @schema, or %NULL.
641  *
642  * Schemas may be single-instance or relocatable.  Single-instance
643  * schemas correspond to exactly one set of keys in the backend
644  * database: those located at the path returned by this function.
645  *
646  * Relocatable schemas can be referenced by other schemas and can
647  * threfore describe multiple sets of keys at different locations.  For
648  * relocatable schemas, this function will return %NULL.
649  *
650  * Returns: (transfer none): the path of the schema, or %NULL
651  *
652  * Since: 2.32
653  **/
654 const gchar *
655 g_settings_schema_get_path (GSettingsSchema *schema)
656 {
657   return schema->path;
658 }
659
660 const gchar *
661 g_settings_schema_get_gettext_domain (GSettingsSchema *schema)
662 {
663   return schema->gettext_domain;
664 }
665
666 gboolean
667 g_settings_schema_has_key (GSettingsSchema *schema,
668                            const gchar     *key)
669 {
670   return gvdb_table_has_value (schema->table, key);
671 }
672
673 const GQuark *
674 g_settings_schema_list (GSettingsSchema *schema,
675                         gint            *n_items)
676 {
677   gint i, j;
678
679   if (schema->items == NULL)
680     {
681       gchar **list;
682       gint len;
683
684       list = gvdb_table_list (schema->table, "");
685       len = list ? g_strv_length (list) : 0;
686
687       schema->items = g_new (GQuark, len);
688       j = 0;
689
690       for (i = 0; i < len; i++)
691         if (list[i][0] != '.')
692           {
693             if (g_str_has_suffix (list[i], "/"))
694               {
695                 /* This is a child.  Check to make sure that
696                  * instantiating the child would actually work before we
697                  * return it from list() and cause a crash.
698                  */
699                 GSettingsSchemaSource *source;
700                 GVariant *child_schema;
701                 GvdbTable *child_table;
702
703                 child_schema = gvdb_table_get_raw_value (schema->table, list[i]);
704                 if (!child_schema)
705                   continue;
706
707                 child_table = NULL;
708
709                 for (source = schema_sources; source; source = source->parent)
710                   if ((child_table = gvdb_table_get_table (source->table, g_variant_get_string (child_schema, NULL))))
711                     break;
712
713                 g_variant_unref (child_schema);
714
715                 /* Schema is not found -> don't add it to the list */
716                 if (child_table == NULL)
717                   continue;
718
719                 /* Make sure the schema is relocatable or at the
720                  * expected path
721                  */
722                 if (gvdb_table_has_value (child_table, ".path"))
723                   {
724                     GVariant *path;
725                     gchar *expected;
726                     gboolean same;
727
728                     path = gvdb_table_get_raw_value (child_table, ".path");
729                     expected = g_strconcat (schema->path, list[i], NULL);
730                     same = g_str_equal (expected, g_variant_get_string (path, NULL));
731                     g_variant_unref (path);
732                     g_free (expected);
733
734                     if (!same)
735                       {
736                         gvdb_table_unref (child_table);
737                         continue;
738                       }
739                   }
740
741                 gvdb_table_unref (child_table);
742                 /* Else, it's good... */
743               }
744
745             schema->items[j++] = g_quark_from_string (list[i]);
746           }
747       schema->n_items = j;
748
749       g_strfreev (list);
750     }
751
752   *n_items = schema->n_items;
753   return schema->items;
754 }
755
756 /**
757  * g_settings_schema_get_id:
758  * @schema: a #GSettingsSchema
759  *
760  * Get the ID of @schema.
761  *
762  * Returns: (transfer none): the ID
763  **/
764 const gchar *
765 g_settings_schema_get_id (GSettingsSchema *schema)
766 {
767   return schema->id;
768 }
769
770 static inline void
771 endian_fixup (GVariant **value)
772 {
773 #if G_BYTE_ORDER == G_BIG_ENDIAN
774   GVariant *tmp;
775
776   tmp = g_variant_byteswap (*value);
777   g_variant_unref (*value);
778   *value = tmp;
779 #endif
780 }
781
782 void
783 g_settings_schema_key_init (GSettingsSchemaKey *key,
784                             GSettingsSchema    *schema,
785                             const gchar        *name)
786 {
787   GVariantIter *iter;
788   GVariant *data;
789   guchar code;
790
791   memset (key, 0, sizeof *key);
792
793   iter = g_settings_schema_get_value (schema, name);
794
795   key->schema = g_settings_schema_ref (schema);
796   key->default_value = g_variant_iter_next_value (iter);
797   endian_fixup (&key->default_value);
798   key->type = g_variant_get_type (key->default_value);
799   key->name = g_intern_string (name);
800
801   while (g_variant_iter_next (iter, "(y*)", &code, &data))
802     {
803       switch (code)
804         {
805         case 'l':
806           /* translation requested */
807           g_variant_get (data, "(y&s)", &key->lc_char, &key->unparsed);
808           break;
809
810         case 'e':
811           /* enumerated types... */
812           key->is_enum = TRUE;
813           goto choice;
814
815         case 'f':
816           /* flags... */
817           key->is_flags = TRUE;
818           goto choice;
819
820         choice: case 'c':
821           /* ..., choices, aliases */
822           key->strinfo = g_variant_get_fixed_array (data, &key->strinfo_length, sizeof (guint32));
823           break;
824
825         case 'r':
826           g_variant_get (data, "(**)", &key->minimum, &key->maximum);
827           endian_fixup (&key->minimum);
828           endian_fixup (&key->maximum);
829           break;
830
831         default:
832           g_warning ("unknown schema extension '%c'", code);
833           break;
834         }
835
836       g_variant_unref (data);
837     }
838
839   g_variant_iter_free (iter);
840 }
841
842 void
843 g_settings_schema_key_clear (GSettingsSchemaKey *key)
844 {
845   if (key->minimum)
846     g_variant_unref (key->minimum);
847
848   if (key->maximum)
849     g_variant_unref (key->maximum);
850
851   g_variant_unref (key->default_value);
852
853   g_settings_schema_unref (key->schema);
854 }
855
856 gboolean
857 g_settings_schema_key_type_check (GSettingsSchemaKey *key,
858                                   GVariant           *value)
859 {
860   g_return_val_if_fail (value != NULL, FALSE);
861
862   return g_variant_is_of_type (value, key->type);
863 }
864
865 gboolean
866 g_settings_schema_key_range_check (GSettingsSchemaKey *key,
867                                    GVariant           *value)
868 {
869   if (key->minimum == NULL && key->strinfo == NULL)
870     return TRUE;
871
872   if (g_variant_is_container (value))
873     {
874       gboolean ok = TRUE;
875       GVariantIter iter;
876       GVariant *child;
877
878       g_variant_iter_init (&iter, value);
879       while (ok && (child = g_variant_iter_next_value (&iter)))
880         {
881           ok = g_settings_schema_key_range_check (key, child);
882           g_variant_unref (child);
883         }
884
885       return ok;
886     }
887
888   if (key->minimum)
889     {
890       return g_variant_compare (key->minimum, value) <= 0 &&
891              g_variant_compare (value, key->maximum) <= 0;
892     }
893
894   return strinfo_is_string_valid (key->strinfo, key->strinfo_length,
895                                   g_variant_get_string (value, NULL));
896 }
897
898 GVariant *
899 g_settings_schema_key_range_fixup (GSettingsSchemaKey *key,
900                                    GVariant           *value)
901 {
902   const gchar *target;
903
904   if (g_settings_schema_key_range_check (key, value))
905     return g_variant_ref (value);
906
907   if (key->strinfo == NULL)
908     return NULL;
909
910   if (g_variant_is_container (value))
911     {
912       GVariantBuilder builder;
913       GVariantIter iter;
914       GVariant *child;
915
916       g_variant_iter_init (&iter, value);
917       g_variant_builder_init (&builder, g_variant_get_type (value));
918
919       while ((child = g_variant_iter_next_value (&iter)))
920         {
921           GVariant *fixed;
922
923           fixed = g_settings_schema_key_range_fixup (key, child);
924           g_variant_unref (child);
925
926           if (fixed == NULL)
927             {
928               g_variant_builder_clear (&builder);
929               return NULL;
930             }
931
932           g_variant_builder_add_value (&builder, fixed);
933           g_variant_unref (fixed);
934         }
935
936       return g_variant_ref_sink (g_variant_builder_end (&builder));
937     }
938
939   target = strinfo_string_from_alias (key->strinfo, key->strinfo_length,
940                                       g_variant_get_string (value, NULL));
941   return target ? g_variant_ref_sink (g_variant_new_string (target)) : NULL;
942 }
943
944
945 GVariant *
946 g_settings_schema_key_get_translated_default (GSettingsSchemaKey *key)
947 {
948   const gchar *translated;
949   GError *error = NULL;
950   const gchar *domain;
951   GVariant *value;
952
953   domain = g_settings_schema_get_gettext_domain (key->schema);
954
955   if (key->lc_char == '\0')
956     /* translation not requested for this key */
957     return NULL;
958
959   if (key->lc_char == 't')
960     translated = g_dcgettext (domain, key->unparsed, LC_TIME);
961   else
962     translated = g_dgettext (domain, key->unparsed);
963
964   if (translated == key->unparsed)
965     /* the default value was not translated */
966     return NULL;
967
968   /* try to parse the translation of the unparsed default */
969   value = g_variant_parse (key->type, translated, NULL, NULL, &error);
970
971   if (value == NULL)
972     {
973       g_warning ("Failed to parse translated string '%s' for "
974                  "key '%s' in schema '%s': %s", key->unparsed, key->name,
975                  g_settings_schema_get_id (key->schema), error->message);
976       g_warning ("Using untranslated default instead.");
977       g_error_free (error);
978     }
979
980   else if (!g_settings_schema_key_range_check (key, value))
981     {
982       g_warning ("Translated default '%s' for key '%s' in schema '%s' "
983                  "is outside of valid range", key->unparsed, key->name,
984                  g_settings_schema_get_id (key->schema));
985       g_variant_unref (value);
986       value = NULL;
987     }
988
989   return value;
990 }
991
992 gint
993 g_settings_schema_key_to_enum (GSettingsSchemaKey *key,
994                                GVariant           *value)
995 {
996   gboolean it_worked;
997   guint result;
998
999   it_worked = strinfo_enum_from_string (key->strinfo, key->strinfo_length,
1000                                         g_variant_get_string (value, NULL),
1001                                         &result);
1002
1003   /* 'value' can only come from the backend after being filtered for validity,
1004    * from the translation after being filtered for validity, or from the schema
1005    * itself (which the schema compiler checks for validity).  If this assertion
1006    * fails then it's really a bug in GSettings or the schema compiler...
1007    */
1008   g_assert (it_worked);
1009
1010   return result;
1011 }
1012
1013 GVariant *
1014 g_settings_schema_key_from_enum (GSettingsSchemaKey *key,
1015                                  gint                value)
1016 {
1017   const gchar *string;
1018
1019   string = strinfo_string_from_enum (key->strinfo, key->strinfo_length, value);
1020
1021   if (string == NULL)
1022     return NULL;
1023
1024   return g_variant_new_string (string);
1025 }
1026
1027 guint
1028 g_settings_schema_key_to_flags (GSettingsSchemaKey *key,
1029                                 GVariant           *value)
1030 {
1031   GVariantIter iter;
1032   const gchar *flag;
1033   guint result;
1034
1035   result = 0;
1036   g_variant_iter_init (&iter, value);
1037   while (g_variant_iter_next (&iter, "&s", &flag))
1038     {
1039       gboolean it_worked;
1040       guint flag_value;
1041
1042       it_worked = strinfo_enum_from_string (key->strinfo, key->strinfo_length, flag, &flag_value);
1043       /* as in g_settings_to_enum() */
1044       g_assert (it_worked);
1045
1046       result |= flag_value;
1047     }
1048
1049   return result;
1050 }
1051
1052 GVariant *
1053 g_settings_schema_key_from_flags (GSettingsSchemaKey *key,
1054                                   guint               value)
1055 {
1056   GVariantBuilder builder;
1057   gint i;
1058
1059   g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
1060
1061   for (i = 0; i < 32; i++)
1062     if (value & (1u << i))
1063       {
1064         const gchar *string;
1065
1066         string = strinfo_string_from_enum (key->strinfo, key->strinfo_length, 1u << i);
1067
1068         if (string == NULL)
1069           {
1070             g_variant_builder_clear (&builder);
1071             return NULL;
1072           }
1073
1074         g_variant_builder_add (&builder, "s", string);
1075       }
1076
1077   return g_variant_builder_end (&builder);
1078 }
1079
1080 G_DEFINE_BOXED_TYPE (GSettingsSchemaKey, g_settings_schema_key, g_settings_schema_key_ref, g_settings_schema_key_unref)
1081
1082 /**
1083  * g_settings_schema_key_ref:
1084  * @key: a #GSettingsSchemaKey
1085  *
1086  * Increase the reference count of @key, returning a new reference.
1087  *
1088  * Returns: a new reference to @key
1089  *
1090  * Since: 2.40
1091  **/
1092 GSettingsSchemaKey *
1093 g_settings_schema_key_ref (GSettingsSchemaKey *key)
1094 {
1095   g_return_val_if_fail (key != NULL, NULL);
1096
1097   g_atomic_int_inc (&key->ref_count);
1098
1099   return key;
1100 }
1101
1102 /**
1103  * g_settings_schema_key_unref:
1104  * @key: a #GSettingsSchemaKey
1105  *
1106  * Decrease the reference count of @key, possibly freeing it.
1107  *
1108  * Since: 2.40
1109  **/
1110 void
1111 g_settings_schema_key_unref (GSettingsSchemaKey *key)
1112 {
1113   g_return_if_fail (key != NULL);
1114
1115   if (g_atomic_int_dec_and_test (&key->ref_count))
1116     {
1117       g_settings_schema_key_clear (key);
1118
1119       g_slice_free (GSettingsSchemaKey, key);
1120     }
1121 }
1122
1123 /**
1124  * g_settings_schema_get_key:
1125  * @schema: a #GSettingsSchema
1126  * @name: the name of a key
1127  *
1128  * Gets the key named @name from @schema.
1129  *
1130  * It is a programmer error to request a key that does not exist.  See
1131  * g_settings_schema_list_keys().
1132  *
1133  * Returns: (transfer full): the #GSettingsSchemaKey for @name
1134  *
1135  * Since: 2.40
1136  **/
1137 GSettingsSchemaKey *
1138 g_settings_schema_get_key (GSettingsSchema *schema,
1139                            const gchar     *name)
1140 {
1141   GSettingsSchemaKey *key;
1142
1143   g_return_val_if_fail (schema != NULL, NULL);
1144   g_return_val_if_fail (name != NULL, NULL);
1145
1146   key = g_slice_new (GSettingsSchemaKey);
1147   g_settings_schema_key_init (key, schema, name);
1148   key->ref_count = 1;
1149
1150   return key;
1151 }