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