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

source code of gtk/subprojects/glib/gio/gsettingsschema.c