Add new files from last commit
[gnome.gobject-introspection] / girepository / gdump.c
1 /* GObject introspection: Dump introspection data
2  *
3  * Copyright (C) 2008 Colin Walters <walters@verbum.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <stdlib.h>
22
23 #include <glib.h>
24 #include <glib-object.h>
25 #include <gio/gio.h>
26
27 #include "girepository.h"
28 #include "config.h"
29
30 #include <string.h>
31
32 static void
33 escaped_printf (GOutputStream *out, const char *fmt, ...)
34 {
35   char *str;
36   va_list args;
37   gsize written;
38   GError *error = NULL;
39
40   va_start (args, fmt);
41
42   str = g_markup_vprintf_escaped (fmt, args);
43   if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL, &error))
44     {
45       g_critical ("failed to write to iochannel: %s", error->message);
46       g_clear_error (&error);
47     }
48   g_free (str);
49
50   va_end (args);
51 }
52
53 static void
54 goutput_write (GOutputStream *out, const char *str)
55 {
56   gsize written;
57   GError *error = NULL;
58   if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL, &error))
59     {
60       g_critical ("failed to write to iochannel: %s", error->message);
61       g_clear_error (&error);
62     }
63 }
64
65 typedef GType (*GetTypeFunc)(void);
66
67 static GType
68 invoke_get_type (GModule *self, const char *symbol, GError **error)
69 {
70   GetTypeFunc sym;
71
72   if (!g_module_symbol (self, symbol, (void**)&sym))
73     {
74       g_set_error (error,
75                    G_IO_ERROR,
76                    G_IO_ERROR_FAILED,
77                    "Failed to find symbol '%s'", symbol);
78       return G_TYPE_INVALID;
79     }
80
81   return sym ();
82 }
83
84 static void
85 dump_properties (GType type, GOutputStream *out)
86 {
87   guint i;
88   guint n_properties;
89   GParamSpec **props;
90
91   if (G_TYPE_FUNDAMENTAL (type) == G_TYPE_OBJECT)
92     {
93       GObjectClass *klass;
94       klass = g_type_class_ref (type);
95       props = g_object_class_list_properties (klass, &n_properties);
96     }
97   else
98     {
99       void *klass;
100       klass = g_type_default_interface_ref (type);
101       props = g_object_interface_list_properties (klass, &n_properties);
102     }
103
104   for (i = 0; i < n_properties; i++)
105     {
106       GParamSpec *prop;
107
108       prop = props[i];
109       if (prop->owner_type != type)
110         continue;
111
112       escaped_printf (out, "    <property name=\"%s\" type=\"%s\" flags=\"%d\"/>\n",
113                       prop->name, g_type_name (prop->value_type), prop->flags);
114     }
115   g_free (props);
116 }
117
118 static void
119 dump_signals (GType type, GOutputStream *out)
120 {
121   guint i;
122   guint n_sigs;
123   guint *sig_ids;
124
125   sig_ids = g_signal_list_ids (type, &n_sigs);
126   for (i = 0; i < n_sigs; i++)
127     {
128       guint sigid;
129       GSignalQuery query;
130       guint j;
131
132       sigid = sig_ids[i];
133       g_signal_query (sigid, &query);
134
135       escaped_printf (out, "    <signal name=\"%s\" return=\"%s\">\n",
136                       query.signal_name, g_type_name (query.return_type));
137
138       for (j = 0; j < query.n_params; j++)
139         {
140           escaped_printf (out, "      <param type=\"%s\"/>\n",
141                           g_type_name (query.param_types[j]));
142         }
143       goutput_write (out, "    </signal>\n");
144     }
145 }
146
147 static void
148 dump_object_type (GType type, const char *symbol, GOutputStream *out)
149 {
150   guint n_interfaces;
151   guint i;
152   GType *interfaces;
153
154   escaped_printf (out, "  <class name=\"%s\" get-type=\"%s\"",
155                   g_type_name (type), symbol);
156   if (type != G_TYPE_OBJECT)
157     escaped_printf (out, " parent=\"%s\"", g_type_name (g_type_parent (type)));
158
159   if (G_TYPE_IS_ABSTRACT (type))
160     escaped_printf (out, " abstract=\"1\"");
161   goutput_write (out, ">\n");
162
163   interfaces = g_type_interfaces (type, &n_interfaces);
164   for (i = 0; i < n_interfaces; i++)
165     {
166       GType itype = interfaces[i];
167       escaped_printf (out, "    <implements name=\"%s\"/>\n",
168                       g_type_name (itype));
169     }
170   dump_properties (type, out);
171   dump_signals (type, out);
172   goutput_write (out, "  </class>\n");
173 }
174
175 static void
176 dump_interface_type (GType type, const char *symbol, GOutputStream *out)
177 {
178   guint n_interfaces;
179   guint i;
180   GType *interfaces;
181
182   escaped_printf (out, "  <interface name=\"%s\" get-type=\"%s\">\n",
183                   g_type_name (type), symbol);
184
185   interfaces = g_type_interface_prerequisites (type, &n_interfaces);
186   for (i = 0; i < n_interfaces; i++)
187     {
188       GType itype = interfaces[i];
189       escaped_printf (out, "    <extends>%s</extends>\n",
190                       g_type_name (itype));
191     }
192   dump_properties (type, out);
193   dump_signals (type, out);
194   goutput_write (out, "  </interface>\n");
195 }
196
197 static void
198 dump_boxed_type (GType type, const char *symbol, GOutputStream *out)
199 {
200   escaped_printf (out, "  <boxed name=\"%s\" get-type=\"%s\"/>\n",
201                   g_type_name (type), symbol);
202 }
203
204 static void
205 dump_flags_type (GType type, const char *symbol, GOutputStream *out)
206 {
207   guint i;
208   GFlagsClass *klass;
209
210   klass = g_type_class_ref (type);
211   escaped_printf (out, "  <flags name=\"%s\" get-type=\"%s\">\n",
212                   g_type_name (type), symbol);
213
214   for (i = 0; i < klass->n_values; i++)
215     {
216       GFlagsValue *value = &(klass->values[i]);
217
218       escaped_printf (out, "    <member name=\"%s\" nick=\"%s\" value=\"%d\"/>\n",
219                       value->value_name, value->value_nick, value->value);
220     }
221   goutput_write (out, "  </flags>\n");
222 }
223
224 static void
225 dump_enum_type (GType type, const char *symbol, GOutputStream *out)
226 {
227   guint i;
228   GEnumClass *klass;
229
230   klass = g_type_class_ref (type);
231   escaped_printf (out, "  <enum name=\"%s\" get-type=\"%s\">\n",
232                   g_type_name (type), symbol);
233
234   for (i = 0; i < klass->n_values; i++)
235     {
236       GEnumValue *value = &(klass->values[i]);
237
238       escaped_printf (out, "    <member name=\"%s\" nick=\"%s\" value=\"%d\"/>\n",
239                       value->value_name, value->value_nick, value->value);
240     }
241   goutput_write (out, "  </enum>");
242 }
243
244 static void
245 dump_type (GType type, const char *symbol, GOutputStream *out)
246 {
247   g_printerr ("processing %s\n", g_type_name (type));
248   switch (g_type_fundamental (type))
249     {
250     case G_TYPE_OBJECT:
251       dump_object_type (type, symbol, out);
252       break;
253     case G_TYPE_INTERFACE:
254       dump_interface_type (type, symbol, out);
255       break;
256     case G_TYPE_BOXED:
257       dump_boxed_type (type, symbol, out);
258       break;
259     case G_TYPE_FLAGS:
260       dump_flags_type (type, symbol, out);
261       break;
262     case G_TYPE_ENUM:
263       dump_enum_type (type, symbol, out);
264       break;
265     case G_TYPE_POINTER:
266       /* GValue, etc.  Just skip them. */
267       break;
268     default:
269       g_warning ("unhandled gtype %s", g_type_name (type));
270     }
271 }
272
273 /**
274  * g_irepository_dump:
275  * @arg: Comma-separated pair of input and output filenames
276  * @error: a %GError
277  *
278  * Argument specified is a comma-separated pair of filenames; i.e. of
279  * the form "input.txt,output.xml".  The input file should be a
280  * UTF-8 Unix-line-ending text file, with each line containing the name
281  * of a GType _get_type function.
282  *
283  * The output file should already exist, but be empty.  This function will
284  * overwrite its contents.
285  *
286  * Returns: %TRUE on success, %FALSE on error
287  */
288 gboolean
289 g_irepository_dump (const char *arg, GError **error)
290 {
291   GHashTable *output_types;
292   char **args;
293   GFile *input_file;
294   GFile *output_file;
295   GFileInputStream *input;
296   GFileOutputStream *output;
297   GDataInputStream *in;
298   GModule *self;
299   gboolean caught_error = FALSE;
300
301   self = g_module_open (NULL, 0);
302   if (!self)
303     {
304       g_set_error (error,
305                    G_IO_ERROR,
306                    G_IO_ERROR_FAILED,
307                    "failed to open self: %s",
308                    g_module_error ());
309       return FALSE;
310     }
311
312   args = g_strsplit (arg, ",", 2);
313
314   input_file = g_file_new_for_path (args[0]);
315   output_file = g_file_new_for_path (args[1]);
316
317   input = g_file_read (input_file, NULL, error);
318   if (input == NULL)
319     return FALSE;
320
321   output = g_file_replace (output_file, NULL, FALSE, 0, NULL, error);
322   if (output == NULL)
323     {
324       g_input_stream_close (G_INPUT_STREAM (input), NULL, NULL);
325       return FALSE;
326     }
327
328   goutput_write (G_OUTPUT_STREAM (output), "<?xml version=\"1.0\"?>\n");
329   goutput_write (G_OUTPUT_STREAM (output), "<dump>\n");
330
331   output_types = g_hash_table_new (NULL, NULL);
332
333   in = g_data_input_stream_new (G_INPUT_STREAM (input));
334   g_object_unref (input);
335
336   while (TRUE)
337     {
338       gsize len;
339       char *line = g_data_input_stream_read_line (in, &len, NULL, NULL);
340       GType type;
341
342       if (line == NULL || *line == '\0')
343         {
344           g_free (line);
345           break;
346         }
347
348       g_strchomp (line);
349       type = invoke_get_type (self, line, error);
350
351       if (type == G_TYPE_INVALID)
352         {
353           caught_error = TRUE;
354           g_free (line);
355           break;
356         }
357
358       if (g_hash_table_lookup (output_types, (gpointer) type))
359         goto next;
360       g_hash_table_insert (output_types, (gpointer) type, (gpointer) type);
361
362       dump_type (type, line, G_OUTPUT_STREAM (output));
363
364     next:
365       g_free (line);
366     }
367
368   g_hash_table_destroy (output_types);
369
370   goutput_write (G_OUTPUT_STREAM (output), "</dump>\n");
371
372   {
373     GError **ioerror;
374     /* Avoid overwriting an earlier set error */
375     if (caught_error)
376       ioerror = NULL;
377     else
378       ioerror = error;
379     if (!g_input_stream_close (G_INPUT_STREAM (in), NULL, ioerror))
380       return FALSE;
381     if (!g_output_stream_close (G_OUTPUT_STREAM (output), NULL, ioerror))
382       return FALSE;
383   }
384
385   return !caught_error;
386 }