LCOV - code coverage report
Current view: top level - glib/gio - glib-compile-schemas.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 729 953 76.5 %
Date: 2024-04-23 05:16:05 Functions: 45 46 97.8 %
Branches: 416 599 69.4 %

           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 (&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                 :            :                   gint 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: */

Generated by: LCOV version 1.14