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