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 : : /* For Visual Studio builds: if a developer shell is detected,
758 : : immediately assume MSVC so we don't DoS the user with octal
759 : : strings.
760 : : See https://developercommunity.visualstudio.com/t/Long-octal-formatted-strings-DoS-the-use/11021201
761 : : */
762 : : if (g_getenv ("VCINSTALLDIR") != NULL)
763 : : {
764 : : compiler = "msvc";
765 : : }
766 : : else if (g_getenv ("MSYSTEM") != NULL)
767 : : {
768 : : const char *compiler_env = g_getenv ("CC");
769 : :
770 : : if (compiler_env == NULL || *compiler_env == '\0')
771 : : compiler = "gcc";
772 : : else
773 : : compiler = compiler_env;
774 : : }
775 : : else
776 : : compiler = "msvc";
777 : : #endif
778 : : }
779 : :
780 : 10 : base = g_path_get_basename (compiler);
781 : 10 : ext_p = strrchr (base, '.');
782 : 10 : if (ext_p != NULL)
783 : : {
784 : 0 : gsize offset = ext_p - base;
785 : 0 : base[offset] = '\0';
786 : : }
787 : :
788 : 10 : compiler = base;
789 : :
790 : 10 : if (g_strcmp0 (compiler, "gcc") == 0)
791 : 10 : compiler_type = COMPILER_GCC;
792 : 0 : else if (g_strcmp0 (compiler, "clang") == 0)
793 : 0 : compiler_type = COMPILER_CLANG;
794 : 0 : else if (g_strcmp0 (compiler, "msvc") == 0)
795 : 0 : compiler_type = COMPILER_MSVC;
796 : : else
797 : 0 : compiler_type = COMPILER_UNKNOWN;
798 : :
799 : 10 : g_free (base);
800 : :
801 : 10 : return compiler_type;
802 : : }
803 : :
804 : : int
805 : 10 : main (int argc, char **argv)
806 : : {
807 : : GError *error;
808 : : GHashTable *table;
809 : : GHashTable *files;
810 : : gchar *srcfile;
811 : 10 : gboolean show_version_and_exit = FALSE;
812 : 10 : gchar *target = NULL;
813 : 10 : gchar *binary_target = NULL;
814 : 10 : gboolean generate_automatic = FALSE;
815 : 10 : gboolean generate_source = FALSE;
816 : 10 : gboolean generate_header = FALSE;
817 : 10 : gboolean manual_register = FALSE;
818 : 10 : gboolean internal = FALSE;
819 : 10 : gboolean external_data = FALSE;
820 : 10 : gboolean generate_dependencies = FALSE;
821 : 10 : gboolean generate_phony_targets = FALSE;
822 : 10 : char *dependency_file = NULL;
823 : 10 : char *c_name = NULL;
824 : : char *c_name_no_underscores;
825 : 10 : const char *linkage = "extern";
826 : 10 : char *compiler = NULL;
827 : 10 : CompilerType compiler_type = COMPILER_GCC;
828 : : GOptionContext *context;
829 : 10 : GOptionEntry entries[] = {
830 : : { "version", 0, 0, G_OPTION_ARG_NONE, &show_version_and_exit, N_("Show program version and exit"), NULL },
831 : : { "target", 0, 0, G_OPTION_ARG_FILENAME, &target, N_("Name of the output file"), N_("FILE") },
832 : : { "sourcedir", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &sourcedirs, N_("The directories to load files referenced in FILE from (default: current directory)"), N_("DIRECTORY") },
833 : : { "generate", 0, 0, G_OPTION_ARG_NONE, &generate_automatic, N_("Generate output in the format selected for by the target filename extension"), NULL },
834 : : { "generate-header", 0, 0, G_OPTION_ARG_NONE, &generate_header, N_("Generate source header"), NULL },
835 : : { "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 },
836 : : { "generate-dependencies", 0, 0, G_OPTION_ARG_NONE, &generate_dependencies, N_("Generate dependency list"), NULL },
837 : : { "dependency-file", 0, 0, G_OPTION_ARG_FILENAME, &dependency_file, N_("Name of the dependency file to generate"), N_("FILE") },
838 : : { "generate-phony-targets", 0, 0, G_OPTION_ARG_NONE, &generate_phony_targets, N_("Include phony targets in the generated dependency file"), NULL },
839 : : { "manual-register", 0, 0, G_OPTION_ARG_NONE, &manual_register, N_("Don’t automatically create and register resource"), NULL },
840 : : { "internal", 0, 0, G_OPTION_ARG_NONE, &internal, N_("Don’t export functions; declare them G_GNUC_INTERNAL"), NULL },
841 : : { "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 },
842 : : { "c-name", 0, 0, G_OPTION_ARG_STRING, &c_name, N_("C identifier name used for the generated source code"), N_("IDENTIFIER") },
843 : : { "compiler", 'C', 0, G_OPTION_ARG_STRING, &compiler, N_("The target C compiler (default: the CC environment variable)"), N_("COMMAND") },
844 : : G_OPTION_ENTRY_NULL
845 : : };
846 : :
847 : : #ifdef G_OS_WIN32
848 : : gchar *tmp;
849 : : gchar **command_line = NULL;
850 : : #endif
851 : :
852 : 10 : setlocale (LC_ALL, GLIB_DEFAULT_LOCALE);
853 : 10 : textdomain (GETTEXT_PACKAGE);
854 : :
855 : : #ifdef G_OS_WIN32
856 : : tmp = _glib_get_locale_dir ();
857 : : bindtextdomain (GETTEXT_PACKAGE, tmp);
858 : : g_free (tmp);
859 : : #else
860 : 10 : bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
861 : : #endif
862 : :
863 : : #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
864 : 10 : bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
865 : : #endif
866 : :
867 : 10 : context = g_option_context_new (N_("FILE"));
868 : 10 : g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
869 : 10 : g_option_context_set_summary (context,
870 : : N_("Compile a resource specification into a resource file.\n"
871 : : "Resource specification files have the extension .gresource.xml,\n"
872 : : "and the resource file have the extension called .gresource."));
873 : 10 : g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
874 : :
875 : 10 : error = NULL;
876 : : #ifdef G_OS_WIN32
877 : : command_line = g_win32_get_command_line ();
878 : : if (!g_option_context_parse_strv (context, &command_line, &error))
879 : : {
880 : : g_printerr ("%s\n", error->message);
881 : : return 1;
882 : : }
883 : : argc = g_strv_length (command_line);
884 : : #else
885 : 10 : if (!g_option_context_parse (context, &argc, &argv, &error))
886 : : {
887 : 0 : g_printerr ("%s\n", error->message);
888 : 0 : return 1;
889 : : }
890 : : #endif
891 : :
892 : 10 : g_option_context_free (context);
893 : :
894 : 10 : if (show_version_and_exit)
895 : : {
896 : 0 : g_print (PACKAGE_VERSION "\n");
897 : 0 : return 0;
898 : : }
899 : :
900 : 10 : if (argc != 2)
901 : : {
902 : 0 : g_printerr (_("You should give exactly one file name\n"));
903 : 0 : g_free (c_name);
904 : 0 : return 1;
905 : : }
906 : :
907 : 10 : if (internal)
908 : 10 : linkage = "G_GNUC_INTERNAL";
909 : :
910 : 10 : compiler_type = get_compiler_id (compiler);
911 : 10 : g_free (compiler);
912 : :
913 : : #ifdef G_OS_WIN32
914 : : srcfile = command_line[1];
915 : : #else
916 : 10 : srcfile = argv[1];
917 : : #endif
918 : :
919 : 10 : xmllint = g_strdup (g_getenv ("XMLLINT"));
920 : 10 : if (xmllint == NULL)
921 : 10 : xmllint = g_find_program_in_path ("xmllint");
922 : :
923 : 10 : jsonformat = g_strdup (g_getenv ("JSON_GLIB_FORMAT"));
924 : 10 : if (jsonformat == NULL)
925 : 10 : jsonformat = g_find_program_in_path ("json-glib-format");
926 : :
927 : 10 : gdk_pixbuf_pixdata = g_strdup (g_getenv ("GDK_PIXBUF_PIXDATA"));
928 : 10 : if (gdk_pixbuf_pixdata == NULL)
929 : 10 : gdk_pixbuf_pixdata = g_find_program_in_path ("gdk-pixbuf-pixdata");
930 : :
931 : 10 : if (target == NULL)
932 : : {
933 : 0 : char *dirname = g_path_get_dirname (srcfile);
934 : 0 : char *base = g_path_get_basename (srcfile);
935 : : char *target_basename;
936 : 0 : if (g_str_has_suffix (base, ".xml"))
937 : 0 : base[strlen(base) - strlen (".xml")] = 0;
938 : :
939 : 0 : if (generate_source)
940 : : {
941 : 0 : if (g_str_has_suffix (base, ".gresource"))
942 : 0 : base[strlen(base) - strlen (".gresource")] = 0;
943 : 0 : target_basename = g_strconcat (base, ".c", NULL);
944 : : }
945 : 0 : else if (generate_header)
946 : : {
947 : 0 : if (g_str_has_suffix (base, ".gresource"))
948 : 0 : base[strlen(base) - strlen (".gresource")] = 0;
949 : 0 : target_basename = g_strconcat (base, ".h", NULL);
950 : : }
951 : : else
952 : : {
953 : 0 : if (g_str_has_suffix (base, ".gresource"))
954 : 0 : target_basename = g_strdup (base);
955 : : else
956 : 0 : target_basename = g_strconcat (base, ".gresource", NULL);
957 : : }
958 : :
959 : 0 : target = g_build_filename (dirname, target_basename, NULL);
960 : 0 : g_free (target_basename);
961 : 0 : g_free (dirname);
962 : 0 : g_free (base);
963 : : }
964 : 10 : else if (generate_automatic)
965 : : {
966 : 3 : if (extension_in_set (target, "c", "cc", "cpp", "cxx", "c++", NULL))
967 : 1 : generate_source = TRUE;
968 : 2 : else if (extension_in_set (target, "h", "hh", "hpp", "hxx", "h++", NULL))
969 : 2 : generate_header = TRUE;
970 : 0 : else if (extension_in_set (target, "gresource", NULL))
971 : : { }
972 : : }
973 : :
974 : 10 : files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)file_data_free);
975 : :
976 : 10 : if ((table = parse_resource_file (srcfile, !generate_dependencies, files)) == NULL)
977 : : {
978 : 0 : g_free (target);
979 : 0 : g_free (c_name);
980 : 0 : g_hash_table_unref (files);
981 : 0 : return 1;
982 : : }
983 : :
984 : : /* This can be used in the same invocation
985 : : as other generate commands */
986 : 10 : if (dependency_file != NULL)
987 : : {
988 : : /* Generate a .d file that describes the dependencies for
989 : : * build tools, gcc -M -MF style */
990 : : GString *dep_string;
991 : : GHashTableIter iter;
992 : : gpointer key, data;
993 : : FileData *file_data;
994 : : char *escaped;
995 : :
996 : 0 : g_hash_table_iter_init (&iter, files);
997 : :
998 : 0 : dep_string = g_string_new (NULL);
999 : 0 : escaped = escape_makefile_string (target);
1000 : 0 : g_string_printf (dep_string, "%s:", escaped);
1001 : 0 : g_free (escaped);
1002 : :
1003 : 0 : escaped = escape_makefile_string (srcfile);
1004 : 0 : g_string_append_printf (dep_string, " %s", escaped);
1005 : 0 : g_free (escaped);
1006 : :
1007 : : /* First rule: foo.c: foo.xml resource1 resource2.. */
1008 : 0 : while (g_hash_table_iter_next (&iter, &key, &data))
1009 : : {
1010 : 0 : file_data = data;
1011 : 0 : if (!g_str_equal (file_data->filename, srcfile))
1012 : : {
1013 : 0 : escaped = escape_makefile_string (file_data->filename);
1014 : 0 : g_string_append_printf (dep_string, " %s", escaped);
1015 : 0 : g_free (escaped);
1016 : : }
1017 : : }
1018 : :
1019 : 0 : g_string_append (dep_string, "\n");
1020 : :
1021 : : /* Optionally include phony targets as it silences `make` but
1022 : : * isn't supported on `ninja` at the moment. See also: `gcc -MP`
1023 : : */
1024 : 0 : if (generate_phony_targets)
1025 : : {
1026 : 0 : g_string_append (dep_string, "\n");
1027 : :
1028 : : /* One rule for every resource: resourceN: */
1029 : 0 : g_hash_table_iter_init (&iter, files);
1030 : 0 : while (g_hash_table_iter_next (&iter, &key, &data))
1031 : : {
1032 : 0 : file_data = data;
1033 : 0 : if (!g_str_equal (file_data->filename, srcfile))
1034 : : {
1035 : 0 : escaped = escape_makefile_string (file_data->filename);
1036 : 0 : g_string_append_printf (dep_string, "%s:\n\n", escaped);
1037 : 0 : g_free (escaped);
1038 : : }
1039 : : }
1040 : : }
1041 : :
1042 : 0 : if (g_str_equal (dependency_file, "-"))
1043 : : {
1044 : 0 : g_print ("%s\n", dep_string->str);
1045 : : }
1046 : : else
1047 : : {
1048 : 0 : if (!g_file_set_contents (dependency_file, dep_string->str, dep_string->len, &error))
1049 : : {
1050 : 0 : g_printerr ("Error writing dependency file: %s\n", error->message);
1051 : 0 : g_string_free (dep_string, TRUE);
1052 : 0 : g_free (dependency_file);
1053 : 0 : g_error_free (error);
1054 : 0 : g_hash_table_unref (files);
1055 : 0 : return 1;
1056 : : }
1057 : : }
1058 : :
1059 : 0 : g_string_free (dep_string, TRUE);
1060 : 0 : g_free (dependency_file);
1061 : : }
1062 : :
1063 : 10 : if (generate_dependencies)
1064 : : {
1065 : : GHashTableIter iter;
1066 : : gpointer key, data;
1067 : : FileData *file_data;
1068 : :
1069 : 0 : g_hash_table_iter_init (&iter, files);
1070 : :
1071 : : /* Generate list of files for direct use as dependencies in a Makefile */
1072 : 0 : while (g_hash_table_iter_next (&iter, &key, &data))
1073 : : {
1074 : 0 : file_data = data;
1075 : 0 : g_print ("%s\n", file_data->filename);
1076 : : }
1077 : : }
1078 : 10 : else if (generate_source || generate_header)
1079 : : {
1080 : 7 : if (generate_source)
1081 : : {
1082 : 5 : int fd = g_file_open_tmp (NULL, &binary_target, &error);
1083 : 5 : if (fd == -1)
1084 : : {
1085 : 0 : g_printerr ("Can't open temp file: %s\n", error->message);
1086 : 0 : g_error_free (error);
1087 : 0 : g_free (c_name);
1088 : 0 : g_hash_table_unref (files);
1089 : 0 : return 1;
1090 : : }
1091 : 5 : close (fd);
1092 : : }
1093 : :
1094 : 7 : if (c_name == NULL)
1095 : : {
1096 : 2 : char *base = g_path_get_basename (srcfile);
1097 : : GString *s;
1098 : : char *dot;
1099 : : int i;
1100 : :
1101 : : /* Remove extensions */
1102 : 2 : dot = strchr (base, '.');
1103 : 2 : if (dot)
1104 : 2 : *dot = 0;
1105 : :
1106 : 2 : s = g_string_new ("");
1107 : :
1108 : 30 : for (i = 0; base[i] != 0; i++)
1109 : : {
1110 : 28 : const char *first = G_CSET_A_2_Z G_CSET_a_2_z "_";
1111 : 28 : const char *rest = G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "_";
1112 : 28 : if (strchr ((s->len == 0) ? first : rest, base[i]) != NULL)
1113 : 22 : g_string_append_c (s, base[i]);
1114 : 6 : else if (base[i] == '-')
1115 : : g_string_append_c (s, '_');
1116 : :
1117 : : }
1118 : :
1119 : 2 : g_free (base);
1120 : 2 : c_name = g_string_free (s, FALSE);
1121 : : }
1122 : : }
1123 : : else
1124 : 6 : binary_target = g_strdup (target);
1125 : :
1126 : 10 : c_name_no_underscores = c_name;
1127 : 17 : while (c_name_no_underscores && *c_name_no_underscores == '_')
1128 : 7 : c_name_no_underscores++;
1129 : :
1130 : 18 : if (binary_target != NULL &&
1131 : 8 : !write_to_file (table, binary_target, &error))
1132 : : {
1133 : 0 : g_printerr ("%s\n", error->message);
1134 : 0 : g_free (target);
1135 : 0 : g_free (c_name);
1136 : 0 : g_hash_table_unref (files);
1137 : 0 : return 1;
1138 : : }
1139 : :
1140 : 10 : if (generate_header)
1141 : : {
1142 : : FILE *file;
1143 : :
1144 : 2 : file = g_fopen (target, "we");
1145 : 2 : if (file == NULL)
1146 : : {
1147 : 0 : g_printerr ("can't write to file %s", target);
1148 : 0 : g_free (c_name);
1149 : 0 : g_hash_table_unref (files);
1150 : 0 : return 1;
1151 : : }
1152 : :
1153 : 2 : g_fprintf (file,
1154 : : "#ifndef __RESOURCE_%s_H__\n"
1155 : : "#define __RESOURCE_%s_H__\n"
1156 : : "\n"
1157 : : "#include <gio/gio.h>\n"
1158 : : "\n"
1159 : : "%s GResource *%s_get_resource (void);\n",
1160 : : c_name, c_name, linkage, c_name);
1161 : :
1162 : 2 : if (manual_register)
1163 : 2 : g_fprintf (file,
1164 : : "\n"
1165 : : "%s void %s_register_resource (void);\n"
1166 : : "%s void %s_unregister_resource (void);\n"
1167 : : "\n",
1168 : : linkage, c_name, linkage, c_name);
1169 : :
1170 : 2 : g_fprintf (file,
1171 : : "#endif\n");
1172 : :
1173 : 2 : fclose (file);
1174 : : }
1175 : 8 : else if (generate_source)
1176 : : {
1177 : : FILE *file;
1178 : : guint8 *data;
1179 : : gsize data_size;
1180 : : gsize i;
1181 : 5 : const char *export = "G_MODULE_EXPORT";
1182 : :
1183 : 5 : if (!g_file_get_contents (binary_target, (char **)&data,
1184 : : &data_size, NULL))
1185 : : {
1186 : 0 : g_printerr ("can't read back temporary file");
1187 : 0 : g_free (c_name);
1188 : 0 : g_hash_table_unref (files);
1189 : 0 : return 1;
1190 : : }
1191 : 5 : g_unlink (binary_target);
1192 : :
1193 : 5 : file = g_fopen (target, "we");
1194 : 5 : if (file == NULL)
1195 : : {
1196 : 0 : g_printerr ("can't write to file %s", target);
1197 : 0 : g_free (c_name);
1198 : 0 : g_hash_table_unref (files);
1199 : 0 : return 1;
1200 : : }
1201 : :
1202 : 5 : if (internal)
1203 : 5 : export = "G_GNUC_INTERNAL";
1204 : :
1205 : 5 : g_fprintf (file,
1206 : : "#include <gio/gio.h>\n"
1207 : : "\n"
1208 : : "#if defined (__ELF__) && ( __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 6))\n"
1209 : : "# define SECTION __attribute__ ((section (\".gresource.%s\"), aligned (sizeof(void *) > 8 ? sizeof(void *) : 8)))\n"
1210 : : "#else\n"
1211 : : "# define SECTION\n"
1212 : : "#endif\n"
1213 : : "\n",
1214 : : c_name_no_underscores);
1215 : :
1216 : 5 : if (external_data)
1217 : : {
1218 : 1 : g_fprintf (file,
1219 : : "extern const %s SECTION union { const guint8 data[%" G_GSIZE_FORMAT "]; const double alignment; void * const ptr;} %s_resource_data;"
1220 : : "\n",
1221 : : export, data_size, c_name);
1222 : : }
1223 : : else
1224 : : {
1225 : 4 : if (compiler_type == COMPILER_MSVC || compiler_type == COMPILER_UNKNOWN)
1226 : : {
1227 : : /* For Visual Studio builds: Avoid surpassing the 65535-character limit for a string, GitLab issue #1580 */
1228 : 0 : g_fprintf (file,
1229 : : "static const SECTION union { const guint8 data[%"G_GSIZE_FORMAT"]; const double alignment; void * const ptr;} %s_resource_data = { {\n",
1230 : : data_size + 1 /* nul terminator */, c_name);
1231 : :
1232 : 0 : for (i = 0; i < data_size; i++)
1233 : : {
1234 : 0 : if (i % 16 == 0)
1235 : 0 : g_fprintf (file, " ");
1236 : 0 : g_fprintf (file, "0%3.3o", (int)data[i]);
1237 : 0 : if (i != data_size - 1)
1238 : 0 : g_fprintf (file, ", ");
1239 : 0 : if (i % 16 == 15 || i == data_size - 1)
1240 : 0 : g_fprintf (file, "\n");
1241 : : }
1242 : :
1243 : 0 : g_fprintf (file, "} };\n");
1244 : : }
1245 : : else
1246 : : {
1247 : 4 : g_fprintf (file,
1248 : : "static const SECTION union { const guint8 data[%"G_GSIZE_FORMAT"]; const double alignment; void * const ptr;} %s_resource_data = {\n \"",
1249 : : data_size + 1 /* nul terminator */, c_name);
1250 : :
1251 : 75978 : for (i = 0; i < data_size; i++)
1252 : : {
1253 : 75974 : g_fprintf (file, "\\%3.3o", (int)data[i]);
1254 : 75974 : if (i % 16 == 15)
1255 : 4747 : g_fprintf (file, "\"\n \"");
1256 : : }
1257 : :
1258 : 4 : g_fprintf (file, "\" };\n");
1259 : : }
1260 : : }
1261 : :
1262 : 5 : g_fprintf (file,
1263 : : "\n"
1264 : : "static GStaticResource static_resource = { %s_resource_data.data, sizeof (%s_resource_data.data)%s, NULL, NULL, NULL };\n"
1265 : : "\n"
1266 : : "%s\n"
1267 : : "GResource *%s_get_resource (void);\n"
1268 : : "GResource *%s_get_resource (void)\n"
1269 : : "{\n"
1270 : : " return g_static_resource_get_resource (&static_resource);\n"
1271 : : "}\n",
1272 : 5 : c_name, c_name, (external_data ? "" : " - 1 /* nul terminator */"),
1273 : : export, c_name, c_name);
1274 : :
1275 : :
1276 : 5 : if (manual_register)
1277 : : {
1278 : 2 : g_fprintf (file,
1279 : : "\n"
1280 : : "%s\n"
1281 : : "void %s_unregister_resource (void);\n"
1282 : : "void %s_unregister_resource (void)\n"
1283 : : "{\n"
1284 : : " g_static_resource_fini (&static_resource);\n"
1285 : : "}\n"
1286 : : "\n"
1287 : : "%s\n"
1288 : : "void %s_register_resource (void);\n"
1289 : : "void %s_register_resource (void)\n"
1290 : : "{\n"
1291 : : " g_static_resource_init (&static_resource);\n"
1292 : : "}\n",
1293 : : export, c_name, c_name,
1294 : : export, c_name, c_name);
1295 : : }
1296 : : else
1297 : : {
1298 : 3 : g_fprintf (file, "%s", gconstructor_code);
1299 : 3 : g_fprintf (file,
1300 : : "\n"
1301 : : "#ifdef G_HAS_CONSTRUCTORS\n"
1302 : : "\n"
1303 : : "#ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA\n"
1304 : : "#pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(%sresource_constructor)\n"
1305 : : "#endif\n"
1306 : : "G_DEFINE_CONSTRUCTOR(%sresource_constructor)\n"
1307 : : "#ifdef G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA\n"
1308 : : "#pragma G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(%sresource_destructor)\n"
1309 : : "#endif\n"
1310 : : "G_DEFINE_DESTRUCTOR(%sresource_destructor)\n"
1311 : : "\n"
1312 : : "#else\n"
1313 : : "#warning \"Constructor not supported on this compiler, linking in resources will not work\"\n"
1314 : : "#endif\n"
1315 : : "\n"
1316 : : "static void %sresource_constructor (void)\n"
1317 : : "{\n"
1318 : : " g_static_resource_init (&static_resource);\n"
1319 : : "}\n"
1320 : : "\n"
1321 : : "static void %sresource_destructor (void)\n"
1322 : : "{\n"
1323 : : " g_static_resource_fini (&static_resource);\n"
1324 : : "}\n",
1325 : : c_name, c_name, c_name,
1326 : : c_name, c_name, c_name);
1327 : : }
1328 : :
1329 : 5 : fclose (file);
1330 : :
1331 : 5 : g_free (data);
1332 : : }
1333 : :
1334 : 10 : g_free (binary_target);
1335 : 10 : g_free (target);
1336 : 10 : g_hash_table_destroy (table);
1337 : 10 : g_free (xmllint);
1338 : 10 : g_free (jsonformat);
1339 : 10 : g_free (c_name);
1340 : 10 : g_hash_table_unref (files);
1341 : :
1342 : : #ifdef G_OS_WIN32
1343 : : g_strfreev (command_line);
1344 : : #endif
1345 : :
1346 : 10 : return 0;
1347 : : }
|