Improve GSettings test coverage
[platform/upstream/glib.git] / gio / glib-compile-schemas.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  * Author: Ryan Lortie <desrt@desrt.ca>
20  */
21
22 /* Prologue {{{1 */
23 #include "config.h"
24
25 #include <gstdio.h>
26 #include <gi18n.h>
27
28 #include <string.h>
29 #include <stdio.h>
30 #include <locale.h>
31
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35
36 #include "gvdb/gvdb-builder.h"
37 #include "strinfo.c"
38
39 static void
40 strip_string (GString *string)
41 {
42   gint i;
43
44   for (i = 0; g_ascii_isspace (string->str[i]); i++);
45   g_string_erase (string, 0, i);
46
47   if (string->len > 0)
48     {
49       /* len > 0, so there must be at least one non-whitespace character */
50       for (i = string->len - 1; g_ascii_isspace (string->str[i]); i--);
51       g_string_truncate (string, i + 1);
52     }
53 }
54
55 /* Handling of <enum> {{{1 */
56 typedef struct
57 {
58   GString *strinfo;
59
60   gboolean is_flags;
61 } EnumState;
62
63 static void
64 enum_state_free (gpointer data)
65 {
66   EnumState *state = data;
67
68   g_string_free (state->strinfo, TRUE);
69   g_slice_free (EnumState, state);
70 }
71
72 static EnumState *
73 enum_state_new (gboolean is_flags)
74 {
75   EnumState *state;
76
77   state = g_slice_new (EnumState);
78   state->strinfo = g_string_new (NULL);
79   state->is_flags = is_flags;
80
81   return state;
82 }
83
84 static void
85 enum_state_add_value (EnumState    *state,
86                       const gchar  *nick,
87                       const gchar  *valuestr,
88                       GError      **error)
89 {
90   gint64 value;
91   gchar *end;
92
93   if (nick[0] == '\0' || nick[1] == '\0')
94     {
95       g_set_error (error, G_MARKUP_ERROR,
96                    G_MARKUP_ERROR_INVALID_CONTENT,
97                    "nick must be a minimum of 2 characters");
98       return;
99     }
100
101   value = g_ascii_strtoll (valuestr, &end, 0);
102   if (*end || state->is_flags ?
103                 (value > G_MAXUINT32 || value < 0) :
104                 (value > G_MAXINT32 || value < G_MININT32))
105     {
106       g_set_error (error, G_MARKUP_ERROR,
107                    G_MARKUP_ERROR_INVALID_CONTENT,
108                    "invalid numeric value");
109       return;
110     }
111
112   if (strinfo_builder_contains (state->strinfo, nick))
113     {
114       g_set_error (error, G_MARKUP_ERROR,
115                    G_MARKUP_ERROR_INVALID_CONTENT,
116                    "<value nick='%s'/> already specified", nick);
117       return;
118     }
119
120   if (strinfo_builder_contains_value (state->strinfo, value))
121     {
122       g_set_error (error, G_MARKUP_ERROR,
123                    G_MARKUP_ERROR_INVALID_CONTENT,
124                    "value='%s' already specified", valuestr);
125       return;
126     }
127
128   /* Silently drop the null case if it is mentioned.
129    * It is properly denoted with an empty array.
130    */
131   if (state->is_flags && value == 0)
132     return;
133
134   if (state->is_flags && (value & (value - 1)))
135     {
136       g_set_error (error, G_MARKUP_ERROR,
137                    G_MARKUP_ERROR_INVALID_CONTENT,
138                    "flags values must have at most 1 bit set");
139       return;
140     }
141
142   /* Since we reject exact duplicates of value='' and we only allow one
143    * bit to be set, it's not possible to have overlaps.
144    *
145    * If we loosen the one-bit-set restriction we need an overlap check.
146    */
147
148   strinfo_builder_append_item (state->strinfo, nick, value);
149 }
150
151 static void
152 enum_state_end (EnumState **state_ptr,
153                 GError    **error)
154 {
155   EnumState *state;
156
157   state = *state_ptr;
158   *state_ptr = NULL;
159
160   if (state->strinfo->len == 0)
161     g_set_error (error,
162                  G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
163                  "<%s> must contain at least one <value>",
164                  state->is_flags ? "flags" : "enum");
165 }
166
167 /* Handling of <key> {{{1 */
168 typedef struct
169 {
170   /* for <child>, @child_schema will be set.
171    * for <key>, everything else will be set.
172    */
173   gchar        *child_schema;
174
175
176   GVariantType *type;
177   gboolean      have_gettext_domain;
178
179   gchar         l10n;
180   gchar        *l10n_context;
181   GString      *unparsed_default_value;
182   GVariant     *default_value;
183
184   GString      *strinfo;
185   gboolean      is_enum;
186   gboolean      is_flags;
187
188   GVariant     *minimum;
189   GVariant     *maximum;
190
191   gboolean      has_choices;
192   gboolean      has_aliases;
193   gboolean      is_override;
194
195   gboolean      checked;
196   GVariant     *serialised;
197 } KeyState;
198
199 static KeyState *
200 key_state_new (const gchar *type_string,
201                const gchar *gettext_domain,
202                gboolean     is_enum,
203                gboolean     is_flags,
204                GString     *strinfo)
205 {
206   KeyState *state;
207
208   state = g_slice_new0 (KeyState);
209   state->type = g_variant_type_new (type_string);
210   state->have_gettext_domain = gettext_domain != NULL;
211   state->is_enum = is_enum;
212   state->is_flags = is_flags;
213
214   if (strinfo)
215     state->strinfo = g_string_new_len (strinfo->str, strinfo->len);
216   else
217     state->strinfo = g_string_new (NULL);
218
219   return state;
220 }
221
222 static KeyState *
223 key_state_override (KeyState    *state,
224                     const gchar *gettext_domain)
225 {
226   KeyState *copy;
227
228   copy = g_slice_new0 (KeyState);
229   copy->type = g_variant_type_copy (state->type);
230   copy->have_gettext_domain = gettext_domain != NULL;
231   copy->strinfo = g_string_new_len (state->strinfo->str,
232                                     state->strinfo->len);
233   copy->is_enum = state->is_enum;
234   copy->is_flags = state->is_flags;
235   copy->is_override = TRUE;
236
237   if (state->minimum)
238     {
239       copy->minimum = g_variant_ref (state->minimum);
240       copy->maximum = g_variant_ref (state->maximum);
241     }
242
243   return copy;
244 }
245
246 static KeyState *
247 key_state_new_child (const gchar *child_schema)
248 {
249   KeyState *state;
250
251   state = g_slice_new0 (KeyState);
252   state->child_schema = g_strdup (child_schema);
253
254   return state;
255 }
256
257 static gboolean
258 is_valid_choices (GVariant *variant,
259                   GString  *strinfo)
260 {
261   switch (g_variant_classify (variant))
262     {
263       case G_VARIANT_CLASS_MAYBE:
264       case G_VARIANT_CLASS_ARRAY:
265         {
266           gboolean valid = TRUE;
267           GVariantIter iter;
268
269           g_variant_iter_init (&iter, variant);
270
271           while (valid && (variant = g_variant_iter_next_value (&iter)))
272             {
273               valid = is_valid_choices (variant, strinfo);
274               g_variant_unref (variant);
275             }
276
277           return valid;
278         }
279
280       case G_VARIANT_CLASS_STRING:
281         return strinfo_is_string_valid ((const guint32 *) strinfo->str,
282                                         strinfo->len / 4,
283                                         g_variant_get_string (variant, NULL));
284
285       default:
286         g_assert_not_reached ();
287     }
288 }
289
290
291 /* Gets called at </default> </choices> or <range/> to check for
292  * validity of the default value so that any inconsistency is
293  * reported as soon as it is encountered.
294  */
295 static void
296 key_state_check_range (KeyState  *state,
297                        GError   **error)
298 {
299   if (state->default_value)
300     {
301       const gchar *tag;
302
303       tag = state->is_override ? "override" : "default";
304
305       if (state->minimum)
306         {
307           if (g_variant_compare (state->default_value, state->minimum) < 0 ||
308               g_variant_compare (state->default_value, state->maximum) > 0)
309             {
310               g_set_error (error, G_MARKUP_ERROR,
311                            G_MARKUP_ERROR_INVALID_CONTENT,
312                            "<%s> is not contained in "
313                            "the specified range", tag);
314             }
315         }
316
317       else if (state->strinfo->len)
318         {
319           if (!is_valid_choices (state->default_value, state->strinfo))
320             {
321               if (state->is_enum)
322                 g_set_error (error, G_MARKUP_ERROR,
323                              G_MARKUP_ERROR_INVALID_CONTENT,
324                              "<%s> is not a valid member of "
325                              "the specified enumerated type", tag);
326
327               else if (state->is_flags)
328                 g_set_error (error, G_MARKUP_ERROR,
329                              G_MARKUP_ERROR_INVALID_CONTENT,
330                              "<%s> contains string not in the "
331                              "specified flags type", tag);
332
333               else
334                 g_set_error (error, G_MARKUP_ERROR,
335                              G_MARKUP_ERROR_INVALID_CONTENT,
336                              "<%s> contains string not in "
337                              "<choices>", tag);
338             }
339         }
340     }
341 }
342
343 static void
344 key_state_set_range (KeyState     *state,
345                      const gchar  *min_str,
346                      const gchar  *max_str,
347                      GError      **error)
348 {
349   const struct {
350     const gchar  type;
351     const gchar *min;
352     const gchar *max;
353   } table[] = {
354     { 'y',                    "0",                  "255" },
355     { 'n',               "-32768",                "32767" },
356     { 'q',                    "0",                "65535" },
357     { 'i',          "-2147483648",           "2147483647" },
358     { 'u',                    "0",           "4294967295" },
359     { 'x', "-9223372036854775808",  "9223372036854775807" },
360     { 't',                    "0", "18446744073709551615" },
361     { 'd',                 "-inf",                  "inf" },
362   };
363   gboolean type_ok = FALSE;
364   gint i;
365
366   if (state->minimum)
367     {
368       g_set_error_literal (error, G_MARKUP_ERROR,
369                            G_MARKUP_ERROR_INVALID_CONTENT,
370                            "<range/> already specified for this key");
371       return;
372     }
373
374   for (i = 0; i < G_N_ELEMENTS (table); i++)
375     if (*(char *) state->type == table[i].type)
376       {
377         min_str = min_str ? min_str : table[i].min;
378         max_str = max_str ? max_str : table[i].max;
379         type_ok = TRUE;
380         break;
381       }
382
383   if (!type_ok)
384     {
385       gchar *type = g_variant_type_dup_string (state->type);
386       g_set_error (error, G_MARKUP_ERROR,
387                   G_MARKUP_ERROR_INVALID_CONTENT,
388                   "<range> not allowed for keys of type '%s'", type);
389       g_free (type);
390       return;
391     }
392
393   state->minimum = g_variant_parse (state->type, min_str, NULL, NULL, error);
394   if (state->minimum == NULL)
395     return;
396
397   state->maximum = g_variant_parse (state->type, max_str, NULL, NULL, error);
398   if (state->maximum == NULL)
399     return;
400
401   if (g_variant_compare (state->minimum, state->maximum) > 0)
402     {
403       g_set_error (error, G_MARKUP_ERROR,
404                    G_MARKUP_ERROR_INVALID_CONTENT,
405                    "<range> specified minimum is greater than maxmimum");
406       return;
407     }
408
409   key_state_check_range (state, error);
410 }
411
412 static GString *
413 key_state_start_default (KeyState     *state,
414                          const gchar  *l10n,
415                          const gchar  *context,
416                          GError      **error)
417 {
418   if (l10n != NULL)
419     {
420       if (strcmp (l10n, "messages") == 0)
421         state->l10n = 'm';
422
423       else if (strcmp (l10n, "time") == 0)
424         state->l10n = 't';
425
426       else
427         {
428           g_set_error (error, G_MARKUP_ERROR,
429                        G_MARKUP_ERROR_INVALID_CONTENT,
430                        "unsupported l10n category: %s", l10n);
431           return NULL;
432         }
433
434       if (!state->have_gettext_domain)
435         {
436           g_set_error_literal (error, G_MARKUP_ERROR,
437                                G_MARKUP_ERROR_INVALID_CONTENT,
438                                "l10n requested, but no "
439                                "gettext domain given");
440           return NULL;
441         }
442
443       state->l10n_context = g_strdup (context);
444     }
445
446   else if (context != NULL)
447     {
448       g_set_error_literal (error, G_MARKUP_ERROR,
449                            G_MARKUP_ERROR_INVALID_CONTENT,
450                            "translation context given for "
451                            " value without l10n enabled");
452       return NULL;
453     }
454
455   return g_string_new (NULL);
456 }
457
458 static void
459 key_state_end_default (KeyState  *state,
460                        GString  **string,
461                        GError   **error)
462 {
463   state->unparsed_default_value = *string;
464   *string = NULL;
465
466   state->default_value = g_variant_parse (state->type,
467                                           state->unparsed_default_value->str,
468                                           NULL, NULL, error);
469   key_state_check_range (state, error);
470 }
471
472 static void
473 key_state_start_choices (KeyState  *state,
474                          GError   **error)
475 {
476   const GVariantType *type = state->type;
477
478   if (state->is_enum)
479     {
480       g_set_error_literal (error, G_MARKUP_ERROR,
481                            G_MARKUP_ERROR_INVALID_CONTENT,
482                            "<choices> cannot be specified for keys "
483                            "tagged as having an enumerated type");
484       return;
485     }
486
487   if (state->has_choices)
488     {
489       g_set_error_literal (error, G_MARKUP_ERROR,
490                            G_MARKUP_ERROR_INVALID_CONTENT,
491                            "<choices> already specified for this key");
492       return;
493     }
494
495   while (g_variant_type_is_maybe (type) || g_variant_type_is_array (type))
496     type = g_variant_type_element (type);
497
498   if (!g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
499     {
500       gchar *type_string = g_variant_type_dup_string (state->type);
501       g_set_error (error, G_MARKUP_ERROR,
502                    G_MARKUP_ERROR_INVALID_CONTENT,
503                    "<choices> not allowed for keys of type '%s'",
504                    type_string);
505       g_free (type_string);
506       return;
507     }
508 }
509
510 static void
511 key_state_add_choice (KeyState     *state,
512                       const gchar  *choice,
513                       GError      **error)
514 {
515   if (strinfo_builder_contains (state->strinfo, choice))
516     {
517       g_set_error (error, G_MARKUP_ERROR,
518                    G_MARKUP_ERROR_INVALID_CONTENT,
519                    "<choice value='%s'/> already given", choice);
520       return;
521     }
522
523   strinfo_builder_append_item (state->strinfo, choice, 0);
524   state->has_choices = TRUE;
525 }
526
527 static void
528 key_state_end_choices (KeyState  *state,
529                        GError   **error)
530 {
531   if (!state->has_choices)
532     {
533       g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
534                    "<choices> must contain at least one <choice>");
535       return;
536     }
537
538   key_state_check_range (state, error);
539 }
540
541 static void
542 key_state_start_aliases (KeyState  *state,
543                          GError   **error)
544 {
545   if (state->has_aliases)
546     g_set_error_literal (error, G_MARKUP_ERROR,
547                          G_MARKUP_ERROR_INVALID_CONTENT,
548                          "<aliases> already specified for this key");
549   else if (!state->is_flags && !state->is_enum && !state->has_choices)
550     g_set_error_literal (error, G_MARKUP_ERROR,
551                          G_MARKUP_ERROR_INVALID_CONTENT,
552                          "<aliases> can only be specified for keys with "
553                          "enumerated or flags types or after <choices>");
554 }
555
556 static void
557 key_state_add_alias (KeyState     *state,
558                      const gchar  *alias,
559                      const gchar  *target,
560                      GError      **error)
561 {
562   if (strinfo_builder_contains (state->strinfo, alias))
563     {
564       if (strinfo_is_string_valid ((guint32 *) state->strinfo->str,
565                                    state->strinfo->len / 4,
566                                    alias))
567         {
568           if (state->is_enum)
569             g_set_error (error, G_MARKUP_ERROR,
570                          G_MARKUP_ERROR_INVALID_CONTENT,
571                          "<alias value='%s'/> given when '%s' is already "
572                          "a member of the enumerated type", alias, alias);
573
574           else
575             g_set_error (error, G_MARKUP_ERROR,
576                          G_MARKUP_ERROR_INVALID_CONTENT,
577                          "<alias value='%s'/> given when "
578                          "<choice value='%s'/> was already given",
579                          alias, alias);
580         }
581
582       else
583         g_set_error (error, G_MARKUP_ERROR,
584                      G_MARKUP_ERROR_INVALID_CONTENT,
585                      "<alias value='%s'/> already specified", alias);
586
587       return;
588     }
589
590   if (!strinfo_builder_append_alias (state->strinfo, alias, target))
591     {
592       g_set_error (error, G_MARKUP_ERROR,
593                    G_MARKUP_ERROR_INVALID_CONTENT,
594                    "alias target '%s' is not in %s", target,
595                    state->is_enum ? "enumerated type" : "<choices>");
596       return;
597     }
598
599   state->has_aliases = TRUE;
600 }
601
602 static void
603 key_state_end_aliases (KeyState  *state,
604                        GError   **error)
605 {
606   if (!state->has_aliases)
607     {
608       g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
609                    "<aliases> must contain at least one <alias>");
610       return;
611     }
612 }
613
614 static gboolean
615 key_state_check (KeyState  *state,
616                  GError   **error)
617 {
618   if (state->checked)
619     return TRUE;
620
621   return state->checked = TRUE;
622 }
623
624 static GVariant *
625 key_state_serialise (KeyState *state)
626 {
627   if (state->serialised == NULL)
628     {
629       if (state->child_schema)
630         {
631           state->serialised = g_variant_new_string (state->child_schema);
632         }
633
634       else
635         {
636           GVariantBuilder builder;
637
638           g_assert (key_state_check (state, NULL));
639
640           g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
641
642           /* default value */
643           g_variant_builder_add_value (&builder, state->default_value);
644
645           /* translation */
646           if (state->l10n)
647             {
648               /* We are going to store the untranslated default for
649                * runtime translation according to the current locale.
650                * We need to strip leading and trailing whitespace from
651                * the string so that it's exactly the same as the one
652                * that ended up in the .po file for translation.
653                *
654                * We want to do this so that
655                *
656                *   <default l10n='messages'>
657                *     ['a', 'b', 'c']
658                *   </default>
659                *
660                * ends up in the .po file like "['a', 'b', 'c']",
661                * omitting the extra whitespace at the start and end.
662                */
663               strip_string (state->unparsed_default_value);
664
665               if (state->l10n_context)
666                 {
667                   gint len;
668
669                   /* Contextified messages are supported by prepending
670                    * the context, followed by '\004' to the start of the
671                    * message string.  We do that here to save GSettings
672                    * the work later on.
673                    */
674                   len = strlen (state->l10n_context);
675                   state->l10n_context[len] = '\004';
676                   g_string_prepend_len (state->unparsed_default_value,
677                                         state->l10n_context, len + 1);
678                   g_free (state->l10n_context);
679                   state->l10n_context = NULL;
680                 }
681
682               g_variant_builder_add (&builder, "(y(y&s))", 'l', state->l10n,
683                                      state->unparsed_default_value->str);
684               g_string_free (state->unparsed_default_value, TRUE);
685               state->unparsed_default_value = NULL;
686             }
687
688           /* choice, aliases, enums */
689           if (state->strinfo->len)
690             {
691               GVariant *array;
692               guint32 *words;
693               gpointer data;
694               gsize size;
695               gint i;
696
697               data = state->strinfo->str;
698               size = state->strinfo->len;
699
700               words = data;
701               for (i = 0; i < size / sizeof (guint32); i++)
702                 words[i] = GUINT32_TO_LE (words[i]);
703
704               array = g_variant_new_from_data (G_VARIANT_TYPE ("au"),
705                                                data, size, TRUE,
706                                                g_free, data);
707
708               g_string_free (state->strinfo, FALSE);
709               state->strinfo = NULL;
710
711               g_variant_builder_add (&builder, "(y@au)",
712                                      state->is_flags ? 'f' :
713                                      state->is_enum ? 'e' : 'c',
714                                      array);
715             }
716
717           /* range */
718           if (state->minimum || state->maximum)
719             g_variant_builder_add (&builder, "(y(**))", 'r',
720                                    state->minimum, state->maximum);
721
722           state->serialised = g_variant_builder_end (&builder);
723         }
724
725       g_variant_ref_sink (state->serialised);
726     }
727
728   return g_variant_ref (state->serialised);
729 }
730
731 static void
732 key_state_free (gpointer data)
733 {
734   KeyState *state = data;
735
736   if (state->type)
737     g_variant_type_free (state->type);
738
739   g_free (state->l10n_context);
740
741   if (state->unparsed_default_value)
742     g_string_free (state->unparsed_default_value, TRUE);
743
744   if (state->default_value)
745     g_variant_unref (state->default_value);
746
747   if (state->strinfo)
748     g_string_free (state->strinfo, TRUE);
749
750   if (state->minimum)
751     g_variant_unref (state->minimum);
752
753   if (state->maximum)
754     g_variant_unref (state->maximum);
755
756   if (state->serialised)
757     g_variant_unref (state->serialised);
758
759   g_slice_free (KeyState, state);
760 }
761
762 /* Key name validity {{{1 */
763 static gboolean allow_any_name = FALSE;
764
765 static gboolean
766 is_valid_keyname (const gchar  *key,
767                   GError      **error)
768 {
769   gint i;
770
771   if (key[0] == '\0')
772     {
773       g_set_error_literal (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
774                            _("empty names are not permitted"));
775       return FALSE;
776     }
777
778   if (allow_any_name)
779     return TRUE;
780
781   if (!g_ascii_islower (key[0]))
782     {
783       g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
784                    _("invalid name '%s': names must begin "
785                      "with a lowercase letter"), key);
786       return FALSE;
787     }
788
789   for (i = 1; key[i]; i++)
790     {
791       if (key[i] != '-' &&
792           !g_ascii_islower (key[i]) &&
793           !g_ascii_isdigit (key[i]))
794         {
795           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
796                        _("invalid name '%s': invalid character '%c'; "
797                          "only lowercase letters, numbers and hyphen ('-') "
798                          "are permitted."), key, key[i]);
799           return FALSE;
800         }
801
802       if (key[i] == '-' && key[i + 1] == '-')
803         {
804           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
805                        _("invalid name '%s': two successive hyphens ('--') "
806                          "are not permitted."), key);
807           return FALSE;
808         }
809     }
810
811   if (key[i - 1] == '-')
812     {
813       g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
814                    _("invalid name '%s': the last character may not be a "
815                      "hyphen ('-')."), key);
816       return FALSE;
817     }
818
819   if (i > 1024)
820     {
821       g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
822                    _("invalid name '%s': maximum length is 1024"), key);
823       return FALSE;
824     }
825
826   return TRUE;
827 }
828
829 /* Handling of <schema> {{{1 */
830 typedef struct _SchemaState SchemaState;
831 struct _SchemaState
832 {
833   SchemaState *extends;
834
835   gchar       *path;
836   gchar       *gettext_domain;
837   gchar       *extends_name;
838   gchar       *list_of;
839
840   GHashTable  *keys;
841 };
842
843 static SchemaState *
844 schema_state_new (const gchar  *path,
845                   const gchar  *gettext_domain,
846                   SchemaState  *extends,
847                   const gchar  *extends_name,
848                   const gchar  *list_of)
849 {
850   SchemaState *state;
851
852   state = g_slice_new (SchemaState);
853   state->path = g_strdup (path);
854   state->gettext_domain = g_strdup (gettext_domain);
855   state->extends = extends;
856   state->extends_name = g_strdup (extends_name);
857   state->list_of = g_strdup (list_of);
858   state->keys = g_hash_table_new_full (g_str_hash, g_str_equal,
859                                        g_free, key_state_free);
860
861   return state;
862 }
863
864 static void
865 schema_state_free (gpointer data)
866 {
867   SchemaState *state = data;
868
869   g_free (state->path);
870   g_free (state->gettext_domain);
871   g_hash_table_unref (state->keys);
872 }
873
874 static void
875 schema_state_add_child (SchemaState  *state,
876                         const gchar  *name,
877                         const gchar  *schema,
878                         GError      **error)
879 {
880   gchar *childname;
881
882   if (!is_valid_keyname (name, error))
883     return;
884
885   childname = g_strconcat (name, "/", NULL);
886
887   if (g_hash_table_lookup (state->keys, childname))
888     {
889       g_set_error (error, G_MARKUP_ERROR,
890                    G_MARKUP_ERROR_INVALID_CONTENT,
891                    _("<child name='%s'> already specified"), name);
892       return;
893     }
894
895   g_hash_table_insert (state->keys, childname,
896                        key_state_new_child (schema));
897 }
898
899 static KeyState *
900 schema_state_add_key (SchemaState  *state,
901                       GHashTable   *enum_table,
902                       GHashTable   *flags_table,
903                       const gchar  *name,
904                       const gchar  *type_string,
905                       const gchar  *enum_type,
906                       const gchar  *flags_type,
907                       GError      **error)
908 {
909   SchemaState *node;
910   GString *strinfo;
911   KeyState *key;
912
913   if (state->list_of)
914     {
915       g_set_error_literal (error, G_MARKUP_ERROR,
916                            G_MARKUP_ERROR_INVALID_CONTENT,
917                            _("cannot add keys to a 'list-of' schema"));
918       return NULL;
919     }
920
921   if (!is_valid_keyname (name, error))
922     return NULL;
923
924   if (g_hash_table_lookup (state->keys, name))
925     {
926       g_set_error (error, G_MARKUP_ERROR,
927                    G_MARKUP_ERROR_INVALID_CONTENT,
928                    _("<key name='%s'> already specified"), name);
929       return NULL;
930     }
931
932   for (node = state; node; node = node->extends)
933     if (node->extends)
934       {
935         KeyState *shadow;
936
937         shadow = g_hash_table_lookup (node->extends->keys, name);
938
939         /* in case of <key> <override> <key> make sure we report the
940          * location of the original <key>, not the <override>.
941          */
942         if (shadow && !shadow->is_override)
943           {
944             g_set_error (error, G_MARKUP_ERROR,
945                          G_MARKUP_ERROR_INVALID_CONTENT,
946                          _("<key name='%s'> shadows <key name='%s'> in "
947                            "<schema id='%s'>; use <override> to modify value"),
948                          name, name, node->extends_name);
949             return NULL;
950           }
951       }
952
953   if ((type_string != NULL) + (enum_type != NULL) + (flags_type != NULL) != 1)
954     {
955       g_set_error (error, G_MARKUP_ERROR,
956                    G_MARKUP_ERROR_MISSING_ATTRIBUTE,
957                    _("exactly one of 'type', 'enum' or 'flags' must "
958                      "be specified as an attribute to <key>"));
959       return NULL;
960     }
961
962   if (type_string == NULL) /* flags or enums was specified */
963     {
964       EnumState *enum_state;
965
966       if (enum_type)
967         enum_state = g_hash_table_lookup (enum_table, enum_type);
968       else
969         enum_state = g_hash_table_lookup (flags_table, flags_type);
970
971
972       if (enum_state == NULL)
973         {
974           g_set_error (error, G_MARKUP_ERROR,
975                        G_MARKUP_ERROR_INVALID_CONTENT,
976                        _("<%s id='%s'> not (yet) defined."),
977                        flags_type ? "flags"    : "enum",
978                        flags_type ? flags_type : enum_type);
979           return NULL;
980         }
981
982       type_string = flags_type ? "as" : "s";
983       strinfo = enum_state->strinfo;
984     }
985   else
986     {
987       if (!g_variant_type_string_is_valid (type_string))
988         {
989           g_set_error (error, G_MARKUP_ERROR,
990                        G_MARKUP_ERROR_INVALID_CONTENT,
991                        _("invalid GVariant type string '%s'"), type_string);
992           return NULL;
993         }
994
995       strinfo = NULL;
996     }
997
998   key = key_state_new (type_string, state->gettext_domain,
999                        enum_type != NULL, flags_type != NULL, strinfo);
1000   g_hash_table_insert (state->keys, g_strdup (name), key);
1001
1002   return key;
1003 }
1004
1005 static void
1006 schema_state_add_override (SchemaState  *state,
1007                            KeyState    **key_state,
1008                            GString     **string,
1009                            const gchar  *key,
1010                            const gchar  *l10n,
1011                            const gchar  *context,
1012                            GError      **error)
1013 {
1014   SchemaState *parent;
1015   KeyState *original;
1016
1017   if (state->extends == NULL)
1018     {
1019       g_set_error_literal (error, G_MARKUP_ERROR,
1020                            G_MARKUP_ERROR_INVALID_CONTENT,
1021                            _("<override> given but schema isn't "
1022                              "extending anything"));
1023       return;
1024     }
1025
1026   for (parent = state->extends; parent; parent = parent->extends)
1027     if ((original = g_hash_table_lookup (parent->keys, key)))
1028       break;
1029
1030   if (original == NULL)
1031     {
1032       g_set_error (error, G_MARKUP_ERROR,
1033                    G_MARKUP_ERROR_INVALID_CONTENT,
1034                    _("no <key name='%s'> to override"), key);
1035       return;
1036     }
1037
1038   if (g_hash_table_lookup (state->keys, key))
1039     {
1040       g_set_error (error, G_MARKUP_ERROR,
1041                    G_MARKUP_ERROR_INVALID_CONTENT,
1042                    _("<override name='%s'> already specified"), key);
1043       return;
1044     }
1045
1046   *key_state = key_state_override (original, state->gettext_domain);
1047   *string = key_state_start_default (*key_state, l10n, context, error);
1048   g_hash_table_insert (state->keys, g_strdup (key), *key_state);
1049 }
1050
1051 static void
1052 override_state_end (KeyState **key_state,
1053                     GString  **string,
1054                     GError   **error)
1055 {
1056   key_state_end_default (*key_state, string, error);
1057   *key_state = NULL;
1058 }
1059
1060 /* Handling of toplevel state {{{1 */
1061 typedef struct
1062 {
1063   GHashTable  *schema_table;            /* string -> SchemaState */
1064   GHashTable  *flags_table;             /* string -> EnumState */
1065   GHashTable  *enum_table;              /* string -> EnumState */
1066
1067   GSList      *this_file_schemas;       /* strings: <schema>s in this file */
1068   GSList      *this_file_flagss;        /* strings: <flags>s in this file */
1069   GSList      *this_file_enums;         /* strings: <enum>s in this file */
1070
1071   gchar       *schemalist_domain;       /* the <schemalist> gettext domain */
1072
1073   SchemaState *schema_state;            /* non-NULL when inside <schema> */
1074   KeyState    *key_state;               /* non-NULL when inside <key> */
1075   EnumState   *enum_state;              /* non-NULL when inside <enum> */
1076
1077   GString     *string;                  /* non-NULL when accepting text */
1078 } ParseState;
1079
1080 static gboolean
1081 is_subclass (const gchar *class_name,
1082              const gchar *possible_parent,
1083              GHashTable  *schema_table)
1084 {
1085   SchemaState *class;
1086
1087   if (strcmp (class_name, possible_parent) == 0)
1088     return TRUE;
1089
1090   class = g_hash_table_lookup (schema_table, class_name);
1091   g_assert (class != NULL);
1092
1093   return class->extends_name &&
1094          is_subclass (class->extends_name, possible_parent, schema_table);
1095 }
1096
1097 static void
1098 parse_state_start_schema (ParseState  *state,
1099                           const gchar  *id,
1100                           const gchar  *path,
1101                           const gchar  *gettext_domain,
1102                           const gchar  *extends_name,
1103                           const gchar  *list_of,
1104                           GError      **error)
1105 {
1106   SchemaState *extends;
1107   gchar *my_id;
1108
1109   if (g_hash_table_lookup (state->schema_table, id))
1110     {
1111       g_set_error (error, G_MARKUP_ERROR,
1112                    G_MARKUP_ERROR_INVALID_CONTENT,
1113                    _("<schema id='%s'> already specified"), id);
1114       return;
1115     }
1116
1117   if (extends_name)
1118     {
1119       extends = g_hash_table_lookup (state->schema_table, extends_name);
1120
1121       if (extends == NULL)
1122         {
1123           g_set_error (error, G_MARKUP_ERROR,
1124                        G_MARKUP_ERROR_INVALID_CONTENT,
1125                        _("<schema id='%s'> extends not-yet-existing "
1126                          "schema '%s'"), id, extends_name);
1127           return;
1128         }
1129     }
1130   else
1131     extends = NULL;
1132
1133   if (list_of)
1134     {
1135       SchemaState *tmp;
1136
1137       if (!(tmp = g_hash_table_lookup (state->schema_table, list_of)))
1138         {
1139           g_set_error (error, G_MARKUP_ERROR,
1140                        G_MARKUP_ERROR_INVALID_CONTENT,
1141                        _("<schema id='%s'> is list of not-yet-existing "
1142                          "schema '%s'"), id, list_of);
1143           return;
1144         }
1145
1146       if (tmp->path)
1147         {
1148           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
1149                        _("Can not be a list of a schema with a path"));
1150           return;
1151         }
1152     }
1153
1154   if (extends)
1155     {
1156       if (extends->path)
1157         {
1158           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
1159                        _("Can not extend a schema with a path"));
1160           return;
1161         }
1162
1163       if (list_of)
1164         {
1165           if (extends->list_of == NULL)
1166             {
1167               g_set_error (error, G_MARKUP_ERROR,
1168                            G_MARKUP_ERROR_INVALID_CONTENT,
1169                            _("<schema id='%s'> is a list, extending "
1170                              "<schema id='%s'> which is not a list"),
1171                            id, extends_name);
1172               return;
1173             }
1174
1175           if (!is_subclass (list_of, extends->list_of, state->schema_table))
1176             {
1177               g_set_error (error, G_MARKUP_ERROR,
1178                            G_MARKUP_ERROR_INVALID_CONTENT,
1179                            _("<schema id='%s' list-of='%s'> extends <schema "
1180                              "id='%s' list-of='%s'> but '%s' does not "
1181                              "extend '%s'"), id, list_of, extends_name,
1182                            extends->list_of, list_of, extends->list_of);
1183               return;
1184             }
1185         }
1186       else
1187         /* by default we are a list of the same thing that the schema
1188          * we are extending is a list of (which might be nothing)
1189          */
1190         list_of = extends->list_of;
1191     }
1192
1193   if (path && !(g_str_has_prefix (path, "/") && g_str_has_suffix (path, "/")))
1194     {
1195       g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
1196                    _("a path, if given, must begin and end with a slash"));
1197       return;
1198     }
1199
1200   if (path && list_of && !g_str_has_suffix (path, ":/"))
1201     {
1202       g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
1203                    _("the path of a list must end with ':/'"));
1204       return;
1205     }
1206
1207   if (path && (g_str_has_prefix (path, "/apps/") ||
1208                g_str_has_prefix (path, "/desktop/") ||
1209                g_str_has_prefix (path, "/system/")))
1210     g_printerr ("warning: Schema '%s' has path '%s'.  Paths starting with "
1211                 "'/apps/', '/desktop/' or '/system/' are deprecated.\n", id, path);
1212
1213   state->schema_state = schema_state_new (path, gettext_domain,
1214                                           extends, extends_name, list_of);
1215
1216   my_id = g_strdup (id);
1217   state->this_file_schemas = g_slist_prepend (state->this_file_schemas, my_id);
1218   g_hash_table_insert (state->schema_table, my_id, state->schema_state);
1219 }
1220
1221 static void
1222 parse_state_start_enum (ParseState   *state,
1223                         const gchar  *id,
1224                         gboolean      is_flags,
1225                         GError      **error)
1226 {
1227   GSList **list = is_flags ? &state->this_file_flagss : &state->this_file_enums;
1228   GHashTable *table = is_flags ? state->flags_table : state->enum_table;
1229   gchar *my_id;
1230
1231   if (g_hash_table_lookup (table, id))
1232     {
1233       g_set_error (error, G_MARKUP_ERROR,
1234                    G_MARKUP_ERROR_INVALID_CONTENT,
1235                    _("<%s id='%s'> already specified"),
1236                    is_flags ? "flags" : "enum", id);
1237       return;
1238     }
1239
1240   state->enum_state = enum_state_new (is_flags);
1241
1242   my_id = g_strdup (id);
1243   *list = g_slist_prepend (*list, my_id);
1244   g_hash_table_insert (table, my_id, state->enum_state);
1245 }
1246
1247 /* GMarkup Parser Functions {{{1 */
1248
1249 /* Start element {{{2 */
1250 static void
1251 start_element (GMarkupParseContext  *context,
1252                const gchar          *element_name,
1253                const gchar         **attribute_names,
1254                const gchar         **attribute_values,
1255                gpointer              user_data,
1256                GError              **error)
1257 {
1258   ParseState *state = user_data;
1259   const GSList *element_stack;
1260   const gchar *container;
1261
1262   element_stack = g_markup_parse_context_get_element_stack (context);
1263   container = element_stack->next ? element_stack->next->data : NULL;
1264
1265 #define COLLECT(first, ...) \
1266   g_markup_collect_attributes (element_name,                                 \
1267                                attribute_names, attribute_values, error,     \
1268                                first, __VA_ARGS__, G_MARKUP_COLLECT_INVALID)
1269 #define OPTIONAL   G_MARKUP_COLLECT_OPTIONAL
1270 #define STRDUP     G_MARKUP_COLLECT_STRDUP
1271 #define STRING     G_MARKUP_COLLECT_STRING
1272 #define NO_ATTRS()  COLLECT (G_MARKUP_COLLECT_INVALID, NULL)
1273
1274   /* Toplevel items {{{3 */
1275   if (container == NULL)
1276     {
1277       if (strcmp (element_name, "schemalist") == 0)
1278         {
1279           COLLECT (OPTIONAL | STRDUP,
1280                    "gettext-domain",
1281                    &state->schemalist_domain);
1282           return;
1283         }
1284     }
1285
1286
1287   /* children of <schemalist> {{{3 */
1288   else if (strcmp (container, "schemalist") == 0)
1289     {
1290       if (strcmp (element_name, "schema") == 0)
1291         {
1292           const gchar *id, *path, *gettext_domain, *extends, *list_of;
1293           if (COLLECT (STRING, "id", &id,
1294                        OPTIONAL | STRING, "path", &path,
1295                        OPTIONAL | STRING, "gettext-domain", &gettext_domain,
1296                        OPTIONAL | STRING, "extends", &extends,
1297                        OPTIONAL | STRING, "list-of", &list_of))
1298             parse_state_start_schema (state, id, path,
1299                                       gettext_domain ? gettext_domain
1300                                                      : state->schemalist_domain,
1301                                       extends, list_of, error);
1302           return;
1303         }
1304
1305       else if (strcmp (element_name, "enum") == 0)
1306         {
1307           const gchar *id;
1308           if (COLLECT (STRING, "id", &id))
1309             parse_state_start_enum (state, id, FALSE, error);
1310           return;
1311         }
1312
1313       else if (strcmp (element_name, "flags") == 0)
1314         {
1315           const gchar *id;
1316           if (COLLECT (STRING, "id", &id))
1317             parse_state_start_enum (state, id, TRUE, error);
1318           return;
1319         }
1320     }
1321
1322
1323   /* children of <schema> {{{3 */
1324   else if (strcmp (container, "schema") == 0)
1325     {
1326       if (strcmp (element_name, "key") == 0)
1327         {
1328           const gchar *name, *type_string, *enum_type, *flags_type;
1329
1330           if (COLLECT (STRING,            "name",  &name,
1331                        OPTIONAL | STRING, "type",  &type_string,
1332                        OPTIONAL | STRING, "enum",  &enum_type,
1333                        OPTIONAL | STRING, "flags", &flags_type))
1334
1335             state->key_state = schema_state_add_key (state->schema_state,
1336                                                      state->enum_table,
1337                                                      state->flags_table,
1338                                                      name, type_string,
1339                                                      enum_type, flags_type,
1340                                                      error);
1341           return;
1342         }
1343       else if (strcmp (element_name, "child") == 0)
1344         {
1345           const gchar *name, *schema;
1346
1347           if (COLLECT (STRING, "name", &name, STRING, "schema", &schema))
1348             schema_state_add_child (state->schema_state,
1349                                     name, schema, error);
1350           return;
1351         }
1352       else if (strcmp (element_name, "override") == 0)
1353         {
1354           const gchar *name, *l10n, *context;
1355
1356           if (COLLECT (STRING,            "name",    &name,
1357                        OPTIONAL | STRING, "l10n",    &l10n,
1358                        OPTIONAL | STRING, "context", &context))
1359             schema_state_add_override (state->schema_state,
1360                                        &state->key_state, &state->string,
1361                                        name, l10n, context, error);
1362           return;
1363         }
1364     }
1365
1366   /* children of <key> {{{3 */
1367   else if (strcmp (container, "key") == 0)
1368     {
1369       if (strcmp (element_name, "default") == 0)
1370         {
1371           const gchar *l10n, *context;
1372           if (COLLECT (STRING | OPTIONAL, "l10n",    &l10n,
1373                        STRING | OPTIONAL, "context", &context))
1374             state->string = key_state_start_default (state->key_state,
1375                                                      l10n, context, error);
1376           return;
1377         }
1378
1379       else if (strcmp (element_name, "summary") == 0 ||
1380                strcmp (element_name, "description") == 0)
1381         {
1382           if (NO_ATTRS ())
1383             state->string = g_string_new (NULL);
1384           return;
1385         }
1386
1387       else if (strcmp (element_name, "range") == 0)
1388         {
1389           const gchar *min, *max;
1390           if (COLLECT (STRING | OPTIONAL, "min", &min,
1391                        STRING | OPTIONAL, "max", &max))
1392             key_state_set_range (state->key_state, min, max, error);
1393           return;
1394         }
1395
1396       else if (strcmp (element_name, "choices") == 0)
1397         {
1398           if (NO_ATTRS ())
1399             key_state_start_choices (state->key_state, error);
1400           return;
1401         }
1402
1403       else if (strcmp (element_name, "aliases") == 0)
1404         {
1405           if (NO_ATTRS ())
1406             key_state_start_aliases (state->key_state, error);
1407           return;
1408         }
1409     }
1410
1411
1412   /* children of <choices> {{{3 */
1413   else if (strcmp (container, "choices") == 0)
1414     {
1415       if (strcmp (element_name, "choice") == 0)
1416         {
1417           const gchar *value;
1418           if (COLLECT (STRING, "value", &value))
1419             key_state_add_choice (state->key_state, value, error);
1420           return;
1421         }
1422     }
1423
1424
1425   /* children of <aliases> {{{3 */
1426   else if (strcmp (container, "aliases") == 0)
1427     {
1428       if (strcmp (element_name, "alias") == 0)
1429         {
1430           const gchar *value, *target;
1431           if (COLLECT (STRING, "value", &value, STRING, "target", &target))
1432             key_state_add_alias (state->key_state, value, target, error);
1433           return;
1434         }
1435     }
1436
1437
1438   /* children of <enum> {{{3 */
1439   else if (strcmp (container, "enum") == 0 ||
1440            strcmp (container, "flags") == 0)
1441     {
1442       if (strcmp (element_name, "value") == 0)
1443         {
1444           const gchar *nick, *valuestr;
1445           if (COLLECT (STRING, "nick", &nick,
1446                        STRING, "value", &valuestr))
1447             enum_state_add_value (state->enum_state, nick, valuestr, error);
1448           return;
1449         }
1450     }
1451   /* 3}}} */
1452
1453   if (container)
1454     g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1455                  _("Element <%s> not allowed inside <%s>"),
1456                  element_name, container);
1457   else
1458     g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1459                  _("Element <%s> not allowed at the top level"), element_name);
1460 }
1461 /* 2}}} */
1462 /* End element {{{2 */
1463
1464 static void
1465 key_state_end (KeyState **state_ptr,
1466                GError   **error)
1467 {
1468   KeyState *state;
1469
1470   state = *state_ptr;
1471   *state_ptr = NULL;
1472
1473   if (state->default_value == NULL)
1474     {
1475       g_set_error_literal (error,
1476                            G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
1477                            "element <default> is required in <key>");
1478       return;
1479     }
1480 }
1481
1482 static void
1483 schema_state_end (SchemaState **state_ptr,
1484                   GError      **error)
1485 {
1486   *state_ptr = NULL;
1487 }
1488
1489 static void
1490 end_element (GMarkupParseContext  *context,
1491              const gchar          *element_name,
1492              gpointer              user_data,
1493              GError              **error)
1494 {
1495   ParseState *state = user_data;
1496
1497   if (strcmp (element_name, "schemalist") == 0)
1498     {
1499       g_free (state->schemalist_domain);
1500       state->schemalist_domain = NULL;
1501     }
1502
1503   else if (strcmp (element_name, "enum") == 0 ||
1504            strcmp (element_name, "flags") == 0)
1505     enum_state_end (&state->enum_state, error);
1506
1507   else if (strcmp (element_name, "schema") == 0)
1508     schema_state_end (&state->schema_state, error);
1509
1510   else if (strcmp (element_name, "override") == 0)
1511     override_state_end (&state->key_state, &state->string, error);
1512
1513   else if (strcmp (element_name, "key") == 0)
1514     key_state_end (&state->key_state, error);
1515
1516   else if (strcmp (element_name, "default") == 0)
1517     key_state_end_default (state->key_state, &state->string, error);
1518
1519   else if (strcmp (element_name, "choices") == 0)
1520     key_state_end_choices (state->key_state, error);
1521
1522   else if (strcmp (element_name, "aliases") == 0)
1523     key_state_end_aliases (state->key_state, error);
1524
1525   if (state->string)
1526     {
1527       g_string_free (state->string, TRUE);
1528       state->string = NULL;
1529     }
1530 }
1531 /* Text {{{2 */
1532 static void
1533 text (GMarkupParseContext  *context,
1534       const gchar          *text,
1535       gsize                 text_len,
1536       gpointer              user_data,
1537       GError              **error)
1538 {
1539   ParseState *state = user_data;
1540
1541   if (state->string)
1542     {
1543       /* we are expecting a string, so store the text data.
1544        *
1545        * we store the data verbatim here and deal with whitespace
1546        * later on.  there are two reasons for that:
1547        *
1548        *  1) whitespace is handled differently depending on the tag
1549        *     type.
1550        *
1551        *  2) we could do leading whitespace removal by refusing to
1552        *     insert it into state->string if it's at the start, but for
1553        *     trailing whitespace, we have no idea if there is another
1554        *     text() call coming or not.
1555        */
1556       g_string_append_len (state->string, text, text_len);
1557     }
1558   else
1559     {
1560       /* string is not expected: accept (and ignore) pure whitespace */
1561       gsize i;
1562
1563       for (i = 0; i < text_len; i++)
1564         if (!g_ascii_isspace (text[i]))
1565           {
1566             g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
1567                          _("text may not appear inside <%s>"),
1568                          g_markup_parse_context_get_element (context));
1569             break;
1570           }
1571     }
1572 }
1573
1574 /* Write to GVDB {{{1 */
1575 typedef struct
1576 {
1577   GHashTable *table;
1578   GvdbItem *root;
1579 } GvdbPair;
1580
1581 static void
1582 gvdb_pair_init (GvdbPair *pair)
1583 {
1584   pair->table = gvdb_hash_table_new (NULL, NULL);
1585   pair->root = gvdb_hash_table_insert (pair->table, "");
1586 }
1587
1588 typedef struct
1589 {
1590   GHashTable *schema_table;
1591   GvdbPair root_pair;
1592 } WriteToFileData;
1593
1594 typedef struct
1595 {
1596   GHashTable *schema_table;
1597   GvdbPair pair;
1598   gboolean l10n;
1599 } OutputSchemaData;
1600
1601 static void
1602 output_key (gpointer key,
1603             gpointer value,
1604             gpointer user_data)
1605 {
1606   OutputSchemaData *data;
1607   const gchar *name;
1608   KeyState *state;
1609   GvdbItem *item;
1610
1611   name = key;
1612   state = value;
1613   data = user_data;
1614
1615   item = gvdb_hash_table_insert (data->pair.table, name);
1616   gvdb_item_set_parent (item, data->pair.root);
1617   gvdb_item_set_value (item, key_state_serialise (state));
1618
1619   if (state->l10n)
1620     data->l10n = TRUE;
1621
1622   if (state->child_schema &&
1623       !g_hash_table_lookup (data->schema_table, state->child_schema))
1624     g_printerr ("warning: undefined reference to <schema id='%s'/>\n",
1625                 state->child_schema);
1626 }
1627
1628 static void
1629 output_schema (gpointer key,
1630                gpointer value,
1631                gpointer user_data)
1632 {
1633   WriteToFileData *wtf_data = user_data;
1634   OutputSchemaData data;
1635   GvdbPair *root_pair;
1636   SchemaState *state;
1637   const gchar *id;
1638   GvdbItem *item;
1639
1640   id = key;
1641   state = value;
1642   root_pair = &wtf_data->root_pair;
1643
1644   data.schema_table = wtf_data->schema_table;
1645   gvdb_pair_init (&data.pair);
1646   data.l10n = FALSE;
1647
1648   item = gvdb_hash_table_insert (root_pair->table, id);
1649   gvdb_item_set_parent (item, root_pair->root);
1650   gvdb_item_set_hash_table (item, data.pair.table);
1651
1652   g_hash_table_foreach (state->keys, output_key, &data);
1653
1654   if (state->path)
1655     gvdb_hash_table_insert_string (data.pair.table, ".path", state->path);
1656
1657   if (state->extends_name)
1658     gvdb_hash_table_insert_string (data.pair.table, ".extends",
1659                                    state->extends_name);
1660
1661   if (state->list_of)
1662     gvdb_hash_table_insert_string (data.pair.table, ".list-of",
1663                                    state->list_of);
1664
1665   if (data.l10n)
1666     gvdb_hash_table_insert_string (data.pair.table,
1667                                    ".gettext-domain",
1668                                    state->gettext_domain);
1669 }
1670
1671 static gboolean
1672 write_to_file (GHashTable   *schema_table,
1673                const gchar  *filename,
1674                GError      **error)
1675 {
1676   WriteToFileData data;
1677   gboolean success;
1678
1679   data.schema_table = schema_table;
1680
1681   gvdb_pair_init (&data.root_pair);
1682
1683   g_hash_table_foreach (schema_table, output_schema, &data);
1684
1685   success = gvdb_table_write_contents (data.root_pair.table, filename,
1686                                        G_BYTE_ORDER != G_LITTLE_ENDIAN,
1687                                        error);
1688   g_hash_table_unref (data.root_pair.table);
1689
1690   return success;
1691 }
1692
1693 /* Parser driver {{{1 */
1694 static GHashTable *
1695 parse_gschema_files (gchar    **files,
1696                      gboolean   strict)
1697 {
1698   GMarkupParser parser = { start_element, end_element, text };
1699   ParseState state = { 0, };
1700   const gchar *filename;
1701   GError *error = NULL;
1702
1703   state.enum_table = g_hash_table_new_full (g_str_hash, g_str_equal,
1704                                             g_free, enum_state_free);
1705
1706   state.flags_table = g_hash_table_new_full (g_str_hash, g_str_equal,
1707                                              g_free, enum_state_free);
1708
1709   state.schema_table = g_hash_table_new_full (g_str_hash, g_str_equal,
1710                                               g_free, schema_state_free);
1711
1712   while ((filename = *files++) != NULL)
1713     {
1714       GMarkupParseContext *context;
1715       gchar *contents;
1716       gsize size;
1717
1718       if (!g_file_get_contents (filename, &contents, &size, &error))
1719         {
1720           fprintf (stderr, "%s\n", error->message);
1721           g_clear_error (&error);
1722           continue;
1723         }
1724
1725       context = g_markup_parse_context_new (&parser,
1726                                             G_MARKUP_TREAT_CDATA_AS_TEXT |
1727                                             G_MARKUP_PREFIX_ERROR_POSITION,
1728                                             &state, NULL);
1729
1730
1731       if (!g_markup_parse_context_parse (context, contents, size, &error) ||
1732           !g_markup_parse_context_end_parse (context, &error))
1733         {
1734           GSList *item;
1735
1736           /* back out any changes from this file */
1737           for (item = state.this_file_schemas; item; item = item->next)
1738             g_hash_table_remove (state.schema_table, item->data);
1739
1740           for (item = state.this_file_flagss; item; item = item->next)
1741             g_hash_table_remove (state.flags_table, item->data);
1742
1743           for (item = state.this_file_enums; item; item = item->next)
1744             g_hash_table_remove (state.enum_table, item->data);
1745
1746           /* let them know */
1747           fprintf (stderr, "%s: %s.  ", filename, error->message);
1748           g_clear_error (&error);
1749
1750           if (strict)
1751             {
1752               /* Translators: Do not translate "--strict". */
1753               fprintf (stderr, _("--strict was specified; exiting.\n"));
1754               g_hash_table_unref (state.schema_table);
1755               g_hash_table_unref (state.flags_table);
1756               g_hash_table_unref (state.enum_table);
1757
1758               return NULL;
1759             }
1760           else
1761             fprintf (stderr, _("This entire file has been ignored.\n"));
1762         }
1763
1764       /* cleanup */
1765       g_markup_parse_context_free (context);
1766       g_slist_free (state.this_file_schemas);
1767       g_slist_free (state.this_file_flagss);
1768       g_slist_free (state.this_file_enums);
1769       state.this_file_schemas = NULL;
1770       state.this_file_flagss = NULL;
1771       state.this_file_enums = NULL;
1772     }
1773
1774   g_hash_table_unref (state.flags_table);
1775   g_hash_table_unref (state.enum_table);
1776
1777   return state.schema_table;
1778 }
1779
1780 static gint
1781 compare_strings (gconstpointer a,
1782                  gconstpointer b)
1783 {
1784   gchar *one = *(gchar **) a;
1785   gchar *two = *(gchar **) b;
1786   gint cmp;
1787
1788   cmp = g_str_has_suffix (two, ".enums.xml") -
1789         g_str_has_suffix (one, ".enums.xml");
1790
1791   if (!cmp)
1792     cmp = strcmp (one, two);
1793
1794   return cmp;
1795 }
1796
1797 static gboolean
1798 set_overrides (GHashTable  *schema_table,
1799                gchar      **files,
1800                gboolean     strict)
1801 {
1802   const gchar *filename;
1803   GError *error = NULL;
1804
1805   while ((filename = *files++))
1806     {
1807       GKeyFile *key_file;
1808       gchar **groups;
1809       gint i;
1810
1811       key_file = g_key_file_new ();
1812       if (!g_key_file_load_from_file (key_file, filename, 0, &error))
1813         {
1814           fprintf (stderr, "%s: %s.  ", filename, error->message);
1815           g_key_file_free (key_file);
1816           g_clear_error (&error);
1817
1818           if (!strict)
1819             {
1820               fprintf (stderr, _("Ignoring this file.\n"));
1821               continue;
1822             }
1823
1824           fprintf (stderr, _("--strict was specified; exiting.\n"));
1825           return FALSE;
1826         }
1827
1828       groups = g_key_file_get_groups (key_file, NULL);
1829
1830       for (i = 0; groups[i]; i++)
1831         {
1832           const gchar *group = groups[i];
1833           SchemaState *schema;
1834           gchar **keys;
1835           gint j;
1836
1837           schema = g_hash_table_lookup (schema_table, group);
1838
1839           if (schema == NULL)
1840             /* Having the schema not be installed is expected to be a
1841              * common case.  Don't even emit an error message about
1842              * that.
1843              */
1844             continue;
1845
1846           keys = g_key_file_get_keys (key_file, group, NULL, NULL);
1847           g_assert (keys != NULL);
1848
1849           for (j = 0; keys[j]; j++)
1850             {
1851               const gchar *key = keys[j];
1852               KeyState *state;
1853               GVariant *value;
1854               gchar *string;
1855
1856               state = g_hash_table_lookup (schema->keys, key);
1857
1858               if (state == NULL)
1859                 {
1860                   fprintf (stderr, _("No such key `%s' in schema `%s' as "
1861                                      "specified in override file `%s'"),
1862                            key, group, filename);
1863
1864                   if (!strict)
1865                     {
1866                       fprintf (stderr, _("; ignoring override for this key.\n"));
1867                       continue;
1868                     }
1869
1870                   fprintf (stderr, _(" and --strict was specified; exiting.\n"));
1871                   g_key_file_free (key_file);
1872                   g_strfreev (groups);
1873                   g_strfreev (keys);
1874
1875                   return FALSE;
1876                 }
1877
1878               string = g_key_file_get_value (key_file, group, key, NULL);
1879               g_assert (string != NULL);
1880
1881               value = g_variant_parse (state->type, string,
1882                                        NULL, NULL, &error);
1883
1884               if (value == NULL)
1885                 {
1886                   fprintf (stderr, _("error parsing key `%s' in schema `%s' "
1887                                      "as specified in override file `%s': "
1888                                      "%s.  "),
1889                            key, group, filename, error->message);
1890
1891                   g_clear_error (&error);
1892                   g_free (string);
1893
1894                   if (!strict)
1895                     {
1896                       fprintf (stderr, _("Ignoring override for this key.\n"));
1897                       continue;
1898                     }
1899
1900                   fprintf (stderr, _("--strict was specified; exiting.\n"));
1901                   g_key_file_free (key_file);
1902                   g_strfreev (groups);
1903                   g_strfreev (keys);
1904
1905                   return FALSE;
1906                 }
1907
1908               if (state->minimum)
1909                 {
1910                   if (g_variant_compare (value, state->minimum) < 0 ||
1911                       g_variant_compare (value, state->maximum) > 0)
1912                     {
1913                       fprintf (stderr,
1914                                _("override for key `%s' in schema `%s' in "
1915                                  "override file `%s' is outside the range "
1916                                  "given in the schema"),
1917                                key, group, filename);
1918
1919                       g_variant_unref (value);
1920                       g_free (string);
1921
1922                       if (!strict)
1923                         {
1924                           fprintf (stderr, _("; ignoring override for this key.\n"));
1925                           continue;
1926                         }
1927
1928                       fprintf (stderr, _(" and --strict was specified; exiting.\n"));
1929                       g_key_file_free (key_file);
1930                       g_strfreev (groups);
1931                       g_strfreev (keys);
1932
1933                       return FALSE;
1934                     }
1935                 }
1936
1937               else if (state->strinfo->len)
1938                 {
1939                   if (!is_valid_choices (value, state->strinfo))
1940                     {
1941                       fprintf (stderr,
1942                                _("override for key `%s' in schema `%s' in "
1943                                  "override file `%s' is not in the list "
1944                                  "of valid choices"),
1945                                key, group, filename);
1946
1947                       g_variant_unref (value);
1948                       g_free (string);
1949
1950                       if (!strict)
1951                         {
1952                           fprintf (stderr, _("; ignoring override for this key.\n"));
1953                           continue;
1954                         }
1955
1956                       fprintf (stderr, _(" and --strict was specified; exiting.\n"));
1957                       g_key_file_free (key_file);
1958                       g_strfreev (groups);
1959                       g_strfreev (keys);
1960
1961                       return FALSE;
1962                     }
1963                 }
1964
1965               g_variant_unref (state->default_value);
1966               state->default_value = value;
1967               g_free (string);
1968             }
1969
1970           g_strfreev (keys);
1971         }
1972
1973       g_strfreev (groups);
1974     }
1975
1976   return TRUE;
1977 }
1978
1979 int
1980 main (int argc, char **argv)
1981 {
1982   GError *error;
1983   GHashTable *table;
1984   GDir *dir;
1985   const gchar *file;
1986   gchar *srcdir;
1987   gchar *targetdir = NULL;
1988   gchar *target;
1989   gboolean dry_run = FALSE;
1990   gboolean strict = FALSE;
1991   gchar **schema_files = NULL;
1992   gchar **override_files = NULL;
1993   GOptionContext *context;
1994   GOptionEntry entries[] = {
1995     { "targetdir", 0, 0, G_OPTION_ARG_FILENAME, &targetdir, N_("where to store the gschemas.compiled file"), N_("DIRECTORY") },
1996     { "strict", 0, 0, G_OPTION_ARG_NONE, &strict, N_("Abort on any errors in schemas"), NULL },
1997     { "dry-run", 0, 0, G_OPTION_ARG_NONE, &dry_run, N_("Do not write the gschema.compiled file"), NULL },
1998     { "allow-any-name", 0, 0, G_OPTION_ARG_NONE, &allow_any_name, N_("Do not enforce key name restrictions") },
1999
2000     /* These options are only for use in the gschema-compile tests */
2001     { "schema-file", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME_ARRAY, &schema_files, NULL, NULL },
2002     { NULL }
2003   };
2004
2005 #ifdef G_OS_WIN32
2006   extern gchar *_glib_get_locale_dir (void);
2007   gchar *tmp;
2008 #endif
2009
2010   setlocale (LC_ALL, "");
2011   textdomain (GETTEXT_PACKAGE);
2012
2013 #ifdef G_OS_WIN32
2014   tmp = _glib_get_locale_dir ();
2015   bindtextdomain (GETTEXT_PACKAGE, tmp);
2016   g_free (tmp);
2017 #else
2018   bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
2019 #endif
2020
2021 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
2022   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2023 #endif
2024
2025   context = g_option_context_new (N_("DIRECTORY"));
2026   g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
2027   g_option_context_set_summary (context,
2028     N_("Compile all GSettings schema files into a schema cache.\n"
2029        "Schema files are required to have the extension .gschema.xml,\n"
2030        "and the cache file is called gschemas.compiled."));
2031   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
2032
2033   error = NULL;
2034   if (!g_option_context_parse (context, &argc, &argv, &error))
2035     {
2036       fprintf (stderr, "%s\n", error->message);
2037       return 1;
2038     }
2039
2040   g_option_context_free (context);
2041
2042   if (!schema_files && argc != 2)
2043     {
2044       fprintf (stderr, _("You should give exactly one directory name\n"));
2045       return 1;
2046     }
2047
2048   srcdir = argv[1];
2049
2050   if (targetdir == NULL)
2051     targetdir = srcdir;
2052
2053   target = g_build_filename (targetdir, "gschemas.compiled", NULL);
2054
2055   if (!schema_files)
2056     {
2057       GPtrArray *overrides;
2058       GPtrArray *files;
2059
2060       files = g_ptr_array_new ();
2061       overrides = g_ptr_array_new ();
2062
2063       dir = g_dir_open (srcdir, 0, &error);
2064       if (dir == NULL)
2065         {
2066           fprintf (stderr, "%s\n", error->message);
2067           return 1;
2068         }
2069
2070       while ((file = g_dir_read_name (dir)) != NULL)
2071         {
2072           if (g_str_has_suffix (file, ".gschema.xml") ||
2073               g_str_has_suffix (file, ".enums.xml"))
2074             g_ptr_array_add (files, g_build_filename (srcdir, file, NULL));
2075
2076           else if (g_str_has_suffix (file, ".gschema.override"))
2077             g_ptr_array_add (overrides,
2078                              g_build_filename (srcdir, file, NULL));
2079         }
2080
2081       if (files->len == 0)
2082         {
2083           fprintf (stdout, _("No schema files found: "));
2084
2085           if (g_unlink (target))
2086             fprintf (stdout, _("doing nothing.\n"));
2087
2088           else
2089             fprintf (stdout, _("removed existing output file.\n"));
2090
2091           return 0;
2092         }
2093       g_ptr_array_sort (files, compare_strings);
2094       g_ptr_array_add (files, NULL);
2095
2096       g_ptr_array_sort (overrides, compare_strings);
2097       g_ptr_array_add (overrides, NULL);
2098
2099       schema_files = (char **) g_ptr_array_free (files, FALSE);
2100       override_files = (gchar **) g_ptr_array_free (overrides, FALSE);
2101     }
2102
2103   if ((table = parse_gschema_files (schema_files, strict)) == NULL)
2104     {
2105       g_free (target);
2106       return 1;
2107     }
2108
2109   if (override_files != NULL &&
2110       !set_overrides (table, override_files, strict))
2111     {
2112       g_free (target);
2113       return 1;
2114     }
2115
2116   if (!dry_run && !write_to_file (table, target, &error))
2117     {
2118       fprintf (stderr, "%s\n", error->message);
2119       g_free (target);
2120       return 1;
2121     }
2122
2123   g_free (target);
2124
2125   return 0;
2126 }
2127
2128 /* Epilogue {{{1 */
2129
2130 /* vim:set foldmethod=marker: */