LCOV - code coverage report
Current view: top level - gio - glib-compile-resources.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 58.6 % 526 308
Test Date: 2024-11-26 05:23:01 Functions: 91.7 % 12 11
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * Copyright © 2011 Red Hat, Inc
       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: Alexander Larsson <alexl@redhat.com>
      20                 :             :  */
      21                 :             : 
      22                 :             : #include "config.h"
      23                 :             : 
      24                 :             : #include <glib.h>
      25                 :             : #include <gstdio.h>
      26                 :             : #include <gi18n.h>
      27                 :             : #include <string.h>
      28                 :             : #include <stdio.h>
      29                 :             : #include <locale.h>
      30                 :             : #include <errno.h>
      31                 :             : #ifdef G_OS_UNIX
      32                 :             : #include <unistd.h>
      33                 :             : #endif
      34                 :             : #ifdef G_OS_WIN32
      35                 :             : #include <io.h>
      36                 :             : #endif
      37                 :             : 
      38                 :             : #define __GIO_GIO_H_INSIDE__
      39                 :             : #include <gio/gioenums.h>
      40                 :             : #include <gio/gmemoryoutputstream.h>
      41                 :             : #include <gio/gzlibcompressor.h>
      42                 :             : #include <gio/gconverteroutputstream.h>
      43                 :             : 
      44                 :             : #include <glib.h>
      45                 :             : #include "gvdb/gvdb-builder.h"
      46                 :             : 
      47                 :             : #include "gconstructor_as_data.h"
      48                 :             : #include "glib/glib-private.h"
      49                 :             : 
      50                 :             : typedef struct
      51                 :             : {
      52                 :             :   char *filename;
      53                 :             :   char *content;
      54                 :             :   gsize content_size;
      55                 :             :   gsize size;
      56                 :             :   guint32 flags;
      57                 :             : } FileData;
      58                 :             : 
      59                 :             : typedef struct
      60                 :             : {
      61                 :             :   GHashTable *table; /* resource path -> FileData */
      62                 :             : 
      63                 :             :   gboolean collect_data;
      64                 :             : 
      65                 :             :   /* per gresource */
      66                 :             :   char *prefix;
      67                 :             : 
      68                 :             :   /* per file */
      69                 :             :   char *alias;
      70                 :             :   gboolean compressed;
      71                 :             :   char *preproc_options;
      72                 :             : 
      73                 :             :   GString *string;  /* non-NULL when accepting text */
      74                 :             : } ParseState;
      75                 :             : 
      76                 :             : static gchar **sourcedirs = NULL;
      77                 :             : static gchar *xmllint = NULL;
      78                 :             : static gchar *jsonformat = NULL;
      79                 :             : static gchar *gdk_pixbuf_pixdata = NULL;
      80                 :             : 
      81                 :             : static void
      82                 :          16 : file_data_free (FileData *data)
      83                 :             : {
      84                 :          16 :   g_free (data->filename);
      85                 :          16 :   g_free (data->content);
      86                 :          16 :   g_free (data);
      87                 :          16 : }
      88                 :             : 
      89                 :             : static void
      90                 :          38 : start_element (GMarkupParseContext  *context,
      91                 :             :                const gchar          *element_name,
      92                 :             :                const gchar         **attribute_names,
      93                 :             :                const gchar         **attribute_values,
      94                 :             :                gpointer              user_data,
      95                 :             :                GError              **error)
      96                 :             : {
      97                 :          38 :   ParseState *state = user_data;
      98                 :             :   const GSList *element_stack;
      99                 :             :   const gchar *container;
     100                 :             : 
     101                 :          38 :   element_stack = g_markup_parse_context_get_element_stack (context);
     102                 :          38 :   container = element_stack->next ? element_stack->next->data : NULL;
     103                 :             : 
     104                 :             : #define COLLECT(first, ...) \
     105                 :             :   g_markup_collect_attributes (element_name,                                 \
     106                 :             :                                attribute_names, attribute_values, error,     \
     107                 :             :                                first, __VA_ARGS__, G_MARKUP_COLLECT_INVALID)
     108                 :             : #define OPTIONAL   G_MARKUP_COLLECT_OPTIONAL
     109                 :             : #define STRDUP     G_MARKUP_COLLECT_STRDUP
     110                 :             : #define STRING     G_MARKUP_COLLECT_STRING
     111                 :             : #define BOOL       G_MARKUP_COLLECT_BOOLEAN
     112                 :             : #define NO_ATTRS()  COLLECT (G_MARKUP_COLLECT_INVALID, NULL)
     113                 :             : 
     114                 :          38 :   if (container == NULL)
     115                 :             :     {
     116                 :          10 :       if (strcmp (element_name, "gresources") == 0)
     117                 :          10 :         return;
     118                 :             :     }
     119                 :          28 :   else if (strcmp (container, "gresources") == 0)
     120                 :             :     {
     121                 :          12 :       if (strcmp (element_name, "gresource") == 0)
     122                 :             :         {
     123                 :          12 :           COLLECT (OPTIONAL | STRDUP,
     124                 :             :                    "prefix", &state->prefix);
     125                 :          12 :           return;
     126                 :             :         }
     127                 :             :     }
     128                 :          16 :   else if (strcmp (container, "gresource") == 0)
     129                 :             :     {
     130                 :          16 :       if (strcmp (element_name, "file") == 0)
     131                 :             :         {
     132                 :          16 :           COLLECT (OPTIONAL | STRDUP, "alias", &state->alias,
     133                 :             :                    OPTIONAL | BOOL, "compressed", &state->compressed,
     134                 :             :                    OPTIONAL | STRDUP, "preprocess", &state->preproc_options);
     135                 :          16 :           state->string = g_string_new ("");
     136                 :          16 :           return;
     137                 :             :         }
     138                 :             :     }
     139                 :             : 
     140                 :           0 :   if (container)
     141                 :           0 :     g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
     142                 :           0 :                  _("Element <%s> not allowed inside <%s>"),
     143                 :             :                  element_name, container);
     144                 :             :   else
     145                 :           0 :     g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
     146                 :           0 :                  _("Element <%s> not allowed at toplevel"), element_name);
     147                 :             : 
     148                 :             : }
     149                 :             : 
     150                 :             : static GvdbItem *
     151                 :          36 : get_parent (GHashTable *table,
     152                 :             :             gchar      *key,
     153                 :             :             gint        length)
     154                 :             : {
     155                 :             :   GvdbItem *grandparent, *parent;
     156                 :             : 
     157                 :          36 :   if (length == 1)
     158                 :          10 :     return NULL;
     159                 :             : 
     160                 :         325 :   while (key[--length - 1] != '/');
     161                 :          26 :   key[length] = '\0';
     162                 :             : 
     163                 :          26 :   parent = g_hash_table_lookup (table, key);
     164                 :             : 
     165                 :          26 :   if (parent == NULL)
     166                 :             :     {
     167                 :          20 :       parent = gvdb_hash_table_insert (table, key);
     168                 :             : 
     169                 :          20 :       grandparent = get_parent (table, key, length);
     170                 :             : 
     171                 :          20 :       if (grandparent != NULL)
     172                 :          10 :         gvdb_item_set_parent (parent, grandparent);
     173                 :             :     }
     174                 :             : 
     175                 :          26 :   return parent;
     176                 :             : }
     177                 :             : 
     178                 :             : static gchar *
     179                 :          16 : find_file (const gchar *filename)
     180                 :             : {
     181                 :             :   guint i;
     182                 :             :   gchar *real_file;
     183                 :             :   gboolean exists;
     184                 :             : 
     185                 :          16 :   if (g_path_is_absolute (filename))
     186                 :           0 :     return g_strdup (filename);
     187                 :             : 
     188                 :             :   /* search all the sourcedirs for the correct files in order */
     189                 :          18 :   for (i = 0; sourcedirs[i] != NULL; i++)
     190                 :             :     {
     191                 :          18 :         real_file = g_build_path ("/", sourcedirs[i], filename, NULL);
     192                 :          18 :         exists = g_file_test (real_file, G_FILE_TEST_EXISTS);
     193                 :          18 :         if (exists)
     194                 :          16 :           return real_file;
     195                 :           2 :         g_free (real_file);
     196                 :             :     }
     197                 :           0 :     return NULL;
     198                 :             : }
     199                 :             : 
     200                 :             : static void
     201                 :          38 : end_element (GMarkupParseContext  *context,
     202                 :             :              const gchar          *element_name,
     203                 :             :              gpointer              user_data,
     204                 :             :              GError              **error)
     205                 :             : {
     206                 :          38 :   ParseState *state = user_data;
     207                 :          38 :   GError *my_error = NULL;
     208                 :             : 
     209                 :          38 :   if (strcmp (element_name, "gresource") == 0)
     210                 :             :     {
     211                 :          12 :       g_free (state->prefix);
     212                 :          12 :       state->prefix = NULL;
     213                 :             :     }
     214                 :             : 
     215                 :          26 :   else if (strcmp (element_name, "file") == 0)
     216                 :             :     {
     217                 :             :       gchar *file;
     218                 :          16 :       gchar *real_file = NULL;
     219                 :             :       gchar *key;
     220                 :          16 :       FileData *data = NULL;
     221                 :          16 :       char *tmp_file = NULL;
     222                 :             : 
     223                 :          16 :       file = state->string->str;
     224                 :          16 :       key = file;
     225                 :          16 :       if (state->alias)
     226                 :           2 :         key = state->alias;
     227                 :             : 
     228                 :          16 :       if (state->prefix)
     229                 :          11 :         key = g_build_path ("/", "/", state->prefix, key, NULL);
     230                 :             :       else
     231                 :           5 :         key = g_build_path ("/", "/", key, NULL);
     232                 :             : 
     233                 :          16 :       if (g_hash_table_lookup (state->table, key) != NULL)
     234                 :             :         {
     235                 :           0 :           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
     236                 :           0 :                        _("File %s appears multiple times in the resource"),
     237                 :             :                        key);
     238                 :           0 :           return;
     239                 :             :         }
     240                 :             : 
     241                 :          16 :       if (sourcedirs != NULL)
     242                 :             :         {
     243                 :          16 :           real_file = find_file (file);
     244                 :          16 :           if (real_file == NULL && state->collect_data)
     245                 :             :             {
     246                 :           0 :                 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
     247                 :           0 :                              _("Failed to locate “%s” in any source directory"), file);
     248                 :           0 :                 return;
     249                 :             :             }
     250                 :             :         }
     251                 :             :       else
     252                 :             :         {
     253                 :             :           gboolean exists;
     254                 :           0 :           exists = g_file_test (file, G_FILE_TEST_EXISTS);
     255                 :           0 :           if (!exists && state->collect_data)
     256                 :             :             {
     257                 :           0 :               g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
     258                 :           0 :                            _("Failed to locate “%s” in current directory"), file);
     259                 :           0 :               return;
     260                 :             :             }
     261                 :             :         }
     262                 :             : 
     263                 :          16 :       if (real_file == NULL)
     264                 :           0 :         real_file = g_strdup (file);
     265                 :             : 
     266                 :          16 :       data = g_new0 (FileData, 1);
     267                 :          16 :       data->filename = g_strdup (real_file);
     268                 :          16 :       if (!state->collect_data)
     269                 :           0 :         goto done;
     270                 :             : 
     271                 :          16 :       if (state->preproc_options)
     272                 :             :         {
     273                 :             :           gchar **options;
     274                 :             :           guint i;
     275                 :           1 :           gboolean xml_stripblanks = FALSE;
     276                 :           1 :           gboolean json_stripblanks = FALSE;
     277                 :           1 :           gboolean to_pixdata = FALSE;
     278                 :             : 
     279                 :           1 :           options = g_strsplit (state->preproc_options, ",", -1);
     280                 :             : 
     281                 :           2 :           for (i = 0; options[i]; i++)
     282                 :             :             {
     283                 :           1 :               if (!strcmp (options[i], "xml-stripblanks"))
     284                 :           1 :                 xml_stripblanks = TRUE;
     285                 :           0 :               else if (!strcmp (options[i], "to-pixdata"))
     286                 :           0 :                 to_pixdata = TRUE;
     287                 :           0 :               else if (!strcmp (options[i], "json-stripblanks"))
     288                 :           0 :                 json_stripblanks = TRUE;
     289                 :             :               else
     290                 :             :                 {
     291                 :           0 :                   g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
     292                 :           0 :                                _("Unknown processing option “%s”"), options[i]);
     293                 :           0 :                   g_strfreev (options);
     294                 :           0 :                   goto cleanup;
     295                 :             :                 }
     296                 :             :             }
     297                 :           1 :           g_strfreev (options);
     298                 :             : 
     299                 :           1 :           if (xml_stripblanks)
     300                 :             :             {
     301                 :             :               /* This is not fatal: pretty-printed XML is still valid XML */
     302                 :           1 :               if (xmllint == NULL)
     303                 :             :                 {
     304                 :             :                   static gboolean xmllint_warned = FALSE;
     305                 :             : 
     306                 :           0 :                   if (!xmllint_warned)
     307                 :             :                     {
     308                 :             :                       /* Translators: the first %s is a gresource XML attribute,
     309                 :             :                        * the second %s is an environment variable, and the third
     310                 :             :                        * %s is a command line tool
     311                 :             :                        */
     312                 :           0 :                       char *warn = g_strdup_printf (_("%s preprocessing requested, but %s is not set, and %s is not in PATH"),
     313                 :             :                                                     "xml-stripblanks",
     314                 :             :                                                     "XMLLINT",
     315                 :             :                                                     "xmllint");
     316                 :           0 :                       g_printerr ("%s\n", warn);
     317                 :           0 :                       g_free (warn);
     318                 :             : 
     319                 :             :                       /* Only warn once */
     320                 :           0 :                       xmllint_warned = TRUE;
     321                 :             :                     }
     322                 :             :                 }
     323                 :             :               else
     324                 :             :                 {
     325                 :             :                   GSubprocess *proc;
     326                 :             :                   int fd;
     327                 :             : 
     328                 :           1 :                   fd = g_file_open_tmp ("resource-XXXXXXXX", &tmp_file, error);
     329                 :           1 :                   if (fd < 0)
     330                 :           0 :                     goto cleanup;
     331                 :             : 
     332                 :           1 :                   close (fd);
     333                 :             : 
     334                 :           1 :                   proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE, error,
     335                 :             :                                            xmllint, "--nonet", "--noblanks", "--output", tmp_file, real_file, NULL);
     336                 :           1 :                   g_free (real_file);
     337                 :           1 :                   real_file = NULL;
     338                 :             : 
     339                 :           1 :                   if (!proc)
     340                 :           0 :                     goto cleanup;
     341                 :             : 
     342                 :           1 :                   if (!g_subprocess_wait_check (proc, NULL, error))
     343                 :             :                     {
     344                 :           0 :                       g_object_unref (proc);
     345                 :           0 :                       goto cleanup;
     346                 :             :                     }
     347                 :             : 
     348                 :           1 :                   g_object_unref (proc);
     349                 :             : 
     350                 :           2 :                   real_file = g_strdup (tmp_file);
     351                 :             :                 }
     352                 :             :             }
     353                 :             : 
     354                 :           1 :           if (json_stripblanks)
     355                 :             :             {
     356                 :             :               /* As above, this is not fatal: pretty-printed JSON is still
     357                 :             :                * valid JSON
     358                 :             :                */
     359                 :           0 :               if (jsonformat == NULL)
     360                 :             :                 {
     361                 :             :                   static gboolean jsonformat_warned = FALSE;
     362                 :             : 
     363                 :           0 :                   if (!jsonformat_warned)
     364                 :             :                     {
     365                 :             :                       /* Translators: the first %s is a gresource XML attribute,
     366                 :             :                        * the second %s is an environment variable, and the third
     367                 :             :                        * %s is a command line tool
     368                 :             :                        */
     369                 :           0 :                       char *warn = g_strdup_printf (_("%s preprocessing requested, but %s is not set, and %s is not in PATH"),
     370                 :             :                                                     "json-stripblanks",
     371                 :             :                                                     "JSON_GLIB_FORMAT",
     372                 :             :                                                     "json-glib-format");
     373                 :           0 :                       g_printerr ("%s\n", warn);
     374                 :           0 :                       g_free (warn);
     375                 :             : 
     376                 :             :                       /* Only warn once */
     377                 :           0 :                       jsonformat_warned = TRUE;
     378                 :             :                     }
     379                 :             :                 }
     380                 :             :               else
     381                 :             :                 {
     382                 :             :                   GSubprocess *proc;
     383                 :             :                   int fd;
     384                 :             : 
     385                 :           0 :                   fd = g_file_open_tmp ("resource-XXXXXXXX", &tmp_file, error);
     386                 :           0 :                   if (fd < 0)
     387                 :           0 :                     goto cleanup;
     388                 :             : 
     389                 :           0 :                   close (fd);
     390                 :             : 
     391                 :           0 :                   proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE, error,
     392                 :             :                                            jsonformat, "--output", tmp_file, real_file, NULL);
     393                 :           0 :                   g_free (real_file);
     394                 :           0 :                   real_file = NULL;
     395                 :             : 
     396                 :           0 :                   if (!proc)
     397                 :           0 :                     goto cleanup;
     398                 :             : 
     399                 :           0 :                   if (!g_subprocess_wait_check (proc, NULL, error))
     400                 :             :                     {
     401                 :           0 :                       g_object_unref (proc);
     402                 :           0 :                       goto cleanup;
     403                 :             :                     }
     404                 :             : 
     405                 :           0 :                   g_object_unref (proc);
     406                 :             : 
     407                 :           0 :                   real_file = g_strdup (tmp_file);
     408                 :             :                 }
     409                 :             :             }
     410                 :             : 
     411                 :           1 :           if (to_pixdata)
     412                 :             :             {
     413                 :             :               GSubprocess *proc;
     414                 :             :               int fd;
     415                 :             : 
     416                 :             :               /* This is a fatal error: if to-pixdata is used it means that
     417                 :             :                * the code loading the GResource expects a specific data format
     418                 :             :                */
     419                 :           0 :               if (gdk_pixbuf_pixdata == NULL)
     420                 :             :                 {
     421                 :             :                   /* Translators: the first %s is a gresource XML attribute,
     422                 :             :                    * the second %s is an environment variable, and the third
     423                 :             :                    * %s is a command line tool
     424                 :             :                    */
     425                 :           0 :                   g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
     426                 :           0 :                                _("%s preprocessing requested, but %s is not set, and %s is not in PATH"),
     427                 :             :                                "to-pixdata",
     428                 :             :                                "GDK_PIXBUF_PIXDATA",
     429                 :             :                                "gdk-pixbuf-pixdata");
     430                 :           0 :                   goto cleanup;
     431                 :             :                 }
     432                 :             : 
     433                 :           0 :               fd = g_file_open_tmp ("resource-XXXXXXXX", &tmp_file, error);
     434                 :           0 :               if (fd < 0)
     435                 :           0 :                 goto cleanup;
     436                 :             : 
     437                 :           0 :               close (fd);
     438                 :             : 
     439                 :           0 :               proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE, error,
     440                 :             :                                        gdk_pixbuf_pixdata, real_file, tmp_file, NULL);
     441                 :           0 :               g_free (real_file);
     442                 :           0 :               real_file = NULL;
     443                 :             : 
     444                 :           0 :               if (!g_subprocess_wait_check (proc, NULL, error))
     445                 :             :                 {
     446                 :           0 :                   g_object_unref (proc);
     447                 :           0 :                   goto cleanup;
     448                 :             :                 }
     449                 :             : 
     450                 :           0 :               g_object_unref (proc);
     451                 :             : 
     452                 :           0 :               real_file = g_strdup (tmp_file);
     453                 :             :             }
     454                 :             :         }
     455                 :             : 
     456                 :          16 :       if (!g_file_get_contents (real_file, &data->content, &data->size, &my_error))
     457                 :             :         {
     458                 :           0 :           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
     459                 :           0 :                        _("Error reading file %s: %s"),
     460                 :           0 :                        real_file, my_error->message);
     461                 :           0 :           g_clear_error (&my_error);
     462                 :           0 :           goto cleanup;
     463                 :             :         }
     464                 :             :       /* Include zero termination in content_size for uncompressed files (but not in size) */
     465                 :          16 :       data->content_size = data->size + 1;
     466                 :             : 
     467                 :          16 :       if (state->compressed)
     468                 :             :         {
     469                 :           3 :           GOutputStream *out = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
     470                 :             :           GZlibCompressor *compressor =
     471                 :           3 :             g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_ZLIB, 9);
     472                 :           3 :           GOutputStream *out2 = g_converter_output_stream_new (out, G_CONVERTER (compressor));
     473                 :             : 
     474                 :           3 :           if (!g_output_stream_write_all (out2, data->content, data->size,
     475                 :           3 :                                           NULL, NULL, NULL) ||
     476                 :           3 :               !g_output_stream_close (out2, NULL, NULL))
     477                 :             :             {
     478                 :           0 :               g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
     479                 :           0 :                            _("Error compressing file %s"),
     480                 :             :                            real_file);
     481                 :           0 :               g_object_unref (compressor);
     482                 :           0 :               g_object_unref (out);
     483                 :           0 :               g_object_unref (out2);
     484                 :           0 :               goto cleanup;
     485                 :             :             }
     486                 :             : 
     487                 :           3 :           g_free (data->content);
     488                 :           3 :           data->content_size = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (out));
     489                 :           3 :           data->content = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (out));
     490                 :             : 
     491                 :           3 :           g_object_unref (compressor);
     492                 :           3 :           g_object_unref (out);
     493                 :           3 :           g_object_unref (out2);
     494                 :             : 
     495                 :           3 :           data->flags |= G_RESOURCE_FLAGS_COMPRESSED;
     496                 :             :         }
     497                 :             : 
     498                 :          13 : done:
     499                 :          16 :       g_hash_table_insert (state->table, key, data);
     500                 :          16 :       data = NULL;
     501                 :             : 
     502                 :          16 :     cleanup:
     503                 :             :       /* Cleanup */
     504                 :             : 
     505                 :          16 :       g_free (state->alias);
     506                 :          16 :       state->alias = NULL;
     507                 :          16 :       g_string_free (state->string, TRUE);
     508                 :          16 :       state->string = NULL;
     509                 :          16 :       g_free (state->preproc_options);
     510                 :          16 :       state->preproc_options = NULL;
     511                 :             : 
     512                 :          16 :       g_free (real_file);
     513                 :             : 
     514                 :          16 :       if (tmp_file)
     515                 :             :         {
     516                 :           1 :           unlink (tmp_file);
     517                 :           1 :           g_free (tmp_file);
     518                 :             :         }
     519                 :             : 
     520                 :          16 :       if (data != NULL)
     521                 :           0 :         file_data_free (data);
     522                 :             :     }
     523                 :             : }
     524                 :             : 
     525                 :             : static void
     526                 :          68 : text (GMarkupParseContext  *context,
     527                 :             :       const gchar          *text,
     528                 :             :       gsize                 text_len,
     529                 :             :       gpointer              user_data,
     530                 :             :       GError              **error)
     531                 :             : {
     532                 :          68 :   ParseState *state = user_data;
     533                 :             :   gsize i;
     534                 :             : 
     535                 :         238 :   for (i = 0; i < text_len; i++)
     536                 :         186 :     if (!g_ascii_isspace (text[i]))
     537                 :             :       {
     538                 :          16 :         if (state->string)
     539                 :          16 :           g_string_append_len (state->string, text, text_len);
     540                 :             : 
     541                 :             :         else
     542                 :           0 :           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
     543                 :           0 :                        _("text may not appear inside <%s>"),
     544                 :             :                        g_markup_parse_context_get_element (context));
     545                 :             : 
     546                 :          16 :         break;
     547                 :             :       }
     548                 :          68 : }
     549                 :             : 
     550                 :             : static GHashTable *
     551                 :          10 : parse_resource_file (const gchar *filename,
     552                 :             :                      gboolean     collect_data,
     553                 :             :                      GHashTable  *files)
     554                 :             : {
     555                 :          10 :   GMarkupParser parser = { start_element, end_element, text, NULL, NULL };
     556                 :          10 :   ParseState state = { 0, };
     557                 :             :   GMarkupParseContext *context;
     558                 :          10 :   GError *error = NULL;
     559                 :             :   gchar *contents;
     560                 :          10 :   GHashTable *table = NULL;
     561                 :             :   gsize size;
     562                 :             : 
     563                 :          10 :   if (!g_file_get_contents (filename, &contents, &size, &error))
     564                 :             :     {
     565                 :           0 :       g_printerr ("%s\n", error->message);
     566                 :           0 :       g_clear_error (&error);
     567                 :           0 :       return NULL;
     568                 :             :     }
     569                 :             : 
     570                 :          10 :   state.collect_data = collect_data;
     571                 :          10 :   state.table = g_hash_table_ref (files);
     572                 :             : 
     573                 :          10 :   context = g_markup_parse_context_new (&parser,
     574                 :             :                                         G_MARKUP_TREAT_CDATA_AS_TEXT |
     575                 :             :                                         G_MARKUP_PREFIX_ERROR_POSITION,
     576                 :             :                                         &state, NULL);
     577                 :             : 
     578                 :          20 :   if (!g_markup_parse_context_parse (context, contents, size, &error) ||
     579                 :          10 :       !g_markup_parse_context_end_parse (context, &error))
     580                 :             :     {
     581                 :           0 :       g_printerr ("%s: %s.\n", filename, error->message);
     582                 :           0 :       g_clear_error (&error);
     583                 :             :     }
     584                 :             :   else
     585                 :             :     {
     586                 :             :       GHashTableIter iter;
     587                 :             :       const char *key;
     588                 :             :       char *mykey;
     589                 :             :       gsize key_len;
     590                 :             :       FileData *data;
     591                 :             :       GVariant *v_data;
     592                 :             :       GVariantBuilder builder;
     593                 :             :       GvdbItem *item;
     594                 :             : 
     595                 :          10 :       table = gvdb_hash_table_new (NULL, NULL);
     596                 :             : 
     597                 :          10 :       g_hash_table_iter_init (&iter, state.table);
     598                 :          26 :       while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&data))
     599                 :             :         {
     600                 :          16 :           key_len = strlen (key);
     601                 :          16 :           mykey = g_strdup (key);
     602                 :             : 
     603                 :          16 :           item = gvdb_hash_table_insert (table, key);
     604                 :          16 :           gvdb_item_set_parent (item,
     605                 :             :                                 get_parent (table, mykey, key_len));
     606                 :             : 
     607                 :          16 :           g_free (mykey);
     608                 :             : 
     609                 :          16 :           g_variant_builder_init_static (&builder, G_VARIANT_TYPE ("(uuay)"));
     610                 :             : 
     611                 :          16 :           g_variant_builder_add (&builder, "u", data->size); /* Size */
     612                 :          16 :           g_variant_builder_add (&builder, "u", data->flags); /* Flags */
     613                 :             : 
     614                 :          16 :           v_data = g_variant_new_from_data (G_VARIANT_TYPE("ay"),
     615                 :          16 :                                             data->content, data->content_size, TRUE,
     616                 :          16 :                                             g_free, data->content);
     617                 :          16 :           g_variant_builder_add_value (&builder, v_data);
     618                 :          16 :           data->content = NULL; /* Take ownership */
     619                 :             : 
     620                 :          16 :           gvdb_item_set_value (item,
     621                 :             :                                g_variant_builder_end (&builder));
     622                 :             :         }
     623                 :             :     }
     624                 :             : 
     625                 :          10 :   g_hash_table_unref (state.table);
     626                 :          10 :   g_markup_parse_context_free (context);
     627                 :          10 :   g_free (contents);
     628                 :             : 
     629                 :          10 :   return table;
     630                 :             : }
     631                 :             : 
     632                 :             : static gboolean
     633                 :           8 : write_to_file (GHashTable   *table,
     634                 :             :                const gchar  *filename,
     635                 :             :                GError      **error)
     636                 :             : {
     637                 :             :   gboolean success;
     638                 :             : 
     639                 :           8 :   success = gvdb_table_write_contents (table, filename,
     640                 :             :                                        G_BYTE_ORDER != G_LITTLE_ENDIAN,
     641                 :             :                                        error);
     642                 :             : 
     643                 :           8 :   return success;
     644                 :             : }
     645                 :             : 
     646                 :             : static gboolean
     647                 :           5 : extension_in_set (const char *str,
     648                 :             :                   ...)
     649                 :             : {
     650                 :             :   va_list list;
     651                 :             :   const char *ext, *value;
     652                 :           5 :   gboolean rv = FALSE;
     653                 :             : 
     654                 :           5 :   ext = strrchr (str, '.');
     655                 :           5 :   if (ext == NULL)
     656                 :           0 :     return FALSE;
     657                 :             : 
     658                 :           5 :   ext++;
     659                 :           5 :   va_start (list, str);
     660                 :          15 :   while ((value = va_arg (list, const char *)) != NULL)
     661                 :             :     {
     662                 :          13 :       if (g_ascii_strcasecmp (ext, value) != 0)
     663                 :          10 :         continue;
     664                 :             : 
     665                 :           3 :       rv = TRUE;
     666                 :           3 :       break;
     667                 :             :     }
     668                 :             : 
     669                 :           5 :   va_end (list);
     670                 :           5 :   return rv;
     671                 :             : }
     672                 :             : 
     673                 :             : /*
     674                 :             :  * We must escape any characters that `make` finds significant.
     675                 :             :  * This is largely a duplicate of the logic in gcc's `mkdeps.c:munge()`.
     676                 :             :  */
     677                 :             : static char *
     678                 :           0 : escape_makefile_string (const char *string)
     679                 :             : {
     680                 :             :   GString *str;
     681                 :             :   const char *p, *q;
     682                 :             : 
     683                 :           0 :   str = g_string_sized_new (strlen (string) + 1);
     684                 :           0 :   for (p = string; *p != '\0'; ++p)
     685                 :             :     {
     686                 :           0 :       switch (*p)
     687                 :             :         {
     688                 :           0 :         case ' ':
     689                 :             :         case '\t':
     690                 :             :           /* GNU make uses a weird quoting scheme for white space.
     691                 :             :              A space or tab preceded by 2N+1 backslashes represents
     692                 :             :              N backslashes followed by space; a space or tab
     693                 :             :              preceded by 2N backslashes represents N backslashes at
     694                 :             :              the end of a file name; and backslashes in other
     695                 :             :              contexts should not be doubled.  */
     696                 :           0 :           for (q = p - 1; string <= q && *q == '\\';  q--)
     697                 :             :             g_string_append_c (str, '\\');
     698                 :             :           g_string_append_c (str, '\\');
     699                 :           0 :           break;
     700                 :             : 
     701                 :           0 :         case '$':
     702                 :             :           g_string_append_c (str, '$');
     703                 :           0 :           break;
     704                 :             : 
     705                 :           0 :         case '#':
     706                 :             :           g_string_append_c (str, '\\');
     707                 :           0 :           break;
     708                 :             :         }
     709                 :           0 :       g_string_append_c (str, *p);
     710                 :             :     }
     711                 :             : 
     712                 :           0 :   return g_string_free (str, FALSE);
     713                 :             : }
     714                 :             : 
     715                 :             : typedef enum {
     716                 :             :   COMPILER_GCC,
     717                 :             :   COMPILER_CLANG,
     718                 :             :   COMPILER_MSVC,
     719                 :             :   COMPILER_UNKNOWN
     720                 :             : } CompilerType;
     721                 :             : 
     722                 :             : /* Get the compiler id from the platform, environment, or command line
     723                 :             :  *
     724                 :             :  * Keep compiler IDs consistent with https://mesonbuild.com/Reference-tables.html#compiler-ids
     725                 :             :  * for simplicity
     726                 :             :  */
     727                 :             : static CompilerType
     728                 :          10 : get_compiler_id (const char *compiler)
     729                 :             : {
     730                 :             :   char *base, *ext_p;
     731                 :             :   CompilerType compiler_type;
     732                 :             : 
     733                 :          10 :   if (compiler == NULL)
     734                 :             :     {
     735                 :             : #ifdef G_OS_UNIX
     736                 :           0 :       const char *compiler_env = g_getenv ("CC");
     737                 :             : 
     738                 :             : # ifdef __APPLE__
     739                 :             :       if (compiler_env == NULL || *compiler_env == '\0')
     740                 :             :         compiler = "clang";
     741                 :             :       else
     742                 :             :         compiler = compiler_env;
     743                 :             : # elif __linux__
     744                 :           0 :       if (compiler_env == NULL || *compiler_env == '\0')
     745                 :           0 :         compiler = "gcc";
     746                 :             :       else
     747                 :           0 :         compiler = compiler_env;
     748                 :             : # else
     749                 :             :       if (compiler_env == NULL || *compiler_env == '\0')
     750                 :             :         compiler = "unknown";
     751                 :             :       else
     752                 :             :         compiler = compiler_env;
     753                 :             : # endif
     754                 :             : #endif
     755                 :             : 
     756                 :             : #ifdef G_OS_WIN32
     757                 :             :       if (g_getenv ("MSYSTEM") != NULL)
     758                 :             :         {
     759                 :             :           const char *compiler_env = g_getenv ("CC");
     760                 :             : 
     761                 :             :           if (compiler_env == NULL || *compiler_env == '\0')
     762                 :             :             compiler = "gcc";
     763                 :             :           else
     764                 :             :             compiler = compiler_env;
     765                 :             :         }
     766                 :             :       else
     767                 :             :         compiler = "msvc";
     768                 :             : #endif
     769                 :             :     }
     770                 :             : 
     771                 :          10 :   base = g_path_get_basename (compiler);
     772                 :          10 :   ext_p = strrchr (base, '.');
     773                 :          10 :   if (ext_p != NULL)
     774                 :             :     {
     775                 :           0 :       gsize offset = ext_p - base;
     776                 :           0 :       base[offset] = '\0';
     777                 :             :     }
     778                 :             : 
     779                 :          10 :   compiler = base;
     780                 :             : 
     781                 :          10 :   if (g_strcmp0 (compiler, "gcc") == 0)
     782                 :          10 :     compiler_type = COMPILER_GCC;
     783                 :           0 :   else if (g_strcmp0 (compiler, "clang") == 0)
     784                 :           0 :     compiler_type = COMPILER_CLANG;
     785                 :           0 :   else if (g_strcmp0 (compiler, "msvc") == 0)
     786                 :           0 :     compiler_type = COMPILER_MSVC;
     787                 :             :   else
     788                 :           0 :     compiler_type = COMPILER_UNKNOWN;
     789                 :             : 
     790                 :          10 :   g_free (base);
     791                 :             : 
     792                 :          10 :   return compiler_type;
     793                 :             : }
     794                 :             : 
     795                 :             : int
     796                 :          10 : main (int argc, char **argv)
     797                 :             : {
     798                 :             :   GError *error;
     799                 :             :   GHashTable *table;
     800                 :             :   GHashTable *files;
     801                 :             :   gchar *srcfile;
     802                 :          10 :   gboolean show_version_and_exit = FALSE;
     803                 :          10 :   gchar *target = NULL;
     804                 :          10 :   gchar *binary_target = NULL;
     805                 :          10 :   gboolean generate_automatic = FALSE;
     806                 :          10 :   gboolean generate_source = FALSE;
     807                 :          10 :   gboolean generate_header = FALSE;
     808                 :          10 :   gboolean manual_register = FALSE;
     809                 :          10 :   gboolean internal = FALSE;
     810                 :          10 :   gboolean external_data = FALSE;
     811                 :          10 :   gboolean generate_dependencies = FALSE;
     812                 :          10 :   gboolean generate_phony_targets = FALSE;
     813                 :          10 :   char *dependency_file = NULL;
     814                 :          10 :   char *c_name = NULL;
     815                 :             :   char *c_name_no_underscores;
     816                 :          10 :   const char *linkage = "extern";
     817                 :          10 :   char *compiler = NULL;
     818                 :          10 :   CompilerType compiler_type = COMPILER_GCC;
     819                 :             :   GOptionContext *context;
     820                 :          10 :   GOptionEntry entries[] = {
     821                 :             :     { "version", 0, 0, G_OPTION_ARG_NONE, &show_version_and_exit, N_("Show program version and exit"), NULL },
     822                 :             :     { "target", 0, 0, G_OPTION_ARG_FILENAME, &target, N_("Name of the output file"), N_("FILE") },
     823                 :             :     { "sourcedir", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &sourcedirs, N_("The directories to load files referenced in FILE from (default: current directory)"), N_("DIRECTORY") },
     824                 :             :     { "generate", 0, 0, G_OPTION_ARG_NONE, &generate_automatic, N_("Generate output in the format selected for by the target filename extension"), NULL },
     825                 :             :     { "generate-header", 0, 0, G_OPTION_ARG_NONE, &generate_header, N_("Generate source header"), NULL },
     826                 :             :     { "generate-source", 0, 0, G_OPTION_ARG_NONE, &generate_source, N_("Generate source code used to link in the resource file into your code"), NULL },
     827                 :             :     { "generate-dependencies", 0, 0, G_OPTION_ARG_NONE, &generate_dependencies, N_("Generate dependency list"), NULL },
     828                 :             :     { "dependency-file", 0, 0, G_OPTION_ARG_FILENAME, &dependency_file, N_("Name of the dependency file to generate"), N_("FILE") },
     829                 :             :     { "generate-phony-targets", 0, 0, G_OPTION_ARG_NONE, &generate_phony_targets, N_("Include phony targets in the generated dependency file"), NULL },
     830                 :             :     { "manual-register", 0, 0, G_OPTION_ARG_NONE, &manual_register, N_("Don’t automatically create and register resource"), NULL },
     831                 :             :     { "internal", 0, 0, G_OPTION_ARG_NONE, &internal, N_("Don’t export functions; declare them G_GNUC_INTERNAL"), NULL },
     832                 :             :     { "external-data", 0, 0, G_OPTION_ARG_NONE, &external_data, N_("Don’t embed resource data in the C file; assume it's linked externally instead"), NULL },
     833                 :             :     { "c-name", 0, 0, G_OPTION_ARG_STRING, &c_name, N_("C identifier name used for the generated source code"), N_("IDENTIFIER") },
     834                 :             :     { "compiler", 'C', 0, G_OPTION_ARG_STRING, &compiler, N_("The target C compiler (default: the CC environment variable)"), N_("COMMAND") },
     835                 :             :     G_OPTION_ENTRY_NULL
     836                 :             :   };
     837                 :             : 
     838                 :             : #ifdef G_OS_WIN32
     839                 :             :   gchar *tmp;
     840                 :             :   gchar **command_line = NULL;
     841                 :             : #endif
     842                 :             : 
     843                 :          10 :   setlocale (LC_ALL, GLIB_DEFAULT_LOCALE);
     844                 :          10 :   textdomain (GETTEXT_PACKAGE);
     845                 :             : 
     846                 :             : #ifdef G_OS_WIN32
     847                 :             :   tmp = _glib_get_locale_dir ();
     848                 :             :   bindtextdomain (GETTEXT_PACKAGE, tmp);
     849                 :             :   g_free (tmp);
     850                 :             : #else
     851                 :          10 :   bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
     852                 :             : #endif
     853                 :             : 
     854                 :             : #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
     855                 :          10 :   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
     856                 :             : #endif
     857                 :             : 
     858                 :          10 :   context = g_option_context_new (N_("FILE"));
     859                 :          10 :   g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
     860                 :          10 :   g_option_context_set_summary (context,
     861                 :             :     N_("Compile a resource specification into a resource file.\n"
     862                 :             :        "Resource specification files have the extension .gresource.xml,\n"
     863                 :             :        "and the resource file have the extension called .gresource."));
     864                 :          10 :   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
     865                 :             : 
     866                 :          10 :   error = NULL;
     867                 :             : #ifdef G_OS_WIN32
     868                 :             :   command_line = g_win32_get_command_line ();
     869                 :             :   if (!g_option_context_parse_strv (context, &command_line, &error))
     870                 :             :     {
     871                 :             :       g_printerr ("%s\n", error->message);
     872                 :             :       return 1;
     873                 :             :     }
     874                 :             :   argc = g_strv_length (command_line);
     875                 :             : #else
     876                 :          10 :   if (!g_option_context_parse (context, &argc, &argv, &error))
     877                 :             :     {
     878                 :           0 :       g_printerr ("%s\n", error->message);
     879                 :           0 :       return 1;
     880                 :             :     }
     881                 :             : #endif
     882                 :             : 
     883                 :          10 :   g_option_context_free (context);
     884                 :             : 
     885                 :          10 :   if (show_version_and_exit)
     886                 :             :     {
     887                 :           0 :       g_print (PACKAGE_VERSION "\n");
     888                 :           0 :       return 0;
     889                 :             :     }
     890                 :             : 
     891                 :          10 :   if (argc != 2)
     892                 :             :     {
     893                 :           0 :       g_printerr (_("You should give exactly one file name\n"));
     894                 :           0 :       g_free (c_name);
     895                 :           0 :       return 1;
     896                 :             :     }
     897                 :             : 
     898                 :          10 :   if (internal)
     899                 :          10 :     linkage = "G_GNUC_INTERNAL";
     900                 :             : 
     901                 :          10 :   compiler_type = get_compiler_id (compiler);
     902                 :          10 :   g_free (compiler);
     903                 :             : 
     904                 :             : #ifdef G_OS_WIN32
     905                 :             :   srcfile = command_line[1];
     906                 :             : #else
     907                 :          10 :   srcfile = argv[1];
     908                 :             : #endif
     909                 :             : 
     910                 :          10 :   xmllint = g_strdup (g_getenv ("XMLLINT"));
     911                 :          10 :   if (xmllint == NULL)
     912                 :          10 :     xmllint = g_find_program_in_path ("xmllint");
     913                 :             : 
     914                 :          10 :   jsonformat = g_strdup (g_getenv ("JSON_GLIB_FORMAT"));
     915                 :          10 :   if (jsonformat == NULL)
     916                 :          10 :     jsonformat = g_find_program_in_path ("json-glib-format");
     917                 :             : 
     918                 :          10 :   gdk_pixbuf_pixdata = g_strdup (g_getenv ("GDK_PIXBUF_PIXDATA"));
     919                 :          10 :   if (gdk_pixbuf_pixdata == NULL)
     920                 :          10 :     gdk_pixbuf_pixdata = g_find_program_in_path ("gdk-pixbuf-pixdata");
     921                 :             : 
     922                 :          10 :   if (target == NULL)
     923                 :             :     {
     924                 :           0 :       char *dirname = g_path_get_dirname (srcfile);
     925                 :           0 :       char *base = g_path_get_basename (srcfile);
     926                 :             :       char *target_basename;
     927                 :           0 :       if (g_str_has_suffix (base, ".xml"))
     928                 :           0 :         base[strlen(base) - strlen (".xml")] = 0;
     929                 :             : 
     930                 :           0 :       if (generate_source)
     931                 :             :         {
     932                 :           0 :           if (g_str_has_suffix (base, ".gresource"))
     933                 :           0 :             base[strlen(base) - strlen (".gresource")] = 0;
     934                 :           0 :           target_basename = g_strconcat (base, ".c", NULL);
     935                 :             :         }
     936                 :           0 :       else if (generate_header)
     937                 :             :         {
     938                 :           0 :           if (g_str_has_suffix (base, ".gresource"))
     939                 :           0 :             base[strlen(base) - strlen (".gresource")] = 0;
     940                 :           0 :           target_basename = g_strconcat (base, ".h", NULL);
     941                 :             :         }
     942                 :             :       else
     943                 :             :         {
     944                 :           0 :           if (g_str_has_suffix (base, ".gresource"))
     945                 :           0 :             target_basename = g_strdup (base);
     946                 :             :           else
     947                 :           0 :             target_basename = g_strconcat (base, ".gresource", NULL);
     948                 :             :         }
     949                 :             : 
     950                 :           0 :       target = g_build_filename (dirname, target_basename, NULL);
     951                 :           0 :       g_free (target_basename);
     952                 :           0 :       g_free (dirname);
     953                 :           0 :       g_free (base);
     954                 :             :     }
     955                 :          10 :   else if (generate_automatic)
     956                 :             :     {
     957                 :           3 :       if (extension_in_set (target, "c", "cc", "cpp", "cxx", "c++", NULL))
     958                 :           1 :         generate_source = TRUE;
     959                 :           2 :       else if (extension_in_set (target, "h", "hh", "hpp", "hxx", "h++", NULL))
     960                 :           2 :         generate_header = TRUE;
     961                 :           0 :       else if (extension_in_set (target, "gresource", NULL))
     962                 :             :         { }
     963                 :             :     }
     964                 :             : 
     965                 :          10 :   files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)file_data_free);
     966                 :             : 
     967                 :          10 :   if ((table = parse_resource_file (srcfile, !generate_dependencies, files)) == NULL)
     968                 :             :     {
     969                 :           0 :       g_free (target);
     970                 :           0 :       g_free (c_name);
     971                 :           0 :       g_hash_table_unref (files);
     972                 :           0 :       return 1;
     973                 :             :     }
     974                 :             : 
     975                 :             :   /* This can be used in the same invocation
     976                 :             :      as other generate commands */
     977                 :          10 :   if (dependency_file != NULL)
     978                 :             :     {
     979                 :             :       /* Generate a .d file that describes the dependencies for
     980                 :             :        * build tools, gcc -M -MF style */
     981                 :             :       GString *dep_string;
     982                 :             :       GHashTableIter iter;
     983                 :             :       gpointer key, data;
     984                 :             :       FileData *file_data;
     985                 :             :       char *escaped;
     986                 :             : 
     987                 :           0 :       g_hash_table_iter_init (&iter, files);
     988                 :             : 
     989                 :           0 :       dep_string = g_string_new (NULL);
     990                 :           0 :       escaped = escape_makefile_string (target);
     991                 :           0 :       g_string_printf (dep_string, "%s:", escaped);
     992                 :           0 :       g_free (escaped);
     993                 :             : 
     994                 :           0 :       escaped = escape_makefile_string (srcfile);
     995                 :           0 :       g_string_append_printf (dep_string, " %s", escaped);
     996                 :           0 :       g_free (escaped);
     997                 :             : 
     998                 :             :       /* First rule: foo.c: foo.xml resource1 resource2.. */
     999                 :           0 :       while (g_hash_table_iter_next (&iter, &key, &data))
    1000                 :             :         {
    1001                 :           0 :           file_data = data;
    1002                 :           0 :           if (!g_str_equal (file_data->filename, srcfile))
    1003                 :             :             {
    1004                 :           0 :               escaped = escape_makefile_string (file_data->filename);
    1005                 :           0 :               g_string_append_printf (dep_string, " %s", escaped);
    1006                 :           0 :               g_free (escaped);
    1007                 :             :             }
    1008                 :             :         }
    1009                 :             : 
    1010                 :           0 :       g_string_append (dep_string, "\n");
    1011                 :             : 
    1012                 :             :       /* Optionally include phony targets as it silences `make` but
    1013                 :             :        * isn't supported on `ninja` at the moment. See also: `gcc -MP`
    1014                 :             :        */
    1015                 :           0 :       if (generate_phony_targets)
    1016                 :             :         {
    1017                 :           0 :                                         g_string_append (dep_string, "\n");
    1018                 :             : 
    1019                 :             :           /* One rule for every resource: resourceN: */
    1020                 :           0 :           g_hash_table_iter_init (&iter, files);
    1021                 :           0 :           while (g_hash_table_iter_next (&iter, &key, &data))
    1022                 :             :             {
    1023                 :           0 :               file_data = data;
    1024                 :           0 :               if (!g_str_equal (file_data->filename, srcfile))
    1025                 :             :                 {
    1026                 :           0 :                   escaped = escape_makefile_string (file_data->filename);
    1027                 :           0 :                   g_string_append_printf (dep_string, "%s:\n\n", escaped);
    1028                 :           0 :                   g_free (escaped);
    1029                 :             :                 }
    1030                 :             :             }
    1031                 :             :         }
    1032                 :             : 
    1033                 :           0 :       if (g_str_equal (dependency_file, "-"))
    1034                 :             :         {
    1035                 :           0 :           g_print ("%s\n", dep_string->str);
    1036                 :             :         }
    1037                 :             :       else
    1038                 :             :         {
    1039                 :           0 :           if (!g_file_set_contents (dependency_file, dep_string->str, dep_string->len, &error))
    1040                 :             :             {
    1041                 :           0 :               g_printerr ("Error writing dependency file: %s\n", error->message);
    1042                 :           0 :               g_string_free (dep_string, TRUE);
    1043                 :           0 :               g_free (dependency_file);
    1044                 :           0 :               g_error_free (error);
    1045                 :           0 :               g_hash_table_unref (files);
    1046                 :           0 :               return 1;
    1047                 :             :             }
    1048                 :             :         }
    1049                 :             : 
    1050                 :           0 :       g_string_free (dep_string, TRUE);
    1051                 :           0 :       g_free (dependency_file);
    1052                 :             :     }
    1053                 :             : 
    1054                 :          10 :   if (generate_dependencies)
    1055                 :             :     {
    1056                 :             :       GHashTableIter iter;
    1057                 :             :       gpointer key, data;
    1058                 :             :       FileData *file_data;
    1059                 :             : 
    1060                 :           0 :       g_hash_table_iter_init (&iter, files);
    1061                 :             : 
    1062                 :             :       /* Generate list of files for direct use as dependencies in a Makefile */
    1063                 :           0 :       while (g_hash_table_iter_next (&iter, &key, &data))
    1064                 :             :         {
    1065                 :           0 :           file_data = data;
    1066                 :           0 :           g_print ("%s\n", file_data->filename);
    1067                 :             :         }
    1068                 :             :     }
    1069                 :          10 :   else if (generate_source || generate_header)
    1070                 :             :     {
    1071                 :           7 :       if (generate_source)
    1072                 :             :         {
    1073                 :           5 :           int fd = g_file_open_tmp (NULL, &binary_target, NULL);
    1074                 :           5 :           if (fd == -1)
    1075                 :             :             {
    1076                 :           0 :               g_printerr ("Can't open temp file\n");
    1077                 :           0 :               g_free (c_name);
    1078                 :           0 :               g_hash_table_unref (files);
    1079                 :           0 :               return 1;
    1080                 :             :             }
    1081                 :           5 :           close (fd);
    1082                 :             :         }
    1083                 :             : 
    1084                 :           7 :       if (c_name == NULL)
    1085                 :             :         {
    1086                 :           2 :           char *base = g_path_get_basename (srcfile);
    1087                 :             :           GString *s;
    1088                 :             :           char *dot;
    1089                 :             :           int i;
    1090                 :             : 
    1091                 :             :           /* Remove extensions */
    1092                 :           2 :           dot = strchr (base, '.');
    1093                 :           2 :           if (dot)
    1094                 :           2 :             *dot = 0;
    1095                 :             : 
    1096                 :           2 :           s = g_string_new ("");
    1097                 :             : 
    1098                 :          30 :           for (i = 0; base[i] != 0; i++)
    1099                 :             :             {
    1100                 :          28 :               const char *first = G_CSET_A_2_Z G_CSET_a_2_z "_";
    1101                 :          28 :               const char *rest = G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "_";
    1102                 :          28 :               if (strchr ((s->len == 0) ? first : rest, base[i]) != NULL)
    1103                 :          22 :                 g_string_append_c (s, base[i]);
    1104                 :           6 :               else if (base[i] == '-')
    1105                 :             :                 g_string_append_c (s, '_');
    1106                 :             : 
    1107                 :             :             }
    1108                 :             : 
    1109                 :           2 :           g_free (base);
    1110                 :           2 :           c_name = g_string_free (s, FALSE);
    1111                 :             :         }
    1112                 :             :     }
    1113                 :             :   else
    1114                 :           6 :     binary_target = g_strdup (target);
    1115                 :             : 
    1116                 :          10 :   c_name_no_underscores = c_name;
    1117                 :          17 :   while (c_name_no_underscores && *c_name_no_underscores == '_')
    1118                 :           7 :     c_name_no_underscores++;
    1119                 :             : 
    1120                 :          18 :   if (binary_target != NULL &&
    1121                 :           8 :       !write_to_file (table, binary_target, &error))
    1122                 :             :     {
    1123                 :           0 :       g_printerr ("%s\n", error->message);
    1124                 :           0 :       g_free (target);
    1125                 :           0 :       g_free (c_name);
    1126                 :           0 :       g_hash_table_unref (files);
    1127                 :           0 :       return 1;
    1128                 :             :     }
    1129                 :             : 
    1130                 :          10 :   if (generate_header)
    1131                 :             :     {
    1132                 :             :       FILE *file;
    1133                 :             : 
    1134                 :           2 :       file = fopen (target, "w");
    1135                 :           2 :       if (file == NULL)
    1136                 :             :         {
    1137                 :           0 :           g_printerr ("can't write to file %s", target);
    1138                 :           0 :           g_free (c_name);
    1139                 :           0 :           g_hash_table_unref (files);
    1140                 :           0 :           return 1;
    1141                 :             :         }
    1142                 :             : 
    1143                 :           2 :       g_fprintf (file,
    1144                 :             :                "#ifndef __RESOURCE_%s_H__\n"
    1145                 :             :                "#define __RESOURCE_%s_H__\n"
    1146                 :             :                "\n"
    1147                 :             :                "#include <gio/gio.h>\n"
    1148                 :             :                "\n"
    1149                 :             :                "%s GResource *%s_get_resource (void);\n",
    1150                 :             :                c_name, c_name, linkage, c_name);
    1151                 :             : 
    1152                 :           2 :       if (manual_register)
    1153                 :           2 :         g_fprintf (file,
    1154                 :             :                  "\n"
    1155                 :             :                  "%s void %s_register_resource (void);\n"
    1156                 :             :                  "%s void %s_unregister_resource (void);\n"
    1157                 :             :                  "\n",
    1158                 :             :                  linkage, c_name, linkage, c_name);
    1159                 :             : 
    1160                 :           2 :       g_fprintf (file,
    1161                 :             :                "#endif\n");
    1162                 :             : 
    1163                 :           2 :       fclose (file);
    1164                 :             :     }
    1165                 :           8 :   else if (generate_source)
    1166                 :             :     {
    1167                 :             :       FILE *file;
    1168                 :             :       guint8 *data;
    1169                 :             :       gsize data_size;
    1170                 :             :       gsize i;
    1171                 :           5 :       const char *export = "G_MODULE_EXPORT";
    1172                 :             : 
    1173                 :           5 :       if (!g_file_get_contents (binary_target, (char **)&data,
    1174                 :             :                                 &data_size, NULL))
    1175                 :             :         {
    1176                 :           0 :           g_printerr ("can't read back temporary file");
    1177                 :           0 :           g_free (c_name);
    1178                 :           0 :           g_hash_table_unref (files);
    1179                 :           0 :           return 1;
    1180                 :             :         }
    1181                 :           5 :       g_unlink (binary_target);
    1182                 :             : 
    1183                 :           5 :       file = fopen (target, "w");
    1184                 :           5 :       if (file == NULL)
    1185                 :             :         {
    1186                 :           0 :           g_printerr ("can't write to file %s", target);
    1187                 :           0 :           g_free (c_name);
    1188                 :           0 :           g_hash_table_unref (files);
    1189                 :           0 :           return 1;
    1190                 :             :         }
    1191                 :             : 
    1192                 :           5 :       if (internal)
    1193                 :           5 :         export = "G_GNUC_INTERNAL";
    1194                 :             : 
    1195                 :           5 :       g_fprintf (file,
    1196                 :             :                "#include <gio/gio.h>\n"
    1197                 :             :                "\n"
    1198                 :             :                "#if defined (__ELF__) && ( __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 6))\n"
    1199                 :             :                "# define SECTION __attribute__ ((section (\".gresource.%s\"), aligned (sizeof(void *) > 8 ? sizeof(void *) : 8)))\n"
    1200                 :             :                "#else\n"
    1201                 :             :                "# define SECTION\n"
    1202                 :             :                "#endif\n"
    1203                 :             :                "\n",
    1204                 :             :                c_name_no_underscores);
    1205                 :             : 
    1206                 :           5 :       if (external_data)
    1207                 :             :         {
    1208                 :           1 :           g_fprintf (file,
    1209                 :             :                      "extern const %s SECTION union { const guint8 data[%" G_GSIZE_FORMAT "]; const double alignment; void * const ptr;}  %s_resource_data;"
    1210                 :             :                      "\n",
    1211                 :             :                      export, data_size, c_name);
    1212                 :             :         }
    1213                 :             :       else
    1214                 :             :         {
    1215                 :           4 :           if (compiler_type == COMPILER_MSVC || compiler_type == COMPILER_UNKNOWN)
    1216                 :             :             {
    1217                 :             :               /* For Visual Studio builds: Avoid surpassing the 65535-character limit for a string, GitLab issue #1580 */
    1218                 :           0 :               g_fprintf (file,
    1219                 :             :                          "static const SECTION union { const guint8 data[%"G_GSIZE_FORMAT"]; const double alignment; void * const ptr;}  %s_resource_data = { {\n",
    1220                 :             :                          data_size + 1 /* nul terminator */, c_name);
    1221                 :             : 
    1222                 :           0 :               for (i = 0; i < data_size; i++)
    1223                 :             :                 {
    1224                 :           0 :                   if (i % 16 == 0)
    1225                 :           0 :                     g_fprintf (file, "  ");
    1226                 :           0 :                   g_fprintf (file, "0%3.3o", (int)data[i]);
    1227                 :           0 :                   if (i != data_size - 1)
    1228                 :           0 :                     g_fprintf (file, ", ");
    1229                 :           0 :                   if (i % 16 == 15 || i == data_size - 1)
    1230                 :           0 :                      g_fprintf (file, "\n");
    1231                 :             :                 }
    1232                 :             : 
    1233                 :           0 :               g_fprintf (file, "} };\n");
    1234                 :             :             }
    1235                 :             :           else
    1236                 :             :             {
    1237                 :           4 :               g_fprintf (file,
    1238                 :             :                          "static const SECTION union { const guint8 data[%"G_GSIZE_FORMAT"]; const double alignment; void * const ptr;}  %s_resource_data = {\n  \"",
    1239                 :             :                          data_size + 1 /* nul terminator */, c_name);
    1240                 :             : 
    1241                 :       75978 :               for (i = 0; i < data_size; i++)
    1242                 :             :                 {
    1243                 :       75974 :                   g_fprintf (file, "\\%3.3o", (int)data[i]);
    1244                 :       75974 :                   if (i % 16 == 15)
    1245                 :        4747 :                     g_fprintf (file, "\"\n  \"");
    1246                 :             :                 }
    1247                 :             : 
    1248                 :           4 :               g_fprintf (file, "\" };\n");
    1249                 :             :             }
    1250                 :             :         }
    1251                 :             : 
    1252                 :           5 :       g_fprintf (file,
    1253                 :             :                "\n"
    1254                 :             :                "static GStaticResource static_resource = { %s_resource_data.data, sizeof (%s_resource_data.data)%s, NULL, NULL, NULL };\n"
    1255                 :             :                "\n"
    1256                 :             :                "%s\n"
    1257                 :             :                "GResource *%s_get_resource (void);\n"
    1258                 :             :                "GResource *%s_get_resource (void)\n"
    1259                 :             :                "{\n"
    1260                 :             :                "  return g_static_resource_get_resource (&static_resource);\n"
    1261                 :             :                "}\n",
    1262                 :           5 :                c_name, c_name, (external_data ? "" : " - 1 /* nul terminator */"),
    1263                 :             :                export, c_name, c_name);
    1264                 :             : 
    1265                 :             : 
    1266                 :           5 :       if (manual_register)
    1267                 :             :         {
    1268                 :           2 :           g_fprintf (file,
    1269                 :             :                    "\n"
    1270                 :             :                    "%s\n"
    1271                 :             :                    "void %s_unregister_resource (void);\n"
    1272                 :             :                    "void %s_unregister_resource (void)\n"
    1273                 :             :                    "{\n"
    1274                 :             :                    "  g_static_resource_fini (&static_resource);\n"
    1275                 :             :                    "}\n"
    1276                 :             :                    "\n"
    1277                 :             :                    "%s\n"
    1278                 :             :                    "void %s_register_resource (void);\n"
    1279                 :             :                    "void %s_register_resource (void)\n"
    1280                 :             :                    "{\n"
    1281                 :             :                    "  g_static_resource_init (&static_resource);\n"
    1282                 :             :                    "}\n",
    1283                 :             :                    export, c_name, c_name,
    1284                 :             :                    export, c_name, c_name);
    1285                 :             :         }
    1286                 :             :       else
    1287                 :             :         {
    1288                 :           3 :           g_fprintf (file, "%s", gconstructor_code);
    1289                 :           3 :           g_fprintf (file,
    1290                 :             :                    "\n"
    1291                 :             :                    "#ifdef G_HAS_CONSTRUCTORS\n"
    1292                 :             :                    "\n"
    1293                 :             :                    "#ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA\n"
    1294                 :             :                    "#pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(%sresource_constructor)\n"
    1295                 :             :                    "#endif\n"
    1296                 :             :                    "G_DEFINE_CONSTRUCTOR(%sresource_constructor)\n"
    1297                 :             :                    "#ifdef G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA\n"
    1298                 :             :                    "#pragma G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(%sresource_destructor)\n"
    1299                 :             :                    "#endif\n"
    1300                 :             :                    "G_DEFINE_DESTRUCTOR(%sresource_destructor)\n"
    1301                 :             :                    "\n"
    1302                 :             :                    "#else\n"
    1303                 :             :                    "#warning \"Constructor not supported on this compiler, linking in resources will not work\"\n"
    1304                 :             :                    "#endif\n"
    1305                 :             :                    "\n"
    1306                 :             :                    "static void %sresource_constructor (void)\n"
    1307                 :             :                    "{\n"
    1308                 :             :                    "  g_static_resource_init (&static_resource);\n"
    1309                 :             :                    "}\n"
    1310                 :             :                    "\n"
    1311                 :             :                    "static void %sresource_destructor (void)\n"
    1312                 :             :                    "{\n"
    1313                 :             :                    "  g_static_resource_fini (&static_resource);\n"
    1314                 :             :                    "}\n",
    1315                 :             :            c_name, c_name, c_name,
    1316                 :             :            c_name, c_name, c_name);
    1317                 :             :         }
    1318                 :             : 
    1319                 :           5 :       fclose (file);
    1320                 :             : 
    1321                 :           5 :       g_free (data);
    1322                 :             :     }
    1323                 :             : 
    1324                 :          10 :   g_free (binary_target);
    1325                 :          10 :   g_free (target);
    1326                 :          10 :   g_hash_table_destroy (table);
    1327                 :          10 :   g_free (xmllint);
    1328                 :          10 :   g_free (jsonformat);
    1329                 :          10 :   g_free (c_name);
    1330                 :          10 :   g_hash_table_unref (files);
    1331                 :             : 
    1332                 :             : #ifdef G_OS_WIN32
    1333                 :             :   g_strfreev (command_line);  
    1334                 :             : #endif
    1335                 :             : 
    1336                 :          10 :   return 0;
    1337                 :             : }
        

Generated by: LCOV version 2.0-1