GSettingsSchema: alter our 'reverse' technology
[platform/upstream/glib.git] / gio / gsettingsschema.c
1 /*
2  * Copyright © 2010 Codethink Limited
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the licence, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
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 <string.h>
30
31 struct _GSettingsSchema
32 {
33   const gchar *gettext_domain;
34   const gchar *path;
35   GQuark *items;
36   gint n_items;
37   GvdbTable *table;
38   gchar *name;
39
40   gint ref_count;
41 };
42
43 static GSList *schema_sources;
44
45 static void
46 initialise_schema_sources (void)
47 {
48   static gsize initialised;
49
50   /* need a separate variable because 'schema_sources' may legitimately
51    * be null if we have zero valid schema sources
52    */
53   if G_UNLIKELY (g_once_init_enter (&initialised))
54     {
55       const gchar * const *dirs;
56       const gchar *path;
57       gint i;
58
59       /* iterate in reverse: count up, then count down */
60       dirs = g_get_system_data_dirs ();
61       for (i = 0; dirs[i]; i++);
62
63       while (i--)
64         {
65           gchar *filename;
66           GvdbTable *table;
67
68           filename = g_build_filename (dirs[i], "glib-2.0", "schemas", "gschemas.compiled", NULL);
69           table = gvdb_table_new (filename, TRUE, NULL);
70
71           if (table != NULL)
72             schema_sources = g_slist_prepend (schema_sources, table);
73
74           g_free (filename);
75         }
76
77       if ((path = g_getenv ("GSETTINGS_SCHEMA_DIR")) != NULL)
78         {
79           gchar *filename;
80           GvdbTable *table;
81
82           filename = g_build_filename (path, "gschemas.compiled", NULL);
83           table = gvdb_table_new (filename, TRUE, NULL);
84
85           if (table != NULL)
86             schema_sources = g_slist_prepend (schema_sources, table);
87
88           g_free (filename);
89         }
90
91       g_once_init_leave (&initialised, TRUE);
92     }
93 }
94
95 static gboolean
96 steal_item (gpointer key,
97             gpointer value,
98             gpointer user_data)
99 {
100   gchar ***ptr = user_data;
101
102   *(*ptr)++ = (gchar *) key;
103
104   return TRUE;
105 }
106
107 static const gchar * const *non_relocatable_schema_list;
108 static const gchar * const *relocatable_schema_list;
109 static gsize schema_lists_initialised;
110
111 static void
112 ensure_schema_lists (void)
113 {
114   if (g_once_init_enter (&schema_lists_initialised))
115     {
116       GHashTable *single, *reloc;
117       const gchar **ptr;
118       GSList *source;
119       gchar **list;
120       gint i;
121
122       initialise_schema_sources ();
123
124       /* We use hash tables to avoid duplicate listings for schemas that
125        * appear in more than one file.
126        */
127       single = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
128       reloc = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
129
130       for (source = schema_sources; source; source = source->next)
131         {
132           list = gvdb_table_list (source->data, "");
133
134           g_assert (list != NULL);
135
136           for (i = 0; list[i]; i++)
137             {
138               if (!g_hash_table_lookup (single, list[i]) &&
139                   !g_hash_table_lookup (reloc, list[i]))
140                 {
141                   GvdbTable *table;
142
143                   table = gvdb_table_get_table (source->data, list[i]);
144                   g_assert (table != NULL);
145
146                   if (gvdb_table_has_value (table, ".path"))
147                     g_hash_table_insert (single, g_strdup (list[i]), NULL);
148                   else
149                     g_hash_table_insert (reloc, g_strdup (list[i]), NULL);
150
151                   gvdb_table_unref (table);
152                 }
153             }
154
155           g_strfreev (list);
156         }
157
158       ptr = g_new (const gchar *, g_hash_table_size (single) + 1);
159       non_relocatable_schema_list = ptr;
160       g_hash_table_foreach_steal (single, steal_item, &ptr);
161       g_hash_table_unref (single);
162       *ptr = NULL;
163
164       ptr = g_new (const gchar *, g_hash_table_size (reloc) + 1);
165       relocatable_schema_list = ptr;
166       g_hash_table_foreach_steal (reloc, steal_item, &ptr);
167       g_hash_table_unref (reloc);
168       *ptr = NULL;
169
170       g_once_init_leave (&schema_lists_initialised, TRUE);
171     }
172 }
173
174 /**
175  * g_settings_list_schemas:
176  *
177  * Gets a list of the #GSettings schemas installed on the system.  The
178  * returned list is exactly the list of schemas for which you may call
179  * g_settings_new() without adverse effects.
180  *
181  * This function does not list the schemas that do not provide their own
182  * paths (ie: schemas for which you must use
183  * g_settings_new_with_path()).  See
184  * g_settings_list_relocatable_schemas() for that.
185  *
186  * Returns: (element-type utf8) (transfer none):  a list of #GSettings
187  *   schemas that are available.  The list must not be modified or
188  *   freed.
189  *
190  * Since: 2.26
191  **/
192 const gchar * const *
193 g_settings_list_schemas (void)
194 {
195   ensure_schema_lists ();
196
197   return non_relocatable_schema_list;
198 }
199
200 /**
201  * g_settings_list_relocatable_schemas:
202  *
203  * Gets a list of the relocatable #GSettings schemas installed on the
204  * system.  These are schemas that do not provide their own path.  It is
205  * usual to instantiate these schemas directly, but if you want to you
206  * can use g_settings_new_with_path() to specify the path.
207  *
208  * The output of this function, tTaken together with the output of
209  * g_settings_list_schemas() represents the complete list of all
210  * installed schemas.
211  *
212  * Returns: (element-type utf8) (transfer none): a list of relocatable
213  *   #GSettings schemas that are available.  The list must not be
214  *   modified or freed.
215  *
216  * Since: 2.28
217  **/
218 const gchar * const *
219 g_settings_list_relocatable_schemas (void)
220 {
221   ensure_schema_lists ();
222
223   return relocatable_schema_list;
224 }
225
226 GSettingsSchema *
227 g_settings_schema_ref (GSettingsSchema *schema)
228 {
229   g_atomic_int_inc (&schema->ref_count);
230
231   return schema;
232 }
233
234 void
235 g_settings_schema_unref (GSettingsSchema *schema)
236 {
237   if (g_atomic_int_dec_and_test (&schema->ref_count))
238     {
239       gvdb_table_unref (schema->table);
240       g_free (schema->items);
241       g_free (schema->name);
242
243       g_slice_free (GSettingsSchema, schema);
244     }
245 }
246
247 const gchar *
248 g_settings_schema_get_string (GSettingsSchema *schema,
249                               const gchar     *key)
250 {
251   const gchar *result = NULL;
252   GVariant *value;
253
254   if ((value = gvdb_table_get_raw_value (schema->table, key)))
255     {
256       result = g_variant_get_string (value, NULL);
257       g_variant_unref (value);
258     }
259
260   return result;
261 }
262
263 GSettingsSchema *
264 g_settings_schema_new (const gchar *name)
265 {
266   GSettingsSchema *schema;
267   GvdbTable *table = NULL;
268   GSList *source;
269
270   g_return_val_if_fail (name != NULL, NULL);
271
272   initialise_schema_sources ();
273
274   for (source = schema_sources; source; source = source->next)
275     {
276       GvdbTable *file = source->data;
277
278       if ((table = gvdb_table_get_table (file, name)))
279         break;
280     }
281
282   if (table == NULL)
283     g_error ("Settings schema '%s' is not installed\n", name);
284
285   schema = g_slice_new0 (GSettingsSchema);
286   schema->ref_count = 1;
287   schema->name = g_strdup (name);
288   schema->table = table;
289   schema->path =
290     g_settings_schema_get_string (schema, ".path");
291   schema->gettext_domain =
292     g_settings_schema_get_string (schema, ".gettext-domain");
293
294   if (schema->gettext_domain)
295     bind_textdomain_codeset (schema->gettext_domain, "UTF-8");
296
297   return schema;
298 }
299
300 GVariantIter *
301 g_settings_schema_get_value (GSettingsSchema *schema,
302                              const gchar     *key)
303 {
304   GVariantIter *iter;
305   GVariant *value;
306
307   value = gvdb_table_get_raw_value (schema->table, key);
308
309   if G_UNLIKELY (value == NULL)
310     g_error ("Settings schema '%s' does not contain a key named '%s'",
311              schema->name, key);
312
313   iter = g_variant_iter_new (value);
314   g_variant_unref (value);
315
316   return iter;
317 }
318
319 const gchar *
320 g_settings_schema_get_path (GSettingsSchema *schema)
321 {
322   return schema->path;
323 }
324
325 const gchar *
326 g_settings_schema_get_gettext_domain (GSettingsSchema *schema)
327 {
328   return schema->gettext_domain;
329 }
330
331 gboolean
332 g_settings_schema_has_key (GSettingsSchema *schema,
333                            const gchar     *key)
334 {
335   return gvdb_table_has_value (schema->table, key);
336 }
337
338 const GQuark *
339 g_settings_schema_list (GSettingsSchema *schema,
340                         gint            *n_items)
341 {
342   gint i, j;
343
344   if (schema->items == NULL)
345     {
346       gchar **list;
347       gint len;
348
349       list = gvdb_table_list (schema->table, "");
350       len = list ? g_strv_length (list) : 0;
351
352       schema->items = g_new (GQuark, len);
353       j = 0;
354
355       for (i = 0; i < len; i++)
356         if (list[i][0] != '.')
357           schema->items[j++] = g_quark_from_string (list[i]);
358       schema->n_items = j;
359
360       g_strfreev (list);
361     }
362
363   *n_items = schema->n_items;
364   return schema->items;
365 }
366
367 const gchar *
368 g_settings_schema_get_name (GSettingsSchema *schema)
369 {
370   return schema->name;
371 }
372
373 static inline void
374 endian_fixup (GVariant **value)
375 {
376 #if G_BYTE_ORDER == G_BIG_ENDIAN
377   GVariant *tmp;
378
379   tmp = g_variant_byteswap (*value);
380   g_variant_unref (*value);
381   *value = tmp;
382 #endif
383 }
384
385 void
386 g_settings_schema_key_init (GSettingsSchemaKey *key,
387                             GSettingsSchema    *schema,
388                             const gchar        *name)
389 {
390   GVariantIter *iter;
391   GVariant *data;
392   guchar code;
393
394   memset (key, 0, sizeof *key);
395
396   iter = g_settings_schema_get_value (schema, name);
397
398   key->schema = g_settings_schema_ref (schema);
399   key->default_value = g_variant_iter_next_value (iter);
400   endian_fixup (&key->default_value);
401   key->type = g_variant_get_type (key->default_value);
402   key->name = g_intern_string (name);
403
404   while (g_variant_iter_next (iter, "(y*)", &code, &data))
405     {
406       switch (code)
407         {
408         case 'l':
409           /* translation requested */
410           g_variant_get (data, "(y&s)", &key->lc_char, &key->unparsed);
411           break;
412
413         case 'e':
414           /* enumerated types... */
415           key->is_enum = TRUE;
416           goto choice;
417
418         case 'f':
419           /* flags... */
420           key->is_flags = TRUE;
421           goto choice;
422
423         choice: case 'c':
424           /* ..., choices, aliases */
425           key->strinfo = g_variant_get_fixed_array (data, &key->strinfo_length, sizeof (guint32));
426           break;
427
428         case 'r':
429           g_variant_get (data, "(**)", &key->minimum, &key->maximum);
430           endian_fixup (&key->minimum);
431           endian_fixup (&key->maximum);
432           break;
433
434         default:
435           g_warning ("unknown schema extension '%c'", code);
436           break;
437         }
438
439       g_variant_unref (data);
440     }
441
442   g_variant_iter_free (iter);
443 }
444
445 void
446 g_settings_schema_key_clear (GSettingsSchemaKey *key)
447 {
448   if (key->minimum)
449     g_variant_unref (key->minimum);
450
451   if (key->maximum)
452     g_variant_unref (key->maximum);
453
454   g_variant_unref (key->default_value);
455
456   g_settings_schema_unref (key->schema);
457 }
458
459 gboolean
460 g_settings_schema_key_type_check (GSettingsSchemaKey *key,
461                                   GVariant           *value)
462 {
463   g_return_val_if_fail (value != NULL, FALSE);
464
465   return g_variant_is_of_type (value, key->type);
466 }
467
468 gboolean
469 g_settings_schema_key_range_check (GSettingsSchemaKey *key,
470                                    GVariant           *value)
471 {
472   if (key->minimum == NULL && key->strinfo == NULL)
473     return TRUE;
474
475   if (g_variant_is_container (value))
476     {
477       gboolean ok = TRUE;
478       GVariantIter iter;
479       GVariant *child;
480
481       g_variant_iter_init (&iter, value);
482       while (ok && (child = g_variant_iter_next_value (&iter)))
483         {
484           ok = g_settings_schema_key_range_check (key, child);
485           g_variant_unref (child);
486         }
487
488       return ok;
489     }
490
491   if (key->minimum)
492     {
493       return g_variant_compare (key->minimum, value) <= 0 &&
494              g_variant_compare (value, key->maximum) <= 0;
495     }
496
497   return strinfo_is_string_valid (key->strinfo, key->strinfo_length,
498                                   g_variant_get_string (value, NULL));
499 }
500
501 GVariant *
502 g_settings_schema_key_range_fixup (GSettingsSchemaKey *key,
503                                    GVariant           *value)
504 {
505   const gchar *target;
506
507   if (g_settings_schema_key_range_check (key, value))
508     return g_variant_ref (value);
509
510   if (key->strinfo == NULL)
511     return NULL;
512
513   if (g_variant_is_container (value))
514     {
515       GVariantBuilder builder;
516       GVariantIter iter;
517       GVariant *child;
518
519       g_variant_iter_init (&iter, value);
520       g_variant_builder_init (&builder, g_variant_get_type (value));
521
522       while ((child = g_variant_iter_next_value (&iter)))
523         {
524           GVariant *fixed;
525
526           fixed = g_settings_schema_key_range_fixup (key, child);
527           g_variant_unref (child);
528
529           if (fixed == NULL)
530             {
531               g_variant_builder_clear (&builder);
532               return NULL;
533             }
534
535           g_variant_builder_add_value (&builder, fixed);
536           g_variant_unref (fixed);
537         }
538
539       return g_variant_ref_sink (g_variant_builder_end (&builder));
540     }
541
542   target = strinfo_string_from_alias (key->strinfo, key->strinfo_length,
543                                       g_variant_get_string (value, NULL));
544   return target ? g_variant_ref_sink (g_variant_new_string (target)) : NULL;
545 }
546
547
548 GVariant *
549 g_settings_schema_key_get_translated_default (GSettingsSchemaKey *key)
550 {
551   const gchar *translated;
552   GError *error = NULL;
553   const gchar *domain;
554   GVariant *value;
555
556   domain = g_settings_schema_get_gettext_domain (key->schema);
557
558   if (key->lc_char == '\0')
559     /* translation not requested for this key */
560     return NULL;
561
562   if (key->lc_char == 't')
563     translated = g_dcgettext (domain, key->unparsed, LC_TIME);
564   else
565     translated = g_dgettext (domain, key->unparsed);
566
567   if (translated == key->unparsed)
568     /* the default value was not translated */
569     return NULL;
570
571   /* try to parse the translation of the unparsed default */
572   value = g_variant_parse (key->type, translated, NULL, NULL, &error);
573
574   if (value == NULL)
575     {
576       g_warning ("Failed to parse translated string `%s' for "
577                  "key `%s' in schema `%s': %s", key->unparsed, key->name,
578                  g_settings_schema_get_name (key->schema), error->message);
579       g_warning ("Using untranslated default instead.");
580       g_error_free (error);
581     }
582
583   else if (!g_settings_schema_key_range_check (key, value))
584     {
585       g_warning ("Translated default `%s' for key `%s' in schema `%s' "
586                  "is outside of valid range", key->unparsed, key->name,
587                  g_settings_schema_get_name (key->schema));
588       g_variant_unref (value);
589       value = NULL;
590     }
591
592   return value;
593 }
594
595 gint
596 g_settings_schema_key_to_enum (GSettingsSchemaKey *key,
597                                GVariant           *value)
598 {
599   gboolean it_worked;
600   guint result;
601
602   it_worked = strinfo_enum_from_string (key->strinfo, key->strinfo_length,
603                                         g_variant_get_string (value, NULL),
604                                         &result);
605
606   /* 'value' can only come from the backend after being filtered for validity,
607    * from the translation after being filtered for validity, or from the schema
608    * itself (which the schema compiler checks for validity).  If this assertion
609    * fails then it's really a bug in GSettings or the schema compiler...
610    */
611   g_assert (it_worked);
612
613   return result;
614 }
615
616 GVariant *
617 g_settings_schema_key_from_enum (GSettingsSchemaKey *key,
618                                  gint                value)
619 {
620   const gchar *string;
621
622   string = strinfo_string_from_enum (key->strinfo, key->strinfo_length, value);
623
624   if (string == NULL)
625     return NULL;
626
627   return g_variant_new_string (string);
628 }
629
630 guint
631 g_settings_schema_key_to_flags (GSettingsSchemaKey *key,
632                                 GVariant           *value)
633 {
634   GVariantIter iter;
635   const gchar *flag;
636   guint result;
637
638   result = 0;
639   g_variant_iter_init (&iter, value);
640   while (g_variant_iter_next (&iter, "&s", &flag))
641     {
642       gboolean it_worked;
643       guint flag_value;
644
645       it_worked = strinfo_enum_from_string (key->strinfo, key->strinfo_length, flag, &flag_value);
646       /* as in g_settings_to_enum() */
647       g_assert (it_worked);
648
649       result |= flag_value;
650     }
651
652   return result;
653 }
654
655 GVariant *
656 g_settings_schema_key_from_flags (GSettingsSchemaKey *key,
657                                   guint               value)
658 {
659   GVariantBuilder builder;
660   gint i;
661
662   g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
663
664   for (i = 0; i < 32; i++)
665     if (value & (1u << i))
666       {
667         const gchar *string;
668
669         string = strinfo_string_from_enum (key->strinfo, key->strinfo_length, 1u << i);
670
671         if (string == NULL)
672           {
673             g_variant_builder_clear (&builder);
674             return NULL;
675           }
676
677         g_variant_builder_add (&builder, "s", string);
678       }
679
680   return g_variant_builder_end (&builder);
681 }