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