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