Fixed headers in galician translation file
[platform/upstream/glib.git] / gio / gschema-compile.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);
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 ('--') are "
734                        "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 extend '%s'",
1088                            id, list_of, extends_name, extends->list_of,
1089                            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,
1103                    G_MARKUP_ERROR_INVALID_CONTENT,
1104                    "a path, if given, must begin and "
1105                    "end with a slash");
1106       return;
1107     }
1108
1109   state->schema_state = schema_state_new (path, gettext_domain,
1110                                           extends, extends_name, list_of);
1111   g_hash_table_insert (state->schema_table, g_strdup (id),
1112                        state->schema_state);
1113 }
1114
1115 static void
1116 parse_state_start_enum (ParseState   *state,
1117                         const gchar  *id,
1118                         gboolean      is_flags,
1119                         GError      **error)
1120 {
1121   GHashTable *table = is_flags ? state->flags_table : state->enum_table;
1122
1123   if (g_hash_table_lookup (table, id))
1124     {
1125       g_set_error (error, G_MARKUP_ERROR,
1126                    G_MARKUP_ERROR_INVALID_CONTENT,
1127                    "<%s id='%s'> already specified",
1128                    is_flags ? "flags" : "enum", id);
1129       return;
1130     }
1131
1132   state->enum_state = enum_state_new (is_flags);
1133   g_hash_table_insert (table, g_strdup (id), state->enum_state);
1134 }
1135
1136 /* GMarkup Parser Functions {{{1 */
1137
1138 /* Start element {{{2 */
1139 static void
1140 start_element (GMarkupParseContext  *context,
1141                const gchar          *element_name,
1142                const gchar         **attribute_names,
1143                const gchar         **attribute_values,
1144                gpointer              user_data,
1145                GError              **error)
1146 {
1147   ParseState *state = user_data;
1148   const GSList *element_stack;
1149   const gchar *container;
1150
1151   element_stack = g_markup_parse_context_get_element_stack (context);
1152   container = element_stack->next ? element_stack->next->data : NULL;
1153
1154 #define COLLECT(first, ...) \
1155   g_markup_collect_attributes (element_name,                                 \
1156                                attribute_names, attribute_values, error,     \
1157                                first, __VA_ARGS__, G_MARKUP_COLLECT_INVALID)
1158 #define OPTIONAL   G_MARKUP_COLLECT_OPTIONAL
1159 #define STRDUP     G_MARKUP_COLLECT_STRDUP
1160 #define STRING     G_MARKUP_COLLECT_STRING
1161 #define NO_ATTRS()  COLLECT (G_MARKUP_COLLECT_INVALID, NULL)
1162
1163   /* Toplevel items {{{3 */
1164   if (container == NULL)
1165     {
1166       if (strcmp (element_name, "schemalist") == 0)
1167         {
1168           COLLECT (OPTIONAL | STRDUP,
1169                    "gettext-domain",
1170                    &state->schemalist_domain);
1171           return;
1172         }
1173     }
1174
1175
1176   /* children of <schemalist> {{{3 */
1177   else if (strcmp (container, "schemalist") == 0)
1178     {
1179       if (strcmp (element_name, "schema") == 0)
1180         {
1181           const gchar *id, *path, *gettext_domain, *extends, *list_of;
1182           if (COLLECT (STRING, "id", &id,
1183                        OPTIONAL | STRING, "path", &path,
1184                        OPTIONAL | STRING, "gettext-domain", &gettext_domain,
1185                        OPTIONAL | STRING, "extends", &extends,
1186                        OPTIONAL | STRING, "list-of", &list_of))
1187             parse_state_start_schema (state, id, path, gettext_domain,
1188                                       extends, list_of, error);
1189           return;
1190         }
1191
1192       else if (strcmp (element_name, "enum") == 0)
1193         {
1194           const gchar *id;
1195           if (COLLECT (STRING, "id", &id))
1196             parse_state_start_enum (state, id, FALSE, error);
1197           return;
1198         }
1199
1200       else if (strcmp (element_name, "flags") == 0)
1201         {
1202           const gchar *id;
1203           if (COLLECT (STRING, "id", &id))
1204             parse_state_start_enum (state, id, TRUE, error);
1205           return;
1206         }
1207     }
1208
1209
1210   /* children of <schema> {{{3 */
1211   else if (strcmp (container, "schema") == 0)
1212     {
1213       if (strcmp (element_name, "key") == 0)
1214         {
1215           const gchar *name, *type_string, *enum_type, *flags_type;
1216
1217           if (COLLECT (STRING,            "name",  &name,
1218                        OPTIONAL | STRING, "type",  &type_string,
1219                        OPTIONAL | STRING, "enum",  &enum_type,
1220                        OPTIONAL | STRING, "flags", &flags_type))
1221
1222             state->key_state = schema_state_add_key (state->schema_state,
1223                                                      state->enum_table,
1224                                                      state->flags_table,
1225                                                      name, type_string,
1226                                                      enum_type, flags_type,
1227                                                      error);
1228           return;
1229         }
1230       else if (strcmp (element_name, "child") == 0)
1231         {
1232           const gchar *name, *schema;
1233
1234           if (COLLECT (STRING, "name", &name, STRING, "schema", &schema))
1235             schema_state_add_child (state->schema_state,
1236                                     name, schema, error);
1237           return;
1238         }
1239       else if (strcmp (element_name, "override") == 0)
1240         {
1241           const gchar *name, *l10n, *context;
1242
1243           if (COLLECT (STRING,            "name",    &name,
1244                        OPTIONAL | STRING, "l10n",    &l10n,
1245                        OPTIONAL | STRING, "context", &context))
1246             schema_state_add_override (state->schema_state,
1247                                        &state->key_state, &state->string,
1248                                        name, l10n, context, error);
1249           return;
1250         }
1251     }
1252
1253   /* children of <key> {{{3 */
1254   else if (strcmp (container, "key") == 0)
1255     {
1256       if (strcmp (element_name, "default") == 0)
1257         {
1258           const gchar *l10n, *context;
1259           if (COLLECT (STRING | OPTIONAL, "l10n",    &l10n,
1260                        STRING | OPTIONAL, "context", &context))
1261             state->string = key_state_start_default (state->key_state,
1262                                                      l10n, context, error);
1263           return;
1264         }
1265
1266       else if (strcmp (element_name, "summary") == 0 ||
1267                strcmp (element_name, "description") == 0)
1268         {
1269           if (NO_ATTRS ())
1270             state->string = g_string_new (NULL);
1271           return;
1272         }
1273
1274       else if (strcmp (element_name, "range") == 0)
1275         {
1276           const gchar *min, *max;
1277           if (COLLECT (STRING, "min", &min, STRING, "max", &max))
1278             key_state_set_range (state->key_state, min, max, error);
1279           return;
1280         }
1281
1282       else if (strcmp (element_name, "choices") == 0)
1283         {
1284           if (NO_ATTRS ())
1285             key_state_start_choices (state->key_state, error);
1286           return;
1287         }
1288
1289       else if (strcmp (element_name, "aliases") == 0)
1290         {
1291           if (NO_ATTRS ())
1292             key_state_start_aliases (state->key_state, error);
1293           return;
1294         }
1295     }
1296
1297
1298   /* children of <choices> {{{3 */
1299   else if (strcmp (container, "choices") == 0)
1300     {
1301       if (strcmp (element_name, "choice") == 0)
1302         {
1303           const gchar *value;
1304           if (COLLECT (STRING, "value", &value))
1305             key_state_add_choice (state->key_state, value, error);
1306           return;
1307         }
1308     }
1309
1310
1311   /* children of <aliases> {{{3 */
1312   else if (strcmp (container, "aliases") == 0)
1313     {
1314       if (strcmp (element_name, "alias") == 0)
1315         {
1316           const gchar *value, *target;
1317           if (COLLECT (STRING, "value", &value, STRING, "target", &target))
1318             key_state_add_alias (state->key_state, value, target, error);
1319           return;
1320         }
1321     }
1322
1323
1324   /* children of <enum> {{{3 */
1325   else if (strcmp (container, "enum") == 0 ||
1326            strcmp (container, "flags") == 0)
1327     {
1328       if (strcmp (element_name, "value") == 0)
1329         {
1330           const gchar *nick, *valuestr;
1331           if (COLLECT (STRING, "nick", &nick,
1332                        STRING, "value", &valuestr))
1333             enum_state_add_value (state->enum_state, nick, valuestr, error);
1334           return;
1335         }
1336     }
1337   /* 3}}} */
1338
1339   if (container)
1340     g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1341                  "Element <%s> not allowed inside <%s>",
1342                  element_name, container);
1343   else
1344     g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1345                  "Element <%s> not allowed at toplevel", element_name);
1346 }
1347 /* 2}}} */
1348 /* End element {{{2 */
1349
1350 static void
1351 key_state_end (KeyState **state_ptr,
1352                GError   **error)
1353 {
1354   KeyState *state;
1355
1356   state = *state_ptr;
1357   *state_ptr = NULL;
1358
1359   if (state->default_value == NULL)
1360     {
1361       g_set_error_literal (error,
1362                            G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
1363                            "element <default> is required in <key>");
1364       return;
1365     }
1366 }
1367
1368 static void
1369 schema_state_end (SchemaState **state_ptr,
1370                   GError      **error)
1371 {
1372   SchemaState *state;
1373
1374   state = *state_ptr;
1375   *state_ptr = NULL;
1376 }
1377
1378 static void
1379 end_element (GMarkupParseContext  *context,
1380              const gchar          *element_name,
1381              gpointer              user_data,
1382              GError              **error)
1383 {
1384   ParseState *state = user_data;
1385
1386   if (strcmp (element_name, "schemalist") == 0)
1387     {
1388       g_free (state->schemalist_domain);
1389       state->schemalist_domain = NULL;
1390     }
1391
1392   else if (strcmp (element_name, "enum") == 0 ||
1393            strcmp (element_name, "flags") == 0)
1394     enum_state_end (&state->enum_state, error);
1395
1396   else if (strcmp (element_name, "schema") == 0)
1397     schema_state_end (&state->schema_state, error);
1398
1399   else if (strcmp (element_name, "override") == 0)
1400     override_state_end (&state->key_state, &state->string, error);
1401
1402   else if (strcmp (element_name, "key") == 0)
1403     key_state_end (&state->key_state, error);
1404
1405   else if (strcmp (element_name, "default") == 0)
1406     key_state_end_default (state->key_state, &state->string, error);
1407
1408   else if (strcmp (element_name, "choices") == 0)
1409     key_state_end_choices (state->key_state, error);
1410
1411   else if (strcmp (element_name, "aliases") == 0)
1412     key_state_end_aliases (state->key_state, error);
1413
1414   if (state->string)
1415     {
1416       g_string_free (state->string, TRUE);
1417       state->string = NULL;
1418     }
1419 }
1420 /* Text {{{2 */
1421 static void
1422 text (GMarkupParseContext  *context,
1423       const gchar          *text,
1424       gsize                 text_len,
1425       gpointer              user_data,
1426       GError              **error)
1427 {
1428   ParseState *state = user_data;
1429   gsize i;
1430
1431   for (i = 0; i < text_len; i++)
1432     if (!g_ascii_isspace (text[i]))
1433       {
1434         if (state->string)
1435           g_string_append_len (state->string, text, text_len);
1436
1437         else
1438           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
1439                        "text may not appear inside <%s>",
1440                        g_markup_parse_context_get_element (context));
1441
1442         break;
1443       }
1444 }
1445
1446 /* Write to GVDB {{{1 */
1447 typedef struct
1448 {
1449   GHashTable *table;
1450   GvdbItem *root;
1451 } GvdbPair;
1452
1453 static void
1454 gvdb_pair_init (GvdbPair *pair)
1455 {
1456   pair->table = gvdb_hash_table_new (NULL, NULL);
1457   pair->root = gvdb_hash_table_insert (pair->table, "");
1458 }
1459
1460 typedef struct
1461 {
1462   GvdbPair pair;
1463   gboolean l10n;
1464 } OutputSchemaData;
1465
1466 static void
1467 output_key (gpointer key,
1468             gpointer value,
1469             gpointer user_data)
1470 {
1471   OutputSchemaData *data;
1472   const gchar *name;
1473   KeyState *state;
1474   GvdbItem *item;
1475
1476   name = key;
1477   state = value;
1478   data = user_data;
1479
1480   item = gvdb_hash_table_insert (data->pair.table, name);
1481   gvdb_item_set_parent (item, data->pair.root);
1482   gvdb_item_set_value (item, key_state_serialise (state));
1483
1484   if (state->l10n)
1485     data->l10n = TRUE;
1486 }
1487
1488 static void
1489 output_schema (gpointer key,
1490                gpointer value,
1491                gpointer user_data)
1492 {
1493   OutputSchemaData data;
1494   GvdbPair *root_pair;
1495   SchemaState *state;
1496   const gchar *id;
1497   GvdbItem *item;
1498
1499   id = key;
1500   state = value;
1501   root_pair = user_data;
1502
1503   gvdb_pair_init (&data.pair);
1504   data.l10n = FALSE;
1505
1506   item = gvdb_hash_table_insert (root_pair->table, id);
1507   gvdb_item_set_parent (item, root_pair->root);
1508   gvdb_item_set_hash_table (item, data.pair.table);
1509
1510   g_hash_table_foreach (state->keys, output_key, &data);
1511
1512   if (state->path)
1513     gvdb_hash_table_insert_string (data.pair.table, ".path", state->path);
1514
1515   if (state->extends_name)
1516     gvdb_hash_table_insert_string (data.pair.table, ".extends",
1517                                    state->extends_name);
1518
1519   if (state->list_of)
1520     gvdb_hash_table_insert_string (data.pair.table, ".list-of",
1521                                    state->extends_name);
1522
1523   if (data.l10n)
1524     gvdb_hash_table_insert_string (data.pair.table,
1525                                    ".gettext-domain",
1526                                    state->gettext_domain);
1527 }
1528
1529 static gboolean
1530 write_to_file (GHashTable   *schema_table,
1531                const gchar  *filename,
1532                GError      **error)
1533 {
1534   gboolean success;
1535   GvdbPair pair;
1536
1537   gvdb_pair_init (&pair);
1538
1539   g_hash_table_foreach (schema_table, output_schema, &pair);
1540
1541   success = gvdb_table_write_contents (pair.table, filename,
1542                                        G_BYTE_ORDER != G_LITTLE_ENDIAN,
1543                                        error);
1544   g_hash_table_unref (pair.table);
1545
1546   return success;
1547 }
1548
1549 /* Parser driver {{{1 */
1550 static GHashTable *
1551 parse_gschema_files (gchar  **files,
1552                      GError **error)
1553 {
1554   GMarkupParser parser = { start_element, end_element, text };
1555   ParseState state = { 0, };
1556   const gchar *filename;
1557
1558   state.enum_table = g_hash_table_new_full (g_str_hash, g_str_equal,
1559                                             g_free, enum_state_free);
1560
1561   state.flags_table = g_hash_table_new_full (g_str_hash, g_str_equal,
1562                                              g_free, enum_state_free);
1563
1564   state.schema_table = g_hash_table_new_full (g_str_hash, g_str_equal,
1565                                               g_free, schema_state_free);
1566
1567   while ((filename = *files++) != NULL)
1568     {
1569       GMarkupParseContext *context;
1570       gchar *contents;
1571       gsize size;
1572
1573       context = g_markup_parse_context_new (&parser,
1574                                             G_MARKUP_PREFIX_ERROR_POSITION,
1575                                             &state, NULL);
1576
1577       if (!g_file_get_contents (filename, &contents, &size, error))
1578         return NULL;
1579
1580       if (!g_markup_parse_context_parse (context, contents, size, error))
1581         {
1582           g_prefix_error (error, "%s: ", filename);
1583           return NULL;
1584         }
1585
1586       if (!g_markup_parse_context_end_parse (context, error))
1587         {
1588           g_prefix_error (error, "%s: ", filename);
1589           return NULL;
1590         }
1591
1592       g_markup_parse_context_free (context);
1593     }
1594
1595   g_hash_table_unref (state.enum_table);
1596
1597   return state.schema_table;
1598 }
1599
1600 static gint
1601 compare_strings (gconstpointer a,
1602                  gconstpointer b)
1603 {
1604   gchar *one = *(gchar **) a;
1605   gchar *two = *(gchar **) b;
1606   gint cmp;
1607
1608   cmp = g_str_has_suffix (two, ".enums.xml") -
1609         g_str_has_suffix (one, ".enums.xml");
1610
1611   if (!cmp)
1612     cmp = strcmp (one, two);
1613
1614   return cmp;
1615 }
1616
1617 int
1618 main (int argc, char **argv)
1619 {
1620   GError *error;
1621   GHashTable *table;
1622   GDir *dir;
1623   const gchar *file;
1624   gchar *srcdir;
1625   gchar *targetdir = NULL;
1626   gchar *target;
1627   gboolean uninstall = FALSE;
1628   gboolean dry_run = FALSE;
1629   gchar **schema_files = NULL;
1630   GOptionContext *context;
1631   GOptionEntry entries[] = {
1632     { "targetdir", 0, 0, G_OPTION_ARG_FILENAME, &targetdir, N_("where to store the gschemas.compiled file"), N_("DIRECTORY") },
1633     { "dry-run", 0, 0, G_OPTION_ARG_NONE, &dry_run, N_("Do not write the gschema.compiled file"), NULL },
1634     { "uninstall", 0, 0, G_OPTION_ARG_NONE, &uninstall, N_("This option will be removed soon.") },
1635     { "allow-any-name", 0, 0, G_OPTION_ARG_NONE, &allow_any_name, N_("Do not enforce key name restrictions") },
1636
1637     /* These options are only for use in the gschema-compile tests */
1638     { "schema-file", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME_ARRAY, &schema_files, NULL, NULL },
1639     { NULL }
1640   };
1641
1642   setlocale (LC_ALL, "");
1643
1644   context = g_option_context_new (N_("DIRECTORY"));
1645   g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
1646   g_option_context_set_summary (context,
1647     N_("Compile all GSettings schema files into a schema cache.\n"
1648        "Schema files are required to have the extension .gschema.xml,\n"
1649        "and the cache file is called gschemas.compiled."));
1650   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
1651
1652   error = NULL;
1653   if (!g_option_context_parse (context, &argc, &argv, &error))
1654     {
1655       fprintf (stderr, "%s", error->message);
1656       return 1;
1657     }
1658
1659   g_option_context_free (context);
1660
1661   if (!schema_files && argc != 2)
1662     {
1663       fprintf (stderr, _("You should give exactly one directory name\n"));
1664       return 1;
1665     }
1666
1667   srcdir = argv[1];
1668
1669   if (targetdir == NULL)
1670     targetdir = srcdir;
1671
1672   target = g_build_filename (targetdir, "gschemas.compiled", NULL);
1673
1674   if (!schema_files)
1675     {
1676       GPtrArray *files;
1677
1678       files = g_ptr_array_new ();
1679
1680       dir = g_dir_open (srcdir, 0, &error);
1681       if (dir == NULL)
1682         {
1683           fprintf (stderr, "%s\n", error->message);
1684           return 1;
1685         }
1686
1687       while ((file = g_dir_read_name (dir)) != NULL)
1688         {
1689           if (g_str_has_suffix (file, ".gschema.xml") ||
1690               g_str_has_suffix (file, ".enums.xml"))
1691             g_ptr_array_add (files, g_build_filename (srcdir, file, NULL));
1692         }
1693
1694       if (files->len == 0)
1695         {
1696           fprintf (stderr, _("No schema files found: "));
1697
1698           if (g_unlink (target))
1699             fprintf (stderr, _("doing nothing.\n"));
1700
1701           else
1702             fprintf (stderr, _("removed existing output file.\n"));
1703
1704           return 0;
1705         }
1706       g_ptr_array_sort (files, compare_strings);
1707       g_ptr_array_add (files, NULL);
1708
1709       schema_files = (char **) g_ptr_array_free (files, FALSE);
1710     }
1711
1712
1713   if (!(table = parse_gschema_files (schema_files, &error)) ||
1714       (!dry_run && !write_to_file (table, target, &error)))
1715     {
1716       fprintf (stderr, "%s\n", error->message);
1717       return 1;
1718     }
1719
1720   g_free (target);
1721
1722   return 0;
1723 }
1724
1725 /* Epilogue {{{1 */
1726
1727 /* vim:set foldmethod=marker: */