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