Imported Upstream version 2.67.4
[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.1 of the License, 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, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "config.h"
20
21 #include "glib-private.h"
22 #include "gsettingsschema-internal.h"
23 #include "gsettings.h"
24
25 #include "gvdb/gvdb-reader.h"
26 #include "strinfo.c"
27
28 #include <glibintl.h>
29 #include <locale.h>
30 #include <string.h>
31 #include <stdlib.h>
32
33 /**
34  * SECTION:gsettingsschema
35  * @short_description: Introspecting and controlling the loading
36  *     of GSettings schemas
37  * @include: gio/gio.h
38  *
39  * The #GSettingsSchemaSource and #GSettingsSchema APIs provide a
40  * mechanism for advanced control over the loading of schemas and a
41  * mechanism for introspecting their content.
42  *
43  * Plugin loading systems that wish to provide plugins a way to access
44  * settings face the problem of how to make the schemas for these
45  * settings visible to GSettings.  Typically, a plugin will want to ship
46  * the schema along with itself and it won't be installed into the
47  * standard system directories for schemas.
48  *
49  * #GSettingsSchemaSource provides a mechanism for dealing with this by
50  * allowing the creation of a new 'schema source' from which schemas can
51  * be acquired.  This schema source can then become part of the metadata
52  * associated with the plugin and queried whenever the plugin requires
53  * access to some settings.
54  *
55  * Consider the following example:
56  *
57  * |[<!-- language="C" -->
58  * typedef struct
59  * {
60  *    ...
61  *    GSettingsSchemaSource *schema_source;
62  *    ...
63  * } Plugin;
64  *
65  * Plugin *
66  * initialise_plugin (const gchar *dir)
67  * {
68  *   Plugin *plugin;
69  *
70  *   ...
71  *
72  *   plugin->schema_source =
73  *     g_settings_schema_source_new_from_directory (dir,
74  *       g_settings_schema_source_get_default (), FALSE, NULL);
75  *
76  *   ...
77  *
78  *   return plugin;
79  * }
80  *
81  * ...
82  *
83  * GSettings *
84  * plugin_get_settings (Plugin      *plugin,
85  *                      const gchar *schema_id)
86  * {
87  *   GSettingsSchema *schema;
88  *
89  *   if (schema_id == NULL)
90  *     schema_id = plugin->identifier;
91  *
92  *   schema = g_settings_schema_source_lookup (plugin->schema_source,
93  *                                             schema_id, FALSE);
94  *
95  *   if (schema == NULL)
96  *     {
97  *       ... disable the plugin or abort, etc ...
98  *     }
99  *
100  *   return g_settings_new_full (schema, NULL, NULL);
101  * }
102  * ]|
103  *
104  * The code above shows how hooks should be added to the code that
105  * initialises (or enables) the plugin to create the schema source and
106  * how an API can be added to the plugin system to provide a convenient
107  * way for the plugin to access its settings, using the schemas that it
108  * ships.
109  *
110  * From the standpoint of the plugin, it would need to ensure that it
111  * ships a gschemas.compiled file as part of itself, and then simply do
112  * the following:
113  *
114  * |[<!-- language="C" -->
115  * {
116  *   GSettings *settings;
117  *   gint some_value;
118  *
119  *   settings = plugin_get_settings (self, NULL);
120  *   some_value = g_settings_get_int (settings, "some-value");
121  *   ...
122  * }
123  * ]|
124  *
125  * It's also possible that the plugin system expects the schema source
126  * files (ie: .gschema.xml files) instead of a gschemas.compiled file.
127  * In that case, the plugin loading system must compile the schemas for
128  * itself before attempting to create the settings source.
129  *
130  * Since: 2.32
131  **/
132
133 /**
134  * GSettingsSchemaKey:
135  *
136  * #GSettingsSchemaKey is an opaque data structure and can only be accessed
137  * using the following functions.
138  **/
139
140 /**
141  * GSettingsSchema:
142  *
143  * This is an opaque structure type.  You may not access it directly.
144  *
145  * Since: 2.32
146  **/
147 struct _GSettingsSchema
148 {
149   GSettingsSchemaSource *source;
150   const gchar *gettext_domain;
151   const gchar *path;
152   GQuark *items;
153   gint n_items;
154   GvdbTable *table;
155   gchar *id;
156
157   GSettingsSchema *extends;
158
159   gint ref_count;
160 };
161
162 /**
163  * G_TYPE_SETTINGS_SCHEMA_SOURCE:
164  *
165  * A boxed #GType corresponding to #GSettingsSchemaSource.
166  *
167  * Since: 2.32
168  **/
169 G_DEFINE_BOXED_TYPE (GSettingsSchemaSource, g_settings_schema_source, g_settings_schema_source_ref, g_settings_schema_source_unref)
170
171 /**
172  * G_TYPE_SETTINGS_SCHEMA:
173  *
174  * A boxed #GType corresponding to #GSettingsSchema.
175  *
176  * Since: 2.32
177  **/
178 G_DEFINE_BOXED_TYPE (GSettingsSchema, g_settings_schema, g_settings_schema_ref, g_settings_schema_unref)
179
180 /**
181  * GSettingsSchemaSource:
182  *
183  * This is an opaque structure type.  You may not access it directly.
184  *
185  * Since: 2.32
186  **/
187 struct _GSettingsSchemaSource
188 {
189   GSettingsSchemaSource *parent;
190   gchar *directory;
191   GvdbTable *table;
192   GHashTable **text_tables;
193
194   gint ref_count;
195 };
196
197 static GSettingsSchemaSource *schema_sources;
198
199 /**
200  * g_settings_schema_source_ref:
201  * @source: a #GSettingsSchemaSource
202  *
203  * Increase the reference count of @source, returning a new reference.
204  *
205  * Returns: a new reference to @source
206  *
207  * Since: 2.32
208  **/
209 GSettingsSchemaSource *
210 g_settings_schema_source_ref (GSettingsSchemaSource *source)
211 {
212   g_atomic_int_inc (&source->ref_count);
213
214   return source;
215 }
216
217 /**
218  * g_settings_schema_source_unref:
219  * @source: a #GSettingsSchemaSource
220  *
221  * Decrease the reference count of @source, possibly freeing it.
222  *
223  * Since: 2.32
224  **/
225 void
226 g_settings_schema_source_unref (GSettingsSchemaSource *source)
227 {
228   if (g_atomic_int_dec_and_test (&source->ref_count))
229     {
230       if (source == schema_sources)
231         g_error ("g_settings_schema_source_unref() called too many times on the default schema source");
232
233       if (source->parent)
234         g_settings_schema_source_unref (source->parent);
235       gvdb_table_free (source->table);
236       g_free (source->directory);
237
238       if (source->text_tables)
239         {
240           g_hash_table_unref (source->text_tables[0]);
241           g_hash_table_unref (source->text_tables[1]);
242           g_free (source->text_tables);
243         }
244
245       g_slice_free (GSettingsSchemaSource, source);
246     }
247 }
248
249 /**
250  * g_settings_schema_source_new_from_directory:
251  * @directory: (type filename): the filename of a directory
252  * @parent: (nullable): a #GSettingsSchemaSource, or %NULL
253  * @trusted: %TRUE, if the directory is trusted
254  * @error: a pointer to a #GError pointer set to %NULL, or %NULL
255  *
256  * Attempts to create a new schema source corresponding to the contents
257  * of the given directory.
258  *
259  * This function is not required for normal uses of #GSettings but it
260  * may be useful to authors of plugin management systems.
261  *
262  * The directory should contain a file called `gschemas.compiled` as
263  * produced by the [glib-compile-schemas][glib-compile-schemas] tool.
264  *
265  * If @trusted is %TRUE then `gschemas.compiled` is trusted not to be
266  * corrupted. This assumption has a performance advantage, but can result
267  * in crashes or inconsistent behaviour in the case of a corrupted file.
268  * Generally, you should set @trusted to %TRUE for files installed by the
269  * system and to %FALSE for files in the home directory.
270  *
271  * In either case, an empty file or some types of corruption in the file will
272  * result in %G_FILE_ERROR_INVAL being returned.
273  *
274  * If @parent is non-%NULL then there are two effects.
275  *
276  * First, if g_settings_schema_source_lookup() is called with the
277  * @recursive flag set to %TRUE and the schema can not be found in the
278  * source, the lookup will recurse to the parent.
279  *
280  * Second, any references to other schemas specified within this
281  * source (ie: `child` or `extends`) references may be resolved
282  * from the @parent.
283  *
284  * For this second reason, except in very unusual situations, the
285  * @parent should probably be given as the default schema source, as
286  * returned by g_settings_schema_source_get_default().
287  *
288  * Since: 2.32
289  **/
290 GSettingsSchemaSource *
291 g_settings_schema_source_new_from_directory (const gchar            *directory,
292                                              GSettingsSchemaSource  *parent,
293                                              gboolean                trusted,
294                                              GError                **error)
295 {
296   GSettingsSchemaSource *source;
297   GvdbTable *table;
298   gchar *filename;
299
300   filename = g_build_filename (directory, "gschemas.compiled", NULL);
301   table = gvdb_table_new (filename, trusted, error);
302   g_free (filename);
303
304   if (table == NULL)
305     return NULL;
306
307   source = g_slice_new (GSettingsSchemaSource);
308   source->directory = g_strdup (directory);
309   source->parent = parent ? g_settings_schema_source_ref (parent) : NULL;
310   source->text_tables = NULL;
311   source->table = table;
312   source->ref_count = 1;
313
314   return source;
315 }
316
317 static void
318 try_prepend_dir (const gchar *directory)
319 {
320   GSettingsSchemaSource *source;
321
322   source = g_settings_schema_source_new_from_directory (directory, schema_sources, TRUE, NULL);
323
324   /* If we successfully created it then prepend it to the global list */
325   if (source != NULL)
326     schema_sources = source;
327 }
328
329 static void
330 try_prepend_data_dir (const gchar *directory)
331 {
332   gchar *dirname = g_build_filename (directory, "glib-2.0", "schemas", NULL);
333   try_prepend_dir (dirname);
334   g_free (dirname);
335 }
336
337 static void
338 initialise_schema_sources (void)
339 {
340   static gsize initialised;
341
342   /* need a separate variable because 'schema_sources' may legitimately
343    * be null if we have zero valid schema sources
344    */
345   if G_UNLIKELY (g_once_init_enter (&initialised))
346     {
347       gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) ();
348       const gchar * const *dirs;
349       const gchar *path;
350       gchar **extra_schema_dirs;
351       gint i;
352
353       /* iterate in reverse: count up, then count down */
354       dirs = g_get_system_data_dirs ();
355       for (i = 0; dirs[i]; i++);
356
357       while (i--)
358         try_prepend_data_dir (dirs[i]);
359
360       try_prepend_data_dir (g_get_user_data_dir ());
361
362       /* Disallow loading extra schemas if running as setuid, as that could
363        * allow reading privileged files. */
364       if (!is_setuid && (path = g_getenv ("GSETTINGS_SCHEMA_DIR")) != NULL)
365         {
366           extra_schema_dirs = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 0);
367           for (i = 0; extra_schema_dirs[i]; i++);
368
369           while (i--)
370             try_prepend_dir (extra_schema_dirs[i]);
371
372           g_strfreev (extra_schema_dirs);
373         }
374
375       g_once_init_leave (&initialised, TRUE);
376     }
377 }
378
379 /**
380  * g_settings_schema_source_get_default:
381  *
382  * Gets the default system schema source.
383  *
384  * This function is not required for normal uses of #GSettings but it
385  * may be useful to authors of plugin management systems or to those who
386  * want to introspect the content of schemas.
387  *
388  * If no schemas are installed, %NULL will be returned.
389  *
390  * The returned source may actually consist of multiple schema sources
391  * from different directories, depending on which directories were given
392  * in `XDG_DATA_DIRS` and `GSETTINGS_SCHEMA_DIR`. For this reason, all
393  * lookups performed against the default source should probably be done
394  * recursively.
395  *
396  * Returns: (transfer none) (nullable): the default schema source
397  *
398  * Since: 2.32
399  **/
400 GSettingsSchemaSource *
401 g_settings_schema_source_get_default (void)
402 {
403   initialise_schema_sources ();
404
405   return schema_sources;
406 }
407
408 /**
409  * g_settings_schema_source_lookup:
410  * @source: a #GSettingsSchemaSource
411  * @schema_id: a schema ID
412  * @recursive: %TRUE if the lookup should be recursive
413  *
414  * Looks up a schema with the identifier @schema_id in @source.
415  *
416  * This function is not required for normal uses of #GSettings but it
417  * may be useful to authors of plugin management systems or to those who
418  * want to introspect the content of schemas.
419  *
420  * If the schema isn't found directly in @source and @recursive is %TRUE
421  * then the parent sources will also be checked.
422  *
423  * If the schema isn't found, %NULL is returned.
424  *
425  * Returns: (nullable) (transfer full): a new #GSettingsSchema
426  *
427  * Since: 2.32
428  **/
429 GSettingsSchema *
430 g_settings_schema_source_lookup (GSettingsSchemaSource *source,
431                                  const gchar           *schema_id,
432                                  gboolean               recursive)
433 {
434   GSettingsSchema *schema;
435   GvdbTable *table;
436   const gchar *extends;
437
438   g_return_val_if_fail (source != NULL, NULL);
439   g_return_val_if_fail (schema_id != NULL, NULL);
440
441   table = gvdb_table_get_table (source->table, schema_id);
442
443   if (table == NULL && recursive)
444     for (source = source->parent; source; source = source->parent)
445       if ((table = gvdb_table_get_table (source->table, schema_id)))
446         break;
447
448   if (table == NULL)
449     return NULL;
450
451   schema = g_slice_new0 (GSettingsSchema);
452   schema->source = g_settings_schema_source_ref (source);
453   schema->ref_count = 1;
454   schema->id = g_strdup (schema_id);
455   schema->table = table;
456   schema->path = g_settings_schema_get_string (schema, ".path");
457   schema->gettext_domain = g_settings_schema_get_string (schema, ".gettext-domain");
458
459   if (schema->gettext_domain)
460     bind_textdomain_codeset (schema->gettext_domain, "UTF-8");
461
462   extends = g_settings_schema_get_string (schema, ".extends");
463   if (extends)
464     {
465       schema->extends = g_settings_schema_source_lookup (source, extends, TRUE);
466       if (schema->extends == NULL)
467         g_warning ("Schema '%s' extends schema '%s' but we could not find it", schema_id, extends);
468     }
469
470   return schema;
471 }
472
473 typedef struct
474 {
475   GHashTable *summaries;
476   GHashTable *descriptions;
477   GSList     *gettext_domain;
478   GSList     *schema_id;
479   GSList     *key_name;
480   GString    *string;
481 } TextTableParseInfo;
482
483 static const gchar *
484 get_attribute_value (GSList *list)
485 {
486   GSList *node;
487
488   for (node = list; node; node = node->next)
489     if (node->data)
490       return node->data;
491
492   return NULL;
493 }
494
495 static void
496 pop_attribute_value (GSList **list)
497 {
498   gchar *top;
499
500   top = (*list)->data;
501   *list = g_slist_remove (*list, top);
502
503   g_free (top);
504 }
505
506 static void
507 push_attribute_value (GSList      **list,
508                       const gchar  *value)
509 {
510   *list = g_slist_prepend (*list, g_strdup (value));
511 }
512
513 static void
514 start_element (GMarkupParseContext  *context,
515                const gchar          *element_name,
516                const gchar         **attribute_names,
517                const gchar         **attribute_values,
518                gpointer              user_data,
519                GError              **error)
520 {
521   TextTableParseInfo *info = user_data;
522   const gchar *gettext_domain = NULL;
523   const gchar *schema_id = NULL;
524   const gchar *key_name = NULL;
525   gint i;
526
527   for (i = 0; attribute_names[i]; i++)
528     {
529       if (g_str_equal (attribute_names[i], "gettext-domain"))
530         gettext_domain = attribute_values[i];
531       else if (g_str_equal (attribute_names[i], "id"))
532         schema_id = attribute_values[i];
533       else if (g_str_equal (attribute_names[i], "name"))
534         key_name = attribute_values[i];
535     }
536
537   push_attribute_value (&info->gettext_domain, gettext_domain);
538   push_attribute_value (&info->schema_id, schema_id);
539   push_attribute_value (&info->key_name, key_name);
540
541   if (info->string)
542     {
543       g_string_free (info->string, TRUE);
544       info->string = NULL;
545     }
546
547   if (g_str_equal (element_name, "summary") || g_str_equal (element_name, "description"))
548     info->string = g_string_new (NULL);
549 }
550
551 static gchar *
552 normalise_whitespace (const gchar *orig)
553 {
554   /* We normalise by the same rules as in intltool:
555    *
556    *   sub cleanup {
557    *       s/^\s+//;
558    *       s/\s+$//;
559    *       s/\s+/ /g;
560    *       return $_;
561    *   }
562    *
563    *   $message = join "\n\n", map &cleanup, split/\n\s*\n+/, $message;
564    *
565    * Where \s is an ascii space character.
566    *
567    * We aim for ease of implementation over efficiency -- this code is
568    * not run in normal applications.
569    */
570   static GRegex *cleanup[3];
571   static GRegex *splitter;
572   gchar **lines;
573   gchar *result;
574   gint i;
575
576   if (g_once_init_enter (&splitter))
577     {
578       GRegex *s;
579
580       cleanup[0] = g_regex_new ("^\\s+", 0, 0, 0);
581       cleanup[1] = g_regex_new ("\\s+$", 0, 0, 0);
582       cleanup[2] = g_regex_new ("\\s+", 0, 0, 0);
583       s = g_regex_new ("\\n\\s*\\n+", 0, 0, 0);
584
585       g_once_init_leave (&splitter, s);
586     }
587
588   lines = g_regex_split (splitter, orig, 0);
589   for (i = 0; lines[i]; i++)
590     {
591       gchar *a, *b, *c;
592
593       a = g_regex_replace_literal (cleanup[0], lines[i], -1, 0, "", 0, 0);
594       b = g_regex_replace_literal (cleanup[1], a, -1, 0, "", 0, 0);
595       c = g_regex_replace_literal (cleanup[2], b, -1, 0, " ", 0, 0);
596       g_free (lines[i]);
597       g_free (a);
598       g_free (b);
599       lines[i] = c;
600     }
601
602   result = g_strjoinv ("\n\n", lines);
603   g_strfreev (lines);
604
605   return result;
606 }
607
608 static void
609 end_element (GMarkupParseContext *context,
610              const gchar *element_name,
611              gpointer user_data,
612              GError **error)
613 {
614   TextTableParseInfo *info = user_data;
615
616   pop_attribute_value (&info->gettext_domain);
617   pop_attribute_value (&info->schema_id);
618   pop_attribute_value (&info->key_name);
619
620   if (info->string)
621     {
622       GHashTable *source_table = NULL;
623       const gchar *gettext_domain;
624       const gchar *schema_id;
625       const gchar *key_name;
626
627       gettext_domain = get_attribute_value (info->gettext_domain);
628       schema_id = get_attribute_value (info->schema_id);
629       key_name = get_attribute_value (info->key_name);
630
631       if (g_str_equal (element_name, "summary"))
632         source_table = info->summaries;
633       else if (g_str_equal (element_name, "description"))
634         source_table = info->descriptions;
635
636       if (source_table && schema_id && key_name)
637         {
638           GHashTable *schema_table;
639           gchar *normalised;
640
641           schema_table = g_hash_table_lookup (source_table, schema_id);
642
643           if (schema_table == NULL)
644             {
645               schema_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
646               g_hash_table_insert (source_table, g_strdup (schema_id), schema_table);
647             }
648
649           normalised = normalise_whitespace (info->string->str);
650
651           if (gettext_domain && normalised[0])
652             {
653               gchar *translated;
654
655               translated = g_strdup (g_dgettext (gettext_domain, normalised));
656               g_free (normalised);
657               normalised = translated;
658             }
659
660           g_hash_table_insert (schema_table, g_strdup (key_name), normalised);
661         }
662
663       g_string_free (info->string, TRUE);
664       info->string = NULL;
665     }
666 }
667
668 static void
669 text (GMarkupParseContext  *context,
670       const gchar          *text,
671       gsize                 text_len,
672       gpointer              user_data,
673       GError              **error)
674 {
675   TextTableParseInfo *info = user_data;
676
677   if (info->string)
678     g_string_append_len (info->string, text, text_len);
679 }
680
681 static void
682 parse_into_text_tables (const gchar *directory,
683                         GHashTable  *summaries,
684                         GHashTable  *descriptions)
685 {
686   GMarkupParser parser = { start_element, end_element, text, NULL, NULL };
687   TextTableParseInfo info = { summaries, descriptions, NULL, NULL, NULL, NULL };
688   const gchar *basename;
689   GDir *dir;
690
691   dir = g_dir_open (directory, 0, NULL);
692   while ((basename = g_dir_read_name (dir)))
693     {
694       gchar *filename;
695       gchar *contents;
696       gsize size;
697
698       filename = g_build_filename (directory, basename, NULL);
699       if (g_file_get_contents (filename, &contents, &size, NULL))
700         {
701           GMarkupParseContext *context;
702
703           context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &info, NULL);
704           /* Ignore errors here, this is best effort only. */
705           if (g_markup_parse_context_parse (context, contents, size, NULL))
706             (void) g_markup_parse_context_end_parse (context, NULL);
707           g_markup_parse_context_free (context);
708
709           /* Clean up dangling stuff in case there was an error. */
710           g_slist_free_full (info.gettext_domain, g_free);
711           g_slist_free_full (info.schema_id, g_free);
712           g_slist_free_full (info.key_name, g_free);
713
714           info.gettext_domain = NULL;
715           info.schema_id = NULL;
716           info.key_name = NULL;
717
718           if (info.string)
719             {
720               g_string_free (info.string, TRUE);
721               info.string = NULL;
722             }
723
724           g_free (contents);
725         }
726
727       g_free (filename);
728     }
729   
730   g_dir_close (dir);
731 }
732
733 static GHashTable **
734 g_settings_schema_source_get_text_tables (GSettingsSchemaSource *source)
735 {
736   if (g_once_init_enter (&source->text_tables))
737     {
738       GHashTable **text_tables;
739
740       text_tables = g_new (GHashTable *, 2);
741       text_tables[0] = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_unref);
742       text_tables[1] = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_unref);
743
744       if (source->directory)
745         parse_into_text_tables (source->directory, text_tables[0], text_tables[1]);
746
747       g_once_init_leave (&source->text_tables, text_tables);
748     }
749
750   return source->text_tables;
751 }
752
753 /**
754  * g_settings_schema_source_list_schemas:
755  * @source: a #GSettingsSchemaSource
756  * @recursive: if we should recurse
757  * @non_relocatable: (out) (transfer full) (array zero-terminated=1): the
758  *   list of non-relocatable schemas, in no defined order
759  * @relocatable: (out) (transfer full) (array zero-terminated=1): the list
760  *   of relocatable schemas, in no defined order
761  *
762  * Lists the schemas in a given source.
763  *
764  * If @recursive is %TRUE then include parent sources.  If %FALSE then
765  * only include the schemas from one source (ie: one directory).  You
766  * probably want %TRUE.
767  *
768  * Non-relocatable schemas are those for which you can call
769  * g_settings_new().  Relocatable schemas are those for which you must
770  * use g_settings_new_with_path().
771  *
772  * Do not call this function from normal programs.  This is designed for
773  * use by database editors, commandline tools, etc.
774  *
775  * Since: 2.40
776  **/
777 void
778 g_settings_schema_source_list_schemas (GSettingsSchemaSource   *source,
779                                        gboolean                 recursive,
780                                        gchar                 ***non_relocatable,
781                                        gchar                 ***relocatable)
782 {
783   GHashTable *single, *reloc;
784   GSettingsSchemaSource *s;
785
786   /* We use hash tables to avoid duplicate listings for schemas that
787    * appear in more than one file.
788    */
789   single = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
790   reloc = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
791
792   for (s = source; s; s = s->parent)
793     {
794       gchar **list;
795       gint i;
796
797       list = gvdb_table_list (s->table, "");
798
799       /* empty schema cache file? */
800       if (list == NULL)
801         continue;
802
803       for (i = 0; list[i]; i++)
804         {
805           if (!g_hash_table_contains (single, list[i]) &&
806               !g_hash_table_contains (reloc, list[i]))
807             {
808               gchar *schema;
809               GvdbTable *table;
810
811               schema = g_strdup (list[i]);
812
813               table = gvdb_table_get_table (s->table, list[i]);
814               g_assert (table != NULL);
815
816               if (gvdb_table_has_value (table, ".path"))
817                 g_hash_table_add (single, schema);
818               else
819                 g_hash_table_add (reloc, schema);
820
821               gvdb_table_free (table);
822             }
823         }
824
825       g_strfreev (list);
826
827       /* Only the first source if recursive not requested */
828       if (!recursive)
829         break;
830     }
831
832   if (non_relocatable)
833     {
834       *non_relocatable = (gchar **) g_hash_table_get_keys_as_array (single, NULL);
835       g_hash_table_steal_all (single);
836     }
837
838   if (relocatable)
839     {
840       *relocatable = (gchar **) g_hash_table_get_keys_as_array (reloc, NULL);
841       g_hash_table_steal_all (reloc);
842     }
843
844   g_hash_table_unref (single);
845   g_hash_table_unref (reloc);
846 }
847
848 static gchar **non_relocatable_schema_list;
849 static gchar **relocatable_schema_list;
850 static gsize schema_lists_initialised;
851
852 static void
853 ensure_schema_lists (void)
854 {
855   if (g_once_init_enter (&schema_lists_initialised))
856     {
857       initialise_schema_sources ();
858
859       g_settings_schema_source_list_schemas (schema_sources, TRUE,
860                                              &non_relocatable_schema_list,
861                                              &relocatable_schema_list);
862
863       g_once_init_leave (&schema_lists_initialised, TRUE);
864     }
865 }
866
867 /**
868  * g_settings_list_schemas:
869  *
870  * Deprecated.
871  *
872  * Returns: (element-type utf8) (transfer none):  a list of #GSettings
873  *   schemas that are available, in no defined order.  The list must not be
874  *   modified or freed.
875  *
876  * Since: 2.26
877  *
878  * Deprecated: 2.40: Use g_settings_schema_source_list_schemas() instead.
879  * If you used g_settings_list_schemas() to check for the presence of
880  * a particular schema, use g_settings_schema_source_lookup() instead
881  * of your whole loop.
882  **/
883 const gchar * const *
884 g_settings_list_schemas (void)
885 {
886   ensure_schema_lists ();
887
888   return (const gchar **) non_relocatable_schema_list;
889 }
890
891 /**
892  * g_settings_list_relocatable_schemas:
893  *
894  * Deprecated.
895  *
896  * Returns: (element-type utf8) (transfer none): a list of relocatable
897  *   #GSettings schemas that are available, in no defined order.  The list must
898  *   not be modified or freed.
899  *
900  * Since: 2.28
901  *
902  * Deprecated: 2.40: Use g_settings_schema_source_list_schemas() instead
903  **/
904 const gchar * const *
905 g_settings_list_relocatable_schemas (void)
906 {
907   ensure_schema_lists ();
908
909   return (const gchar **) relocatable_schema_list;
910 }
911
912 /**
913  * g_settings_schema_ref:
914  * @schema: a #GSettingsSchema
915  *
916  * Increase the reference count of @schema, returning a new reference.
917  *
918  * Returns: a new reference to @schema
919  *
920  * Since: 2.32
921  **/
922 GSettingsSchema *
923 g_settings_schema_ref (GSettingsSchema *schema)
924 {
925   g_atomic_int_inc (&schema->ref_count);
926
927   return schema;
928 }
929
930 /**
931  * g_settings_schema_unref:
932  * @schema: a #GSettingsSchema
933  *
934  * Decrease the reference count of @schema, possibly freeing it.
935  *
936  * Since: 2.32
937  **/
938 void
939 g_settings_schema_unref (GSettingsSchema *schema)
940 {
941   if (g_atomic_int_dec_and_test (&schema->ref_count))
942     {
943       if (schema->extends)
944         g_settings_schema_unref (schema->extends);
945
946       g_settings_schema_source_unref (schema->source);
947       gvdb_table_free (schema->table);
948       g_free (schema->items);
949       g_free (schema->id);
950
951       g_slice_free (GSettingsSchema, schema);
952     }
953 }
954
955 const gchar *
956 g_settings_schema_get_string (GSettingsSchema *schema,
957                               const gchar     *key)
958 {
959   const gchar *result = NULL;
960   GVariant *value;
961
962   if ((value = gvdb_table_get_raw_value (schema->table, key)))
963     {
964       result = g_variant_get_string (value, NULL);
965       g_variant_unref (value);
966     }
967
968   return result;
969 }
970
971 GVariantIter *
972 g_settings_schema_get_value (GSettingsSchema *schema,
973                              const gchar     *key)
974 {
975   GSettingsSchema *s = schema;
976   GVariantIter *iter;
977   GVariant *value = NULL;
978
979   g_return_val_if_fail (schema != NULL, NULL);
980
981   for (s = schema; s; s = s->extends)
982     if ((value = gvdb_table_get_raw_value (s->table, key)))
983       break;
984
985   if G_UNLIKELY (value == NULL || !g_variant_is_of_type (value, G_VARIANT_TYPE_TUPLE))
986     g_error ("Settings schema '%s' does not contain a key named '%s'", schema->id, key);
987
988   iter = g_variant_iter_new (value);
989   g_variant_unref (value);
990
991   return iter;
992 }
993
994 /**
995  * g_settings_schema_get_path:
996  * @schema: a #GSettingsSchema
997  *
998  * Gets the path associated with @schema, or %NULL.
999  *
1000  * Schemas may be single-instance or relocatable.  Single-instance
1001  * schemas correspond to exactly one set of keys in the backend
1002  * database: those located at the path returned by this function.
1003  *
1004  * Relocatable schemas can be referenced by other schemas and can
1005  * therefore describe multiple sets of keys at different locations.  For
1006  * relocatable schemas, this function will return %NULL.
1007  *
1008  * Returns: (nullable) (transfer none): the path of the schema, or %NULL
1009  *
1010  * Since: 2.32
1011  **/
1012 const gchar *
1013 g_settings_schema_get_path (GSettingsSchema *schema)
1014 {
1015   return schema->path;
1016 }
1017
1018 const gchar *
1019 g_settings_schema_get_gettext_domain (GSettingsSchema *schema)
1020 {
1021   return schema->gettext_domain;
1022 }
1023
1024 /**
1025  * g_settings_schema_has_key:
1026  * @schema: a #GSettingsSchema
1027  * @name: the name of a key
1028  *
1029  * Checks if @schema has a key named @name.
1030  *
1031  * Returns: %TRUE if such a key exists
1032  *
1033  * Since: 2.40
1034  **/
1035 gboolean
1036 g_settings_schema_has_key (GSettingsSchema *schema,
1037                            const gchar     *key)
1038 {
1039   return gvdb_table_has_value (schema->table, key);
1040 }
1041
1042 /**
1043  * g_settings_schema_list_children:
1044  * @schema: a #GSettingsSchema
1045  *
1046  * Gets the list of children in @schema.
1047  *
1048  * You should free the return value with g_strfreev() when you are done
1049  * with it.
1050  *
1051  * Returns: (transfer full) (element-type utf8): a list of the children on
1052  *    @settings, in no defined order
1053  *
1054  * Since: 2.44
1055  */
1056 gchar **
1057 g_settings_schema_list_children (GSettingsSchema *schema)
1058 {
1059   const GQuark *keys;
1060   gchar **strv;
1061   gint n_keys;
1062   gint i, j;
1063
1064   g_return_val_if_fail (schema != NULL, NULL);
1065
1066   keys = g_settings_schema_list (schema, &n_keys);
1067   strv = g_new (gchar *, n_keys + 1);
1068   for (i = j = 0; i < n_keys; i++)
1069     {
1070       const gchar *key = g_quark_to_string (keys[i]);
1071
1072       if (g_str_has_suffix (key, "/"))
1073         {
1074           gsize length = strlen (key);
1075
1076           strv[j] = g_memdup2 (key, length);
1077           strv[j][length - 1] = '\0';
1078           j++;
1079         }
1080     }
1081   strv[j] = NULL;
1082
1083   return strv;
1084 }
1085
1086 /**
1087  * g_settings_schema_list_keys:
1088  * @schema: a #GSettingsSchema
1089  *
1090  * Introspects the list of keys on @schema.
1091  *
1092  * You should probably not be calling this function from "normal" code
1093  * (since you should already know what keys are in your schema).  This
1094  * function is intended for introspection reasons.
1095  *
1096  * Returns: (transfer full) (element-type utf8): a list of the keys on
1097  *   @schema, in no defined order
1098  *
1099  * Since: 2.46
1100  */
1101 gchar **
1102 g_settings_schema_list_keys (GSettingsSchema *schema)
1103 {
1104   const GQuark *keys;
1105   gchar **strv;
1106   gint n_keys;
1107   gint i, j;
1108
1109   g_return_val_if_fail (schema != NULL, NULL);
1110
1111   keys = g_settings_schema_list (schema, &n_keys);
1112   strv = g_new (gchar *, n_keys + 1);
1113   for (i = j = 0; i < n_keys; i++)
1114     {
1115       const gchar *key = g_quark_to_string (keys[i]);
1116
1117       if (!g_str_has_suffix (key, "/"))
1118         strv[j++] = g_strdup (key);
1119     }
1120   strv[j] = NULL;
1121
1122   return strv;
1123 }
1124
1125 const GQuark *
1126 g_settings_schema_list (GSettingsSchema *schema,
1127                         gint            *n_items)
1128 {
1129   if (schema->items == NULL)
1130     {
1131       GSettingsSchema *s;
1132       GHashTableIter iter;
1133       GHashTable *items;
1134       gpointer name;
1135       gint len;
1136       gint i;
1137
1138       items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1139
1140       for (s = schema; s; s = s->extends)
1141         {
1142           gchar **list;
1143
1144           list = gvdb_table_list (s->table, "");
1145
1146           if (list)
1147             {
1148               for (i = 0; list[i]; i++)
1149                 g_hash_table_add (items, list[i]); /* transfer ownership */
1150
1151               g_free (list); /* free container only */
1152             }
1153         }
1154
1155       /* Do a first pass to eliminate child items that do not map to
1156        * valid schemas (ie: ones that would crash us if we actually
1157        * tried to create them).
1158        */
1159       g_hash_table_iter_init (&iter, items);
1160       while (g_hash_table_iter_next (&iter, &name, NULL))
1161         if (g_str_has_suffix (name, "/"))
1162           {
1163             GSettingsSchemaSource *source;
1164             GVariant *child_schema;
1165             GvdbTable *child_table;
1166
1167             child_schema = gvdb_table_get_raw_value (schema->table, name);
1168             if (!child_schema)
1169               continue;
1170
1171             child_table = NULL;
1172
1173             for (source = schema->source; source; source = source->parent)
1174               if ((child_table = gvdb_table_get_table (source->table, g_variant_get_string (child_schema, NULL))))
1175                 break;
1176
1177             g_variant_unref (child_schema);
1178
1179             /* Schema is not found -> remove it from the list */
1180             if (child_table == NULL)
1181               {
1182                 g_hash_table_iter_remove (&iter);
1183                 continue;
1184               }
1185
1186             /* Make sure the schema is relocatable or at the
1187              * expected path
1188              */
1189             if (gvdb_table_has_value (child_table, ".path"))
1190               {
1191                 GVariant *path;
1192                 gchar *expected;
1193                 gboolean same;
1194
1195                 path = gvdb_table_get_raw_value (child_table, ".path");
1196                 expected = g_strconcat (schema->path, name, NULL);
1197                 same = g_str_equal (expected, g_variant_get_string (path, NULL));
1198                 g_variant_unref (path);
1199                 g_free (expected);
1200
1201                 /* Schema is non-relocatable and did not have the
1202                  * expected path -> remove it from the list
1203                  */
1204                 if (!same)
1205                   g_hash_table_iter_remove (&iter);
1206               }
1207
1208             gvdb_table_free (child_table);
1209           }
1210
1211       /* Now create the list */
1212       len = g_hash_table_size (items);
1213       schema->items = g_new (GQuark, len);
1214       i = 0;
1215       g_hash_table_iter_init (&iter, items);
1216
1217       while (g_hash_table_iter_next (&iter, &name, NULL))
1218         schema->items[i++] = g_quark_from_string (name);
1219       schema->n_items = i;
1220       g_assert (i == len);
1221
1222       g_hash_table_unref (items);
1223     }
1224
1225   *n_items = schema->n_items;
1226   return schema->items;
1227 }
1228
1229 /**
1230  * g_settings_schema_get_id:
1231  * @schema: a #GSettingsSchema
1232  *
1233  * Get the ID of @schema.
1234  *
1235  * Returns: (transfer none): the ID
1236  **/
1237 const gchar *
1238 g_settings_schema_get_id (GSettingsSchema *schema)
1239 {
1240   return schema->id;
1241 }
1242
1243 static inline void
1244 endian_fixup (GVariant **value)
1245 {
1246 #if G_BYTE_ORDER == G_BIG_ENDIAN
1247   GVariant *tmp;
1248
1249   tmp = g_variant_byteswap (*value);
1250   g_variant_unref (*value);
1251   *value = tmp;
1252 #endif
1253 }
1254
1255 void
1256 g_settings_schema_key_init (GSettingsSchemaKey *key,
1257                             GSettingsSchema    *schema,
1258                             const gchar        *name)
1259 {
1260   GVariantIter *iter;
1261   GVariant *data;
1262   guchar code;
1263
1264   memset (key, 0, sizeof *key);
1265
1266   iter = g_settings_schema_get_value (schema, name);
1267
1268   key->schema = g_settings_schema_ref (schema);
1269   key->default_value = g_variant_iter_next_value (iter);
1270   endian_fixup (&key->default_value);
1271   key->type = g_variant_get_type (key->default_value);
1272   key->name = g_intern_string (name);
1273
1274   while (g_variant_iter_next (iter, "(y*)", &code, &data))
1275     {
1276       switch (code)
1277         {
1278         case 'l':
1279           /* translation requested */
1280           g_variant_get (data, "(y&s)", &key->lc_char, &key->unparsed);
1281           break;
1282
1283         case 'e':
1284           /* enumerated types... */
1285           key->is_enum = TRUE;
1286           goto choice;
1287
1288         case 'f':
1289           /* flags... */
1290           key->is_flags = TRUE;
1291           goto choice;
1292
1293         choice: case 'c':
1294           /* ..., choices, aliases */
1295           key->strinfo = g_variant_get_fixed_array (data, &key->strinfo_length, sizeof (guint32));
1296           break;
1297
1298         case 'r':
1299           g_variant_get (data, "(**)", &key->minimum, &key->maximum);
1300           endian_fixup (&key->minimum);
1301           endian_fixup (&key->maximum);
1302           break;
1303
1304         case 'd':
1305           g_variant_get (data, "@a{sv}", &key->desktop_overrides);
1306           endian_fixup (&key->desktop_overrides);
1307           break;
1308
1309         default:
1310           g_warning ("unknown schema extension '%c'", code);
1311           break;
1312         }
1313
1314       g_variant_unref (data);
1315     }
1316
1317   g_variant_iter_free (iter);
1318 }
1319
1320 void
1321 g_settings_schema_key_clear (GSettingsSchemaKey *key)
1322 {
1323   if (key->minimum)
1324     g_variant_unref (key->minimum);
1325
1326   if (key->maximum)
1327     g_variant_unref (key->maximum);
1328
1329   if (key->desktop_overrides)
1330     g_variant_unref (key->desktop_overrides);
1331
1332   g_variant_unref (key->default_value);
1333
1334   g_settings_schema_unref (key->schema);
1335 }
1336
1337 gboolean
1338 g_settings_schema_key_type_check (GSettingsSchemaKey *key,
1339                                   GVariant           *value)
1340 {
1341   g_return_val_if_fail (value != NULL, FALSE);
1342
1343   return g_variant_is_of_type (value, key->type);
1344 }
1345
1346 GVariant *
1347 g_settings_schema_key_range_fixup (GSettingsSchemaKey *key,
1348                                    GVariant           *value)
1349 {
1350   const gchar *target;
1351
1352   if (g_settings_schema_key_range_check (key, value))
1353     return g_variant_ref (value);
1354
1355   if (key->strinfo == NULL)
1356     return NULL;
1357
1358   if (g_variant_is_container (value))
1359     {
1360       GVariantBuilder builder;
1361       GVariantIter iter;
1362       GVariant *child;
1363
1364       g_variant_iter_init (&iter, value);
1365       g_variant_builder_init (&builder, g_variant_get_type (value));
1366
1367       while ((child = g_variant_iter_next_value (&iter)))
1368         {
1369           GVariant *fixed;
1370
1371           fixed = g_settings_schema_key_range_fixup (key, child);
1372           g_variant_unref (child);
1373
1374           if (fixed == NULL)
1375             {
1376               g_variant_builder_clear (&builder);
1377               return NULL;
1378             }
1379
1380           g_variant_builder_add_value (&builder, fixed);
1381           g_variant_unref (fixed);
1382         }
1383
1384       return g_variant_ref_sink (g_variant_builder_end (&builder));
1385     }
1386
1387   target = strinfo_string_from_alias (key->strinfo, key->strinfo_length,
1388                                       g_variant_get_string (value, NULL));
1389   return target ? g_variant_ref_sink (g_variant_new_string (target)) : NULL;
1390 }
1391
1392 GVariant *
1393 g_settings_schema_key_get_translated_default (GSettingsSchemaKey *key)
1394 {
1395   const gchar *translated;
1396   GError *error = NULL;
1397   const gchar *domain;
1398   GVariant *value;
1399
1400   domain = g_settings_schema_get_gettext_domain (key->schema);
1401
1402   if (key->lc_char == '\0')
1403     /* translation not requested for this key */
1404     return NULL;
1405
1406   if (key->lc_char == 't')
1407     translated = g_dcgettext (domain, key->unparsed, LC_TIME);
1408   else
1409     translated = g_dgettext (domain, key->unparsed);
1410
1411   if (translated == key->unparsed)
1412     /* the default value was not translated */
1413     return NULL;
1414
1415   /* try to parse the translation of the unparsed default */
1416   value = g_variant_parse (key->type, translated, NULL, NULL, &error);
1417
1418   if (value == NULL)
1419     {
1420       g_warning ("Failed to parse translated string '%s' for "
1421                  "key '%s' in schema '%s': %s", translated, key->name,
1422                  g_settings_schema_get_id (key->schema), error->message);
1423       g_warning ("Using untranslated default instead.");
1424       g_error_free (error);
1425     }
1426
1427   else if (!g_settings_schema_key_range_check (key, value))
1428     {
1429       g_warning ("Translated default '%s' for key '%s' in schema '%s' "
1430                  "is outside of valid range", key->unparsed, key->name,
1431                  g_settings_schema_get_id (key->schema));
1432       g_variant_unref (value);
1433       value = NULL;
1434     }
1435
1436   return value;
1437 }
1438
1439 GVariant *
1440 g_settings_schema_key_get_per_desktop_default (GSettingsSchemaKey *key)
1441 {
1442   static const gchar * const *current_desktops;
1443   GVariant *value = NULL;
1444   gint i;
1445
1446   if (!key->desktop_overrides)
1447     return NULL;
1448
1449   if (g_once_init_enter (&current_desktops))
1450     {
1451       const gchar *xdg_current_desktop = g_getenv ("XDG_CURRENT_DESKTOP");
1452       gchar **tmp;
1453
1454       if (xdg_current_desktop != NULL && xdg_current_desktop[0] != '\0')
1455         tmp = g_strsplit (xdg_current_desktop, G_SEARCHPATH_SEPARATOR_S, -1);
1456       else
1457         tmp = g_new0 (gchar *, 0 + 1);
1458
1459       g_once_init_leave (&current_desktops, (const gchar **) tmp);
1460     }
1461
1462   for (i = 0; value == NULL && current_desktops[i] != NULL; i++)
1463     value = g_variant_lookup_value (key->desktop_overrides, current_desktops[i], NULL);
1464
1465   return value;
1466 }
1467
1468 gint
1469 g_settings_schema_key_to_enum (GSettingsSchemaKey *key,
1470                                GVariant           *value)
1471 {
1472   gboolean it_worked G_GNUC_UNUSED  /* when compiling with G_DISABLE_ASSERT */;
1473   guint result;
1474
1475   it_worked = strinfo_enum_from_string (key->strinfo, key->strinfo_length,
1476                                         g_variant_get_string (value, NULL),
1477                                         &result);
1478
1479   /* 'value' can only come from the backend after being filtered for validity,
1480    * from the translation after being filtered for validity, or from the schema
1481    * itself (which the schema compiler checks for validity).  If this assertion
1482    * fails then it's really a bug in GSettings or the schema compiler...
1483    */
1484   g_assert (it_worked);
1485
1486   return result;
1487 }
1488
1489 /* Returns a new floating #GVariant. */
1490 GVariant *
1491 g_settings_schema_key_from_enum (GSettingsSchemaKey *key,
1492                                  gint                value)
1493 {
1494   const gchar *string;
1495
1496   string = strinfo_string_from_enum (key->strinfo, key->strinfo_length, value);
1497
1498   if (string == NULL)
1499     return NULL;
1500
1501   return g_variant_new_string (string);
1502 }
1503
1504 guint
1505 g_settings_schema_key_to_flags (GSettingsSchemaKey *key,
1506                                 GVariant           *value)
1507 {
1508   GVariantIter iter;
1509   const gchar *flag;
1510   guint result;
1511
1512   result = 0;
1513   g_variant_iter_init (&iter, value);
1514   while (g_variant_iter_next (&iter, "&s", &flag))
1515     {
1516       gboolean it_worked G_GNUC_UNUSED  /* when compiling with G_DISABLE_ASSERT */;
1517       guint flag_value;
1518
1519       it_worked = strinfo_enum_from_string (key->strinfo, key->strinfo_length, flag, &flag_value);
1520       /* as in g_settings_to_enum() */
1521       g_assert (it_worked);
1522
1523       result |= flag_value;
1524     }
1525
1526   return result;
1527 }
1528
1529 /* Returns a new floating #GVariant. */
1530 GVariant *
1531 g_settings_schema_key_from_flags (GSettingsSchemaKey *key,
1532                                   guint               value)
1533 {
1534   GVariantBuilder builder;
1535   gint i;
1536
1537   g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
1538
1539   for (i = 0; i < 32; i++)
1540     if (value & (1u << i))
1541       {
1542         const gchar *string;
1543
1544         string = strinfo_string_from_enum (key->strinfo, key->strinfo_length, 1u << i);
1545
1546         if (string == NULL)
1547           {
1548             g_variant_builder_clear (&builder);
1549             return NULL;
1550           }
1551
1552         g_variant_builder_add (&builder, "s", string);
1553       }
1554
1555   return g_variant_builder_end (&builder);
1556 }
1557
1558 G_DEFINE_BOXED_TYPE (GSettingsSchemaKey, g_settings_schema_key, g_settings_schema_key_ref, g_settings_schema_key_unref)
1559
1560 /**
1561  * g_settings_schema_key_ref:
1562  * @key: a #GSettingsSchemaKey
1563  *
1564  * Increase the reference count of @key, returning a new reference.
1565  *
1566  * Returns: a new reference to @key
1567  *
1568  * Since: 2.40
1569  **/
1570 GSettingsSchemaKey *
1571 g_settings_schema_key_ref (GSettingsSchemaKey *key)
1572 {
1573   g_return_val_if_fail (key != NULL, NULL);
1574
1575   g_atomic_int_inc (&key->ref_count);
1576
1577   return key;
1578 }
1579
1580 /**
1581  * g_settings_schema_key_unref:
1582  * @key: a #GSettingsSchemaKey
1583  *
1584  * Decrease the reference count of @key, possibly freeing it.
1585  *
1586  * Since: 2.40
1587  **/
1588 void
1589 g_settings_schema_key_unref (GSettingsSchemaKey *key)
1590 {
1591   g_return_if_fail (key != NULL);
1592
1593   if (g_atomic_int_dec_and_test (&key->ref_count))
1594     {
1595       g_settings_schema_key_clear (key);
1596
1597       g_slice_free (GSettingsSchemaKey, key);
1598     }
1599 }
1600
1601 /**
1602  * g_settings_schema_get_key:
1603  * @schema: a #GSettingsSchema
1604  * @name: the name of a key
1605  *
1606  * Gets the key named @name from @schema.
1607  *
1608  * It is a programmer error to request a key that does not exist.  See
1609  * g_settings_schema_list_keys().
1610  *
1611  * Returns: (transfer full): the #GSettingsSchemaKey for @name
1612  *
1613  * Since: 2.40
1614  **/
1615 GSettingsSchemaKey *
1616 g_settings_schema_get_key (GSettingsSchema *schema,
1617                            const gchar     *name)
1618 {
1619   GSettingsSchemaKey *key;
1620
1621   g_return_val_if_fail (schema != NULL, NULL);
1622   g_return_val_if_fail (name != NULL, NULL);
1623
1624   key = g_slice_new (GSettingsSchemaKey);
1625   g_settings_schema_key_init (key, schema, name);
1626   key->ref_count = 1;
1627
1628   return key;
1629 }
1630
1631 /**
1632  * g_settings_schema_key_get_name:
1633  * @key: a #GSettingsSchemaKey
1634  *
1635  * Gets the name of @key.
1636  *
1637  * Returns: the name of @key.
1638  *
1639  * Since: 2.44
1640  */
1641 const gchar *
1642 g_settings_schema_key_get_name (GSettingsSchemaKey *key)
1643 {
1644   g_return_val_if_fail (key != NULL, NULL);
1645
1646   return key->name;
1647 }
1648
1649 /**
1650  * g_settings_schema_key_get_summary:
1651  * @key: a #GSettingsSchemaKey
1652  *
1653  * Gets the summary for @key.
1654  *
1655  * If no summary has been provided in the schema for @key, returns
1656  * %NULL.
1657  *
1658  * The summary is a short description of the purpose of the key; usually
1659  * one short sentence.  Summaries can be translated and the value
1660  * returned from this function is is the current locale.
1661  *
1662  * This function is slow.  The summary and description information for
1663  * the schemas is not stored in the compiled schema database so this
1664  * function has to parse all of the source XML files in the schema
1665  * directory.
1666  *
1667  * Returns: (nullable): the summary for @key, or %NULL
1668  *
1669  * Since: 2.34
1670  **/
1671 const gchar *
1672 g_settings_schema_key_get_summary (GSettingsSchemaKey *key)
1673 {
1674   GHashTable **text_tables;
1675   GHashTable *summaries;
1676
1677   text_tables = g_settings_schema_source_get_text_tables (key->schema->source);
1678   summaries = g_hash_table_lookup (text_tables[0], key->schema->id);
1679
1680   return summaries ? g_hash_table_lookup (summaries, key->name) : NULL;
1681 }
1682
1683 /**
1684  * g_settings_schema_key_get_description:
1685  * @key: a #GSettingsSchemaKey
1686  *
1687  * Gets the description for @key.
1688  *
1689  * If no description has been provided in the schema for @key, returns
1690  * %NULL.
1691  *
1692  * The description can be one sentence to several paragraphs in length.
1693  * Paragraphs are delimited with a double newline.  Descriptions can be
1694  * translated and the value returned from this function is is the
1695  * current locale.
1696  *
1697  * This function is slow.  The summary and description information for
1698  * the schemas is not stored in the compiled schema database so this
1699  * function has to parse all of the source XML files in the schema
1700  * directory.
1701  *
1702  * Returns: (nullable): the description for @key, or %NULL
1703  *
1704  * Since: 2.34
1705  **/
1706 const gchar *
1707 g_settings_schema_key_get_description (GSettingsSchemaKey *key)
1708 {
1709   GHashTable **text_tables;
1710   GHashTable *descriptions;
1711
1712   text_tables = g_settings_schema_source_get_text_tables (key->schema->source);
1713   descriptions = g_hash_table_lookup (text_tables[1], key->schema->id);
1714
1715   return descriptions ? g_hash_table_lookup (descriptions, key->name) : NULL;
1716 }
1717
1718 /**
1719  * g_settings_schema_key_get_value_type:
1720  * @key: a #GSettingsSchemaKey
1721  *
1722  * Gets the #GVariantType of @key.
1723  *
1724  * Returns: (transfer none): the type of @key
1725  *
1726  * Since: 2.40
1727  **/
1728 const GVariantType *
1729 g_settings_schema_key_get_value_type (GSettingsSchemaKey *key)
1730 {
1731   g_return_val_if_fail (key, NULL);
1732
1733   return key->type;
1734 }
1735
1736 /**
1737  * g_settings_schema_key_get_default_value:
1738  * @key: a #GSettingsSchemaKey
1739  *
1740  * Gets the default value for @key.
1741  *
1742  * Note that this is the default value according to the schema.  System
1743  * administrator defaults and lockdown are not visible via this API.
1744  *
1745  * Returns: (transfer full): the default value for the key
1746  *
1747  * Since: 2.40
1748  **/
1749 GVariant *
1750 g_settings_schema_key_get_default_value (GSettingsSchemaKey *key)
1751 {
1752   GVariant *value;
1753
1754   g_return_val_if_fail (key, NULL);
1755
1756   value = g_settings_schema_key_get_translated_default (key);
1757
1758   if (!value)
1759     value = g_settings_schema_key_get_per_desktop_default (key);
1760
1761   if (!value)
1762     value = g_variant_ref (key->default_value);
1763
1764   return value;
1765 }
1766
1767 /**
1768  * g_settings_schema_key_get_range:
1769  * @key: a #GSettingsSchemaKey
1770  *
1771  * Queries the range of a key.
1772  *
1773  * This function will return a #GVariant that fully describes the range
1774  * of values that are valid for @key.
1775  *
1776  * The type of #GVariant returned is `(sv)`. The string describes
1777  * the type of range restriction in effect. The type and meaning of
1778  * the value contained in the variant depends on the string.
1779  *
1780  * If the string is `'type'` then the variant contains an empty array.
1781  * The element type of that empty array is the expected type of value
1782  * and all values of that type are valid.
1783  *
1784  * If the string is `'enum'` then the variant contains an array
1785  * enumerating the possible values. Each item in the array is
1786  * a possible valid value and no other values are valid.
1787  *
1788  * If the string is `'flags'` then the variant contains an array. Each
1789  * item in the array is a value that may appear zero or one times in an
1790  * array to be used as the value for this key. For example, if the
1791  * variant contained the array `['x', 'y']` then the valid values for
1792  * the key would be `[]`, `['x']`, `['y']`, `['x', 'y']` and
1793  * `['y', 'x']`.
1794  *
1795  * Finally, if the string is `'range'` then the variant contains a pair
1796  * of like-typed values -- the minimum and maximum permissible values
1797  * for this key.
1798  *
1799  * This information should not be used by normal programs.  It is
1800  * considered to be a hint for introspection purposes.  Normal programs
1801  * should already know what is permitted by their own schema.  The
1802  * format may change in any way in the future -- but particularly, new
1803  * forms may be added to the possibilities described above.
1804  *
1805  * You should free the returned value with g_variant_unref() when it is
1806  * no longer needed.
1807  *
1808  * Returns: (transfer full): a #GVariant describing the range
1809  *
1810  * Since: 2.40
1811  **/
1812 GVariant *
1813 g_settings_schema_key_get_range (GSettingsSchemaKey *key)
1814 {
1815   const gchar *type;
1816   GVariant *range;
1817
1818   if (key->minimum)
1819     {
1820       range = g_variant_new ("(**)", key->minimum, key->maximum);
1821       type = "range";
1822     }
1823   else if (key->strinfo)
1824     {
1825       range = strinfo_enumerate (key->strinfo, key->strinfo_length);
1826       type = key->is_flags ? "flags" : "enum";
1827     }
1828   else
1829     {
1830       range = g_variant_new_array (key->type, NULL, 0);
1831       type = "type";
1832     }
1833
1834   return g_variant_ref_sink (g_variant_new ("(sv)", type, range));
1835 }
1836
1837 /**
1838  * g_settings_schema_key_range_check:
1839  * @key: a #GSettingsSchemaKey
1840  * @value: the value to check
1841  *
1842  * Checks if the given @value is of the correct type and within the
1843  * permitted range for @key.
1844  *
1845  * It is a programmer error if @value is not of the correct type -- you
1846  * must check for this first.
1847  *
1848  * Returns: %TRUE if @value is valid for @key
1849  *
1850  * Since: 2.40
1851  **/
1852 gboolean
1853 g_settings_schema_key_range_check (GSettingsSchemaKey *key,
1854                                    GVariant           *value)
1855 {
1856   if (key->minimum == NULL && key->strinfo == NULL)
1857     return TRUE;
1858
1859   if (g_variant_is_container (value))
1860     {
1861       gboolean ok = TRUE;
1862       GVariantIter iter;
1863       GVariant *child;
1864
1865       g_variant_iter_init (&iter, value);
1866       while (ok && (child = g_variant_iter_next_value (&iter)))
1867         {
1868           ok = g_settings_schema_key_range_check (key, child);
1869           g_variant_unref (child);
1870         }
1871
1872       return ok;
1873     }
1874
1875   if (key->minimum)
1876     {
1877       return g_variant_compare (key->minimum, value) <= 0 &&
1878              g_variant_compare (value, key->maximum) <= 0;
1879     }
1880
1881   return strinfo_is_string_valid (key->strinfo, key->strinfo_length,
1882                                   g_variant_get_string (value, NULL));
1883 }