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