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