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 : : }
|