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