Fixed a typo
[platform/upstream/glib.git] / gio / glib-compile-resources.c
1 /*
2  * Copyright © 2011 Red Hat, Inc
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the licence, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  * Author: 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 <gioenums.h>
28
29 #include <string.h>
30 #include <stdio.h>
31 #include <locale.h>
32 #include <errno.h>
33 #ifdef G_OS_WIN32
34 #include <io.h>
35 #endif
36 #ifdef HAVE_SYS_WAIT_H
37 #include <sys/wait.h>
38 #endif
39
40 #include <gio/gmemoryoutputstream.h>
41 #include <gio/gzlibcompressor.h>
42 #include <gio/gconverteroutputstream.h>
43
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47
48 #include <glib.h>
49 #include "gvdb/gvdb-builder.h"
50
51 #include "gconstructor_as_data.h"
52
53 typedef struct
54 {
55   char *filename;
56   char *content;
57   gsize content_size;
58   gsize size;
59   guint32 flags;
60 } FileData;
61
62 typedef struct
63 {
64   GHashTable *table; /* resource path -> FileData */
65
66   gboolean collect_data;
67
68   /* per gresource */
69   char *prefix;
70
71   /* per file */
72   char *alias;
73   gboolean compressed;
74   char *preproc_options;
75
76   GString *string;  /* non-NULL when accepting text */
77 } ParseState;
78
79 static gchar **sourcedirs = NULL;
80 static gchar *xmllint = NULL;
81 static gchar *gdk_pixbuf_pixdata = NULL;
82
83 static void
84 file_data_free (FileData *data)
85 {
86   g_free (data->filename);
87   g_free (data->content);
88   g_free (data);
89 }
90
91 static void
92 start_element (GMarkupParseContext  *context,
93                const gchar          *element_name,
94                const gchar         **attribute_names,
95                const gchar         **attribute_values,
96                gpointer              user_data,
97                GError              **error)
98 {
99   ParseState *state = user_data;
100   const GSList *element_stack;
101   const gchar *container;
102
103   element_stack = g_markup_parse_context_get_element_stack (context);
104   container = element_stack->next ? element_stack->next->data : NULL;
105
106 #define COLLECT(first, ...) \
107   g_markup_collect_attributes (element_name,                                 \
108                                attribute_names, attribute_values, error,     \
109                                first, __VA_ARGS__, G_MARKUP_COLLECT_INVALID)
110 #define OPTIONAL   G_MARKUP_COLLECT_OPTIONAL
111 #define STRDUP     G_MARKUP_COLLECT_STRDUP
112 #define STRING     G_MARKUP_COLLECT_STRING
113 #define BOOL       G_MARKUP_COLLECT_BOOLEAN
114 #define NO_ATTRS()  COLLECT (G_MARKUP_COLLECT_INVALID, NULL)
115
116   if (container == NULL)
117     {
118       if (strcmp (element_name, "gresources") == 0)
119         return;
120     }
121   else if (strcmp (container, "gresources") == 0)
122     {
123       if (strcmp (element_name, "gresource") == 0)
124         {
125           COLLECT (OPTIONAL | STRDUP,
126                    "prefix", &state->prefix);
127           return;
128         }
129     }
130   else if (strcmp (container, "gresource") == 0)
131     {
132       if (strcmp (element_name, "file") == 0)
133         {
134           COLLECT (OPTIONAL | STRDUP, "alias", &state->alias,
135                    OPTIONAL | BOOL, "compressed", &state->compressed,
136                    OPTIONAL | STRDUP, "preprocess", &state->preproc_options);
137           state->string = g_string_new ("");
138           return;
139         }
140     }
141
142   if (container)
143     g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
144                  _("Element <%s> not allowed inside <%s>"),
145                  element_name, container);
146   else
147     g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
148                  _("Element <%s> not allowed at toplevel"), element_name);
149
150 }
151
152 static GvdbItem *
153 get_parent (GHashTable *table,
154             gchar      *key,
155             gint        length)
156 {
157   GvdbItem *grandparent, *parent;
158
159   if (length == 1)
160     return NULL;
161
162   while (key[--length - 1] != '/');
163   key[length] = '\0';
164
165   parent = g_hash_table_lookup (table, key);
166
167   if (parent == NULL)
168     {
169       parent = gvdb_hash_table_insert (table, key);
170
171       grandparent = get_parent (table, key, length);
172
173       if (grandparent != NULL)
174         gvdb_item_set_parent (parent, grandparent);
175     }
176
177   return parent;
178 }
179
180 static gchar *
181 find_file (const gchar *filename)
182 {
183   guint i;
184   gchar *real_file;
185   gboolean exists;
186
187   /* search all the sourcedirs for the correct files in order */
188   for (i = 0; sourcedirs[i] != NULL; i++)
189     {
190         real_file = g_build_filename (sourcedirs[i], filename, NULL);
191         exists = g_file_test (real_file, G_FILE_TEST_EXISTS);
192         if (exists)
193           return real_file;
194         g_free (real_file);
195     }
196     return NULL;
197 }
198
199 static void
200 end_element (GMarkupParseContext  *context,
201              const gchar          *element_name,
202              gpointer              user_data,
203              GError              **error)
204 {
205   ParseState *state = user_data;
206   GError *my_error = NULL;
207
208   if (strcmp (element_name, "gresource") == 0)
209     {
210       g_free (state->prefix);
211       state->prefix = NULL;
212     }
213
214   else if (strcmp (element_name, "file") == 0)
215     {
216       gchar *file, *real_file;
217       gchar *key;
218       FileData *data;
219       char *tmp_file = NULL;
220       char *tmp_file2 = NULL;
221
222       file = state->string->str;
223       key = file;
224       if (state->alias)
225         key = state->alias;
226
227       if (state->prefix)
228         key = g_build_path ("/", "/", state->prefix, key, NULL);
229       else
230         key = g_build_path ("/", "/", key, NULL);
231
232       if (g_hash_table_lookup (state->table, key) != NULL)
233         {
234           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
235                        _("File %s appears multiple times in the resource"),
236                        key);
237           return;
238         }
239
240       data = g_new0 (FileData, 1);
241
242       if (sourcedirs != NULL)
243         {
244           real_file = find_file (file);
245           if (real_file == NULL)
246             {
247                 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
248                              _("Failed to locate '%s' in any source directory"), file);
249                 return;
250             }
251         }
252       else
253         {
254           gboolean exists;
255           exists = g_file_test (file, G_FILE_TEST_EXISTS);
256           if (!exists)
257             {
258               g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
259                            _("Failed to locate '%s' in current directory"), file);
260               return;
261             }
262           real_file = g_strdup (file);
263         }
264
265       data->filename = g_strdup (real_file);
266       if (!state->collect_data)
267         goto done;
268
269       if (state->preproc_options)
270         {
271           gchar **options;
272           guint i;
273           gboolean xml_stripblanks = FALSE;
274           gboolean to_pixdata = FALSE;
275
276           options = g_strsplit (state->preproc_options, ",", -1);
277
278           for (i = 0; options[i]; i++)
279             {
280               if (!strcmp (options[i], "xml-stripblanks"))
281                 xml_stripblanks = TRUE;
282               else if (!strcmp (options[i], "to-pixdata"))
283                 to_pixdata = TRUE;
284               else
285                 {
286                   g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
287                                _("Unknown processing option \"%s\""), options[i]);
288                   g_strfreev (options);
289                   goto cleanup;
290                 }
291             }
292           g_strfreev (options);
293
294           if (xml_stripblanks && xmllint != NULL)
295             {
296               gchar *argv[8];
297               int status, fd, argc;
298
299               tmp_file = g_strdup ("resource-XXXXXXXX");
300               if ((fd = g_mkstemp (tmp_file)) == -1)
301                 {
302                   int errsv = errno;
303
304                   g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
305                                _("Failed to create temp file: %s"),
306                               g_strerror (errsv));
307                   g_free (tmp_file);
308                   tmp_file = NULL;
309                   goto cleanup;
310                 }
311               close (fd);
312
313               argc = 0;
314               argv[argc++] = (gchar *) xmllint;
315               argv[argc++] = "--nonet";
316               argv[argc++] = "--noblanks";
317               argv[argc++] = "--output";
318               argv[argc++] = tmp_file;
319               argv[argc++] = real_file;
320               argv[argc++] = NULL;
321               g_assert (argc <= G_N_ELEMENTS (argv));
322
323               if (!g_spawn_sync (NULL /* cwd */, argv, NULL /* envv */,
324                                  G_SPAWN_STDOUT_TO_DEV_NULL |
325                                  G_SPAWN_STDERR_TO_DEV_NULL,
326                                  NULL, NULL, NULL, NULL, &status, &my_error))
327                 {
328                   g_propagate_error (error, my_error);
329                   goto cleanup;
330                 }
331 #ifdef HAVE_SYS_WAIT_H
332               if (!WIFEXITED (status) || WEXITSTATUS (status) != 0)
333                 {
334                   g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
335                                       _("Error processing input file with xmllint"));
336                   goto cleanup;
337                 }
338 #endif
339
340               g_free (real_file);
341               real_file = g_strdup (tmp_file);
342             }
343
344           if (to_pixdata)
345             {
346               gchar *argv[4];
347               int status, fd, argc;
348
349               if (gdk_pixbuf_pixdata == NULL)
350                 {
351                   g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
352                                        "to-pixbuf preprocessing requested but GDK_PIXBUF_PIXDATA "
353                                        "not set and gdk-pixbuf-pixdata not found in path");
354                   goto cleanup;
355                 }
356
357               tmp_file2 = g_strdup ("resource-XXXXXXXX");
358               if ((fd = g_mkstemp (tmp_file2)) == -1)
359                 {
360                   int errsv = errno;
361
362                   g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
363                                _("Failed to create temp file: %s"),
364                                g_strerror (errsv));
365                   g_free (tmp_file2);
366                   tmp_file2 = NULL;
367                   goto cleanup;
368                 }
369               close (fd);
370
371               argc = 0;
372               argv[argc++] = (gchar *) gdk_pixbuf_pixdata;
373               argv[argc++] = real_file;
374               argv[argc++] = tmp_file2;
375               argv[argc++] = NULL;
376               g_assert (argc <= G_N_ELEMENTS (argv));
377
378               if (!g_spawn_sync (NULL /* cwd */, argv, NULL /* envv */,
379                                  G_SPAWN_STDOUT_TO_DEV_NULL |
380                                  G_SPAWN_STDERR_TO_DEV_NULL,
381                                  NULL, NULL, NULL, NULL, &status, &my_error))
382                 {
383                   g_propagate_error (error, my_error);
384                   goto cleanup;
385                 }
386 #ifdef HAVE_SYS_WAIT_H
387               if (!WIFEXITED (status) || WEXITSTATUS (status) != 0)
388                 {
389                   g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
390                                        _("Error processing input file with to-pixdata"));
391                   goto cleanup;
392                 }
393 #endif
394
395               g_free (real_file);
396               real_file = g_strdup (tmp_file2);
397             }
398         }
399
400       if (!g_file_get_contents (real_file, &data->content, &data->size, &my_error))
401         {
402           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
403                        _("Error reading file %s: %s"),
404                        real_file, my_error->message);
405           g_clear_error (&my_error);
406           goto cleanup;
407         }
408       /* Include zero termination in content_size for uncompressed files (but not in size) */
409       data->content_size = data->size + 1;
410
411       if (state->compressed)
412         {
413           GOutputStream *out = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
414           GZlibCompressor *compressor =
415             g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_ZLIB, 9);
416           GOutputStream *out2 = g_converter_output_stream_new (out, G_CONVERTER (compressor));
417
418           if (!g_output_stream_write_all (out2, data->content, data->size,
419                                           NULL, NULL, NULL) ||
420               !g_output_stream_close (out2, NULL, NULL))
421             {
422               g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
423                            _("Error compressing file %s"),
424                            real_file);
425               goto cleanup;
426             }
427
428           g_free (data->content);
429           data->content_size = g_memory_output_stream_get_size (G_MEMORY_OUTPUT_STREAM (out));
430           data->content = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (out));
431
432           g_object_unref (compressor);
433           g_object_unref (out);
434           g_object_unref (out2);
435
436           data->flags |= G_RESOURCE_FLAGS_COMPRESSED;
437         }
438
439     done:
440
441       g_hash_table_insert (state->table, key, data);
442
443     cleanup:
444       /* Cleanup */
445
446       g_free (state->alias);
447       state->alias = NULL;
448       g_string_free (state->string, TRUE);
449       state->string = NULL;
450       g_free (state->preproc_options);
451       state->preproc_options = NULL;
452
453       g_free (real_file);
454
455       if (tmp_file)
456         {
457           unlink (tmp_file);
458           g_free (tmp_file);
459         }
460
461       if (tmp_file2)
462         {
463           unlink (tmp_file2);
464           g_free (tmp_file2);
465         }
466     }
467 }
468
469 static void
470 text (GMarkupParseContext  *context,
471       const gchar          *text,
472       gsize                 text_len,
473       gpointer              user_data,
474       GError              **error)
475 {
476   ParseState *state = user_data;
477   gsize i;
478
479   for (i = 0; i < text_len; i++)
480     if (!g_ascii_isspace (text[i]))
481       {
482         if (state->string)
483           g_string_append_len (state->string, text, text_len);
484
485         else
486           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
487                        _("text may not appear inside <%s>"),
488                        g_markup_parse_context_get_element (context));
489
490         break;
491       }
492 }
493
494 static GHashTable *
495 parse_resource_file (const gchar *filename,
496                      gboolean collect_data)
497 {
498   GMarkupParser parser = { start_element, end_element, text };
499   ParseState state = { 0, };
500   GMarkupParseContext *context;
501   GError *error = NULL;
502   gchar *contents;
503   GHashTable *table = NULL;
504   gsize size;
505
506   if (!g_file_get_contents (filename, &contents, &size, &error))
507     {
508       g_printerr ("%s\n", error->message);
509       g_clear_error (&error);
510       return NULL;
511     }
512
513   state.table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)file_data_free);
514   state.collect_data = collect_data;
515
516   context = g_markup_parse_context_new (&parser,
517                                         G_MARKUP_TREAT_CDATA_AS_TEXT |
518                                         G_MARKUP_PREFIX_ERROR_POSITION,
519                                         &state, NULL);
520
521   if (!g_markup_parse_context_parse (context, contents, size, &error) ||
522       !g_markup_parse_context_end_parse (context, &error))
523     {
524       g_printerr ("%s: %s.\n", filename, error->message);
525       g_clear_error (&error);
526     }
527   else if (collect_data)
528     {
529       GHashTableIter iter;
530       const char *key;
531       char *mykey;
532       gsize key_len;
533       FileData *data;
534       GVariant *v_data;
535       GVariantBuilder builder;
536       GvdbItem *item;
537
538       table = gvdb_hash_table_new (NULL, NULL);
539
540       g_hash_table_iter_init (&iter, state.table);
541       while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&data))
542         {
543           key_len = strlen (key);
544           mykey = g_strdup (key);
545
546           item = gvdb_hash_table_insert (table, key);
547           gvdb_item_set_parent (item,
548                                 get_parent (table, mykey, key_len));
549
550           g_free (mykey);
551
552           g_variant_builder_init (&builder, G_VARIANT_TYPE ("(uuay)"));
553
554           g_variant_builder_add (&builder, "u", data->size); /* Size */
555           g_variant_builder_add (&builder, "u", data->flags); /* Flags */
556
557           v_data = g_variant_new_from_data (G_VARIANT_TYPE("ay"),
558                                             data->content, data->content_size, TRUE,
559                                             g_free, data->content);
560           g_variant_builder_add_value (&builder, v_data);
561           data->content = NULL; /* Take ownership */
562
563           gvdb_item_set_value (item,
564                                g_variant_builder_end (&builder));
565         }
566     }
567   else
568     {
569       table = g_hash_table_ref (state.table);
570     }
571
572   g_hash_table_unref (state.table);
573   g_markup_parse_context_free (context);
574   g_free (contents);
575
576   return table;
577 }
578
579 static gboolean
580 write_to_file (GHashTable   *table,
581                const gchar  *filename,
582                GError      **error)
583 {
584   gboolean success;
585
586   success = gvdb_table_write_contents (table, filename,
587                                        G_BYTE_ORDER != G_LITTLE_ENDIAN,
588                                        error);
589
590   return success;
591 }
592
593 int
594 main (int argc, char **argv)
595 {
596   GError *error;
597   GHashTable *table;
598   gchar *srcfile;
599   gchar *target = NULL;
600   gchar *binary_target = NULL;
601   gboolean generate_automatic = FALSE;
602   gboolean generate_source = FALSE;
603   gboolean generate_header = FALSE;
604   gboolean manual_register = FALSE;
605   gboolean generate_dependencies = FALSE;
606   char *c_name = NULL;
607   char *c_name_no_underscores;
608   GOptionContext *context;
609   GOptionEntry entries[] = {
610     { "target", 0, 0, G_OPTION_ARG_FILENAME, &target, N_("name of the output file"), N_("FILE") },
611     { "sourcedir", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &sourcedirs, N_("The directories where files are to be read from (default to current directory)"), N_("DIRECTORY") },
612     { "generate", 0, 0, G_OPTION_ARG_NONE, &generate_automatic, N_("Generate output in the format selected for by the target filename extension"), NULL },
613     { "generate-header", 0, 0, G_OPTION_ARG_NONE, &generate_header, N_("Generate source header"), NULL },
614     { "generate-source", 0, 0, G_OPTION_ARG_NONE, &generate_source, N_("Generate sourcecode used to link in the resource file into your code"), NULL },
615     { "generate-dependencies", 0, 0, G_OPTION_ARG_NONE, &generate_dependencies, N_("Generate dependency list"), NULL },
616     { "manual-register", 0, 0, G_OPTION_ARG_NONE, &manual_register, N_("Don't automatically create and register resource"), NULL },
617     { "c-name", 0, 0, G_OPTION_ARG_STRING, &c_name, N_("C identifier name used for the generated source code"), NULL },
618     { NULL }
619   };
620
621 #ifdef G_OS_WIN32
622   extern gchar *_glib_get_locale_dir (void);
623   gchar *tmp;
624 #endif
625
626   setlocale (LC_ALL, "");
627   textdomain (GETTEXT_PACKAGE);
628
629 #ifdef G_OS_WIN32
630   tmp = _glib_get_locale_dir ();
631   bindtextdomain (GETTEXT_PACKAGE, tmp);
632   g_free (tmp);
633 #else
634   bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
635 #endif
636
637 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
638   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
639 #endif
640
641   g_type_init ();
642
643   context = g_option_context_new (N_("FILE"));
644   g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
645   g_option_context_set_summary (context,
646     N_("Compile a resource specification into a resource file.\n"
647        "Resource specification files have the extension .gresource.xml,\n"
648        "and the resource file have the extension called .gresource."));
649   g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
650
651   error = NULL;
652   if (!g_option_context_parse (context, &argc, &argv, &error))
653     {
654       g_printerr ("%s\n", error->message);
655       return 1;
656     }
657
658   g_option_context_free (context);
659
660   if (argc != 2)
661     {
662       g_printerr (_("You should give exactly one file name\n"));
663       return 1;
664     }
665
666   srcfile = argv[1];
667
668   xmllint = g_strdup (g_getenv ("XMLLINT"));
669   if (xmllint == NULL)
670     xmllint = g_find_program_in_path ("xmllint");
671   if (xmllint == NULL)
672     g_printerr ("XMLLINT not set and xmllint not found in path; skipping xml preprocessing.\n");
673
674   gdk_pixbuf_pixdata = g_strdup (g_getenv ("GDK_PIXBUF_PIXDATA"));
675   if (gdk_pixbuf_pixdata == NULL)
676     gdk_pixbuf_pixdata = g_find_program_in_path ("gdk-pixbuf-pixdata");
677
678   if (target == NULL)
679     {
680       char *dirname = g_path_get_dirname (srcfile);
681       char *base = g_path_get_basename (srcfile);
682       char *target_basename;
683       if (g_str_has_suffix (base, ".xml"))
684         base[strlen(base) - strlen (".xml")] = 0;
685
686       if (generate_source)
687         {
688           if (g_str_has_suffix (base, ".gresource"))
689             base[strlen(base) - strlen (".gresource")] = 0;
690           target_basename = g_strconcat (base, ".c", NULL);
691         }
692       else
693         {
694           if (g_str_has_suffix (base, ".gresource"))
695             target_basename = g_strdup (base);
696           else
697             target_basename = g_strconcat (base, ".gresource", NULL);
698         }
699
700       target = g_build_filename (dirname, target_basename, NULL);
701       g_free (target_basename);
702       g_free (dirname);
703       g_free (base);
704     }
705   else if (generate_automatic)
706     {
707       if (g_str_has_suffix (target, ".c"))
708         generate_source = TRUE;
709       else if (g_str_has_suffix (target, ".h"))
710         generate_header = TRUE;
711       else if (g_str_has_suffix (target, ".gresource"))
712         ;
713     }
714
715   if ((table = parse_resource_file (srcfile, !generate_dependencies)) == NULL)
716     {
717       g_free (target);
718       return 1;
719     }
720
721   if (generate_dependencies)
722     {
723       GHashTableIter iter;
724       gpointer key, data;
725       FileData *file_data;
726
727       g_hash_table_iter_init (&iter, table);
728       while (g_hash_table_iter_next (&iter, &key, &data))
729         {
730           file_data = data;
731           g_print ("%s\n",file_data->filename);
732         }
733     }
734   else if (generate_source || generate_header)
735     {
736       if (generate_source)
737         {
738           int fd = g_file_open_tmp (NULL, &binary_target, NULL);
739           if (fd == -1)
740             {
741               g_printerr ("Can't open temp file\n");
742               return 1;
743             }
744           close (fd);
745         }
746
747       if (c_name == NULL)
748         {
749           char *base = g_path_get_basename (srcfile);
750           GString *s;
751           char *dot;
752           int i;
753
754           /* Remove extensions */
755           dot = strchr (base, '.');
756           if (dot)
757             *dot = 0;
758
759           s = g_string_new ("");
760
761           for (i = 0; base[i] != 0; i++)
762             {
763               const char *first = G_CSET_A_2_Z G_CSET_a_2_z "_";
764               const char *rest = G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "_";
765               if (strchr ((i == 0) ? first : rest, base[i]) != NULL)
766                 g_string_append_c (s, base[i]);
767               else if (base[i] == '-')
768                 g_string_append_c (s, '_');
769
770             }
771
772           c_name = g_string_free (s, FALSE);
773         }
774     }
775   else
776     binary_target = g_strdup (target);
777
778   c_name_no_underscores = c_name;
779   while (c_name_no_underscores && *c_name_no_underscores == '_')
780     c_name_no_underscores++;
781
782   if (binary_target != NULL &&
783       !write_to_file (table, binary_target, &error))
784     {
785       g_printerr ("%s\n", error->message);
786       g_free (target);
787       return 1;
788     }
789
790   if (generate_header)
791     {
792       FILE *file;
793
794       file = fopen (target, "w");
795       if (file == NULL)
796         {
797           g_printerr ("can't write to file %s", target);
798           return 1;
799         }
800
801       fprintf (file,
802                "#ifndef __RESOURCE_%s_H__\n"
803                "#define __RESOURCE_%s_H__\n"
804                "\n"
805                "#include <gio/gio.h>\n"
806                "\n"
807                "extern GResource *%s_get_resource (void);\n",
808                c_name, c_name, c_name);
809
810       if (manual_register)
811         fprintf (file,
812                  "\n"
813                  "extern void %s_register_resource (void);\n"
814                  "extern void %s_unregister_resource (void);\n"
815                  "\n",
816                  c_name, c_name);
817
818       fprintf (file,
819                "#endif\n");
820
821       fclose (file);
822     }
823   else if (generate_source)
824     {
825       FILE *file;
826       guint8 *data;
827       gsize data_size;
828       gsize i;
829
830       if (!g_file_get_contents (binary_target, (char **)&data,
831                                 &data_size, NULL))
832         {
833           g_printerr ("can't read back temporary file");
834           return 1;
835         }
836       g_unlink (binary_target);
837
838       file = fopen (target, "w");
839       if (file == NULL)
840         {
841           g_printerr ("can't write to file %s", target);
842           return 1;
843         }
844
845       fprintf (file,
846                "#include <gio/gio.h>\n"
847                "\n"
848                "#if defined (__ELF__) && ( __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 6))\n"
849                "# define SECTION __attribute__ ((section (\".gresource.%s\"), aligned (8)))\n"
850                "#else\n"
851                "# define SECTION\n"
852                "#endif\n"
853                "\n"
854                "static const SECTION union { const guint8 data[%"G_GSIZE_FORMAT"]; const double alignment; void * const ptr;}  %s_resource_data = { {\n",
855                c_name_no_underscores, data_size, c_name);
856
857       for (i = 0; i < data_size; i++) {
858         if (i % 8 == 0)
859           fprintf (file, "  ");
860         fprintf (file, "0x%2.2x", (int)data[i]);
861         if (i != data_size - 1)
862           fprintf (file, ", ");
863         if ((i % 8 == 7) || (i == data_size - 1))
864           fprintf (file, "\n");
865       }
866
867       fprintf (file, "} };\n");
868
869       fprintf (file,
870                "\n"
871                "static GStaticResource static_resource = { %s_resource_data.data, sizeof (%s_resource_data.data) };\n"
872                "extern GResource *%s_get_resource (void);\n"
873                "GResource *%s_get_resource (void)\n"
874                "{\n"
875                "  return g_static_resource_get_resource (&static_resource);\n"
876                "}\n",
877                c_name, c_name, c_name, c_name);
878
879
880       if (manual_register)
881         {
882           fprintf (file,
883                    "\n"
884                    "extern void %s_unregister_resource (void);\n"
885                    "void %s_unregister_resource (void)\n"
886                    "{\n"
887                    "  g_static_resource_fini (&static_resource);\n"
888                    "}\n"
889                    "\n"
890                    "extern void %s_register_resource (void);\n"
891                    "void %s_register_resource (void)\n"
892                    "{\n"
893                    "  g_static_resource_init (&static_resource);\n"
894                    "}\n",
895                    c_name, c_name, c_name, c_name);
896         }
897       else
898         {
899           fprintf (file, "%s", gconstructor_code);
900           fprintf (file,
901                    "\n"
902                    "#ifdef G_HAS_CONSTRUCTORS\n"
903                    "\n"
904                    "#ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA\n"
905                    "#pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(resource_constructor)\n"
906                    "#endif\n"
907                    "G_DEFINE_CONSTRUCTOR(resource_constructor)\n"
908                    "#ifdef G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA\n"
909                    "#pragma G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(resource_destructor)\n"
910                    "#endif\n"
911                    "G_DEFINE_DESTRUCTOR(resource_destructor)\n"
912                    "\n"
913                    "#else\n"
914                    "#warning \"Constructor not supported on this compiler, linking in resources will not work\"\n"
915                    "#endif\n"
916                    "\n"
917                    "static void resource_constructor (void)\n"
918                    "{\n"
919                    "  g_static_resource_init (&static_resource);\n"
920                    "}\n"
921                    "\n"
922                    "static void resource_destructor (void)\n"
923                    "{\n"
924                    "  g_static_resource_fini (&static_resource);\n"
925                    "}\n");
926         }
927
928       fclose (file);
929
930       g_free (data);
931     }
932
933   g_free (binary_target);
934   g_free (target);
935   g_hash_table_destroy (table);
936   g_free (xmllint);
937
938   return 0;
939 }