Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : * GObject introspection: Dump introspection data
3 : : *
4 : : * Copyright (C) 2008 Colin Walters <walters@verbum.org>
5 : : *
6 : : * SPDX-License-Identifier: LGPL-2.1-or-later
7 : : *
8 : : * This library is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU Lesser General Public
10 : : * License as published by the Free Software Foundation; either
11 : : * version 2 of the License, or (at your option) any later version.
12 : : *
13 : : * This library is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : : * Lesser General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU Lesser General Public
19 : : * License along with this library; if not, write to the
20 : : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 : : * Boston, MA 02111-1307, USA.
22 : : */
23 : :
24 : : /* This file is both compiled into libgirepository.so, and installed
25 : : * on the filesystem. But for the dumper, we want to avoid linking
26 : : * to libgirepository; see
27 : : * https://bugzilla.gnome.org/show_bug.cgi?id=630342
28 : : */
29 : : #ifdef GI_COMPILATION
30 : : #include "config.h"
31 : : #include "girepository.h"
32 : : #endif
33 : :
34 : : #include <glib.h>
35 : : #include <glib-object.h>
36 : : #include <glib/gstdio.h>
37 : : #include <gmodule.h>
38 : :
39 : : #include <stdlib.h>
40 : : #include <stdint.h>
41 : : #include <stdio.h>
42 : : #include <string.h>
43 : :
44 : : /* Analogue of g_output_stream_write_all(). */
45 : : static gboolean
46 : 58 : write_all (FILE *out,
47 : : const void *buffer,
48 : : size_t count,
49 : : size_t *bytes_written,
50 : : GError **error)
51 : : {
52 : : size_t ret;
53 : :
54 : 58 : ret = fwrite (buffer, 1, count, out);
55 : :
56 : 58 : if (bytes_written != NULL)
57 : 58 : *bytes_written = ret;
58 : :
59 : 58 : if (ret < count)
60 : : {
61 : 0 : g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
62 : : "Failed to write to file");
63 : 0 : return FALSE;
64 : : }
65 : :
66 : 58 : return TRUE;
67 : 29 : }
68 : :
69 : : /* Analogue of g_data_input_stream_read_line(). */
70 : : static char *
71 : 24 : read_line (FILE *input,
72 : : size_t *len_out,
73 : : gboolean *out_reached_eof,
74 : : GError **error)
75 : : {
76 : 24 : GByteArray *buffer = g_byte_array_new ();
77 : 24 : const uint8_t nul = '\0';
78 : :
79 : 24 : *out_reached_eof = FALSE;
80 : :
81 : 198 : while (TRUE)
82 : 186 : {
83 : : size_t ret;
84 : : uint8_t byte;
85 : : int saved_errno;
86 : :
87 : 396 : ret = fread (&byte, 1, 1, input);
88 : 396 : saved_errno = errno;
89 : 396 : if (ret == 0)
90 : : {
91 : 10 : if (ferror (input))
92 : : {
93 : 0 : g_set_error (error, G_FILE_ERROR, (int) g_file_error_from_errno (saved_errno),
94 : : "Error reading from input file: %s",
95 : 0 : g_strerror (saved_errno));
96 : 0 : }
97 : 10 : else if (feof (input))
98 : : {
99 : 10 : *out_reached_eof = TRUE;
100 : 5 : }
101 : 10 : break;
102 : : }
103 : :
104 : 386 : if (byte == '\n')
105 : 14 : break;
106 : :
107 : 372 : g_byte_array_append (buffer, &byte, 1);
108 : : }
109 : :
110 : 24 : g_byte_array_append (buffer, &nul, 1);
111 : :
112 : 24 : if (len_out != NULL)
113 : 24 : *len_out = buffer->len - 1; /* don’t include terminating nul */
114 : :
115 : 24 : return (char *) g_byte_array_free (buffer, FALSE);
116 : : }
117 : :
118 : : static void
119 : : escaped_printf (FILE *out, const char *fmt, ...) G_GNUC_PRINTF (2, 3);
120 : :
121 : : static void
122 : 16 : escaped_printf (FILE *out, const char *fmt, ...)
123 : : {
124 : : char *str;
125 : : va_list args;
126 : : size_t written;
127 : 16 : GError *error = NULL;
128 : :
129 : 16 : va_start (args, fmt);
130 : :
131 : 16 : str = g_markup_vprintf_escaped (fmt, args);
132 : 16 : if (!write_all (out, str, strlen (str), &written, &error))
133 : : {
134 : 0 : g_critical ("failed to write to iochannel: %s", error->message);
135 : 0 : g_clear_error (&error);
136 : 0 : }
137 : 16 : g_free (str);
138 : :
139 : 16 : va_end (args);
140 : 16 : }
141 : :
142 : : static void
143 : 42 : goutput_write (FILE *out, const char *str)
144 : : {
145 : : size_t written;
146 : 42 : GError *error = NULL;
147 : 42 : if (!write_all (out, str, strlen (str), &written, &error))
148 : : {
149 : 0 : g_critical ("failed to write to iochannel: %s", error->message);
150 : 0 : g_clear_error (&error);
151 : 0 : }
152 : 42 : }
153 : :
154 : : typedef GType (*GetTypeFunc)(void);
155 : : typedef GQuark (*ErrorQuarkFunc)(void);
156 : :
157 : : static GType
158 : 10 : invoke_get_type (GModule *self, const char *symbol, GError **error)
159 : : {
160 : : GetTypeFunc sym;
161 : : GType ret;
162 : :
163 : 10 : if (!g_module_symbol (self, symbol, (void**)&sym))
164 : : {
165 : 3 : g_set_error (error,
166 : 1 : G_FILE_ERROR,
167 : : G_FILE_ERROR_FAILED,
168 : 1 : "Failed to find symbol '%s'", symbol);
169 : 2 : return G_TYPE_INVALID;
170 : : }
171 : :
172 : 8 : ret = sym ();
173 : 8 : if (ret == G_TYPE_INVALID)
174 : : {
175 : 0 : g_set_error (error,
176 : 0 : G_FILE_ERROR,
177 : : G_FILE_ERROR_FAILED,
178 : 0 : "Function '%s' returned G_TYPE_INVALID", symbol);
179 : 0 : }
180 : 8 : return ret;
181 : 5 : }
182 : :
183 : : static GQuark
184 : 2 : invoke_error_quark (GModule *self, const char *symbol, GError **error)
185 : : {
186 : : ErrorQuarkFunc sym;
187 : :
188 : 2 : if (!g_module_symbol (self, symbol, (void**)&sym))
189 : : {
190 : 3 : g_set_error (error,
191 : 1 : G_FILE_ERROR,
192 : : G_FILE_ERROR_FAILED,
193 : 1 : "Failed to find symbol '%s'", symbol);
194 : 2 : return G_TYPE_INVALID;
195 : : }
196 : :
197 : 0 : return sym ();
198 : 1 : }
199 : :
200 : : static char *
201 : 0 : value_transform_to_string (const GValue *value)
202 : : {
203 : 0 : GValue tmp = G_VALUE_INIT;
204 : 0 : char *s = NULL;
205 : :
206 : 0 : g_value_init (&tmp, G_TYPE_STRING);
207 : :
208 : 0 : if (g_value_transform (value, &tmp))
209 : : {
210 : 0 : const char *str = g_value_get_string (&tmp);
211 : :
212 : 0 : if (str != NULL)
213 : 0 : s = g_strescape (str, NULL);
214 : 0 : }
215 : :
216 : 0 : g_value_unset (&tmp);
217 : :
218 : 0 : return s;
219 : : }
220 : :
221 : : /* A simpler version of g_strdup_value_contents(), but with stable
222 : : * output and less complex semantics
223 : : */
224 : : static char *
225 : 0 : value_to_string (const GValue *value)
226 : : {
227 : 0 : if (value == NULL)
228 : 0 : return NULL;
229 : :
230 : 0 : if (G_VALUE_HOLDS_STRING (value))
231 : : {
232 : 0 : const char *s = g_value_get_string (value);
233 : :
234 : 0 : if (s == NULL)
235 : 0 : return g_strdup ("NULL");
236 : :
237 : 0 : return g_strescape (s, NULL);
238 : : }
239 : : else
240 : : {
241 : 0 : GType value_type = G_VALUE_TYPE (value);
242 : :
243 : 0 : switch (G_TYPE_FUNDAMENTAL (value_type))
244 : : {
245 : 0 : case G_TYPE_BOXED:
246 : 0 : if (g_value_get_boxed (value) == NULL)
247 : 0 : return NULL;
248 : : else
249 : 0 : return value_transform_to_string (value);
250 : : break;
251 : :
252 : 0 : case G_TYPE_OBJECT:
253 : 0 : if (g_value_get_object (value) == NULL)
254 : 0 : return NULL;
255 : : else
256 : 0 : return value_transform_to_string (value);
257 : : break;
258 : :
259 : 0 : case G_TYPE_POINTER:
260 : 0 : return NULL;
261 : :
262 : 0 : default:
263 : 0 : return value_transform_to_string (value);
264 : : }
265 : : }
266 : :
267 : : return NULL;
268 : 0 : }
269 : :
270 : : static void
271 : 8 : dump_properties (GType type, FILE *out)
272 : : {
273 : : unsigned int i;
274 : 8 : unsigned int n_properties = 0;
275 : : GParamSpec **props;
276 : :
277 : 8 : if (G_TYPE_FUNDAMENTAL (type) == G_TYPE_OBJECT)
278 : : {
279 : : GObjectClass *klass;
280 : 4 : klass = g_type_class_ref (type);
281 : 4 : props = g_object_class_list_properties (klass, &n_properties);
282 : 2 : }
283 : : else
284 : : {
285 : : void *klass;
286 : 4 : klass = g_type_default_interface_ref (type);
287 : 4 : props = g_object_interface_list_properties (klass, &n_properties);
288 : : }
289 : :
290 : 8 : for (i = 0; i < n_properties; i++)
291 : : {
292 : : GParamSpec *prop;
293 : :
294 : 0 : prop = props[i];
295 : 0 : if (prop->owner_type != type)
296 : 0 : continue;
297 : :
298 : 0 : const GValue *v = g_param_spec_get_default_value (prop);
299 : 0 : char *default_value = value_to_string (v);
300 : :
301 : 0 : if (v != NULL && default_value != NULL)
302 : : {
303 : 0 : escaped_printf (out, " <property name=\"%s\" type=\"%s\" flags=\"%d\" default-value=\"%s\"/>\n",
304 : 0 : prop->name,
305 : 0 : g_type_name (prop->value_type),
306 : 0 : prop->flags,
307 : 0 : default_value);
308 : 0 : }
309 : : else
310 : : {
311 : 0 : escaped_printf (out, " <property name=\"%s\" type=\"%s\" flags=\"%d\"/>\n",
312 : 0 : prop->name,
313 : 0 : g_type_name (prop->value_type),
314 : 0 : prop->flags);
315 : : }
316 : :
317 : 0 : g_free (default_value);
318 : 0 : }
319 : :
320 : 8 : g_free (props);
321 : 8 : }
322 : :
323 : : static void
324 : 8 : dump_signals (GType type, FILE *out)
325 : : {
326 : : unsigned int i;
327 : : unsigned int n_sigs;
328 : : unsigned int *sig_ids;
329 : :
330 : 8 : sig_ids = g_signal_list_ids (type, &n_sigs);
331 : 8 : for (i = 0; i < n_sigs; i++)
332 : : {
333 : : unsigned int sigid;
334 : : GSignalQuery query;
335 : : unsigned int j;
336 : :
337 : 0 : sigid = sig_ids[i];
338 : 0 : g_signal_query (sigid, &query);
339 : :
340 : 0 : escaped_printf (out, " <signal name=\"%s\" return=\"%s\"",
341 : 0 : query.signal_name, g_type_name (query.return_type));
342 : :
343 : 0 : if (query.signal_flags & G_SIGNAL_RUN_FIRST)
344 : 0 : escaped_printf (out, " when=\"first\"");
345 : 0 : else if (query.signal_flags & G_SIGNAL_RUN_LAST)
346 : 0 : escaped_printf (out, " when=\"last\"");
347 : 0 : else if (query.signal_flags & G_SIGNAL_RUN_CLEANUP)
348 : 0 : escaped_printf (out, " when=\"cleanup\"");
349 : 0 : else if (query.signal_flags & G_SIGNAL_MUST_COLLECT)
350 : 0 : escaped_printf (out, " when=\"must-collect\"");
351 : 0 : if (query.signal_flags & G_SIGNAL_NO_RECURSE)
352 : 0 : escaped_printf (out, " no-recurse=\"1\"");
353 : :
354 : 0 : if (query.signal_flags & G_SIGNAL_DETAILED)
355 : 0 : escaped_printf (out, " detailed=\"1\"");
356 : :
357 : 0 : if (query.signal_flags & G_SIGNAL_ACTION)
358 : 0 : escaped_printf (out, " action=\"1\"");
359 : :
360 : 0 : if (query.signal_flags & G_SIGNAL_NO_HOOKS)
361 : 0 : escaped_printf (out, " no-hooks=\"1\"");
362 : :
363 : 0 : goutput_write (out, ">\n");
364 : :
365 : 0 : for (j = 0; j < query.n_params; j++)
366 : : {
367 : 0 : escaped_printf (out, " <param type=\"%s\"/>\n",
368 : 0 : g_type_name (query.param_types[j]));
369 : 0 : }
370 : 0 : goutput_write (out, " </signal>\n");
371 : 0 : }
372 : 8 : g_free (sig_ids);
373 : 8 : }
374 : :
375 : : static void
376 : 4 : dump_object_type (GType type, const char *symbol, FILE *out)
377 : : {
378 : : unsigned int n_interfaces;
379 : : unsigned int i;
380 : : GType *interfaces;
381 : :
382 : 6 : escaped_printf (out, " <class name=\"%s\" get-type=\"%s\"",
383 : 2 : g_type_name (type), symbol);
384 : 4 : if (type != G_TYPE_OBJECT)
385 : : {
386 : : GString *parent_str;
387 : : GType parent;
388 : 4 : gboolean first = TRUE;
389 : :
390 : 4 : parent = g_type_parent (type);
391 : 4 : parent_str = g_string_new ("");
392 : 8 : while (parent != G_TYPE_INVALID)
393 : : {
394 : 4 : if (first)
395 : 4 : first = FALSE;
396 : : else
397 : 0 : g_string_append_c (parent_str, ',');
398 : 4 : g_string_append (parent_str, g_type_name (parent));
399 : 4 : parent = g_type_parent (parent);
400 : : }
401 : :
402 : 4 : escaped_printf (out, " parents=\"%s\"", parent_str->str);
403 : :
404 : 4 : g_string_free (parent_str, TRUE);
405 : 2 : }
406 : :
407 : 4 : if (G_TYPE_IS_ABSTRACT (type))
408 : 0 : escaped_printf (out, " abstract=\"1\"");
409 : :
410 : 4 : if (G_TYPE_IS_FINAL (type))
411 : 4 : escaped_printf (out, " final=\"1\"");
412 : :
413 : 4 : goutput_write (out, ">\n");
414 : :
415 : 4 : interfaces = g_type_interfaces (type, &n_interfaces);
416 : 4 : for (i = 0; i < n_interfaces; i++)
417 : : {
418 : 0 : GType itype = interfaces[i];
419 : 0 : escaped_printf (out, " <implements name=\"%s\"/>\n",
420 : 0 : g_type_name (itype));
421 : 0 : }
422 : 4 : g_free (interfaces);
423 : :
424 : 4 : dump_properties (type, out);
425 : 4 : dump_signals (type, out);
426 : 4 : goutput_write (out, " </class>\n");
427 : 4 : }
428 : :
429 : : static void
430 : 4 : dump_interface_type (GType type, const char *symbol, FILE *out)
431 : : {
432 : : unsigned int n_interfaces;
433 : : unsigned int i;
434 : : GType *interfaces;
435 : :
436 : 6 : escaped_printf (out, " <interface name=\"%s\" get-type=\"%s\">\n",
437 : 2 : g_type_name (type), symbol);
438 : :
439 : 4 : interfaces = g_type_interface_prerequisites (type, &n_interfaces);
440 : 8 : for (i = 0; i < n_interfaces; i++)
441 : : {
442 : 4 : GType itype = interfaces[i];
443 : 4 : if (itype == G_TYPE_OBJECT)
444 : : {
445 : : /* Treat this as implicit for now; in theory GInterfaces are
446 : : * supported on things like GstMiniObject, but right now
447 : : * the introspection system only supports GObject.
448 : : * http://bugzilla.gnome.org/show_bug.cgi?id=559706
449 : : */
450 : 4 : continue;
451 : : }
452 : 0 : escaped_printf (out, " <prerequisite name=\"%s\"/>\n",
453 : 0 : g_type_name (itype));
454 : 0 : }
455 : 4 : g_free (interfaces);
456 : :
457 : 4 : dump_properties (type, out);
458 : 4 : dump_signals (type, out);
459 : 4 : goutput_write (out, " </interface>\n");
460 : 4 : }
461 : :
462 : : static void
463 : 0 : dump_boxed_type (GType type, const char *symbol, FILE *out)
464 : : {
465 : 0 : escaped_printf (out, " <boxed name=\"%s\" get-type=\"%s\"/>\n",
466 : 0 : g_type_name (type), symbol);
467 : 0 : }
468 : :
469 : : static void
470 : 0 : dump_pointer_type (GType type, const char *symbol, FILE *out)
471 : : {
472 : 0 : escaped_printf (out, " <pointer name=\"%s\" get-type=\"%s\"/>\n",
473 : 0 : g_type_name (type), symbol);
474 : 0 : }
475 : :
476 : : static void
477 : 0 : dump_flags_type (GType type, const char *symbol, FILE *out)
478 : : {
479 : : unsigned int i;
480 : : GFlagsClass *klass;
481 : :
482 : 0 : klass = g_type_class_ref (type);
483 : 0 : escaped_printf (out, " <flags name=\"%s\" get-type=\"%s\">\n",
484 : 0 : g_type_name (type), symbol);
485 : :
486 : 0 : for (i = 0; i < klass->n_values; i++)
487 : : {
488 : 0 : GFlagsValue *value = &(klass->values[i]);
489 : :
490 : 0 : escaped_printf (out, " <member name=\"%s\" nick=\"%s\" value=\"%u\"/>\n",
491 : 0 : value->value_name, value->value_nick, value->value);
492 : 0 : }
493 : 0 : goutput_write (out, " </flags>\n");
494 : 0 : }
495 : :
496 : : static void
497 : 0 : dump_enum_type (GType type, const char *symbol, FILE *out)
498 : : {
499 : : unsigned int i;
500 : : GEnumClass *klass;
501 : :
502 : 0 : klass = g_type_class_ref (type);
503 : 0 : escaped_printf (out, " <enum name=\"%s\" get-type=\"%s\">\n",
504 : 0 : g_type_name (type), symbol);
505 : :
506 : 0 : for (i = 0; i < klass->n_values; i++)
507 : : {
508 : 0 : GEnumValue *value = &(klass->values[i]);
509 : :
510 : 0 : escaped_printf (out, " <member name=\"%s\" nick=\"%s\" value=\"%d\"/>\n",
511 : 0 : value->value_name, value->value_nick, value->value);
512 : 0 : }
513 : 0 : goutput_write (out, " </enum>");
514 : 0 : }
515 : :
516 : : static void
517 : 0 : dump_fundamental_type (GType type, const char *symbol, FILE *out)
518 : : {
519 : : unsigned int n_interfaces;
520 : : unsigned int i;
521 : : GType *interfaces;
522 : : GString *parent_str;
523 : : GType parent;
524 : 0 : gboolean first = TRUE;
525 : :
526 : :
527 : 0 : escaped_printf (out, " <fundamental name=\"%s\" get-type=\"%s\"",
528 : 0 : g_type_name (type), symbol);
529 : :
530 : 0 : if (G_TYPE_IS_ABSTRACT (type))
531 : 0 : escaped_printf (out, " abstract=\"1\"");
532 : :
533 : 0 : if (G_TYPE_IS_FINAL (type))
534 : 0 : escaped_printf (out, " final=\"1\"");
535 : :
536 : 0 : if (G_TYPE_IS_INSTANTIATABLE (type))
537 : 0 : escaped_printf (out, " instantiatable=\"1\"");
538 : :
539 : 0 : parent = g_type_parent (type);
540 : 0 : parent_str = g_string_new ("");
541 : 0 : while (parent != G_TYPE_INVALID)
542 : : {
543 : 0 : if (first)
544 : 0 : first = FALSE;
545 : : else
546 : 0 : g_string_append_c (parent_str, ',');
547 : 0 : if (!g_type_name (parent))
548 : 0 : break;
549 : 0 : g_string_append (parent_str, g_type_name (parent));
550 : 0 : parent = g_type_parent (parent);
551 : : }
552 : :
553 : 0 : if (parent_str->len > 0)
554 : 0 : escaped_printf (out, " parents=\"%s\"", parent_str->str);
555 : 0 : g_string_free (parent_str, TRUE);
556 : :
557 : 0 : goutput_write (out, ">\n");
558 : :
559 : 0 : interfaces = g_type_interfaces (type, &n_interfaces);
560 : 0 : for (i = 0; i < n_interfaces; i++)
561 : : {
562 : 0 : GType itype = interfaces[i];
563 : 0 : escaped_printf (out, " <implements name=\"%s\"/>\n",
564 : 0 : g_type_name (itype));
565 : 0 : }
566 : 0 : g_free (interfaces);
567 : 0 : goutput_write (out, " </fundamental>\n");
568 : 0 : }
569 : :
570 : : static void
571 : 8 : dump_type (GType type, const char *symbol, FILE *out)
572 : : {
573 : 8 : switch (g_type_fundamental (type))
574 : : {
575 : 2 : case G_TYPE_OBJECT:
576 : 4 : dump_object_type (type, symbol, out);
577 : 4 : break;
578 : 2 : case G_TYPE_INTERFACE:
579 : 4 : dump_interface_type (type, symbol, out);
580 : 4 : break;
581 : 0 : case G_TYPE_BOXED:
582 : 0 : dump_boxed_type (type, symbol, out);
583 : 0 : break;
584 : 0 : case G_TYPE_FLAGS:
585 : 0 : dump_flags_type (type, symbol, out);
586 : 0 : break;
587 : 0 : case G_TYPE_ENUM:
588 : 0 : dump_enum_type (type, symbol, out);
589 : 0 : break;
590 : 0 : case G_TYPE_POINTER:
591 : 0 : dump_pointer_type (type, symbol, out);
592 : 0 : break;
593 : 0 : default:
594 : 0 : dump_fundamental_type (type, symbol, out);
595 : 0 : break;
596 : : }
597 : 8 : }
598 : :
599 : : static void
600 : 0 : dump_error_quark (GQuark quark, const char *symbol, FILE *out)
601 : : {
602 : 0 : escaped_printf (out, " <error-quark function=\"%s\" domain=\"%s\"/>\n",
603 : 0 : symbol, g_quark_to_string (quark));
604 : 0 : }
605 : :
606 : : /**
607 : : * gi_repository_dump:
608 : : * @input_filename: (type filename): Input filename (for example `input.txt`)
609 : : * @output_filename: (type filename): Output filename (for example `output.xml`)
610 : : * @error: a %GError
611 : : *
612 : : * Dump the introspection data from the types specified in @input_filename to
613 : : * @output_filename.
614 : : *
615 : : * The input file should be a
616 : : * UTF-8 Unix-line-ending text file, with each line containing either
617 : : * `get-type:` followed by the name of a [type@GObject.Type] `_get_type`
618 : : * function, or `error-quark:` followed by the name of an error quark function.
619 : : * No extra whitespace is allowed.
620 : : *
621 : : * This function will overwrite the contents of the output file.
622 : : *
623 : : * Returns: true on success, false on error
624 : : * Since: 2.80
625 : : */
626 : : #ifndef GI_COMPILATION
627 : : static gboolean
628 : : dump_irepository (const char *input_filename,
629 : : const char *output_filename,
630 : : GError **error) G_GNUC_UNUSED;
631 : : static gboolean
632 : 0 : dump_irepository (const char *input_filename,
633 : : const char *output_filename,
634 : : GError **error)
635 : : #else
636 : : gboolean
637 : 10 : gi_repository_dump (const char *input_filename,
638 : : const char *output_filename,
639 : : GError **error)
640 : : #endif
641 : : {
642 : : GHashTable *output_types;
643 : : FILE *input;
644 : : FILE *output;
645 : : GModule *self;
646 : 10 : gboolean caught_error = FALSE;
647 : 10 : gboolean reached_eof = FALSE;
648 : :
649 : 10 : self = g_module_open (NULL, 0);
650 : 10 : if (!self)
651 : : {
652 : 0 : g_set_error (error,
653 : 0 : G_FILE_ERROR,
654 : : G_FILE_ERROR_FAILED,
655 : : "failed to open self: %s",
656 : 0 : g_module_error ());
657 : 0 : return FALSE;
658 : : }
659 : :
660 : 10 : input = g_fopen (input_filename, "rbe");
661 : 10 : if (input == NULL)
662 : : {
663 : 0 : int saved_errno = errno;
664 : 0 : g_set_error (error, G_FILE_ERROR, (int) g_file_error_from_errno (saved_errno),
665 : 0 : "Failed to open ‘%s’: %s", input_filename, g_strerror (saved_errno));
666 : :
667 : 0 : g_module_close (self);
668 : :
669 : 0 : return FALSE;
670 : : }
671 : :
672 : 10 : output = g_fopen (output_filename, "wbe");
673 : 10 : if (output == NULL)
674 : : {
675 : 0 : int saved_errno = errno;
676 : 0 : g_set_error (error, G_FILE_ERROR, (int) g_file_error_from_errno (saved_errno),
677 : 0 : "Failed to open ‘%s’: %s", output_filename, g_strerror (saved_errno));
678 : :
679 : 0 : fclose (input);
680 : 0 : g_module_close (self);
681 : :
682 : 0 : return FALSE;
683 : : }
684 : :
685 : 10 : goutput_write (output, "<?xml version=\"1.0\"?>\n");
686 : 10 : goutput_write (output, "<dump>\n");
687 : :
688 : 10 : output_types = g_hash_table_new (NULL, NULL);
689 : :
690 : 30 : while (!reached_eof)
691 : : {
692 : : size_t len;
693 : 24 : char *line = read_line (input, &len, &reached_eof, error);
694 : : const char *function;
695 : :
696 : 24 : if (line == NULL)
697 : : {
698 : 0 : caught_error = TRUE;
699 : 2 : break;
700 : : }
701 : :
702 : 24 : g_strchomp (line);
703 : :
704 : 24 : if (*line == '\0')
705 : 12 : goto next;
706 : :
707 : 16 : if (strncmp (line, "get-type:", strlen ("get-type:")) == 0)
708 : : {
709 : : GType type;
710 : :
711 : 10 : function = line + strlen ("get-type:");
712 : :
713 : 10 : type = invoke_get_type (self, function, error);
714 : :
715 : 10 : if (type == G_TYPE_INVALID)
716 : : {
717 : 2 : g_printerr ("Invalid GType function: '%s'\n", function);
718 : 2 : caught_error = TRUE;
719 : 2 : g_free (line);
720 : 2 : break;
721 : : }
722 : :
723 : 8 : if (g_hash_table_lookup (output_types, (gpointer) type))
724 : 0 : goto next;
725 : 8 : g_hash_table_insert (output_types, (gpointer) type, (gpointer) type);
726 : :
727 : 8 : dump_type (type, function, output);
728 : 4 : }
729 : 2 : else if (strncmp (line, "error-quark:", strlen ("error-quark:")) == 0)
730 : : {
731 : : GQuark quark;
732 : 2 : function = line + strlen ("error-quark:");
733 : 2 : quark = invoke_error_quark (self, function, error);
734 : :
735 : 2 : if (quark == 0)
736 : : {
737 : 2 : g_printerr ("Invalid error quark function: '%s'\n", function);
738 : 2 : caught_error = TRUE;
739 : 2 : g_free (line);
740 : 2 : break;
741 : : }
742 : :
743 : 0 : dump_error_quark (quark, function, output);
744 : 0 : }
745 : :
746 : :
747 : 0 : next:
748 : 20 : g_free (line);
749 : : }
750 : :
751 : 10 : g_hash_table_destroy (output_types);
752 : :
753 : 10 : goutput_write (output, "</dump>\n");
754 : :
755 : : {
756 : : /* Avoid overwriting an earlier set error */
757 : 10 : if (fclose (input) != 0 && !caught_error)
758 : : {
759 : 0 : int saved_errno = errno;
760 : :
761 : 0 : g_set_error (error, G_FILE_ERROR, (int) g_file_error_from_errno (saved_errno),
762 : 0 : "Error closing input file ‘%s’: %s", input_filename,
763 : 0 : g_strerror (saved_errno));
764 : 0 : caught_error = TRUE;
765 : 0 : }
766 : :
767 : 10 : if (fclose (output) != 0 && !caught_error)
768 : : {
769 : 0 : int saved_errno = errno;
770 : :
771 : 0 : g_set_error (error, G_FILE_ERROR, (int) g_file_error_from_errno (saved_errno),
772 : 0 : "Error closing output file ‘%s’: %s", output_filename,
773 : 0 : g_strerror (saved_errno));
774 : 0 : caught_error = TRUE;
775 : 0 : }
776 : : }
777 : :
778 : 10 : return !caught_error;
779 : 5 : }
|