1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
3 * Copyright (C) 2006 Armin Burgmeier
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.
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.
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.
20 #include "transform.h"
25 typedef struct _CgTransformParamGuess CgTransformParamGuess
;
26 struct _CgTransformParamGuess
29 const gchar
*paramspec
;
32 typedef struct _CgTransformGTypeGuess CgTransformGTypeGuess
;
33 struct _CgTransformGTypeGuess
36 const gchar
*gtype_prefix
;
37 const gchar
*gtype_name
;
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
*
43 cg_transform_lookup_flag (const CgElementEditorFlags
*flags
,
47 const CgElementEditorFlags
*flag
;
48 for (flag
= flags
; flag
->name
!= NULL
; ++ flag
)
50 if (strncmp (flag
->abbrevation
, abbrevation
, len
) == 0)
52 if(flag
->abbrevation
[len
] == '\0')
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. */
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
)
69 static const CgTransformGTypeGuess DEFAULT_TYPES
[] =
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" },
108 const CgTransformGTypeGuess
*guess
;
110 for (guess
= DEFAULT_TYPES
; guess
->ctype
!= NULL
; ++ guess
)
112 if (strcmp (guess
->ctype
, c_type
) == 0)
114 *g_type_prefix
= guess
->gtype_prefix
;
115 *g_type_name
= guess
->gtype_name
;
123 /* This function tries to convert a custom c_type like GtkTreeIter to
124 * a gobject type like GTK_TYPE_TREE_ITER. It does this by parsing the C type.
125 * The code is mostly borrowed from old action-callbacks.c by Dave
126 * Huseby and Massimo Cora'. */
128 cg_transform_custom_c_type_to_g_type (const gchar
*c_type
,
129 gchar
**g_type_prefix
,
131 gchar
**g_func_prefix
)
136 GString
*str_type_prefix
= NULL
;
137 GString
*str_type_name
= NULL
;
138 GString
*str_func_prefix
= NULL
;
140 name_len
= strlen (c_type
);
142 if (g_type_prefix
!= NULL
) str_type_prefix
= g_string_sized_new (name_len
);
143 if (g_type_name
!= NULL
) str_type_name
= g_string_sized_new (name_len
);
144 if (g_type_prefix
!= NULL
) str_func_prefix
= g_string_sized_new (name_len
);
149 for (; *c_type
!= '\0'; ++ c_type
)
153 if (g_func_prefix
!= NULL
)
154 g_string_append_c (str_func_prefix
, tolower (*c_type
));
155 if (g_type_prefix
!= NULL
)
156 g_string_append_c (str_type_prefix
, toupper (*c_type
));
162 if (isupper (*c_type
))
164 if (g_func_prefix
!= NULL
)
165 g_string_append_c (str_func_prefix
, '_');
170 if (g_func_prefix
!= NULL
)
171 g_string_append_c (str_func_prefix
, tolower (*c_type
));
175 if (g_type_prefix
!= NULL
)
176 g_string_append_c (str_type_prefix
, toupper (*c_type
));
180 if (g_type_name
!= NULL
)
182 if (isupper (*c_type
) && str_type_name
->len
> 0)
183 g_string_append_c (str_type_name
, '_');
185 g_string_append_c (str_type_name
, toupper (*c_type
));
191 if (g_type_prefix
!= NULL
)
192 *g_type_prefix
= g_string_free (str_type_prefix
, FALSE
);
194 if (g_type_name
!= NULL
)
195 *g_type_name
= g_string_free (str_type_name
, FALSE
);
197 if (g_func_prefix
!= NULL
)
198 *g_func_prefix
= g_string_free (str_func_prefix
, FALSE
);
201 /* This function tries to convert any possible C type to its corresponding
202 * GObject type. First, it looks whether the C type is a default type. If not,
203 * it strips leading const or a trailing * away (because it does not matter
204 * whether we have const GtkTreeIter* or GtkTreeIter, it all results in the
205 * same gobject type, namely GTK_TYPE_TREE_ITER) and calls
206 * cg_transform_custom_c_type_to_g_type. */
208 cg_transform_c_type_to_g_type (const gchar
*c_type
,
209 gchar
**g_type_prefix
,
212 const gchar
*default_prefix
;
213 const gchar
*default_name
;
218 result
= cg_transform_default_c_type_to_g_type (c_type
, &default_prefix
,
223 *g_type_prefix
= g_strdup (default_prefix
);
224 *g_type_name
= g_strdup (default_name
);
228 if (strncmp (c_type
, "const ", 6) == 0)
229 plain_c_type
= g_strdup (c_type
+ 6);
231 plain_c_type
= g_strdup (c_type
);
233 len
= strlen (plain_c_type
);
234 if (plain_c_type
[len
- 1] == '*')
236 plain_c_type
[len
- 1] = '\0';
237 g_strchomp (plain_c_type
);
240 cg_transform_custom_c_type_to_g_type (plain_c_type
, g_type_prefix
,
243 g_free (plain_c_type
);
247 /* Looks up the given index in the hash table and removes enclosing quotes,
248 * if any. Those are added again by the autogen template. */
250 cg_transform_string (GHashTable
*table
,
257 str
= g_hash_table_lookup (table
, index
);
262 if (len
>= 2 && str
[0] == '\"' && str
[len
- 1] == '\"')
264 /* Unescape string because it was most likely already escaped
265 * by the user because s/he also added quotes around it. */
266 str
= g_strndup (str
+ 1, len
- 2);
267 unescaped
= g_strcompress (str
);
270 g_hash_table_insert (table
, (gpointer
) index
, unescaped
);
275 /* Looks up the given index in the hash table which is assumed to be a string
276 * with '|'-separated abbrevations of the given flags. This function replaces
277 * the abbrevations by the full names. If no flags are set, it produces "0". */
279 cg_transform_flags (GHashTable
*table
,
281 const CgElementEditorFlags
*flags
)
283 const CgElementEditorFlags
*flag
;
289 flags_str
= g_hash_table_lookup (table
, index
);
290 res_str
= g_string_sized_new (128);
292 if (flags_str
!= NULL
)
297 while (*prev
!= '\0')
299 while (*pos
!= '|' && *pos
!= '\0')
302 flag
= cg_transform_lookup_flag (flags
, prev
, pos
- prev
);
303 g_assert (flag
!= NULL
);
305 if (res_str
->len
> 0) g_string_append (res_str
, " | ");
306 g_string_append (res_str
, flag
->name
);
308 if (*pos
!= '\0') ++ pos
;
313 if (res_str
->len
== 0) g_string_append_c (res_str
, '0');
314 g_hash_table_insert (table
, (gpointer
) index
,
315 g_string_free (res_str
, FALSE
));
318 /* Looks up the given param_index in the hash table. If it contains
319 * guess_entry, the value is replaced by guessing the param spec from
320 * the GObject type stored in type_index. */
322 cg_transform_guess_paramspec (GHashTable
*table
,
323 const gchar
*param_index
,
324 const gchar
*type_index
,
325 const gchar
*guess_entry
)
327 const CgTransformParamGuess GUESS_TABLE
[] =
329 { "G_TYPE_BOOLEAN", "g_param_spec_boolean" },
330 { "G_TYPE_BOXED", "g_param_spec_boxed" },
331 { "G_TYPE_CHAR", "g_param_spec_char" },
332 { "G_TYPE_DOUBLE", "g_param_spec_double" },
333 { "G_TYPE_ENUM", "g_param_spec_enum" },
334 { "G_TYPE_FLAGS", "g_param_spec_flags" },
335 { "G_TYPE_FLOAT", "g_param_spec_float" },
336 { "G_TYPE_INT", "g_param_spec_int" },
337 { "G_TYPE_INT64", "g_param_spec_int64" },
338 { "G_TYPE_LONG", "g_param_spec_long" },
339 { "G_TYPE_OBJECT", "g_param_spec_object" },
340 { "G_TYPE_PARAM", "g_param_spec_param" },
341 { "G_TYPE_POINTER", "g_param_spec_pointer" },
342 { "G_TYPE_STRING", "g_param_spec_string" },
343 { "G_TYPE_UCHAR", "g_param_spec_uchar" },
344 { "G_TYPE_UINT", "g_param_spec_uint" },
345 { "G_TYPE_UINT64", "g_param_spec_uint64" },
346 { "G_TYPE_ULONG", "g_param_spec_ulong" },
347 { "G_TYPE_UNICHAR", "g_param_spec_unichar" },
351 const CgTransformParamGuess
*guess
;
355 paramspec
= g_hash_table_lookup (table
, param_index
);
357 if (paramspec
!= NULL
&& strcmp (paramspec
, guess_entry
) == 0)
359 type
= g_hash_table_lookup (table
, type_index
);
362 for (guess
= GUESS_TABLE
; guess
->gtype
!= NULL
; ++ guess
)
364 if (strcmp (type
, guess
->gtype
) == 0)
366 paramspec
= g_strdup (guess
->paramspec
);
371 /* Not in list, so assume it is an object */
372 if (guess
->gtype
== NULL
)
373 paramspec
= g_strdup ("g_param_spec_object");
375 g_hash_table_insert (table
, (gpointer
) param_index
, paramspec
);
380 /* This function looks up index in the given hash table and encloses it by
381 * surrounding parenthesis if they do not already exist and the field is
382 * non-empty. If make_void is TRUE and the arguments are only "()" it makes
383 * "(void)" out of it to stay ANSI C compliant. */
385 cg_transform_arguments (GHashTable
*table
,
393 arguments
= g_hash_table_lookup (table
, index
);
395 if (arguments
!= NULL
)
397 g_strstrip (arguments
);
398 len
= strlen (arguments
);
400 /* Do nothing if the field was left empty */
403 /* Surround arguments with paranthesis if they do
404 * not already exist. */
405 if (arguments
[0] != '(' && arguments
[len
- 1] != ')')
406 arg_res
= g_strdup_printf ("(%s)", arguments
);
407 else if (arguments
[0] != '(')
408 arg_res
= g_strdup_printf ("(%s", arguments
);
409 else if (arguments
[len
- 1] != ')')
410 arg_res
= g_strdup_printf ("%s)", arguments
);
414 /* Set arguments to the transformed result, if a transformation
419 if (make_void
== TRUE
)
421 /* Make "(void)" out of "()" if make_void is set. If this is
422 * the case, we do not need to store arg_res in the hash
423 * table lateron, so we delete it right here. */
424 if (strcmp (arguments
, "()") == 0)
426 g_hash_table_insert (table
, (gpointer
) index
,
427 g_strdup ("(void)"));
435 g_hash_table_insert (table
, (gpointer
) index
, arg_res
);
440 /* This function makes a valid C identifier out of a string. It does this
441 * by ignoring anything but digits, letters, hyphens and underscores. Digits
442 * at the beginning of the string are also ignored. Hpyhens are transformed
445 cg_transform_string_to_identifier (GHashTable
*table
,
446 const gchar
*string_index
,
447 const gchar
*identifier_index
)
450 gchar
*identifier_name
;
454 name
= g_hash_table_lookup (table
, "Name");
457 name_len
= strlen (name
);
458 identifier_name
= g_malloc ((name_len
+ 1) * sizeof(gchar
));
460 for (i
= 0, j
= 0; i
< name_len
; ++ i
)
462 if (isupper (name
[i
]) || islower (name
[i
]))
463 identifier_name
[j
++] = name
[i
];
464 else if (isdigit (name
[i
]) && j
> 0)
465 identifier_name
[j
++] = name
[i
];
466 else if (isspace (name
[i
]) || name
[i
] == '-' || name
[i
] == '_')
467 identifier_name
[j
++] = '_';
470 identifier_name
[j
] = '\0';
472 g_hash_table_insert (table
, (gpointer
) identifier_index
,
475 /* Ownership is given to hash table, so no g_free here. */
479 /* This function looks up the given index in the hash table and expects an
480 * argument list like cg_transform_arguments generates it. It then checks
481 * whether the first argument is of the given type. If it is, it does
482 * nothing, otherwise it adds the first argument to the argument list
483 * and writes the result back into the hash table. */
485 cg_transform_first_argument (GHashTable
*table
,
491 guint arg_pointer_count
;
493 const gchar
*type_pos
;
496 gboolean arg_present
;
499 arguments
= g_hash_table_lookup (table
, index
);
501 /* Count the length of the basic type */
503 while (isalnum (type
[type_len
])) ++ type_len
;
504 type_pos
= type
+ type_len
;
506 /* Count pointer indicators */
508 for (type_pos
= type
+ type_len
; *type_pos
!= '\0'; ++ type_pos
)
509 if (*type_pos
== '*') ++ pointer_count
;
511 /* Build a string of all pointer indicators that we can append to the
512 * basic type to get the final type. */
513 pointer_str
= g_malloc ((pointer_count
+ 2) * sizeof(gchar
));
514 pointer_str
[0] = ' ';
515 pointer_str
[pointer_count
+ 1] = '\0';
516 for (i
= 0; i
< pointer_count
; ++ i
) pointer_str
[i
+ 1] = '*';
518 /* We do not just prepend type to the argument string because then
519 * we would have to fiddle around where spaces and pointer indicators
520 * belong. We can now always just build a string like
521 * BasicType+PointerStr+Name to get a fully qualified parameter like
522 * GtkTreeIter *iter, gint the_int or gchar **dest. */
524 if (arguments
== NULL
|| *arguments
== '\0')
526 arguments
= g_strdup_printf ("(%.*s%sself)", (int)type_len
,
529 g_hash_table_insert (table
, (gpointer
) index
, arguments
);
533 g_assert (arguments
[0] == '(');
535 /* Step over '(' and any leading whitespace */
537 while (isspace (*arguments
)) ++ arguments
;
540 if (strncmp (arguments
, type
, type_len
) == 0)
542 /* We cannot just check (via string comparison) whether arguments
543 * begins with the whole type because the pointer indicator might
544 * be directly behind the basic type, but there might as well exist
545 * an arbitrary amount of spaces inbetween.
546 * GtkTreeIter* self vs. GtkTreeIter *self vs.
547 * GtkTreeIter *self. */
548 arg_pointer_count
= 0;
549 for (arg_pos
= arguments
+ type_len
;
550 isspace (*arg_pos
) || *arg_pos
== '*';
553 if (*arg_pos
== '*') ++ arg_pointer_count
;
557 if (arg_pointer_count
== pointer_count
)
561 /* The argument list does not contain the specified type as first
562 * argument, so add it. */
563 if (arg_present
== FALSE
)
565 arguments
= g_strdup_printf ("(%.*s%sself, %s", (int) type_len
,
566 type
, pointer_str
, arguments
);
567 g_hash_table_insert (table
, (gpointer
) index
, arguments
);
571 g_free (pointer_str
);
574 /* This function looks up the given arguments_index in the hash table and
575 * expects an argument list with at least one argument like
576 * cg_transform_first_argument generates it. Then, it makes a new
577 * comma-separated list of the corresponding GTypes and writes it to
578 * gtypes_index. It returns the amount of arguments. The first
579 * argument is not part of the output.
581 * Additionally, if the arguments field was left empty, it makes (void) out
582 * of it. Normally, the arguments field may be left empty to indicate that
583 * the whole thing is a variable rather than a function. However, if someone
584 * requires a list of gtypes of the arguments, it is most likely a function
585 * and only a function. */
587 cg_transform_arguments_to_gtypes (GHashTable
*table
,
588 const gchar
*arguments_index
,
589 const gchar
*gtypes_index
)
597 gchar
*arg_type
= NULL
;
599 gchar
*argtype_prefix
;
600 gchar
*argtype_suffix
;
603 arg_str
= g_string_sized_new (128);
604 arguments
= g_hash_table_lookup (table
, arguments_index
);
606 g_assert (arguments
!= NULL
&& *arguments
!= '\0');
609 arg_prev
= arguments
+ 1;
611 /* Ignore first argument */
612 while (*arg_prev
!= ',' && *arg_prev
!= ')') ++ arg_prev
;
615 if (*arg_prev
== ',') ++ arg_prev
;
617 while (isspace (*arg_prev
)) ++ arg_prev
;
620 while (*arg_prev
!= ')')
624 /* Advance to end of this argument. */
625 while (*arg_pos
!= ',' && *arg_pos
!= ')')
628 /* Try to find argument type by going back the last identifier
629 * which should be the argument name. */
630 if (arg_pos
> arg_prev
)
632 arg_type
= arg_pos
- 1;
633 while (isspace (*arg_type
)) -- arg_type
;
636 while ((isalnum (*arg_type
) || *arg_type
== '_') &&
642 /* If the name is everything in this arguments this is most
643 * probably the type and the name has been omitted. If a type was
644 * given that ends on a special character (i.e. '*' because it is
645 * a pointer) we also want to get that character. */
646 if (arg_type
== arg_prev
|| !isspace (*arg_type
))
649 /* Go back any whitespace to find end of type. Note that
650 * *(arg_type - 1) is always valid because arg_prev is at
651 * a character that is not a whitespace and arg_type is
652 * always >= arg_prev. */
653 if (arg_type
> arg_prev
)
654 while (isspace(*(arg_type
- 1)))
657 /* The arguments type should now be enclosed by arg_prev and
659 arg_type
= g_strndup (arg_prev
, arg_type
- arg_prev
);
660 cg_transform_c_type_to_g_type (arg_type
, &argtype_prefix
,
664 if (arg_str
->len
> 0) g_string_append (arg_str
, ", ");
665 g_string_append (arg_str
, argtype_prefix
);
666 g_string_append (arg_str
, "_TYPE_");
667 g_string_append (arg_str
, argtype_suffix
);
669 g_free (argtype_prefix
);
670 g_free (argtype_suffix
);
674 /* Step over comma and following whitespace */
676 while (isspace (*arg_pos
)) ++ arg_pos
;
682 g_hash_table_insert (table
, (gpointer
) gtypes_index
,
683 g_string_free (arg_str
, FALSE
));