1 /* GObject introspection: scanner
2  *
3  * Copyright (C) 2007-2008  Jürg Billeter
4  * Copyright (C) 2007  Johan Dahlin
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, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  *
21  * Author:
22  * 	Jürg Billeter <j@bitron.ch>
23  */
24 
25 #include <string.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <glib.h>
31 #include <glib/gstdio.h>
32 #include <glib-object.h>
33 #include <signal.h>
34 #include <gmodule.h>
35 #include "scanner.h"
36 #include "gidlparser.h"
37 #include "gidlmodule.h"
38 #include "gidlnode.h"
39 #include "gidlwriter.h"
40 #include "grealpath.h"
41 
42 #ifndef _WIN32
43 #include <sys/wait.h> /* waitpid */
44 #endif
45 
46 
47 typedef GType (*TypeFunction) (void);
48 
49 static void g_igenerator_parse_macros (GIGenerator * igenerator);
50 
51 static GIGenerator *
g_igenerator_new(const gchar * namespace,const gchar * shared_library)52 g_igenerator_new (const gchar *namespace,
53 		  const gchar *shared_library)
54 {
55   GIGenerator *igenerator = g_new0 (GIGenerator, 1);
56   igenerator->namespace = g_strdup (namespace);
57   igenerator->shared_library = g_strdup (shared_library);
58   igenerator->lower_case_namespace =
59     g_ascii_strdown (igenerator->namespace, -1);
60   igenerator->module = g_idl_module_new (namespace, shared_library);
61 
62   igenerator->typedef_table = g_hash_table_new (g_str_hash, g_str_equal);
63   igenerator->struct_or_union_or_enum_table =
64     g_hash_table_new (g_str_hash, g_str_equal);
65 
66   igenerator->type_map = g_hash_table_new (g_str_hash, g_str_equal);
67   igenerator->type_by_lower_case_prefix =
68     g_hash_table_new (g_str_hash, g_str_equal);
69   igenerator->symbols = g_hash_table_new (g_str_hash, g_str_equal);
70 
71   return igenerator;
72 }
73 
74 static void
g_igenerator_free(GIGenerator * generator)75 g_igenerator_free (GIGenerator *generator)
76 {
77   g_free (generator->namespace);
78   g_free (generator->shared_library);
79   g_free (generator->lower_case_namespace);
80 #if 0
81   g_idl_module_free (generator->module);
82 #endif
83   g_hash_table_destroy (generator->typedef_table);
84   g_hash_table_destroy (generator->struct_or_union_or_enum_table);
85   g_hash_table_destroy (generator->type_map);
86   g_hash_table_destroy (generator->type_by_lower_case_prefix);
87   g_hash_table_destroy (generator->symbols);
88   g_list_foreach (generator->filenames, (GFunc)g_free, NULL);
89   g_list_free (generator->filenames);
90 #if 0
91   g_list_foreach (generator->symbol_list, (GFunc)csymbol_free, NULL);
92   g_list_free (generator->symbol_list);
93 #endif
94   g_free (generator);
95 }
96 
97 static GIdlNodeType *
create_node_from_gtype(GType type_id)98 create_node_from_gtype (GType type_id)
99 {
100   GIdlNodeType *node;
101   GType fundamental;
102 
103   node = (GIdlNodeType *) g_idl_node_new (G_IDL_NODE_TYPE);
104 
105   fundamental = g_type_fundamental (type_id);
106   switch (fundamental)
107     {
108     case G_TYPE_STRING:
109       node->unparsed = g_strdup ("char*");
110       break;
111     case G_TYPE_INTERFACE:
112     case G_TYPE_BOXED:
113     case G_TYPE_OBJECT:
114       node->unparsed = g_strdup_printf ("%s*", g_type_name (type_id));
115       break;
116     case G_TYPE_PARAM:
117       node->unparsed = g_strdup ("GParamSpec*");
118       break;
119     default:
120       if (fundamental == G_TYPE_STRV)
121 	node->unparsed = g_strdup ("char*[]");
122       else
123 	node->unparsed = g_strdup (g_type_name (type_id));
124       break;
125     }
126 
127   return node;
128 }
129 
130 static GIdlNodeType *
create_node_from_ctype(CType * ctype)131 create_node_from_ctype (CType * ctype)
132 {
133   GIdlNodeType *node;
134 
135   node = (GIdlNodeType *) g_idl_node_new (G_IDL_NODE_TYPE);
136 
137   switch (ctype->type)
138     {
139     case CTYPE_VOID:
140       node->unparsed = g_strdup ("void");
141       break;
142     case CTYPE_BASIC_TYPE:
143       node->unparsed = g_strdup (ctype->name);
144       break;
145     case CTYPE_TYPEDEF:
146       node->unparsed = g_strdup (ctype->name);
147       break;
148     case CTYPE_STRUCT:
149       if (ctype->name == NULL)
150 	/* anonymous struct */
151 	node->unparsed = g_strdup ("gpointer");
152       else
153 	node->unparsed = g_strdup_printf ("struct %s", ctype->name);
154       break;
155     case CTYPE_UNION:
156       if (ctype->name == NULL)
157 	/* anonymous union */
158 	node->unparsed = g_strdup ("gpointer");
159       else
160 	node->unparsed = g_strdup_printf ("union %s", ctype->name);
161       break;
162     case CTYPE_ENUM:
163       if (ctype->name == NULL)
164 	/* anonymous enum */
165 	node->unparsed = g_strdup ("gint");
166       else
167 	node->unparsed = g_strdup_printf ("enum %s", ctype->name);
168       break;
169     case CTYPE_POINTER:
170       if (ctype->base_type->type == CTYPE_FUNCTION)
171 	/* anonymous function pointer */
172 	node->unparsed = g_strdup ("GCallback");
173       else
174 	{
175 	  GIdlNodeType *gibasetype = create_node_from_ctype (ctype->base_type);
176 	  node->unparsed = g_strdup_printf ("%s*", gibasetype->unparsed);
177 	}
178       break;
179     case CTYPE_ARRAY:
180       {
181 	GIdlNodeType *gibasetype = create_node_from_ctype (ctype->base_type);
182 	node->unparsed = g_strdup_printf ("%s[]", gibasetype->unparsed);
183 	break;
184       }
185     default:
186       node->unparsed = g_strdup ("unknown");
187       break;
188     }
189 
190   return node;
191 }
192 
193 static char *
str_replace(const char * str,const char * needle,const char * replacement)194 str_replace (const char *str, const char *needle, const char *replacement)
195 {
196   char **strings = g_strsplit (str, needle, 0);
197   char *result = g_strjoinv (replacement, strings);
198   g_strfreev (strings);
199   return result;
200 }
201 
202 static void
g_igenerator_process_properties(GIGenerator * igenerator,GIdlNodeInterface * node,GType type_id)203 g_igenerator_process_properties (GIGenerator * igenerator,
204 				 GIdlNodeInterface * node, GType type_id)
205 {
206   int i;
207   guint n_properties;
208   GParamSpec **properties;
209 
210   if (node->node.type == G_IDL_NODE_OBJECT)
211     {
212       GObjectClass *type_class = g_type_class_ref (type_id);
213       properties = g_object_class_list_properties (type_class, &n_properties);
214     }
215   else if (node->node.type == G_IDL_NODE_INTERFACE)
216     {
217       GTypeInterface *iface = g_type_default_interface_ref (type_id);
218       properties = g_object_interface_list_properties (iface, &n_properties);
219     }
220   else
221     {
222       g_assert_not_reached ();
223     }
224 
225   for (i = 0; i < n_properties; i++)
226     {
227       GIdlNodeProperty *giprop;
228 
229       /* ignore inherited properties */
230       if (properties[i]->owner_type != type_id)
231 	{
232 	  continue;
233 	}
234       giprop = (GIdlNodeProperty *) g_idl_node_new (G_IDL_NODE_PROPERTY);
235       giprop->node.name = properties[i]->name;
236       node->members =
237 	g_list_insert_sorted (node->members, giprop,
238 			      (GCompareFunc) g_idl_node_cmp);
239       giprop->type = create_node_from_gtype (properties[i]->value_type);
240       giprop->readable = (properties[i]->flags & G_PARAM_READABLE) != 0;
241       giprop->writable = (properties[i]->flags & G_PARAM_WRITABLE) != 0;
242       giprop->construct = (properties[i]->flags & G_PARAM_CONSTRUCT) != 0;
243       giprop->construct_only =
244 	(properties[i]->flags & G_PARAM_CONSTRUCT_ONLY) != 0;
245     }
246 }
247 
248 static void
g_igenerator_process_signals(GIGenerator * igenerator,GIdlNodeInterface * node,GType type_id)249 g_igenerator_process_signals (GIGenerator * igenerator,
250 			      GIdlNodeInterface * node, GType type_id)
251 {
252   int i, j;
253   guint n_signal_ids;
254   guint *signal_ids = g_signal_list_ids (type_id, &n_signal_ids);
255 
256   for (i = 0; i < n_signal_ids; i++)
257     {
258       GSignalQuery signal_query;
259       GIdlNodeSignal *gisig;
260       GIdlNodeParam *giparam;
261 
262       g_signal_query (signal_ids[i], &signal_query);
263       gisig = (GIdlNodeSignal *) g_idl_node_new (G_IDL_NODE_SIGNAL);
264       gisig->node.name = g_strdup (signal_query.signal_name);
265       node->members =
266 	g_list_insert_sorted (node->members, gisig,
267 			      (GCompareFunc) g_idl_node_cmp);
268 
269       gisig->run_first =
270 	(signal_query.signal_flags & G_SIGNAL_RUN_FIRST) != 0;
271       gisig->run_last = (signal_query.signal_flags & G_SIGNAL_RUN_LAST) != 0;
272       gisig->run_cleanup =
273 	(signal_query.signal_flags & G_SIGNAL_RUN_CLEANUP) != 0;
274 
275       /* add sender parameter */
276       giparam = (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM);
277       gisig->parameters = g_list_append (gisig->parameters, giparam);
278       giparam->node.name = g_strdup ("object");
279       giparam->type = create_node_from_gtype (type_id);
280 
281       for (j = 0; j < signal_query.n_params; j++)
282 	{
283 	  giparam = (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM);
284 	  gisig->parameters = g_list_append (gisig->parameters, giparam);
285 	  giparam->node.name = g_strdup_printf ("p%d", j);
286 	  giparam->type = create_node_from_gtype (signal_query.param_types[j]);
287 	}
288       gisig->result = (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM);
289       gisig->result->type = create_node_from_gtype (signal_query.return_type);
290     }
291 }
292 
293 static const gchar *
lookup_symbol(GIGenerator * igenerator,const gchar * typename)294 lookup_symbol (GIGenerator *igenerator, const gchar *typename)
295 {
296   const gchar *name =
297     g_hash_table_lookup (igenerator->symbols, typename);
298 
299   if (!name)
300     {
301       g_printerr ("Unknown symbol: %s\n", typename);
302       return typename;
303     }
304 
305   return name;
306 }
307 
308 static void
g_igenerator_create_object(GIGenerator * igenerator,const char * symbol_name,GType type_id,char * lower_case_prefix)309 g_igenerator_create_object (GIGenerator *igenerator,
310 			    const char *symbol_name,
311 			    GType type_id,
312 			    char *lower_case_prefix)
313 
314 {
315   char *alt_lower_case_prefix;
316   GIdlNodeInterface *node;
317   guint n_type_interfaces;
318   GType *type_interfaces;
319   int i;
320 
321   node = (GIdlNodeInterface *) g_idl_node_new (G_IDL_NODE_OBJECT);
322   node->node.name = g_strdup (g_type_name (type_id));
323   igenerator->module->entries =
324     g_list_insert_sorted (igenerator->module->entries, node,
325 			  (GCompareFunc) g_idl_node_cmp);
326   g_hash_table_insert (igenerator->type_map, node->node.name,
327 		       node);
328   g_hash_table_insert (igenerator->type_by_lower_case_prefix,
329 		       lower_case_prefix, node);
330   alt_lower_case_prefix = g_ascii_strdown (node->node.name, -1);
331 
332   if (strcmp (alt_lower_case_prefix, lower_case_prefix) != 0)
333     {
334       /* alternative prefix sometimes necessary, for example
335        * for GdkWindow
336        */
337       g_hash_table_insert (igenerator->type_by_lower_case_prefix,
338 			   alt_lower_case_prefix, node);
339     }
340   else
341     {
342       g_free (alt_lower_case_prefix);
343     }
344 
345   node->gtype_name = node->node.name;
346   node->gtype_init = g_strdup (symbol_name);
347   node->parent = g_strdup (lookup_symbol (igenerator,
348 					    g_type_name (g_type_parent (type_id))));
349 
350   type_interfaces = g_type_interfaces (type_id, &n_type_interfaces);
351   for (i = 0; i < n_type_interfaces; i++)
352     {
353       char *iface_name =
354 	g_strdup (g_type_name (type_interfaces[i]));
355       /* workaround for AtkImplementorIface */
356       if (g_str_has_suffix (iface_name, "Iface"))
357 	{
358 	  iface_name[strlen (iface_name) - strlen ("Iface")] =
359 	    '\0';
360 	}
361       node->interfaces =
362 	g_list_append (node->interfaces, iface_name);
363     }
364 
365   g_hash_table_insert (igenerator->symbols,
366 		       g_strdup (node->gtype_name),
367 		       /* FIXME: Strip igenerator->namespace */
368 		       g_strdup (node->node.name));
369 
370   g_igenerator_process_properties (igenerator, node, type_id);
371   g_igenerator_process_signals (igenerator, node, type_id);
372 }
373 
374 static void
g_igenerator_create_interface(GIGenerator * igenerator,const char * symbol_name,GType type_id,char * lower_case_prefix)375 g_igenerator_create_interface (GIGenerator *igenerator,
376 			       const char *symbol_name,
377 			       GType type_id,
378 			       char *lower_case_prefix)
379 
380 {
381   GIdlNodeInterface *node;
382   gboolean is_gobject = FALSE;
383   guint n_iface_prereqs;
384   GType *iface_prereqs;
385   int i;
386 
387   node = (GIdlNodeInterface *) g_idl_node_new (G_IDL_NODE_INTERFACE);
388   node->node.name = g_strdup (g_type_name (type_id));
389 
390   /* workaround for AtkImplementorIface */
391   if (g_str_has_suffix (node->node.name, "Iface"))
392     {
393       node->node.name[strlen (node->node.name) -
394 			strlen ("Iface")] = '\0';
395     }
396   igenerator->module->entries =
397     g_list_insert_sorted (igenerator->module->entries, node,
398 			  (GCompareFunc) g_idl_node_cmp);
399   g_hash_table_insert (igenerator->type_map, node->node.name,
400 		       node);
401   g_hash_table_insert (igenerator->type_by_lower_case_prefix,
402 		       lower_case_prefix, node);
403   node->gtype_name = node->node.name;
404   node->gtype_init = g_strdup (symbol_name);
405 
406   iface_prereqs =
407     g_type_interface_prerequisites (type_id, &n_iface_prereqs);
408 
409   for (i = 0; i < n_iface_prereqs; i++)
410     {
411       if (g_type_fundamental (iface_prereqs[i]) == G_TYPE_OBJECT)
412 	{
413 	  is_gobject = TRUE;
414 	}
415       node->prerequisites =
416 	g_list_append (node->prerequisites,
417 		       g_strdup (g_type_name (iface_prereqs[i])));
418     }
419 
420   if (is_gobject)
421     g_igenerator_process_properties (igenerator, node, type_id);
422   else
423     g_type_default_interface_ref (type_id);
424 
425   g_igenerator_process_signals (igenerator, node, type_id);
426 }
427 
428 static void
g_igenerator_create_boxed(GIGenerator * igenerator,const char * symbol_name,GType type_id,char * lower_case_prefix)429 g_igenerator_create_boxed (GIGenerator *igenerator,
430 			   const char *symbol_name,
431 			   GType type_id,
432 			   char *lower_case_prefix)
433 {
434   GIdlNodeBoxed *node =
435     (GIdlNodeBoxed *) g_idl_node_new (G_IDL_NODE_BOXED);
436   node->node.name = g_strdup (g_type_name (type_id));
437   igenerator->module->entries =
438     g_list_insert_sorted (igenerator->module->entries, node,
439 			  (GCompareFunc) g_idl_node_cmp);
440   g_hash_table_insert (igenerator->type_map, node->node.name,
441 		       node);
442   g_hash_table_insert (igenerator->type_by_lower_case_prefix,
443 		       lower_case_prefix, node);
444   node->gtype_name = node->node.name;
445   node->gtype_init = g_strdup (symbol_name);
446 }
447 
448 static void
g_igenerator_create_enum(GIGenerator * igenerator,const char * symbol_name,GType type_id,char * lower_case_prefix)449 g_igenerator_create_enum (GIGenerator *igenerator,
450 			  const char *symbol_name,
451 			  GType type_id,
452 			  char *lower_case_prefix)
453 {
454   GIdlNodeEnum *node;
455   int i;
456   GEnumClass *type_class;
457 
458   node = (GIdlNodeEnum *) g_idl_node_new (G_IDL_NODE_ENUM);
459   node->node.name = g_strdup (g_type_name (type_id));
460   igenerator->module->entries =
461     g_list_insert_sorted (igenerator->module->entries, node,
462 			  (GCompareFunc) g_idl_node_cmp);
463   g_hash_table_insert (igenerator->type_map, node->node.name,
464 		       node);
465   g_hash_table_insert (igenerator->type_by_lower_case_prefix,
466 		       lower_case_prefix, node);
467   node->gtype_name = node->node.name;
468   node->gtype_init = g_strdup (symbol_name);
469 
470   type_class = g_type_class_ref (type_id);
471 
472   for (i = 0; i < type_class->n_values; i++)
473     {
474       GIdlNodeValue *gival =
475 	(GIdlNodeValue *) g_idl_node_new (G_IDL_NODE_VALUE);
476       node->values = g_list_append (node->values, gival);
477       gival->node.name =
478 	g_strdup (type_class->values[i].value_name);
479       gival->value = type_class->values[i].value;
480     }
481 }
482 
483 static void
g_igenerator_create_flags(GIGenerator * igenerator,const char * symbol_name,GType type_id,char * lower_case_prefix)484 g_igenerator_create_flags (GIGenerator *igenerator,
485 			   const char *symbol_name,
486 			   GType type_id,
487 			   char *lower_case_prefix)
488 {
489   GIdlNodeEnum *node;
490   GFlagsClass *type_class;
491   int i;
492 
493   node = (GIdlNodeEnum *) g_idl_node_new (G_IDL_NODE_FLAGS);
494   node->node.name = g_strdup (g_type_name (type_id));
495   igenerator->module->entries =
496     g_list_insert_sorted (igenerator->module->entries, node,
497 			  (GCompareFunc) g_idl_node_cmp);
498   g_hash_table_insert (igenerator->type_map, node->node.name,
499 		       node);
500   g_hash_table_insert (igenerator->type_by_lower_case_prefix,
501 		       lower_case_prefix, node);
502   node->gtype_name = node->node.name;
503   node->gtype_init = g_strdup (symbol_name);
504 
505   type_class = g_type_class_ref (type_id);
506 
507   for (i = 0; i < type_class->n_values; i++)
508     {
509       GIdlNodeValue *gival =
510 	(GIdlNodeValue *) g_idl_node_new (G_IDL_NODE_VALUE);
511       node->values = g_list_append (node->values, gival);
512       gival->node.name =
513 	g_strdup (type_class->values[i].value_name);
514       gival->value = type_class->values[i].value;
515     }
516 }
517 
518 static gboolean
g_igenerator_process_module_symbol(GIGenerator * igenerator,GModule * module,const gchar * symbol_name)519 g_igenerator_process_module_symbol (GIGenerator *igenerator,
520 				    GModule *module,
521 				    const gchar *symbol_name)
522 {
523   TypeFunction type_fun;
524   GType type_id;
525   GType type_fundamental;
526   char *lower_case_prefix;
527 
528   /* ignore already processed functions */
529   if (symbol_name == NULL)
530     return FALSE;
531 
532   if (!g_module_symbol (module,
533 			symbol_name,
534 			(gpointer *) & type_fun))
535     return FALSE;
536 
537   type_id = type_fun ();
538   type_fundamental = g_type_fundamental (type_id);
539   lower_case_prefix =
540     str_replace (g_strndup
541 		 (symbol_name,
542 		  strlen (symbol_name) - strlen ("_get_type")),
543 		 "_", "");
544 
545   switch (type_fundamental)
546     {
547     case G_TYPE_OBJECT:
548       g_igenerator_create_object (igenerator, symbol_name, type_id,
549 				  lower_case_prefix);
550       break;
551     case G_TYPE_INTERFACE:
552       g_igenerator_create_interface (igenerator, symbol_name, type_id,
553 				     lower_case_prefix);
554       break;
555     case G_TYPE_BOXED:
556       g_igenerator_create_boxed (igenerator, symbol_name, type_id,
557 				 lower_case_prefix);
558       break;
559     case G_TYPE_ENUM:
560       g_igenerator_create_enum (igenerator, symbol_name, type_id,
561 				lower_case_prefix);
562       break;
563     case G_TYPE_FLAGS:
564       g_igenerator_create_flags (igenerator, symbol_name, type_id,
565 				 lower_case_prefix);
566       break;
567     default:
568       break;
569     }
570   return TRUE;
571 }
572 
573 static void
g_igenerator_process_module(GIGenerator * igenerator,const gchar * filename)574 g_igenerator_process_module (GIGenerator * igenerator,
575 			     const gchar *filename)
576 {
577   GModule *module;
578   GList *l;
579 
580   module = g_module_open (filename,
581 			  G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
582 
583   if (module == NULL)
584     {
585       g_critical ("Couldn't open module: %s", filename);
586       return;
587     }
588 
589   for (l = igenerator->get_type_symbols; l != NULL; l = l->next)
590     {
591       if (g_igenerator_process_module_symbol (igenerator,
592 					      module, (const char *)l->data))
593 	/* symbol found, ignore in future iterations */
594 	l->data = NULL;
595     }
596 }
597 
598 static void
g_igenerator_process_function_symbol(GIGenerator * igenerator,CSymbol * sym)599 g_igenerator_process_function_symbol (GIGenerator * igenerator, CSymbol * sym)
600 {
601   GIdlNodeFunction *func;
602   char *last_underscore;
603   GList *param_l;
604   int i;
605   GSList *l;
606 
607   func = (GIdlNodeFunction *) g_idl_node_new (G_IDL_NODE_FUNCTION);
608 
609   /* check whether this is a type method */
610   last_underscore = strrchr (sym->ident, '_');
611 
612   while (last_underscore != NULL)
613     {
614       char *prefix;
615       GIdlNode *node;
616 
617       prefix = g_strndup (sym->ident, last_underscore - sym->ident);
618       prefix = str_replace (prefix, "_", "");
619 
620       node = g_hash_table_lookup (igenerator->type_by_lower_case_prefix,
621 				  prefix);
622       if (node != NULL )
623 	{
624 	  func->node.name = g_strdup (last_underscore + 1);
625 
626 	  /* ignore get_type functions in registered types */
627 	  if (strcmp (func->node.name, "get_type") == 0)
628 	    return;
629 
630 	  if ((node->type == G_IDL_NODE_OBJECT ||
631 	       node->type == G_IDL_NODE_BOXED) &&
632 	      g_str_has_prefix (func->node.name, "new"))
633 	    func->is_constructor = TRUE;
634 	  else
635 	    func->is_method = TRUE;
636 	  if (g_idl_node_can_have_member (node))
637 	    {
638 	      g_idl_node_add_member (node, func);
639 	      break;
640 	    }
641 	  else
642 	    {
643 	      /* reset function attributes */
644 	      g_free (func->node.name);
645 	      func->node.name = NULL;
646 	      func->is_constructor = FALSE;
647 	      func->is_method = FALSE;
648 	    }
649 	}
650       else if (strcmp (igenerator->lower_case_namespace, prefix) == 0)
651 	{
652 	  func->node.name = g_strdup (last_underscore + 1);
653 	  igenerator->module->entries =
654 	    g_list_insert_sorted (igenerator->module->entries, func,
655 				  (GCompareFunc) g_idl_node_cmp);
656 	  break;
657 	}
658       last_underscore =
659 	g_utf8_strrchr (sym->ident, last_underscore - sym->ident, '_');
660     }
661 
662   /* create a namespace function if no prefix matches */
663   if (func->node.name == NULL)
664     {
665       func->node.name = sym->ident;
666       func->is_constructor = FALSE;
667       func->is_method = FALSE;
668       igenerator->module->entries =
669 	g_list_insert_sorted (igenerator->module->entries, func,
670 			      (GCompareFunc) g_idl_node_cmp);
671     }
672 
673   func->symbol = sym->ident;
674   func->result = (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM);
675   func->result->type = create_node_from_ctype (sym->base_type->base_type);
676 
677   for (param_l = sym->base_type->child_list, i = 1; param_l != NULL;
678        param_l = param_l->next, i++)
679     {
680       CSymbol *param_sym = param_l->data;
681       GIdlNodeParam *param;
682 
683       param = (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM);
684       param->type = create_node_from_ctype (param_sym->base_type);
685 
686       if (param_sym->ident == NULL)
687 	param->node.name = g_strdup_printf ("p%d", i);
688       else
689 	param->node.name = param_sym->ident;
690 
691       func->parameters = g_list_append (func->parameters, param);
692     }
693 
694   for (l = sym->directives; l; l = l->next)
695     {
696       CDirective *directive = (CDirective*)l->data;
697 
698       if (!strcmp (directive->name, "deprecated"))
699 	func->deprecated = TRUE;
700       else
701 	g_printerr ("Unknown function directive: %s\n",
702 		    directive->name);
703     }
704 }
705 
706 static void
g_igenerator_process_unregistered_struct_typedef(GIGenerator * igenerator,CSymbol * sym,CType * struct_type)707 g_igenerator_process_unregistered_struct_typedef (GIGenerator * igenerator,
708 						  CSymbol * sym,
709 						  CType * struct_type)
710 {
711   GIdlNodeStruct *node =
712     (GIdlNodeStruct *) g_idl_node_new (G_IDL_NODE_STRUCT);
713   GList *member_l;
714   char *lower_case_prefix;
715 
716   node->node.name = sym->ident;
717   igenerator->module->entries =
718     g_list_insert_sorted (igenerator->module->entries, node,
719 			  (GCompareFunc) g_idl_node_cmp);
720   lower_case_prefix = g_ascii_strdown (sym->ident, -1);
721   g_hash_table_insert (igenerator->type_map, sym->ident, node);
722   g_hash_table_insert (igenerator->type_by_lower_case_prefix,
723 		       lower_case_prefix, node);
724 
725   for (member_l = struct_type->child_list; member_l != NULL;
726        member_l = member_l->next)
727     {
728       CSymbol *member = member_l->data;
729       GIdlNodeField *gifield =
730 	(GIdlNodeField *) g_idl_node_new (G_IDL_NODE_FIELD);
731 
732       node->members = g_list_append (node->members, gifield);
733       gifield->node.name = member->ident;
734       gifield->type = create_node_from_ctype (member->base_type);
735     }
736 }
737 
738 static void
g_igenerator_process_struct_typedef(GIGenerator * igenerator,CSymbol * sym)739 g_igenerator_process_struct_typedef (GIGenerator * igenerator, CSymbol * sym)
740 {
741   CType *struct_type = sym->base_type;
742   gboolean opaque_type = FALSE;
743   GIdlNode *type;
744 
745   if (struct_type->child_list == NULL)
746     {
747       CSymbol *struct_symbol;
748       g_assert (struct_type->name != NULL);
749       struct_symbol =
750 	g_hash_table_lookup (igenerator->struct_or_union_or_enum_table,
751 			     struct_type->name);
752       if (struct_symbol != NULL)
753 	{
754 	  struct_type = struct_symbol->base_type;
755 	}
756     }
757 
758   if (struct_type->child_list == NULL)
759     {
760       opaque_type = TRUE;
761     }
762 
763   type = g_hash_table_lookup (igenerator->type_map, sym->ident);
764   if (type != NULL)
765     {
766       /* struct of a GTypeInstance */
767       if (!opaque_type
768 	  && (type->type == G_IDL_NODE_OBJECT
769 	      || type->type == G_IDL_NODE_INTERFACE))
770 	{
771 	  GIdlNodeInterface *node = (GIdlNodeInterface *) type;
772 	  GList *member_l;
773 	  /* ignore first field => parent */
774 	  for (member_l = struct_type->child_list->next; member_l != NULL;
775 	       member_l = member_l->next)
776 	    {
777 	      CSymbol *member = member_l->data;
778 	      GIdlNodeField *gifield;
779 	      /* ignore private / reserved members */
780 	      if (member->ident[0] == '_'
781 		  || g_str_has_prefix (member->ident, "priv"))
782 		{
783 		  continue;
784 		}
785 	      gifield =
786 		(GIdlNodeField *) g_idl_node_new (G_IDL_NODE_FIELD);
787 	      node->members = g_list_append (node->members, gifield);
788 	      gifield->node.name = member->ident;
789 	      gifield->type = create_node_from_ctype (member->base_type);
790 	    }
791 	}
792       else if (type->type == G_IDL_NODE_BOXED)
793 	{
794 	  GIdlNodeBoxed *node = (GIdlNodeBoxed *) type;
795 	  GList *member_l;
796 	  for (member_l = struct_type->child_list; member_l != NULL;
797 	       member_l = member_l->next)
798 	    {
799 	      CSymbol *member = member_l->data;
800 	      GIdlNodeField *gifield =
801 		(GIdlNodeField *) g_idl_node_new (G_IDL_NODE_FIELD);
802 	      node->members = g_list_append (node->members, gifield);
803 	      gifield->node.name = member->ident;
804 	      gifield->type = create_node_from_ctype (member->base_type);
805 	    }
806 	}
807     }
808   else if (!opaque_type
809 	   && (g_str_has_suffix (sym->ident, "Class")
810 	       || g_str_has_suffix (sym->ident, "Iface")
811 	       || g_str_has_suffix (sym->ident, "Interface")))
812     {
813       char *base_name;
814       GList *member_l;
815       GIdlNodeInterface *node;
816 
817       if (g_str_has_suffix (sym->ident, "Interface"))
818 	{
819 	  base_name =
820 	    g_strndup (sym->ident,
821 		       strlen (sym->ident) - strlen ("Interface"));
822 	}
823       else
824 	{
825 	  base_name =
826 	    g_strndup (sym->ident, strlen (sym->ident) - strlen ("Class"));
827 	}
828       type = g_hash_table_lookup (igenerator->type_map, base_name);
829       if (type == NULL
830 	  || (type->type != G_IDL_NODE_OBJECT
831 	      && type->type != G_IDL_NODE_INTERFACE))
832 	{
833 	  g_igenerator_process_unregistered_struct_typedef (igenerator, sym,
834 							    struct_type);
835 	  return;
836 	}
837       node = (GIdlNodeInterface *) type;
838 
839       /* ignore first field => parent */
840       for (member_l = struct_type->child_list->next; member_l != NULL;
841 	   member_l = member_l->next)
842 	{
843 	  CSymbol *member = member_l->data;
844 	  /* ignore private / reserved members */
845 	  if (member->ident[0] == '_')
846 	    {
847 	      continue;
848 	    }
849 	  if (member->base_type->type == CTYPE_POINTER
850 	      && member->base_type->base_type->type == CTYPE_FUNCTION)
851 	    {
852 	      /* ignore default handlers of signals */
853 	      gboolean found_signal = FALSE;
854 	      GList *type_member_l;
855 	      GList *param_l;
856 	      int i;
857 	      GIdlNodeVFunc *givfunc;
858 
859 	      for (type_member_l = node->members; type_member_l != NULL;
860 		   type_member_l = type_member_l->next)
861 		{
862 		  GIdlNode *type_member = type_member_l->data;
863 		  char *normalized_name =
864 		    str_replace (type_member->name, "-", "_");
865 		  if (type_member->type == G_IDL_NODE_SIGNAL
866 		      && strcmp (normalized_name, member->ident) == 0)
867 		    {
868 		      GList *vfunc_param_l;
869 		      GList *sig_param_l;
870 		      GIdlNodeSignal *sig = (GIdlNodeSignal *) type_member;
871 		      found_signal = TRUE;
872 		      /* set signal parameter names */
873 		      for (vfunc_param_l =
874 			   member->base_type->base_type->child_list,
875 			   sig_param_l = sig->parameters;
876 			   vfunc_param_l != NULL && sig_param_l != NULL;
877 			   vfunc_param_l = vfunc_param_l->next, sig_param_l =
878 			   sig_param_l->next)
879 			{
880 			  CSymbol *vfunc_param = vfunc_param_l->data;
881 			  GIdlNodeParam *sig_param = sig_param_l->data;
882 			  if (vfunc_param->ident != NULL)
883 			    {
884 			      g_free (sig_param->node.name);
885 			      sig_param->node.name =
886 				g_strdup (vfunc_param->ident);
887 			    }
888 			}
889 		      break;
890 		    }
891 		}
892 	      if (found_signal)
893 		{
894 		  continue;
895 		}
896 
897 	      givfunc = (GIdlNodeVFunc *) g_idl_node_new (G_IDL_NODE_VFUNC);
898 	      givfunc->node.name = member->ident;
899 	      node->members =
900 		g_list_insert_sorted (node->members, givfunc,
901 				      (GCompareFunc) g_idl_node_cmp);
902 	      givfunc->result =
903 		(GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM);
904 	      givfunc->result->type =
905 		create_node_from_ctype (member->base_type->base_type->base_type);
906 	      for (param_l = member->base_type->base_type->child_list, i = 1;
907 		   param_l != NULL; param_l = param_l->next, i++)
908 		{
909 		  CSymbol *param_sym = param_l->data;
910 		  GIdlNodeParam *param =
911 		    (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM);
912 		  if (param_sym->ident == NULL)
913 		    {
914 		      param->node.name = g_strdup_printf ("p%d", i);
915 		    }
916 		  else
917 		    {
918 		      param->node.name = param_sym->ident;
919 		    }
920 		  param->type = create_node_from_ctype (param_sym->base_type);
921 		  givfunc->parameters =
922 		    g_list_append (givfunc->parameters, param);
923 		}
924 	    }
925 	}
926     }
927   else if (g_str_has_suffix (sym->ident, "Private"))
928     {
929       /* ignore private structs */
930     }
931   else
932     {
933       g_igenerator_process_unregistered_struct_typedef (igenerator, sym,
934 							struct_type);
935     }
936 }
937 
938 static void
g_igenerator_process_union_typedef(GIGenerator * igenerator,CSymbol * sym)939 g_igenerator_process_union_typedef (GIGenerator * igenerator, CSymbol * sym)
940 {
941   CType *union_type = sym->base_type;
942   gboolean opaque_type = FALSE;
943   GIdlNode *type;
944 
945   if (union_type->child_list == NULL)
946     {
947       CSymbol *union_symbol;
948       g_assert (union_type->name != NULL);
949       union_symbol =
950 	g_hash_table_lookup (igenerator->struct_or_union_or_enum_table,
951 			     union_type->name);
952       if (union_symbol != NULL)
953 	{
954 	  union_type = union_symbol->base_type;
955 	}
956     }
957   if (union_type->child_list == NULL)
958     {
959       opaque_type = TRUE;
960     }
961 
962   type = g_hash_table_lookup (igenerator->type_map, sym->ident);
963   if (type != NULL)
964     {
965       GIdlNodeBoxed *node;
966       GList *member_l;
967       g_assert (type->type == G_IDL_NODE_BOXED);
968       node = (GIdlNodeBoxed *) type;
969       for (member_l = union_type->child_list; member_l != NULL;
970 	   member_l = member_l->next)
971 	{
972 	  CSymbol *member = member_l->data;
973 	  GIdlNodeField *gifield =
974 	    (GIdlNodeField *) g_idl_node_new (G_IDL_NODE_FIELD);
975 	  node->members = g_list_append (node->members, gifield);
976 	  gifield->node.name = member->ident;
977 	  gifield->type = create_node_from_ctype (member->base_type);
978 	}
979     }
980   else
981     {
982       GIdlNodeUnion *node =
983 	(GIdlNodeUnion *) g_idl_node_new (G_IDL_NODE_UNION);
984       char *lower_case_prefix;
985       GList *member_l;
986 
987       node->node.name = sym->ident;
988       igenerator->module->entries =
989 	g_list_insert_sorted (igenerator->module->entries, node,
990 			      (GCompareFunc) g_idl_node_cmp);
991       lower_case_prefix = g_ascii_strdown (sym->ident, -1);
992       g_hash_table_insert (igenerator->type_map, sym->ident, node);
993       g_hash_table_insert (igenerator->type_by_lower_case_prefix,
994 			   lower_case_prefix, node);
995 
996       node->node.name = sym->ident;
997       for (member_l = union_type->child_list; member_l != NULL;
998 	   member_l = member_l->next)
999 	{
1000 	  CSymbol *member = member_l->data;
1001 	  GIdlNodeField *gifield =
1002 	    (GIdlNodeField *) g_idl_node_new (G_IDL_NODE_FIELD);
1003 	  node->members = g_list_append (node->members, gifield);
1004 	  gifield->node.name = member->ident;
1005 	  gifield->type = create_node_from_ctype (member->base_type);
1006 	}
1007     }
1008 }
1009 
1010 static void
g_igenerator_process_enum_typedef(GIGenerator * igenerator,CSymbol * sym)1011 g_igenerator_process_enum_typedef (GIGenerator * igenerator, CSymbol * sym)
1012 {
1013   CType *enum_type;
1014   GList *member_l;
1015   GIdlNodeEnum *node;
1016   CSymbol *enum_symbol;
1017 
1018   enum_type = sym->base_type;
1019   if (enum_type->child_list == NULL)
1020     {
1021       g_assert (enum_type->name != NULL);
1022       enum_symbol =
1023 	g_hash_table_lookup (igenerator->struct_or_union_or_enum_table,
1024 			     enum_type->name);
1025       if (enum_symbol != NULL)
1026 	{
1027 	  enum_type = enum_symbol->base_type;
1028 	}
1029     }
1030   if (enum_type->child_list == NULL)
1031     {
1032       /* opaque type */
1033       return;
1034     }
1035 
1036   node = g_hash_table_lookup (igenerator->type_map, sym->ident);
1037   if (node != NULL)
1038     {
1039       return;
1040     }
1041 
1042   node = (GIdlNodeEnum *) g_idl_node_new (G_IDL_NODE_ENUM);
1043   node->node.name = sym->ident;
1044   igenerator->module->entries =
1045     g_list_insert_sorted (igenerator->module->entries, node,
1046 			  (GCompareFunc) g_idl_node_cmp);
1047 
1048   for (member_l = enum_type->child_list; member_l != NULL;
1049        member_l = member_l->next)
1050     {
1051       CSymbol *member = member_l->data;
1052       GIdlNodeValue *gival =
1053 	(GIdlNodeValue *) g_idl_node_new (G_IDL_NODE_VALUE);
1054       node->values = g_list_append (node->values, gival);
1055       gival->node.name = member->ident;
1056       gival->value = member->const_int;
1057     }
1058 }
1059 
1060 static void
g_igenerator_process_function_typedef(GIGenerator * igenerator,CSymbol * sym)1061 g_igenerator_process_function_typedef (GIGenerator * igenerator,
1062 				       CSymbol * sym)
1063 {
1064   GList *param_l;
1065   int i;
1066 
1067   /* handle callback types */
1068   GIdlNodeFunction *gifunc =
1069     (GIdlNodeFunction *) g_idl_node_new (G_IDL_NODE_CALLBACK);
1070 
1071   gifunc->node.name = sym->ident;
1072   igenerator->module->entries =
1073     g_list_insert_sorted (igenerator->module->entries, gifunc,
1074 			  (GCompareFunc) g_idl_node_cmp);
1075 
1076   gifunc->symbol = sym->ident;
1077   gifunc->result = (GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM);
1078   gifunc->result->type =
1079     create_node_from_ctype (sym->base_type->base_type->base_type);
1080 
1081   for (param_l = sym->base_type->base_type->child_list, i = 1;
1082        param_l != NULL; param_l = param_l->next, i++)
1083     {
1084       CSymbol *param_sym = param_l->data;
1085       GIdlNodeParam *param =
1086 	(GIdlNodeParam *) g_idl_node_new (G_IDL_NODE_PARAM);
1087       if (param_sym->ident == NULL)
1088 	{
1089 	  param->node.name = g_strdup_printf ("p%d", i);
1090 	}
1091       else
1092 	{
1093 	  param->node.name = param_sym->ident;
1094 	}
1095       param->type = create_node_from_ctype (param_sym->base_type);
1096       gifunc->parameters = g_list_append (gifunc->parameters, param);
1097     }
1098 }
1099 
1100 static void
g_igenerator_process_constant(GIGenerator * igenerator,CSymbol * sym)1101 g_igenerator_process_constant (GIGenerator * igenerator, CSymbol * sym)
1102 {
1103   GIdlNodeConstant *giconst =
1104     (GIdlNodeConstant *) g_idl_node_new (G_IDL_NODE_CONSTANT);
1105   giconst->node.name = sym->ident;
1106   igenerator->module->entries =
1107     g_list_insert_sorted (igenerator->module->entries, giconst,
1108 			  (GCompareFunc) g_idl_node_cmp);
1109 
1110   giconst->type = (GIdlNodeType *) g_idl_node_new (G_IDL_NODE_TYPE);
1111   if (sym->const_int_set)
1112     {
1113       giconst->type->unparsed = g_strdup ("int");
1114       giconst->value = g_strdup_printf ("%d", sym->const_int);
1115     }
1116   else if (sym->const_string != NULL)
1117     {
1118       giconst->type->unparsed = g_strdup ("char*");
1119       giconst->value = sym->const_string;
1120     }
1121 }
1122 
1123 static void
g_igenerator_process_symbols(GIGenerator * igenerator)1124 g_igenerator_process_symbols (GIGenerator * igenerator)
1125 {
1126   GList *l;
1127   /* process type symbols first to ensure complete type hashtables */
1128   /* type symbols */
1129   for (l = igenerator->symbol_list; l != NULL; l = l->next)
1130     {
1131       CSymbol *sym = l->data;
1132       if (sym->ident[0] == '_')
1133 	{
1134 	  /* ignore private / reserved symbols */
1135 	  continue;
1136 	}
1137       if (sym->type == CSYMBOL_TYPE_TYPEDEF)
1138 	{
1139 	  if (sym->base_type->type == CTYPE_STRUCT)
1140 	    {
1141 	      g_igenerator_process_struct_typedef (igenerator, sym);
1142 	    }
1143 	  else if (sym->base_type->type == CTYPE_UNION)
1144 	    {
1145 	      g_igenerator_process_union_typedef (igenerator, sym);
1146 	    }
1147 	  else if (sym->base_type->type == CTYPE_ENUM)
1148 	    {
1149 	      g_igenerator_process_enum_typedef (igenerator, sym);
1150 	    }
1151 	  else if (sym->base_type->type == CTYPE_POINTER
1152 		   && sym->base_type->base_type->type == CTYPE_FUNCTION)
1153 	    {
1154 	      g_igenerator_process_function_typedef (igenerator, sym);
1155 	    }
1156 	  else
1157 	    {
1158 	      GIdlNodeStruct *node =
1159 		(GIdlNodeStruct *) g_idl_node_new (G_IDL_NODE_STRUCT);
1160 	      char *lower_case_prefix;
1161 
1162 	      node->node.name = sym->ident;
1163 	      igenerator->module->entries =
1164 		g_list_insert_sorted (igenerator->module->entries, node,
1165 				      (GCompareFunc) g_idl_node_cmp);
1166 	      lower_case_prefix = g_ascii_strdown (sym->ident, -1);
1167 	      g_hash_table_insert (igenerator->type_map, sym->ident, node);
1168 	      g_hash_table_insert (igenerator->type_by_lower_case_prefix,
1169 				   lower_case_prefix, node);
1170 	    }
1171 	}
1172     }
1173   /* other symbols */
1174   for (l = igenerator->symbol_list; l != NULL; l = l->next)
1175     {
1176       CSymbol *sym = l->data;
1177       if (sym->ident[0] == '_')
1178 	{
1179 	  /* ignore private / reserved symbols */
1180 	  continue;
1181 	}
1182       if (sym->type == CSYMBOL_TYPE_FUNCTION)
1183 	{
1184 	  g_igenerator_process_function_symbol (igenerator, sym);
1185 	}
1186       else if (sym->type == CSYMBOL_TYPE_CONST)
1187 	{
1188 	  g_igenerator_process_constant (igenerator, sym);
1189 	}
1190     }
1191 }
1192 
1193 void
g_igenerator_add_symbol(GIGenerator * igenerator,CSymbol * symbol)1194 g_igenerator_add_symbol (GIGenerator * igenerator, CSymbol * symbol)
1195 {
1196   GList *l;
1197 
1198   /* only add symbols of main file */
1199   gboolean found_filename = FALSE;
1200 
1201   if (igenerator->current_filename)
1202     {
1203       for (l = igenerator->filenames; l != NULL; l = l->next)
1204         {
1205           if (strcmp (l->data, igenerator->current_filename) == 0)
1206             {
1207 	      found_filename = TRUE;
1208               break;
1209             }
1210         }
1211     }
1212 
1213   symbol->directives = g_slist_reverse (igenerator->directives);
1214   igenerator->directives = NULL;
1215 
1216   /* that's not very optimized ! */
1217   for (l = igenerator->symbol_list; l != NULL; l = l->next)
1218     {
1219       CSymbol *other_symbol = (CSymbol *)l->data;
1220       if (g_str_equal (other_symbol->ident, symbol->ident)
1221           && other_symbol->type == symbol->type)
1222         {
1223           g_printerr ("Dropping %s duplicate\n", symbol->ident);
1224           return;
1225         }
1226   }
1227 
1228   if (found_filename || igenerator->macro_scan)
1229     {
1230       igenerator->symbol_list =
1231 	g_list_prepend (igenerator->symbol_list, symbol);
1232     }
1233 
1234   if (symbol->type == CSYMBOL_TYPE_TYPEDEF)
1235 
1236     {
1237       g_hash_table_insert (igenerator->typedef_table, symbol->ident, symbol);
1238     }
1239   else if (symbol->type == CSYMBOL_TYPE_STRUCT
1240 	   || symbol->type == CSYMBOL_TYPE_UNION
1241 	   || symbol->type == CSYMBOL_TYPE_ENUM)
1242     {
1243       g_hash_table_insert (igenerator->struct_or_union_or_enum_table,
1244 			   symbol->ident, symbol);
1245     }
1246 }
1247 
1248 gboolean
g_igenerator_is_typedef(GIGenerator * igenerator,const char * name)1249 g_igenerator_is_typedef (GIGenerator * igenerator, const char *name)
1250 {
1251   gboolean b = g_hash_table_lookup (igenerator->typedef_table, name) != NULL;
1252   return b;
1253 }
1254 
1255 static void
g_igenerator_generate(GIGenerator * igenerator,const gchar * filename,GList * libraries)1256 g_igenerator_generate (GIGenerator * igenerator,
1257 		       const gchar * filename,
1258 		       GList *libraries)
1259 {
1260   GList *l;
1261 
1262   for (l = igenerator->symbol_list; l != NULL; l = l->next)
1263     {
1264       CSymbol *sym = l->data;
1265       if (sym->type == CSYMBOL_TYPE_FUNCTION
1266 	  && g_str_has_suffix (sym->ident, "_get_type"))
1267 	{
1268 	  if (sym->base_type->child_list == NULL)
1269 	    {
1270 	      // ignore get_type functions with parameters
1271 	      igenerator->get_type_symbols =
1272 		g_list_prepend (igenerator->get_type_symbols, sym->ident);
1273 	    }
1274 	}
1275     }
1276 
1277   /* ensure to initialize GObject */
1278   g_type_class_ref (G_TYPE_OBJECT);
1279 
1280   for (l = libraries; l; l = l->next)
1281       g_igenerator_process_module (igenerator, (const gchar*)l->data);
1282 
1283   g_igenerator_process_symbols (igenerator);
1284 
1285   g_idl_writer_save_file (igenerator->module, filename);
1286 }
1287 
1288 static int
eat_hspace(FILE * f)1289 eat_hspace (FILE * f)
1290 {
1291   int c;
1292   do
1293     {
1294       c = fgetc (f);
1295     }
1296   while (c == ' ' || c == '\t');
1297   return c;
1298 }
1299 
1300 static int
eat_line(FILE * f,int c)1301 eat_line (FILE * f, int c)
1302 {
1303   while (c != EOF && c != '\n')
1304     {
1305       c = fgetc (f);
1306     }
1307   if (c == '\n')
1308     {
1309       c = fgetc (f);
1310       if (c == ' ' || c == '\t')
1311 	{
1312 	  c = eat_hspace (f);
1313 	}
1314     }
1315   return c;
1316 }
1317 
1318 static int
read_identifier(FILE * f,int c,char ** identifier)1319 read_identifier (FILE * f, int c, char **identifier)
1320 {
1321   GString *id = g_string_new ("");
1322   while (isalnum (c) || c == '_')
1323     {
1324       g_string_append_c (id, c);
1325       c = fgetc (f);
1326     }
1327   *identifier = g_string_free (id, FALSE);
1328   return c;
1329 }
1330 
1331 static void
g_igenerator_parse_macros(GIGenerator * igenerator)1332 g_igenerator_parse_macros (GIGenerator * igenerator)
1333 {
1334   GError *error = NULL;
1335   char *tmp_name = NULL;
1336   GList *l;
1337   FILE *fmacros =
1338     fdopen (g_file_open_tmp ("gen-introspect-XXXXXX.h", &tmp_name, &error),
1339 	    "w+");
1340   g_unlink (tmp_name);
1341 
1342   for (l = igenerator->filenames; l != NULL; l = l->next)
1343     {
1344       FILE *f = fopen (l->data, "r");
1345       int line = 1;
1346 
1347       GString *define_line;
1348       char *str;
1349       gboolean error_line = FALSE;
1350       int c = eat_hspace (f);
1351       while (c != EOF)
1352 	{
1353 	  if (c != '#')
1354 	    {
1355 	      /* ignore line */
1356 	      c = eat_line (f, c);
1357 	      line++;
1358 	      continue;
1359 	    }
1360 
1361 	  /* print current location */
1362 	  str = g_strescape (l->data, "");
1363 	  fprintf (fmacros, "# %d \"%s\"\n", line, str);
1364 	  g_free (str);
1365 
1366 	  c = eat_hspace (f);
1367 	  c = read_identifier (f, c, &str);
1368 	  if (strcmp (str, "define") != 0 || (c != ' ' && c != '\t'))
1369 	    {
1370 	      g_free (str);
1371 	      /* ignore line */
1372 	      c = eat_line (f, c);
1373 	      line++;
1374 	      continue;
1375 	    }
1376 	  g_free (str);
1377 	  c = eat_hspace (f);
1378 	  c = read_identifier (f, c, &str);
1379 	  if (strlen (str) == 0 || (c != ' ' && c != '\t' && c != '('))
1380 	    {
1381 	      g_free (str);
1382 	      /* ignore line */
1383 	      c = eat_line (f, c);
1384 	      line++;
1385 	      continue;
1386 	    }
1387 	  define_line = g_string_new ("#define ");
1388 	  g_string_append (define_line, str);
1389 	  g_free (str);
1390 	  if (c == '(')
1391 	    {
1392 	      while (c != ')')
1393 		{
1394 		  g_string_append_c (define_line, c);
1395 		  c = fgetc (f);
1396 		  if (c == EOF || c == '\n')
1397 		    {
1398 		      error_line = TRUE;
1399 		      break;
1400 		    }
1401 		}
1402 	      if (error_line)
1403 		{
1404 		  g_string_free (define_line, TRUE);
1405 		  /* ignore line */
1406 		  c = eat_line (f, c);
1407 		  line++;
1408 		  continue;
1409 		}
1410 
1411 	      g_assert (c == ')');
1412 	      g_string_append_c (define_line, c);
1413 	      c = fgetc (f);
1414 
1415 	      /* found function-like macro */
1416 	      fprintf (fmacros, "%s\n", define_line->str);
1417 
1418 	      g_string_free (define_line, TRUE);
1419 	      /* ignore rest of line */
1420 	      c = eat_line (f, c);
1421 	      line++;
1422 	      continue;
1423 	    }
1424 	  if (c != ' ' && c != '\t')
1425 	    {
1426 	      g_string_free (define_line, TRUE);
1427 	      /* ignore line */
1428 	      c = eat_line (f, c);
1429 	      line++;
1430 	      continue;
1431 	    }
1432 	  while (c != EOF && c != '\n')
1433 	    {
1434 	      g_string_append_c (define_line, c);
1435 	      c = fgetc (f);
1436 	      if (c == '\\')
1437 		{
1438 		  c = fgetc (f);
1439 		  if (c == '\n')
1440 		    {
1441 		      /* fold lines when seeing backslash new-line sequence */
1442 		      c = fgetc (f);
1443 		    }
1444 		  else
1445 		    {
1446 		      g_string_append_c (define_line, '\\');
1447 		    }
1448 		}
1449 	    }
1450 
1451 	  /* found object-like macro */
1452 	  fprintf (fmacros, "%s\n", define_line->str);
1453 
1454 	  c = eat_line (f, c);
1455 	  line++;
1456 	}
1457 
1458       fclose (f);
1459     }
1460 
1461   igenerator->macro_scan = TRUE;
1462   rewind (fmacros);
1463 
1464   g_igenerator_parse_file (igenerator, fmacros);
1465   fclose (fmacros);
1466 
1467   igenerator->macro_scan = FALSE;
1468 }
1469 
1470 static void
g_igenerator_add_module(GIGenerator * igenerator,GIdlModule * module)1471 g_igenerator_add_module (GIGenerator *igenerator,
1472 			 GIdlModule *module)
1473 {
1474   GList *l;
1475 
1476   for (l = module->entries; l; l = l->next)
1477     {
1478       GIdlNode *node = (GIdlNode*)l->data;
1479 
1480       if (node->type == G_IDL_NODE_OBJECT)
1481 	{
1482 	  GIdlNodeInterface *object = (GIdlNodeInterface*)node;
1483 	  gchar *name;
1484 	  if (strcmp(module->name, igenerator->namespace) == 0)
1485 	    name = g_strdup (node->name);
1486 	  else
1487 	    name = g_strdup_printf ("%s.%s", module->name, node->name);
1488 	  g_hash_table_insert (igenerator->symbols,
1489 			       g_strdup (object->gtype_name),
1490 			       name);
1491 	}
1492     }
1493 }
1494 
1495 static void
g_igenerator_add_include_idl(GIGenerator * igenerator,const gchar * filename)1496 g_igenerator_add_include_idl (GIGenerator *igenerator,
1497 			      const gchar *filename)
1498 {
1499   GList *l;
1500   GList *modules;
1501 
1502   GError *error = NULL;
1503 
1504   modules = g_idl_parse_file (filename, &error);
1505   if (error)
1506     {
1507       g_printerr ("An error occurred while parsing %s: %s\n",
1508 		  filename, error->message);
1509       return;
1510     }
1511 
1512   for (l = modules; l; l = l->next)
1513     {
1514       GIdlModule *module = (GIdlModule*)l->data;
1515       g_igenerator_add_module (igenerator, module);
1516     }
1517 }
1518 
1519 static FILE *
g_igenerator_start_preprocessor(GIGenerator * igenerator,GList * cpp_options)1520 g_igenerator_start_preprocessor (GIGenerator *igenerator,
1521 				 GList       *cpp_options)
1522 {
1523   int cpp_out = -1, cpp_in = -1;
1524   int cpp_argc = 0;
1525   char **cpp_argv;
1526   GList *l;
1527   GError *error = NULL;
1528   FILE *f, *out;
1529   GPid pid;
1530   int status = 0;
1531   int read_bytes;
1532   int i;
1533   char **buffer;
1534   int tmp;
1535   char *tmpname;
1536 
1537   cpp_argv = g_new0 (char *, g_list_length (cpp_options) + 5);
1538   cpp_argv[cpp_argc++] = "cpp";
1539   cpp_argv[cpp_argc++] = "-C";
1540 
1541   /* Disable GCC extensions as we cannot parse them yet */
1542   cpp_argv[cpp_argc++] = "-U__GNUC__";
1543 
1544   /* Help system headers cope with the lack of __GNUC__ by pretending to be lint */
1545   cpp_argv[cpp_argc++] = "-Dlint";
1546 
1547   for (l = cpp_options; l; l = l->next)
1548     cpp_argv[cpp_argc++] = (char*)l->data;
1549 
1550   cpp_argv[cpp_argc++] = NULL;
1551 
1552   if (igenerator->verbose)
1553     {
1554       GString *args = g_string_new ("");
1555 
1556       for (i = 0; i < cpp_argc - 1; i++)
1557 	{
1558 	  g_string_append (args, cpp_argv[i]);
1559 	  if (i < cpp_argc - 2)
1560 	    g_string_append_c (args, ' ');
1561 	}
1562 
1563       g_printf ("Executing '%s'\n", args->str);
1564       g_string_free (args, FALSE);
1565     }
1566   g_spawn_async_with_pipes (NULL, cpp_argv, NULL,
1567 			    G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
1568 			    NULL, NULL, &pid, &cpp_in, &cpp_out, NULL, &error);
1569 
1570   g_free (cpp_argv);
1571   if (error != NULL)
1572     {
1573       g_error ("%s", error->message);
1574       return NULL;
1575     }
1576 
1577   f = fdopen (cpp_in, "w");
1578 
1579   for (l = igenerator->filenames; l != NULL; l = l->next)
1580     {
1581       if (igenerator->verbose)
1582 	g_printf ("Pre-processing %s\n", (char*)l->data);
1583 
1584       fprintf (f, "#include <%s>\n", (char *) l->data);
1585 
1586     }
1587 
1588   fclose (f);
1589   close (cpp_in);
1590 
1591   tmp = g_file_open_tmp (NULL, &tmpname, &error);
1592   if (error != NULL)
1593     {
1594       g_error ("%s", error->message);
1595       return NULL;
1596     }
1597 
1598   buffer = g_malloc0 (4096 * sizeof (char));
1599 
1600   while (1)
1601     {
1602       read_bytes = read (cpp_out, buffer, 4096);
1603       if (read_bytes == 0)
1604 	break;
1605       write (tmp, buffer, read_bytes);
1606     }
1607 
1608   g_free (buffer);
1609 
1610   close (cpp_out);
1611 
1612 #ifndef _WIN32
1613   if (waitpid (pid, &status, 0) > 0)
1614 #else
1615   /* We don't want to include <windows.h> as it clashes horribly
1616    * with token names from scannerparser.h. So just declare
1617    * WaitForSingleObject, GetExitCodeProcess and INFINITE here.
1618    */
1619   extern unsigned long __stdcall WaitForSingleObject(void*, int);
1620   extern int __stdcall GetExitCodeProcess(void*, int*);
1621 #define INFINITE 0xffffffff
1622 
1623   WaitForSingleObject (pid, INFINITE);
1624 
1625   if (GetExitCodeProcess (pid, &status))
1626 #endif
1627     {
1628       if (status != 0)
1629 	{
1630 	  g_spawn_close_pid (pid);
1631 #ifndef _WIN32
1632 	  kill (pid, SIGKILL);
1633 #endif
1634 
1635 	  g_error ("cpp returned error code: %d\n", status);
1636 	  unlink (tmpname);
1637 	  g_free (tmpname);
1638 	  return NULL;
1639 	}
1640     }
1641 
1642   f = fdopen (tmp, "r");
1643   if (!f)
1644     {
1645       g_error ("%s", strerror (errno));
1646       unlink (tmpname);
1647       g_free (tmpname);
1648       return NULL;
1649     }
1650   rewind (f);
1651   unlink (tmpname);
1652   g_free (tmpname);
1653 
1654   return f;
1655 }
1656 
1657 
1658 void
g_igenerator_set_verbose(GIGenerator * igenerator,gboolean verbose)1659 g_igenerator_set_verbose (GIGenerator *igenerator,
1660 			  gboolean     verbose)
1661 {
1662   igenerator->verbose = verbose;
1663 }
1664 
1665 int
main(int argc,char ** argv)1666 main (int argc, char **argv)
1667 {
1668   GOptionContext *ctx;
1669   gchar *namespace = NULL;
1670   gchar *shared_library = NULL;
1671   gchar **include_idls = NULL;
1672   gchar *output = NULL;
1673   gboolean verbose = FALSE;
1674 
1675   GIGenerator *igenerator;
1676   int gopt_argc, i;
1677   char **gopt_argv;
1678   GList *filenames = NULL;
1679   GError *error = NULL;
1680   GList *l, *libraries = NULL;
1681   GList *cpp_options = NULL;
1682   char *buffer;
1683   size_t size;
1684   FILE *tmp;
1685   GOptionEntry entries[] =
1686     {
1687       { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
1688 	"Be verbose" },
1689       { "output", 'o', 0, G_OPTION_ARG_STRING, &output,
1690 	"write output here instead of stdout", "FILE" },
1691       { "namespace", 'n', 0, G_OPTION_ARG_STRING, &namespace,
1692 	"Namespace of the module, like 'Gtk'", "NAMESPACE" },
1693       { "shared-library", 0, 0, G_OPTION_ARG_FILENAME, &shared_library,
1694 	"Shared library which contains the symbols", "FILE" },
1695       { "include-idl", 0, 0, G_OPTION_ARG_STRING_ARRAY, &include_idls,
1696 	"Other gidls to include", "IDL" },
1697       { NULL }
1698     };
1699 
1700   gopt_argc = 1;
1701   gopt_argv = (char**)g_malloc (argc * sizeof (char*));
1702   gopt_argv[0] = argv[0];
1703 
1704   for (i = 1; i < argc; i++)
1705     {
1706       if (argv[i][0] == '-')
1707 	{
1708 	  switch (argv[i][1])
1709 	    {
1710 	    case 'I':
1711 	    case 'D':
1712 	    case 'U':
1713 	      cpp_options = g_list_prepend (cpp_options, g_strdup (argv[i]));
1714 	      break;
1715 	    case 'p':
1716 	      /*ignore -pthread*/
1717 	      if (0==strcmp("-pthread", argv[i]))
1718 		break;
1719 	    case 'm':
1720 	      /*ignore -mfpmath=sse -msse -msse2*/
1721 	      break;
1722 	    default:
1723 	      gopt_argv[gopt_argc++] = argv[i];
1724 	      break;
1725 	    }
1726 	}
1727       else if (g_str_has_suffix (argv[i], ".h"))
1728 	{
1729 	  gchar* filename;
1730 
1731 	  if (!g_path_is_absolute (argv[i]))
1732 	    {
1733 	      gchar *dir = g_get_current_dir ();
1734 	      filename = g_strdup_printf ("%s/%s", dir,
1735 					    argv[i]);
1736 	      g_free (dir);
1737 	    }
1738 	  else
1739 	    filename = g_strdup (argv[i]);
1740 
1741 	  filenames = g_list_append (filenames, g_realpath(filename));
1742 	  g_free(filename);
1743 	}
1744       else if (g_str_has_suffix (argv[i], ".la") ||
1745 	       g_str_has_suffix (argv[i], ".so") ||
1746 	       g_str_has_suffix (argv[i], ".dll"))
1747 	{
1748 	  libraries = g_list_prepend (libraries, g_strdup (argv[i]));
1749 	}
1750       else
1751 	{
1752 	  gopt_argv[gopt_argc++] = argv[i];
1753 	}
1754     }
1755 
1756   ctx = g_option_context_new ("");
1757   g_option_context_add_main_entries (ctx, entries, NULL);
1758 
1759   if (!g_option_context_parse (ctx, &gopt_argc, &gopt_argv, &error))
1760     {
1761       g_printerr ("Parsing error: %s\n", error->message);
1762       g_option_context_free (ctx);
1763       return 1;
1764     }
1765 
1766   g_free (gopt_argv);
1767   g_option_context_free (ctx);
1768 
1769   if (!namespace)
1770     {
1771       g_printerr ("ERROR: namespace must be specified\n");
1772       return 1;
1773     }
1774 
1775   igenerator = g_igenerator_new (namespace, shared_library);
1776 
1777   if (verbose)
1778     g_igenerator_set_verbose (igenerator, TRUE);
1779 
1780   if (!filenames)
1781     {
1782       g_printerr ("ERROR: Need at least one header file.\n");
1783       g_igenerator_free (igenerator);
1784       return 1;
1785     }
1786   igenerator->filenames = filenames;
1787   cpp_options = g_list_reverse (cpp_options);
1788   libraries = g_list_reverse (libraries);
1789 
1790   if (include_idls)
1791     {
1792       for (i = 0; i < g_strv_length (include_idls); i++)
1793 	g_igenerator_add_include_idl (igenerator, include_idls[i]);
1794     }
1795 
1796   tmp = g_igenerator_start_preprocessor (igenerator, cpp_options);
1797   if (!tmp)
1798     {
1799       g_error ("ERROR in pre-processor.\n");
1800       g_igenerator_free (igenerator);
1801       return 1;
1802     }
1803 
1804   if (!g_igenerator_parse_file (igenerator, tmp))
1805     {
1806       fclose (tmp);
1807       g_igenerator_free (igenerator);
1808       return 1;
1809     }
1810 
1811   g_igenerator_parse_macros (igenerator);
1812 
1813   g_igenerator_generate (igenerator, output, libraries);
1814 
1815   fclose (tmp);
1816   g_igenerator_free (igenerator);
1817 
1818   return 0;
1819 }
1820 
1821