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