1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*  transform.c
3  *	Copyright (C) 2006 Armin Burgmeier
4  *
5  *	This program is free software; you can redistribute it and/or modify
6  *	it under the terms of the GNU General Public License as published by
7  *	the Free Software Foundation; either version 2 of the License, or
8  *	(at your option) any later version.
9  *
10  *	This program 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
13  *	GNU Library General Public License for more details.
14  *
15  *	You should have received a copy of the GNU General Public License
16  *	along with this program; if not, write to the Free Software
17  *	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 #include "transform.h"
21 
22 #include <string.h>
23 #include <ctype.h>
24 
25 typedef struct _CgTransformParamGuess CgTransformParamGuess;
26 struct _CgTransformParamGuess
27 {
28 	const gchar *gtype;
29 	const gchar *paramspec;
30 };
31 
32 typedef struct _CgTransformGTypeGuess CgTransformGTypeGuess;
33 struct _CgTransformGTypeGuess
34 {
35 	const gchar *ctype;
36 	const gchar *gtype_prefix;
37 	const gchar *gtype_name;
38 };
39 
40 /* This function looks up a flag with the given abbrevation (which has not
41  * to be null-terminated) in the given flag list. */
42 static const CgElementEditorFlags *
cg_transform_lookup_flag(const CgElementEditorFlags * flags,gchar * abbrevation,size_t len)43 cg_transform_lookup_flag (const CgElementEditorFlags *flags,
44                           gchar *abbrevation,
45                           size_t len)
46 {
47 	const CgElementEditorFlags *flag;
48 	for (flag = flags; flag->name != NULL; ++ flag)
49 	{
50 		if (strncmp (flag->abbrevation, abbrevation, len) == 0)
51 		{
52 			if(flag->abbrevation[len] == '\0')
53 			{
54 				return flag;
55 			}
56 		}
57 	}
58 
59 	return NULL;
60 }
61 
62 /* This function tries to convert a native C Type like int, float or
63  * unsigned long into a GType like G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_ULONG. */
64 gboolean
cg_transform_default_c_type_to_g_type(const gchar * c_type,const gchar ** g_type_prefix,const gchar ** g_type_name)65 cg_transform_default_c_type_to_g_type (const gchar *c_type,
66                                        const gchar **g_type_prefix,
67                                        const gchar **g_type_name)
68 {
69 	static const CgTransformGTypeGuess DEFAULT_TYPES[] =
70 	{
71 		{ "int", "G", "INT" },
72 		{ "gint", "G", "INT" },
73 		{ "unsigned int", "G", "UINT" },
74 		{ "guint", "G", "UINT" },
75 		{ "char", "G", "CHAR" },
76 		{ "gchar", "G", "CHAR" },
77 		{ "unsigned char", "G", "UCHAR" },
78 		{ "guchar", "G", "UCHAR" },
79 		{ "long", "G", "LONG" },
80 		{ "glong", "G", "LONG" },
81 		{ "unsigned long", "G", "ULONG" },
82 		{ "gulong", "G", "ULONG" },
83 		{ "gint64", "G", "INT64" },
84 		{ "guint64", "G", "UINT64" },
85 		{ "float", "G", "FLOAT" },
86 		{ "double", "G", "DOUBLE" },
87 		{ "char*", "G", "STRING" },
88 		{ "gchar*", "G", "STRING" },
89 		{ "char *", "G", "STRING" },
90 		{ "gchar *", "G", "STRING" },
91 		{ "const char*", "G", "STRING" },
92 		{ "const gchar*", "G", "STRING" },
93 		{ "const char *", "G", "STRING" },
94 		{ "const gchar *", "G", "STRING" },
95 		{ "gpointer", "G", "POINTER" },
96 		{ "void*", "G", "POINTER" },
97 		{ "void *", "G", "POINTER" },
98 		{ "gconstpointer", "G", "POINTER" },
99 		{ "const void*", "G", "POINTER" },
100 		{ "const void *", "G", "POINTER" },
101 		{ "void", "G", "NONE" },
102 		{ "gboolean", "G", "BOOLEAN" },
103 		{ "GParamSpec*", "G", "PARAM" },
104 		{ "GParamSpec *", "G", "PARAM" },
105 		{ NULL, NULL, NULL }
106 	};
107 
108 	const CgTransformGTypeGuess *guess;
109 
110 	for (guess = DEFAULT_TYPES; guess->ctype != NULL; ++ guess)
111 	{
112 		if (strcmp (guess->ctype, c_type) == 0)
113 		{
114 			*g_type_prefix = guess->gtype_prefix;
115 			*g_type_name = guess->gtype_name;
116 			return TRUE;
117 		}
118 	}
119 
120 	return FALSE;
121 }
122 
123 /* This function tries to convert a c_type like GtkTreeIter to add a separator
124  * between each word, then convert to upper case or lower case. */
125 gchar *
cg_transform_custom_c_type(const gchar * c_type,gboolean upper_case,gchar separator)126 cg_transform_custom_c_type (const gchar *c_type,
127                             gboolean upper_case,
128                             gchar separator)
129 {
130 	GString *str;
131 	const gchar *pos;
132 	gchar (*tocase_func) (gchar);
133 
134 	if (upper_case)
135 		tocase_func = g_ascii_toupper;
136 	else
137 		tocase_func = g_ascii_tolower;
138 
139 	str = g_string_sized_new (128);
140 	for (pos = c_type; *pos != '\0'; ++ pos)
141 	{
142 		if (!g_ascii_isalnum (*pos))
143 			continue;
144 
145 		if (isupper (*pos) &&				/* Upper case only */
146 		    pos > c_type &&					/* Can't be first character, to check the previous character */
147 		    !isupper (*(pos-1)))			/* Previous character is not upper case */
148 		{
149 			/* This will add the separator if the previous character is
150 			 * not upper case and the current character is upper case */
151 			g_string_append_c (str, separator);
152 		}
153 		else if (isupper (*pos) &&			/* Upper case only */
154 		         pos-1 == c_type &&			/* Second character */
155 		         *(pos+1) != '\0' &&		/* Next character shouldn't be the last */
156 		         !isupper (*(pos+1)))		/* Next of next character is not upper case */
157 		{
158 			/* This will add the separator if the prefix is single character,
159 			 * the current character is upper case and the next character is not
160 			 * upper case
161 			 * In summary, this will catch single character prefixes
162 			 * Example: GObject -> G_OBJECT where O is the current character */
163 			g_string_append_c (str, separator);
164 		}
165 		else if (isupper (*pos) &&			/* Upper case only */
166 		         pos-1 > c_type &&			/* Can't be first two characters, to check the two previous characters */
167 		         isupper (*(pos-1)) &&		/* Previous character is upper case */
168 		         isupper (*(pos-2)) &&		/* Previous of previous character is upper case */
169 		         *(pos+1) != '\0' &&		/* Next character shouldn't be the last */
170 		         !isupper (*(pos+1)))		/* Next of next character is not upper case */
171 		{
172 			/* This will add the separator if there are three or more upper case
173 			 * (the last one is this character) and the next one is not upper case */
174 			/* Example: GtkUIManager -> GTK_UI_MANAGER where M is the current character */
175 			g_string_append_c (str, separator);
176 		}
177 
178 		g_string_append_c (str, tocase_func (*pos));
179 	}
180 
181 	return g_string_free (str, FALSE);
182 }
183 
184 /* This function tries to convert a custom c_type like GtkTreeIter to
185  * a gobject type like GTK_TYPE_TREE_ITER. It does this by parsing the C type.
186  * The code is mostly borrowed from old action-callbacks.c by Dave
187  * Huseby and Massimo Cora'. */
188 void
cg_transform_custom_c_type_to_g_type(const gchar * c_type,gchar ** g_type_prefix,gchar ** g_type_name,gchar ** g_func_prefix)189 cg_transform_custom_c_type_to_g_type (const gchar *c_type,
190                                       gchar **g_type_prefix,
191                                       gchar **g_type_name,
192                                       gchar **g_func_prefix)
193 {
194 	gchar *c_type_transformed, **c_type_split;
195 
196 	c_type_transformed = cg_transform_custom_c_type (c_type, TRUE, '_');
197 
198 	if (g_type_prefix != NULL || g_type_name != NULL)
199 	{
200 		c_type_split = g_strsplit (c_type_transformed, "_", 2);
201 
202 		/* If c_type is empty string, first string is NULL */
203 		if (c_type_split[0])
204 		{
205 			if (g_type_prefix != NULL)
206 				*g_type_prefix = c_type_split[0];
207 			else
208 				g_free (c_type_split[0]);
209 
210 			/* If prefix only, second string is NULL */
211 			if (c_type_split[1])
212 			{
213 				if (g_type_name != NULL)
214 					*g_type_name = c_type_split[1];
215 				else
216 					g_free (c_type_split[1]);
217 			}
218 			else if (g_type_name != NULL)
219 				*g_type_name = g_strdup ("");
220 		}
221 		else
222 		{
223 			if (g_type_prefix != NULL)
224 				*g_type_prefix = g_strdup ("");
225 
226 			if (g_type_name != NULL)
227 				*g_type_name = g_strdup ("");
228 		}
229 
230 		/* Free only the array */
231 		g_free (c_type_split);
232 	}
233 
234 	if (g_func_prefix != NULL)
235 		*g_func_prefix = g_ascii_strdown (c_type_transformed, -1);
236 
237 	g_free (c_type_transformed);
238 }
239 
240 /* This function tries to convert any possible C type to its corresponding
241  * GObject type. First, it looks whether the C type is a default type. If not,
242  * it strips leading const or a trailing * away (because it does not matter
243  * whether we have const GtkTreeIter* or GtkTreeIter, it all results in the
244  * same gobject type, namely GTK_TYPE_TREE_ITER) and calls
245  * cg_transform_custom_c_type_to_g_type. */
246 void
cg_transform_c_type_to_g_type(const gchar * c_type,gchar ** g_type_prefix,gchar ** g_type_name)247 cg_transform_c_type_to_g_type (const gchar *c_type,
248                                gchar **g_type_prefix,
249                                gchar **g_type_name)
250 {
251 	const gchar *default_prefix;
252 	const gchar *default_name;
253 	gchar *plain_c_type;
254 	gboolean result;
255 	size_t len;
256 
257 	result = cg_transform_default_c_type_to_g_type (c_type, &default_prefix,
258 	                                                &default_name);
259 
260 	if (result == TRUE)
261 	{
262 		*g_type_prefix = g_strdup (default_prefix);
263 		*g_type_name = g_strdup (default_name);
264 	}
265 	else
266 	{
267 		if (strncmp (c_type, "const ", 6) == 0)
268 			plain_c_type = g_strdup (c_type + 6);
269 		else
270 			plain_c_type = g_strdup (c_type);
271 
272 		len = strlen (plain_c_type);
273 		if (plain_c_type[len - 1] == '*')
274 		{
275 			plain_c_type[len - 1] = '\0';
276 			g_strchomp (plain_c_type);
277 		}
278 
279 		cg_transform_custom_c_type_to_g_type (plain_c_type, g_type_prefix,
280 		                                      g_type_name, NULL);
281 
282 		g_free (plain_c_type);
283 	}
284 }
285 
286 /* Looks up the given index in the hash table and removes enclosing quotes,
287  * if any. Those are added again by the autogen template. */
288 void
cg_transform_string(GHashTable * table,const gchar * index)289 cg_transform_string (GHashTable *table,
290                      const gchar *index)
291 {
292 	gchar *str;
293 	gchar *unescaped;
294 	size_t len;
295 
296 	str = g_hash_table_lookup (table, index);
297 
298 	if (str != NULL)
299 	{
300 		len = strlen (str);
301 		if (len >= 2 && str[0] == '\"' && str[len - 1] == '\"')
302 		{
303 			/* Unescape string because it was most likely already escaped
304 			 * by the user because s/he also added quotes around it. */
305 			str = g_strndup (str + 1, len - 2);
306 			unescaped = g_strcompress (str);
307 			g_free (str);
308 
309 			g_hash_table_insert (table, (gpointer) index, unescaped);
310 		}
311 	}
312 }
313 
314 /* Looks up the given index in the hash table which is assumed to be a string
315  * with '|'-separated abbrevations of the given flags. This function replaces
316  * the abbrevations by the full names. If no flags are set, it produces "0". */
317 void
cg_transform_flags(GHashTable * table,const gchar * index,const CgElementEditorFlags * flags)318 cg_transform_flags (GHashTable *table,
319                     const gchar *index,
320                     const CgElementEditorFlags *flags)
321 {
322 	const CgElementEditorFlags *flag;
323 	GString *res_str;
324 	gchar *flags_str;
325 	gchar *prev;
326 	gchar *pos;
327 
328 	flags_str = g_hash_table_lookup (table, index);
329 	res_str = g_string_sized_new (128);
330 
331 	if (flags_str != NULL)
332 	{
333 		prev = flags_str;
334 		pos = flags_str;
335 
336 		while (*prev != '\0')
337 		{
338 			while (*pos != '|' && *pos != '\0')
339 				++ pos;
340 
341 			flag = cg_transform_lookup_flag (flags, prev, pos - prev);
342 			g_assert (flag != NULL);
343 
344 			if (res_str->len > 0) g_string_append (res_str, " | ");
345 			g_string_append (res_str, flag->name);
346 
347 			if (*pos != '\0') ++ pos;
348 			prev = pos;
349 		}
350 	}
351 
352 	if (res_str->len == 0) g_string_append_c (res_str, '0');
353 	g_hash_table_insert (table, (gpointer) index,
354 	                     g_string_free (res_str, FALSE));
355 }
356 
357 /* Looks up the given param_index in the hash table. If it contains
358  * guess_entry, the value is replaced by guessing the param spec from
359  * the GObject type stored in type_index. */
360 void
cg_transform_guess_paramspec(GHashTable * table,const gchar * param_index,const gchar * type_index,const gchar * guess_entry)361 cg_transform_guess_paramspec (GHashTable *table,
362                               const gchar *param_index,
363                               const gchar *type_index,
364                               const gchar *guess_entry)
365 {
366 	const CgTransformParamGuess GUESS_TABLE[] =
367 	{
368 		{ "G_TYPE_BOOLEAN", "g_param_spec_boolean" },
369 		{ "G_TYPE_BOXED", "g_param_spec_boxed" },
370 		{ "G_TYPE_CHAR", "g_param_spec_char" },
371 		{ "G_TYPE_DOUBLE", "g_param_spec_double" },
372 		{ "G_TYPE_ENUM", "g_param_spec_enum" },
373 		{ "G_TYPE_FLAGS", "g_param_spec_flags" },
374 		{ "G_TYPE_FLOAT", "g_param_spec_float" },
375 		{ "G_TYPE_INT", "g_param_spec_int" },
376 		{ "G_TYPE_INT64", "g_param_spec_int64" },
377 		{ "G_TYPE_LONG", "g_param_spec_long" },
378 		{ "G_TYPE_OBJECT", "g_param_spec_object" },
379 		{ "G_TYPE_PARAM", "g_param_spec_param" },
380 		{ "G_TYPE_POINTER", "g_param_spec_pointer" },
381 		{ "G_TYPE_STRING", "g_param_spec_string" },
382 		{ "G_TYPE_UCHAR", "g_param_spec_uchar" },
383 		{ "G_TYPE_UINT", "g_param_spec_uint" },
384 		{ "G_TYPE_UINT64", "g_param_spec_uint64" },
385 		{ "G_TYPE_ULONG", "g_param_spec_ulong" },
386 		{ "G_TYPE_UNICHAR", "g_param_spec_unichar" },
387 		{ NULL, NULL }
388 	};
389 
390 	const CgTransformParamGuess *guess;
391 	gchar *paramspec;
392 	gchar *type;
393 
394 	paramspec = g_hash_table_lookup (table, param_index);
395 
396 	if (paramspec != NULL && strcmp (paramspec, guess_entry) == 0)
397 	{
398 		type = g_hash_table_lookup (table, type_index);
399 		if (type != NULL)
400 		{
401 			for (guess = GUESS_TABLE; guess->gtype != NULL; ++ guess)
402 			{
403 				if (strcmp (type, guess->gtype) == 0)
404 				{
405 					paramspec = g_strdup (guess->paramspec);
406 					break;
407 				}
408 			}
409 
410 			/* Not in list, so assume it is an object */
411 			if (guess->gtype == NULL)
412 				paramspec = g_strdup ("g_param_spec_object");
413 
414 			g_hash_table_insert (table, (gpointer) param_index, paramspec);
415 		}
416 	}
417 }
418 
419 /* This function looks up index in the given hash table and encloses it by
420  * surrounding parenthesis if they do not already exist and the field is
421  * non-empty. If make_void is TRUE and the arguments are only "()" it makes
422  * "(void)" out of it to stay ANSI C compliant. */
423 void
cg_transform_arguments(GHashTable * table,const gchar * index,gboolean make_void)424 cg_transform_arguments (GHashTable *table,
425                         const gchar *index,
426                         gboolean make_void)
427 {
428 	gchar *arg_res;
429 	gchar *arguments;
430 	size_t len;
431 
432 	arguments = g_hash_table_lookup (table, index);
433 
434 	if (arguments != NULL)
435 	{
436 		g_strstrip (arguments);
437 		len = strlen (arguments);
438 
439 		/* Do nothing if the field was left empty */
440 		if (len > 0)
441 		{
442 			/* Surround arguments with paranthesis if they do
443 			 * not already exist. */
444 			if (arguments[0] != '(' && arguments[len - 1] != ')')
445 				arg_res = g_strdup_printf ("(%s)", arguments);
446 			else if (arguments[0] != '(')
447 				arg_res = g_strdup_printf ("(%s", arguments);
448 			else if (arguments[len - 1] != ')')
449 				arg_res = g_strdup_printf ("%s)", arguments);
450 			else
451 				arg_res = NULL;
452 
453 			/* Set arguments to the transformed result, if a transformation
454 			 * has happend. */
455 			if (arg_res != NULL)
456 				arguments = arg_res;
457 
458 			if (make_void == TRUE)
459 			{
460 				/* Make "(void)" out of "()" if make_void is set. If this is
461 				 * the case, we do not need to store arg_res in the hash
462 				 * table lateron, so we delete it right here. */
463 				if (strcmp (arguments, "()") == 0)
464 				{
465 					g_hash_table_insert (table, (gpointer) index,
466 					                     g_strdup ("(void)"));
467 
468 					g_free (arg_res);
469 					arg_res = NULL;
470 				}
471 			}
472 
473 			if (arg_res != NULL)
474 				g_hash_table_insert (table, (gpointer) index, arg_res);
475 		}
476 	}
477 }
478 
479 /**
480  * Add a self reference to the arguments list, if necessary. Python only.
481  */
482 void
cg_transform_python_arguments(GHashTable * table,const gchar * index)483 cg_transform_python_arguments (GHashTable *table,
484 			       const gchar *index)
485 {
486 	gchar *arg_res;
487 	gchar *arguments;
488 	size_t len;
489 
490 	arguments = g_hash_table_lookup (table, index);
491 
492 	arg_res = NULL;
493 	if (arguments != NULL)
494 	{
495 		g_strstrip (arguments);
496 		len = strlen (arguments);
497 		/* Do nothing if the field was left empty */
498 		if (len > 0)
499 		{
500 			if (arguments[0] != '(')
501 			{
502 				/* Check if self is in arguments. If yes,
503 				 * cg_transform_arguments will take care of the
504 				 * rest, if not then add the self argument. */
505 				if (g_strcmp0 (arguments, "self") != 0)
506 				{
507 					g_hash_table_insert (table, (gpointer) index,
508 							     g_strdup_printf ("(self, %s)", arguments));
509 
510 					g_free (arg_res);
511 					arg_res = NULL;
512 				}
513 			}
514 			else
515 			{
516 				if (g_strcmp0 (arguments, "()") == 0)
517 				{
518 					g_hash_table_insert (table, (gpointer) index,
519 							     g_strdup ("(self)"));
520 
521 					g_free (arg_res);
522 					arg_res = NULL;
523 				}
524 			}
525 		}
526 		else
527 		{
528 			g_hash_table_insert (table, (gpointer) index,
529 						     g_strdup_printf ("%s", "(self)"));
530 			g_free (arg_res);
531 			arg_res = NULL;
532 		}
533 	}
534 
535 	cg_transform_arguments (table, index, FALSE);
536 }
537 
538 /* This function makes a valid C identifier out of a string. It does this
539  * by ignoring anything but digits, letters, hyphens and underscores. Digits
540  * at the beginning of the string are also ignored. Hpyhens are transformed
541  * to underscores. */
542 void
cg_transform_string_to_identifier(GHashTable * table,const gchar * string_index,const gchar * identifier_index)543 cg_transform_string_to_identifier (GHashTable *table,
544                                    const gchar *string_index,
545                                    const gchar *identifier_index)
546 {
547 	gchar *name;
548 	gchar *identifier_name;
549 	size_t name_len;
550 	size_t i, j;
551 
552 	name = g_hash_table_lookup (table, "Name");
553 	if (name != NULL)
554 	{
555 		name_len = strlen (name);
556 		identifier_name = g_malloc ((name_len + 1) * sizeof(gchar));
557 
558 		for (i = 0, j = 0; i < name_len; ++ i)
559 		{
560 			if (isupper (name[i]) || islower (name[i]))
561 				identifier_name[j ++] = name[i];
562 			else if (isdigit (name[i]) && j > 0)
563 				identifier_name[j ++] = name[i];
564 			else if (isspace (name[i]) || name[i] == '-' || name[i] == '_')
565 				identifier_name[j ++] = '_';
566 		}
567 
568 		identifier_name[j] = '\0';
569 
570 		g_hash_table_insert (table, (gpointer) identifier_index,
571 		                     identifier_name);
572 
573 		/* Ownership is given to hash table, so no g_free here. */
574 	}
575 }
576 
577 /* This function looks up the given index in the hash table and expects an
578  * argument list like cg_transform_arguments generates it. It then checks
579  * whether the first argument is of the given type. If it is, it does
580  * nothing, otherwise it adds the first argument to the argument list
581  * and writes the result back into the hash table. */
582 void
cg_transform_first_argument(GHashTable * table,const gchar * index,const gchar * type)583 cg_transform_first_argument (GHashTable *table,
584                              const gchar *index,
585                              const gchar *type)
586 {
587 	gchar *arguments;
588 	guint pointer_count;
589 	guint arg_pointer_count;
590 	size_t type_len;
591 	const gchar *type_pos;
592 	gchar *arg_pos;
593 	gchar *pointer_str;
594 	gboolean arg_present;
595 	guint i;
596 
597 	arguments = g_hash_table_lookup (table, index);
598 
599 	/* Count the length of the basic type */
600 	type_len = 0;
601 	while (isalnum (type[type_len])) ++ type_len;
602 	type_pos = type + type_len;
603 
604 	/* Count pointer indicators */
605 	pointer_count = 0;
606 	for (type_pos = type + type_len; *type_pos != '\0'; ++ type_pos)
607 		if (*type_pos == '*') ++ pointer_count;
608 
609 	/* Build a string of all pointer indicators that we can append to the
610 	 * basic type to get the final type. */
611 	pointer_str = g_malloc ((pointer_count + 2) * sizeof(gchar));
612 	pointer_str[0] = ' ';
613 	pointer_str[pointer_count + 1] = '\0';
614 	for (i = 0; i < pointer_count; ++ i) pointer_str[i + 1] = '*';
615 
616 	/* We do not just prepend type to the argument string because then
617 	 * we would have to fiddle around where spaces and pointer indicators
618 	 * belong. We can now always just build a string like
619 	 * BasicType+PointerStr+Name to get a fully qualified parameter like
620 	 * GtkTreeIter *iter, gint the_int or gchar **dest. */
621 
622 	if (arguments == NULL || *arguments == '\0')
623 	{
624 		arguments = g_strdup_printf ("(%.*s%sself)", (int)type_len,
625 		                             type, pointer_str);
626 
627 		g_hash_table_insert (table, (gpointer) index, arguments);
628 	}
629 	else
630 	{
631 		g_assert (arguments[0] == '(');
632 
633 		/* Step over '(' and any leading whitespace */
634 		++ arguments;
635 		while (isspace (*arguments)) ++ arguments;
636 
637 		arg_present = FALSE;
638 		if (strncmp (arguments, type, type_len) == 0)
639 		{
640 			/* We cannot just check (via string comparison) whether arguments
641 			 * begins with the whole type because the pointer indicator might
642 			 * be directly behind the basic type, but there might as well exist
643 			 * an arbitrary amount of spaces inbetween.
644 			 * GtkTreeIter* self vs. GtkTreeIter *self vs.
645 			 * GtkTreeIter     *self. */
646 			arg_pointer_count = 0;
647 			for (arg_pos = arguments + type_len;
648 			     isspace (*arg_pos) || *arg_pos == '*';
649 			     ++ arg_pos)
650 			{
651 				if (*arg_pos == '*') ++ arg_pointer_count;
652 			}
653 
654 			/* Type matches */
655 			if (arg_pointer_count == pointer_count)
656 				arg_present = TRUE;
657 		}
658 
659 		/* The argument list does not contain the specified type as first
660 		 * argument, so add it. */
661 		if (arg_present == FALSE)
662 		{
663 			arguments = g_strdup_printf ("(%.*s%sself, %s", (int) type_len,
664 			                             type, pointer_str, arguments);
665 			g_hash_table_insert (table, (gpointer) index, arguments);
666 		}
667 	}
668 
669 	g_free (pointer_str);
670 }
671 
672 /* This function looks up the given arguments_index in the hash table and
673  * expects an argument list with at least one argument like
674  * cg_transform_first_argument generates it. Then, it makes a new
675  * comma-separated list of the corresponding GTypes and writes it to
676  * gtypes_index. It returns the amount of arguments. The first
677  * argument is not part of the output.
678  *
679  * Additionally, if the arguments field was left empty, it makes (void) out
680  * of it. Normally, the arguments field may be left empty to indicate that
681  * the whole thing is a variable rather than a function. However, if someone
682  * requires a list of gtypes of the arguments, it is most likely a function
683  * and only a function. */
684 guint
cg_transform_arguments_to_gtypes(GHashTable * table,const gchar * arguments_index,const gchar * gtypes_index)685 cg_transform_arguments_to_gtypes (GHashTable *table,
686                                   const gchar *arguments_index,
687                                   const gchar *gtypes_index)
688 {
689 	guint arg_count;
690 	GString *arg_str;
691 	gchar *arguments;
692 
693 	gchar *arg_prev;
694 	gchar *arg_pos;
695 	gchar *arg_type = NULL;
696 
697 	gchar *argtype_prefix;
698 	gchar *argtype_suffix;
699 
700 	arg_count = 0;
701 	arg_str = g_string_sized_new (128);
702 	arguments = g_hash_table_lookup (table, arguments_index);
703 
704 	g_assert (arguments != NULL && *arguments != '\0');
705 
706 	/* Step over '(' */
707 	arg_prev = arguments + 1;
708 
709 	/* Ignore first argument */
710 	while (*arg_prev != ',' && *arg_prev != ')') ++ arg_prev;
711 
712 	/* Step over ',' */
713 	if (*arg_prev == ',') ++ arg_prev;
714 
715 	while (isspace (*arg_prev)) ++ arg_prev;
716 	arg_pos = arg_prev;
717 
718 	while (*arg_prev != ')')
719 	{
720 		++ arg_count;
721 
722 		/* Advance to end of this argument. */
723 		while (*arg_pos != ',' && *arg_pos != ')')
724 			++ arg_pos;
725 
726 		/* Try to find argument type by going back the last identifier
727 		 * which should be the argument name. */
728 		if (arg_pos > arg_prev)
729 		{
730 			arg_type = arg_pos - 1;
731 			while (isspace (*arg_type)) -- arg_type;
732 		}
733 
734 		while ((isalnum (*arg_type) || *arg_type == '_') &&
735 		      arg_type > arg_prev)
736 		{
737 			-- arg_type;
738 		}
739 
740 		/* If the name is everything in this arguments this is most
741 		 * probably the type and the name has been omitted. If a type was
742 		 * given that ends on a special character (i.e. '*' because it is
743 		 * a pointer) we also want to get that character. */
744 		if (arg_type == arg_prev || !isspace (*arg_type))
745 			arg_type = arg_pos;
746 
747 		/* Go back any whitespace to find end of type. Note that
748 		 * *(arg_type - 1) is always valid because arg_prev is at
749 		 * a character that is not a whitespace and arg_type is
750 		 * always >= arg_prev. */
751 		if (arg_type > arg_prev)
752 			while (isspace(*(arg_type - 1)))
753 				-- arg_type;
754 
755 		/* The arguments type should now be enclosed by arg_prev and
756 		 * arg_type. */
757 		arg_type = g_strndup (arg_prev, arg_type - arg_prev);
758 		cg_transform_c_type_to_g_type (arg_type, &argtype_prefix,
759 		                               &argtype_suffix);
760 		g_free (arg_type);
761 
762 		if (arg_str->len > 0) g_string_append (arg_str, ", ");
763 		g_string_append (arg_str, argtype_prefix);
764 		g_string_append (arg_str, "_TYPE_");
765 		g_string_append (arg_str, argtype_suffix);
766 
767 		g_free (argtype_prefix);
768 		g_free (argtype_suffix);
769 
770 		if (*arg_pos != ')')
771 		{
772 			/* Step over comma and following whitespace */
773 			++ arg_pos;
774 			while (isspace (*arg_pos)) ++ arg_pos;
775 		}
776 
777 		arg_prev = arg_pos;
778 	}
779 
780 	g_hash_table_insert (table, (gpointer) gtypes_index,
781 	                     g_string_free (arg_str, FALSE));
782 
783 	return arg_count;
784 }
785