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