glib/girepository/tools/generate.c
Colin Walters ecd9c2f3e7 Support (out caller-allocates)
People have wanted support for marking (out) on functions of the
form:

/**
 * clutter_color_from_pixel:
 * @pixel: A pixel
 * @color: (out): Color to initialize with value of @pixel
 */
void
clutter_color_from_pixel (guint32 pixel, ClutterColor *color);

Where the caller is supposed to have allocated the argument; the
C function just initializes it.  This patch adds support for this
argument passing style to introspection.  In this case, we see the
(out), and notice that there's only a single indirection (*) on
the argument, and assume that this means (out caller-allocates).

https://bugzilla.gnome.org/show_bug.cgi?id=604749
2024-02-08 13:53:59 +00:00

1523 lines
39 KiB
C

/* -*- Mode: C; c-file-style: "gnu"; -*- */
/* GObject introspection: IDL generator
*
* Copyright (C) 2005 Matthias Clasen
* Copyright (C) 2008,2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <errno.h>
#include <string.h>
#include <glib.h>
#include <glib-object.h>
#include <glib/gstdio.h>
#include "girepository.h"
#include "gtypelib.h"
/* FIXME: Avoid global */
static gchar *output = NULL;
gchar **includedirs = NULL;
static gboolean show_all = FALSE;
typedef struct {
FILE *file;
GSList *stack;
} Xml;
typedef struct {
char *name;
guint has_children : 1;
} XmlElement;
static XmlElement *
xml_element_new (const char *name)
{
XmlElement *elem;
elem = g_new (XmlElement, 1);
elem->name = g_strdup (name);
elem->has_children = FALSE;
return elem;
}
static void
xml_element_free (XmlElement *elem)
{
g_free (elem->name);
g_free (elem);
}
static void
xml_printf (Xml *xml, const char *fmt, ...)
{
va_list ap;
char *s;
va_start (ap, fmt);
s = g_markup_vprintf_escaped (fmt, ap);
fputs (s, xml->file);
g_free (s);
va_end (ap);
}
static void
xml_start_element (Xml *xml, const char *element_name)
{
XmlElement *parent = NULL;
if (xml->stack)
{
parent = xml->stack->data;
if (!parent->has_children)
xml_printf (xml, ">\n");
parent->has_children = TRUE;
}
xml_printf (xml, "%*s<%s", g_slist_length(xml->stack)*2, "", element_name);
xml->stack = g_slist_prepend (xml->stack, xml_element_new (element_name));
}
static void
xml_end_element (Xml *xml, const char *name)
{
XmlElement *elem;
g_assert (xml->stack != NULL);
elem = xml->stack->data;
xml->stack = g_slist_delete_link (xml->stack, xml->stack);
if (name != NULL)
g_assert_cmpstr (name, ==, elem->name);
if (elem->has_children)
xml_printf (xml, "%*s</%s>\n", g_slist_length (xml->stack)*2, "", elem->name);
else
xml_printf (xml, "/>\n");
xml_element_free (elem);
}
static void
xml_end_element_unchecked (Xml *xml)
{
xml_end_element (xml, NULL);
}
static Xml *
xml_open (FILE *file)
{
Xml *xml;
xml = g_new (Xml, 1);
xml->file = file;
xml->stack = NULL;
return xml;
}
static void
xml_close (Xml *xml)
{
g_assert (xml->stack == NULL);
if (xml->file != NULL)
{
fflush (xml->file);
if (xml->file != stdout)
fclose (xml->file);
xml->file = NULL;
}
}
static void
xml_free (Xml *xml)
{
xml_close (xml);
g_free (xml);
}
static void
check_unresolved (GIBaseInfo *info)
{
if (g_base_info_get_type (info) != GI_INFO_TYPE_UNRESOLVED)
return;
g_critical ("Found unresolved type '%s' '%s'\n",
g_base_info_get_name (info), g_base_info_get_namespace (info));
}
static void
write_type_name (const gchar *namespace,
GIBaseInfo *info,
Xml *file)
{
if (strcmp (namespace, g_base_info_get_namespace (info)) != 0)
xml_printf (file, "%s.", g_base_info_get_namespace (info));
xml_printf (file, "%s", g_base_info_get_name (info));
}
static void
write_type_name_attribute (const gchar *namespace,
GIBaseInfo *info,
const char *attr_name,
Xml *file)
{
xml_printf (file, " %s=\"", attr_name);
write_type_name (namespace, info, file);
xml_printf (file, "\"");
}
static void
write_type_info (const gchar *namespace,
GITypeInfo *info,
Xml *file)
{
gint tag;
gint i;
GITypeInfo *type;
gboolean is_pointer;
check_unresolved ((GIBaseInfo*)info);
tag = g_type_info_get_tag (info);
is_pointer = g_type_info_is_pointer (info);
if (tag == GI_TYPE_TAG_VOID)
{
xml_start_element (file, "type");
xml_printf (file, " name=\"%s\"", is_pointer ? "any" : "none");
xml_end_element (file, "type");
}
else if (G_TYPE_TAG_IS_BASIC (tag))
{
xml_start_element (file, "type");
xml_printf (file, " name=\"%s\"", g_type_tag_to_string (tag));
xml_end_element (file, "type");
}
else if (tag == GI_TYPE_TAG_ARRAY)
{
gint length, size;
xml_start_element (file, "array");
type = g_type_info_get_param_type (info, 0);
length = g_type_info_get_array_length (info);
if (length >= 0)
xml_printf (file, " length=\"%d\"", length);
size = g_type_info_get_array_fixed_size (info);
if (size >= 0)
xml_printf (file, " fixed-size=\"%d\"", size);
if (g_type_info_is_zero_terminated (info))
xml_printf (file, " zero-terminated=\"1\"");
write_type_info (namespace, type, file);
g_base_info_unref ((GIBaseInfo *)type);
xml_end_element (file, "array");
}
else if (tag == GI_TYPE_TAG_INTERFACE)
{
GIBaseInfo *iface = g_type_info_get_interface (info);
xml_start_element (file, "type");
write_type_name_attribute (namespace, iface, "name", file);
xml_end_element (file, "type");
g_base_info_unref (iface);
}
else if (tag == GI_TYPE_TAG_GLIST)
{
xml_start_element (file, "type");
xml_printf (file, " name=\"GLib.List\"");
type = g_type_info_get_param_type (info, 0);
if (type)
{
write_type_info (namespace, type, file);
g_base_info_unref ((GIBaseInfo *)type);
}
xml_end_element (file, "type");
}
else if (tag == GI_TYPE_TAG_GSLIST)
{
xml_start_element (file, "type");
xml_printf (file, " name=\"GLib.SList\"");
type = g_type_info_get_param_type (info, 0);
if (type)
{
write_type_info (namespace, type, file);
g_base_info_unref ((GIBaseInfo *)type);
}
xml_end_element (file, "type");
}
else if (tag == GI_TYPE_TAG_GHASH)
{
xml_start_element (file, "type");
xml_printf (file, " name=\"GLib.HashTable\"");
type = g_type_info_get_param_type (info, 0);
if (type)
{
write_type_info (namespace, type, file);
g_base_info_unref ((GIBaseInfo *)type);
type = g_type_info_get_param_type (info, 1);
write_type_info (namespace, type, file);
g_base_info_unref ((GIBaseInfo *)type);
}
xml_end_element (file, "type");
}
else if (tag == GI_TYPE_TAG_ERROR)
{
gint n;
xml_start_element (file, "type");
xml_printf (file, " name=\"GLib.Error\"");
n = g_type_info_get_n_error_domains (info);
if (n > 0)
{
for (i = 0; i < n; i++)
{
GIErrorDomainInfo *ed = g_type_info_get_error_domain (info, i);
xml_start_element (file, "type");
write_type_name_attribute (namespace, (GIBaseInfo *)ed, "name", file);
xml_end_element (file, "type");
g_base_info_unref ((GIBaseInfo *)ed);
}
}
xml_end_element (file, "type");
}
else
{
g_printerr ("Unhandled type tag %d\n", tag);
g_assert_not_reached ();
}
}
static void
write_attributes (Xml *file,
GIBaseInfo *info)
{
GIAttributeIter iter = { 0, };
char *name, *value;
while (g_base_info_iterate_attributes (info, &iter, &name, &value))
{
xml_start_element (file, "attribute");
xml_printf (file, " name=\"%s\" value=\"%s\"", name, value);
xml_end_element (file, "attribute");
}
}
static void
write_constant_value (const gchar *namespace,
GITypeInfo *info,
GArgument *argument,
Xml *file);
static void
write_callback_info (const gchar *namespace,
GICallbackInfo *info,
Xml *file);
static void
write_field_info (const gchar *namespace,
GIFieldInfo *info,
GIConstantInfo *branch,
Xml *file)
{
const gchar *name;
GIFieldInfoFlags flags;
gint size;
gint offset;
GITypeInfo *type;
GIBaseInfo *interface;
GArgument value;
name = g_base_info_get_name ((GIBaseInfo *)info);
flags = g_field_info_get_flags (info);
size = g_field_info_get_size (info);
offset = g_field_info_get_offset (info);
xml_start_element (file, "field");
xml_printf (file, " name=\"%s\"", name);
/* Fields are assumed to be read-only
* (see also girwriter.py and girparser.c)
*/
if (!(flags & GI_FIELD_IS_READABLE))
xml_printf (file, " readable=\"0\"");
if (flags & GI_FIELD_IS_WRITABLE)
xml_printf (file, " writable=\"1\"");
if (size)
xml_printf (file, " bits=\"%d\"", size);
write_attributes (file, (GIBaseInfo*) info);
type = g_field_info_get_type (info);
if (branch)
{
xml_printf (file, " branch=\"");
type = g_constant_info_get_type (branch);
g_constant_info_get_value (branch, &value);
write_constant_value (namespace, type, &value, file);
xml_printf (file, "\"");
}
if (show_all)
{
if (offset >= 0)
xml_printf (file, "offset=\"%d\"", offset);
}
interface = g_type_info_get_interface (type);
if (interface && g_base_info_get_type(interface) == GI_INFO_TYPE_CALLBACK)
write_callback_info (namespace, (GICallbackInfo *)interface, file);
else
write_type_info (namespace, type, file);
if (interface)
g_base_info_unref (interface);
g_base_info_unref ((GIBaseInfo *)type);
xml_end_element (file, "field");
}
static void
write_callable_info (const gchar *namespace,
GICallableInfo *info,
Xml *file)
{
GITypeInfo *type;
gint i;
write_attributes (file, (GIBaseInfo*) info);
type = g_callable_info_get_return_type (info);
xml_start_element (file, "return-value");
switch (g_callable_info_get_caller_owns (info))
{
case GI_TRANSFER_NOTHING:
xml_printf (file, " transfer-ownership=\"none\"");
break;
case GI_TRANSFER_CONTAINER:
xml_printf (file, " transfer-ownership=\"container\"");
break;
case GI_TRANSFER_EVERYTHING:
xml_printf (file, " transfer-ownership=\"full\"");
break;
default:
g_assert_not_reached ();
}
if (g_callable_info_may_return_null (info))
xml_printf (file, " allow-none=\"1\"");
write_type_info (namespace, type, file);
xml_end_element (file, "return-value");
if (g_callable_info_get_n_args (info) <= 0)
return;
xml_start_element (file, "parameters");
for (i = 0; i < g_callable_info_get_n_args (info); i++)
{
GIArgInfo *arg = g_callable_info_get_arg (info, i);
xml_start_element (file, "parameter");
xml_printf (file, " name=\"%s\"",
g_base_info_get_name ((GIBaseInfo *) arg));
switch (g_arg_info_get_ownership_transfer (arg))
{
case GI_TRANSFER_NOTHING:
xml_printf (file, " transfer-ownership=\"none\"");
break;
case GI_TRANSFER_CONTAINER:
xml_printf (file, " transfer-ownership=\"container\"");
break;
case GI_TRANSFER_EVERYTHING:
xml_printf (file, " transfer-ownership=\"full\"");
break;
default:
g_assert_not_reached ();
}
switch (g_arg_info_get_direction (arg))
{
case GI_DIRECTION_IN:
break;
case GI_DIRECTION_OUT:
xml_printf (file, " direction=\"out\" caller-allocates=\"%s\"",
g_arg_info_is_caller_allocates (arg) ? "1" : "0");
break;
case GI_DIRECTION_INOUT:
xml_printf (file, " direction=\"inout\"");
break;
}
if (g_arg_info_may_be_null (arg))
xml_printf (file, " allow-none=\"1\"");
if (g_arg_info_is_return_value (arg))
xml_printf (file, " retval=\"1\"");
if (g_arg_info_is_optional (arg))
xml_printf (file, " optional=\"1\"");
switch (g_arg_info_get_scope (arg))
{
case GI_SCOPE_TYPE_INVALID:
break;
case GI_SCOPE_TYPE_CALL:
xml_printf (file, " scope=\"call\"");
break;
case GI_SCOPE_TYPE_ASYNC:
xml_printf (file, " scope=\"async\"");
break;
case GI_SCOPE_TYPE_NOTIFIED:
xml_printf (file, " scope=\"notified\"");
break;
}
if (g_arg_info_get_closure (arg) >= 0)
xml_printf (file, " closure=\"%d\"", g_arg_info_get_closure (arg));
if (g_arg_info_get_destroy (arg) >= 0)
xml_printf (file, " destroy=\"%d\"", g_arg_info_get_destroy (arg));
type = g_arg_info_get_type (arg);
write_type_info (namespace, type, file);
xml_end_element (file, "parameter");
g_base_info_unref ((GIBaseInfo *)arg);
}
xml_end_element (file, "parameters");
g_base_info_unref ((GIBaseInfo *)type);
}
static void
write_function_info (const gchar *namespace,
GIFunctionInfo *info,
Xml *file)
{
GIFunctionInfoFlags flags;
const gchar *tag;
const gchar *name;
const gchar *symbol;
gboolean deprecated;
gboolean throws;
flags = g_function_info_get_flags (info);
name = g_base_info_get_name ((GIBaseInfo *)info);
symbol = g_function_info_get_symbol (info);
deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info);
throws = flags & GI_FUNCTION_THROWS;
if (flags & GI_FUNCTION_IS_CONSTRUCTOR)
tag = "constructor";
else if (flags & GI_FUNCTION_IS_METHOD)
tag = "method";
else
tag = "function";
xml_start_element (file, tag);
xml_printf (file, " name=\"%s\" c:identifier=\"%s\"",
name, symbol);
if (flags & GI_FUNCTION_IS_SETTER)
xml_printf (file, " type=\"setter\"");
else if (flags & GI_FUNCTION_IS_GETTER)
xml_printf (file, " type=\"getter\"");
if (deprecated)
xml_printf (file, " deprecated=\"1\"");
if (throws)
xml_printf (file, " throws=\"1\"");
write_callable_info (namespace, (GICallableInfo*)info, file);
xml_end_element (file, tag);
}
static void
write_callback_info (const gchar *namespace,
GICallbackInfo *info,
Xml *file)
{
const gchar *name;
gboolean deprecated;
name = g_base_info_get_name ((GIBaseInfo *)info);
deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info);
xml_start_element (file, "callback");
xml_printf (file, " name=\"%s\"", name);
if (deprecated)
xml_printf (file, " deprecated=\"1\"");
write_callable_info (namespace, (GICallableInfo*)info, file);
xml_end_element (file, "callback");
}
static void
write_struct_info (const gchar *namespace,
GIStructInfo *info,
Xml *file)
{
const gchar *name;
const gchar *type_name;
const gchar *type_init;
gboolean deprecated;
gboolean is_gtype_struct;
gboolean foreign;
gint i;
gint size;
int n_elts;
name = g_base_info_get_name ((GIBaseInfo *)info);
deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info);
type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info);
type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info);
if (g_base_info_get_type ((GIBaseInfo *)info) == GI_INFO_TYPE_BOXED)
{
xml_start_element (file, "glib:boxed");
xml_printf (file, " glib:name=\"%s\"", name);
}
else
{
xml_start_element (file, "record");
xml_printf (file, " name=\"%s\"", name);
}
if (type_name != NULL)
xml_printf (file, " glib:type-name=\"%s\" glib:get-type=\"%s\"", type_name, type_init);
if (deprecated)
xml_printf (file, " deprecated=\"1\"");
is_gtype_struct = g_struct_info_is_gtype_struct (info);
if (is_gtype_struct)
xml_printf (file, " glib:is-gtype-struct=\"1\"");
write_attributes (file, (GIBaseInfo*) info);
size = g_struct_info_get_size (info);
if (show_all && size >= 0)
xml_printf (file, " size=\"%d\"", size);
foreign = g_struct_info_is_foreign (info);
if (foreign)
xml_printf (file, " foreign=\"1\"");
n_elts = g_struct_info_get_n_fields (info) + g_struct_info_get_n_methods (info);
if (n_elts > 0)
{
for (i = 0; i < g_struct_info_get_n_fields (info); i++)
{
GIFieldInfo *field = g_struct_info_get_field (info, i);
write_field_info (namespace, field, NULL, file);
g_base_info_unref ((GIBaseInfo *)field);
}
for (i = 0; i < g_struct_info_get_n_methods (info); i++)
{
GIFunctionInfo *function = g_struct_info_get_method (info, i);
write_function_info (namespace, function, file);
g_base_info_unref ((GIBaseInfo *)function);
}
}
xml_end_element_unchecked (file);
}
static void
write_value_info (const gchar *namespace,
GIValueInfo *info,
Xml *file)
{
const gchar *name;
glong value;
gboolean deprecated;
name = g_base_info_get_name ((GIBaseInfo *)info);
value = g_value_info_get_value (info);
deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info);
xml_start_element (file, "member");
xml_printf (file, " name=\"%s\" value=\"%ld\"", name, value);
if (deprecated)
xml_printf (file, " deprecated=\"1\"");
write_attributes (file, (GIBaseInfo*) info);
xml_end_element (file, "member");
}
static void
write_constant_value (const gchar *namespace,
GITypeInfo *type,
GArgument *value,
Xml *file)
{
switch (g_type_info_get_tag (type))
{
case GI_TYPE_TAG_BOOLEAN:
xml_printf (file, "%d", value->v_boolean);
break;
case GI_TYPE_TAG_INT8:
xml_printf (file, "%d", value->v_int8);
break;
case GI_TYPE_TAG_UINT8:
xml_printf (file, "%d", value->v_uint8);
break;
case GI_TYPE_TAG_INT16:
xml_printf (file, "%" G_GINT16_FORMAT, value->v_int16);
break;
case GI_TYPE_TAG_UINT16:
xml_printf (file, "%" G_GUINT16_FORMAT, value->v_uint16);
break;
case GI_TYPE_TAG_INT32:
xml_printf (file, "%" G_GINT32_FORMAT, value->v_int32);
break;
case GI_TYPE_TAG_UINT32:
xml_printf (file, "%" G_GUINT32_FORMAT, value->v_uint32);
break;
case GI_TYPE_TAG_INT64:
xml_printf (file, "%" G_GINT64_FORMAT, value->v_int64);
break;
case GI_TYPE_TAG_UINT64:
xml_printf (file, "%" G_GUINT64_FORMAT, value->v_uint64);
break;
case GI_TYPE_TAG_INT:
xml_printf (file, "%d", value->v_int);
break;
case GI_TYPE_TAG_UINT:
xml_printf (file, "%d", value->v_uint);
break;
case GI_TYPE_TAG_LONG:
xml_printf (file, "%ld", value->v_long);
break;
case GI_TYPE_TAG_ULONG:
xml_printf (file, "%ld", value->v_ulong);
break;
case GI_TYPE_TAG_SSIZE:
xml_printf (file, "%zd", value->v_ssize);
break;
case GI_TYPE_TAG_SIZE:
xml_printf (file, "%zd", value->v_size);
break;
case GI_TYPE_TAG_FLOAT:
xml_printf (file, "%f", value->v_float);
break;
case GI_TYPE_TAG_DOUBLE:
xml_printf (file, "%f", value->v_double);
break;
case GI_TYPE_TAG_UTF8:
case GI_TYPE_TAG_FILENAME:
xml_printf (file, "%s", value->v_string);
break;
default:
g_assert_not_reached ();
}
}
static void
write_constant_info (const gchar *namespace,
GIConstantInfo *info,
Xml *file)
{
GITypeInfo *type;
const gchar *name;
gboolean deprecated;
GArgument value;
name = g_base_info_get_name ((GIBaseInfo *)info);
deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info);
xml_start_element (file, "constant");
xml_printf (file, " name=\"%s\"", name);
type = g_constant_info_get_type (info);
xml_printf (file, " value=\"");
g_constant_info_get_value (info, &value);
write_constant_value (namespace, type, &value, file);
xml_printf (file, "\"");
write_type_info (namespace, type, file);
write_attributes (file, (GIBaseInfo*) info);
xml_end_element (file, "constant");
g_base_info_unref ((GIBaseInfo *)type);
}
static void
write_enum_info (const gchar *namespace,
GIEnumInfo *info,
Xml *file)
{
const gchar *name;
const gchar *type_name;
const gchar *type_init;
gboolean deprecated;
gint i;
name = g_base_info_get_name ((GIBaseInfo *)info);
deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info);
type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info);
type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info);
if (g_base_info_get_type ((GIBaseInfo *)info) == GI_INFO_TYPE_ENUM)
xml_start_element (file, "enumeration");
else
xml_start_element (file, "bitfield");
xml_printf (file, " name=\"%s\"", name);
if (type_init)
xml_printf (file, " glib:type-name=\"%s\" glib:get-type=\"%s\"", type_name, type_init);
if (deprecated)
xml_printf (file, " deprecated=\"1\"");
write_attributes (file, (GIBaseInfo*) info);
for (i = 0; i < g_enum_info_get_n_values (info); i++)
{
GIValueInfo *value = g_enum_info_get_value (info, i);
write_value_info (namespace, value, file);
g_base_info_unref ((GIBaseInfo *)value);
}
xml_end_element_unchecked (file);
}
static void
write_signal_info (const gchar *namespace,
GISignalInfo *info,
Xml *file)
{
GSignalFlags flags;
const gchar *name;
gboolean deprecated;
name = g_base_info_get_name ((GIBaseInfo *)info);
flags = g_signal_info_get_flags (info);
deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info);
xml_start_element (file, "glib:signal");
xml_printf (file, " name=\"%s\"", name);
if (deprecated)
xml_printf (file, " deprecated=\"1\"");
if (flags & G_SIGNAL_RUN_FIRST)
xml_printf (file, " when=\"FIRST\"");
else if (flags & G_SIGNAL_RUN_LAST)
xml_printf (file, " when=\"LAST\"");
else if (flags & G_SIGNAL_RUN_CLEANUP)
xml_printf (file, " when=\"CLEANUP\"");
if (flags & G_SIGNAL_NO_RECURSE)
xml_printf (file, " no-recurse=\"1\"");
if (flags & G_SIGNAL_DETAILED)
xml_printf (file, " detailed=\"1\"");
if (flags & G_SIGNAL_ACTION)
xml_printf (file, " action=\"1\"");
if (flags & G_SIGNAL_NO_HOOKS)
xml_printf (file, " no-hooks=\"1\"");
write_callable_info (namespace, (GICallableInfo*)info, file);
xml_end_element (file, "glib:signal");
}
static void
write_vfunc_info (const gchar *namespace,
GIVFuncInfo *info,
Xml *file)
{
GIVFuncInfoFlags flags;
const gchar *name;
GIFunctionInfo *invoker;
gboolean deprecated;
gint offset;
name = g_base_info_get_name ((GIBaseInfo *)info);
flags = g_vfunc_info_get_flags (info);
deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info);
offset = g_vfunc_info_get_offset (info);
invoker = g_vfunc_info_get_invoker (info);
xml_start_element (file, "virtual-method");
xml_printf (file, " name=\"%s\"", name);
if (deprecated)
xml_printf (file, " deprecated=\"1\"");
if (flags & GI_VFUNC_MUST_CHAIN_UP)
xml_printf (file, " must-chain-up=\"1\"");
if (flags & GI_VFUNC_MUST_OVERRIDE)
xml_printf (file, " override=\"always\"");
else if (flags & GI_VFUNC_MUST_NOT_OVERRIDE)
xml_printf (file, " override=\"never\"");
xml_printf (file, " offset=\"%d\"", offset);
if (invoker)
xml_printf (file, " invoker=\"%s\"", g_base_info_get_name ((GIBaseInfo*)invoker));
write_callable_info (namespace, (GICallableInfo*)info, file);
xml_end_element (file, "virtual-method");
}
static void
write_property_info (const gchar *namespace,
GIPropertyInfo *info,
Xml *file)
{
GParamFlags flags;
const gchar *name;
gboolean deprecated;
GITypeInfo *type;
name = g_base_info_get_name ((GIBaseInfo *)info);
flags = g_property_info_get_flags (info);
deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info);
xml_start_element (file, "property");
xml_printf (file, " name=\"%s\"", name);
if (deprecated)
xml_printf (file, " deprecated=\"1\"");
/* Properties are assumed to be read-only (see also girwriter.py) */
if (!(flags & G_PARAM_READABLE))
xml_printf (file, " readable=\"0\"");
if (flags & G_PARAM_WRITABLE)
xml_printf (file, " writable=\"1\"");
if (flags & G_PARAM_CONSTRUCT)
xml_printf (file, " construct=\"1\"");
if (flags & G_PARAM_CONSTRUCT_ONLY)
xml_printf (file, " construct-only=\"1\"");
write_attributes (file, (GIBaseInfo*) info);
type = g_property_info_get_type (info);
write_type_info (namespace, type, file);
xml_end_element (file, "property");
}
static void
write_object_info (const gchar *namespace,
GIObjectInfo *info,
Xml *file)
{
const gchar *name;
const gchar *type_name;
const gchar *type_init;
gboolean deprecated;
gboolean is_abstract;
GIObjectInfo *pnode;
GIStructInfo *class_struct;
gint i;
name = g_base_info_get_name ((GIBaseInfo *)info);
deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info);
is_abstract = g_object_info_get_abstract (info);
type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info);
type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info);
xml_start_element (file, "class");
xml_printf (file, " name=\"%s\"", name);
pnode = g_object_info_get_parent (info);
if (pnode)
{
write_type_name_attribute (namespace, (GIBaseInfo *)pnode, "parent", file);
g_base_info_unref ((GIBaseInfo *)pnode);
}
class_struct = g_object_info_get_class_struct (info);
if (class_struct)
{
write_type_name_attribute (namespace, (GIBaseInfo*) class_struct, "glib:type-struct", file);
g_base_info_unref ((GIBaseInfo*)class_struct);
}
if (is_abstract)
xml_printf (file, " abstract=\"1\"");
xml_printf (file, " glib:type-name=\"%s\" glib:get-type=\"%s\"", type_name, type_init);
if (deprecated)
xml_printf (file, " deprecated=\"1\"");
write_attributes (file, (GIBaseInfo*) info);
if (g_object_info_get_n_interfaces (info) > 0)
{
for (i = 0; i < g_object_info_get_n_interfaces (info); i++)
{
GIInterfaceInfo *imp = g_object_info_get_interface (info, i);
xml_start_element (file, "implements");
write_type_name_attribute (namespace, (GIBaseInfo *)imp, "name", file);
xml_end_element (file, "implements");
g_base_info_unref ((GIBaseInfo*)imp);
}
}
for (i = 0; i < g_object_info_get_n_fields (info); i++)
{
GIFieldInfo *field = g_object_info_get_field (info, i);
write_field_info (namespace, field, NULL, file);
g_base_info_unref ((GIBaseInfo *)field);
}
for (i = 0; i < g_object_info_get_n_methods (info); i++)
{
GIFunctionInfo *function = g_object_info_get_method (info, i);
write_function_info (namespace, function, file);
g_base_info_unref ((GIBaseInfo *)function);
}
for (i = 0; i < g_object_info_get_n_properties (info); i++)
{
GIPropertyInfo *prop = g_object_info_get_property (info, i);
write_property_info (namespace, prop, file);
g_base_info_unref ((GIBaseInfo *)prop);
}
for (i = 0; i < g_object_info_get_n_signals (info); i++)
{
GISignalInfo *signal = g_object_info_get_signal (info, i);
write_signal_info (namespace, signal, file);
g_base_info_unref ((GIBaseInfo *)signal);
}
for (i = 0; i < g_object_info_get_n_vfuncs (info); i++)
{
GIVFuncInfo *vfunc = g_object_info_get_vfunc (info, i);
write_vfunc_info (namespace, vfunc, file);
g_base_info_unref ((GIBaseInfo *)vfunc);
}
for (i = 0; i < g_object_info_get_n_constants (info); i++)
{
GIConstantInfo *constant = g_object_info_get_constant (info, i);
write_constant_info (namespace, constant, file);
g_base_info_unref ((GIBaseInfo *)constant);
}
xml_end_element (file, "class");
}
static void
write_interface_info (const gchar *namespace,
GIInterfaceInfo *info,
Xml *file)
{
const gchar *name;
const gchar *type_name;
const gchar *type_init;
GIStructInfo *class_struct;
gboolean deprecated;
gint i;
name = g_base_info_get_name ((GIBaseInfo *)info);
deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info);
type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info);
type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info);
xml_start_element (file, "interface");
xml_printf (file, " name=\"%s\" glib:type-name=\"%s\" glib:get-type=\"%s\"",
name, type_name, type_init);
class_struct = g_interface_info_get_iface_struct (info);
if (class_struct)
{
write_type_name_attribute (namespace, (GIBaseInfo*) class_struct, "glib:type-struct", file);
g_base_info_unref ((GIBaseInfo*)class_struct);
}
if (deprecated)
xml_printf (file, " deprecated=\"1\"");
write_attributes (file, (GIBaseInfo*) info);
if (g_interface_info_get_n_prerequisites (info) > 0)
{
for (i = 0; i < g_interface_info_get_n_prerequisites (info); i++)
{
GIBaseInfo *req = g_interface_info_get_prerequisite (info, i);
xml_start_element (file, "prerequisite");
write_type_name_attribute (namespace, req, "name", file);
xml_end_element_unchecked (file);
g_base_info_unref (req);
}
}
for (i = 0; i < g_interface_info_get_n_methods (info); i++)
{
GIFunctionInfo *function = g_interface_info_get_method (info, i);
write_function_info (namespace, function, file);
g_base_info_unref ((GIBaseInfo *)function);
}
for (i = 0; i < g_interface_info_get_n_properties (info); i++)
{
GIPropertyInfo *prop = g_interface_info_get_property (info, i);
write_property_info (namespace, prop, file);
g_base_info_unref ((GIBaseInfo *)prop);
}
for (i = 0; i < g_interface_info_get_n_signals (info); i++)
{
GISignalInfo *signal = g_interface_info_get_signal (info, i);
write_signal_info (namespace, signal, file);
g_base_info_unref ((GIBaseInfo *)signal);
}
for (i = 0; i < g_interface_info_get_n_vfuncs (info); i++)
{
GIVFuncInfo *vfunc = g_interface_info_get_vfunc (info, i);
write_vfunc_info (namespace, vfunc, file);
g_base_info_unref ((GIBaseInfo *)vfunc);
}
for (i = 0; i < g_interface_info_get_n_constants (info); i++)
{
GIConstantInfo *constant = g_interface_info_get_constant (info, i);
write_constant_info (namespace, constant, file);
g_base_info_unref ((GIBaseInfo *)constant);
}
xml_end_element (file, "interface");
}
static void
write_error_domain_info (const gchar *namespace,
GIErrorDomainInfo *info,
Xml *file)
{
GIBaseInfo *enum_;
const gchar *name, *quark;
name = g_base_info_get_name ((GIBaseInfo *)info);
quark = g_error_domain_info_get_quark (info);
enum_ = (GIBaseInfo *)g_error_domain_info_get_codes (info);
xml_start_element (file, "errordomain");
xml_printf (file, " name=\"%s\" get-quark=\"%s\"",
name, quark);
write_type_name_attribute (namespace, enum_, "codes", file);
xml_end_element (file, "errordomain");
g_base_info_unref (enum_);
}
static void
write_union_info (const gchar *namespace,
GIUnionInfo *info,
Xml *file)
{
const gchar *name;
const gchar *type_name;
const gchar *type_init;
gboolean deprecated;
gint i;
gint size;
name = g_base_info_get_name ((GIBaseInfo *)info);
deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info);
type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info);
type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info);
xml_start_element (file, "union");
xml_printf (file, " name=\"%s\"", name);
if (type_name)
xml_printf (file, " type-name=\"%s\" get-type=\"%s\"", type_name, type_init);
if (deprecated)
xml_printf (file, " deprecated=\"1\"");
size = g_union_info_get_size (info);
if (show_all && size >= 0)
xml_printf (file, " size=\"%d\"", size);
write_attributes (file, (GIBaseInfo*) info);
if (g_union_info_is_discriminated (info))
{
gint offset;
GITypeInfo *type;
offset = g_union_info_get_discriminator_offset (info);
type = g_union_info_get_discriminator_type (info);
xml_start_element (file, "discriminator");
xml_printf (file, " offset=\"%d\" type=\"", offset);
write_type_info (namespace, type, file);
xml_end_element (file, "discriminator");
g_base_info_unref ((GIBaseInfo *)type);
}
for (i = 0; i < g_union_info_get_n_fields (info); i++)
{
GIFieldInfo *field = g_union_info_get_field (info, i);
GIConstantInfo *constant = g_union_info_get_discriminator (info, i);
write_field_info (namespace, field, constant, file);
g_base_info_unref ((GIBaseInfo *)field);
if (constant)
g_base_info_unref ((GIBaseInfo *)constant);
}
for (i = 0; i < g_union_info_get_n_methods (info); i++)
{
GIFunctionInfo *function = g_union_info_get_method (info, i);
write_function_info (namespace, function, file);
g_base_info_unref ((GIBaseInfo *)function);
}
xml_end_element (file, "union");
}
static void
write_repository (const char *namespace,
gboolean needs_prefix)
{
FILE *ofile;
gint i, j;
char **dependencies;
GIRepository *repository;
Xml *xml;
repository = g_irepository_get_default ();
if (output == NULL)
ofile = stdout;
else
{
gchar *filename;
if (needs_prefix)
filename = g_strdup_printf ("%s-%s", namespace, output);
else
filename = g_strdup (output);
ofile = g_fopen (filename, "w");
if (ofile == NULL)
{
g_fprintf (stderr, "failed to open '%s': %s\n",
filename, g_strerror (errno));
g_free (filename);
return;
}
g_free (filename);
}
xml = xml_open (ofile);
xml_printf (xml, "<?xml version=\"1.0\"?>\n");
xml_start_element (xml, "repository");
xml_printf (xml, " version=\"1.0\"\n"
" xmlns=\"http://www.gtk.org/introspection/core/1.0\"\n"
" xmlns:c=\"http://www.gtk.org/introspection/c/1.0\"\n"
" xmlns:glib=\"http://www.gtk.org/introspection/glib/1.0\"");
dependencies = g_irepository_get_dependencies (repository,
namespace);
if (dependencies != NULL)
{
for (i = 0; dependencies[i]; i++)
{
char **parts = g_strsplit (dependencies[i], "-", 2);
xml_start_element (xml, "include");
xml_printf (xml, " name=\"%s\" version=\"%s\"", parts[0], parts[1]);
xml_end_element (xml, "include");
g_strfreev (parts);
}
}
if (TRUE)
{
const gchar *shared_library;
const gchar *c_prefix;
const char *ns = namespace;
const char *version;
version = g_irepository_get_version (repository, ns);
shared_library = g_irepository_get_shared_library (repository, ns);
c_prefix = g_irepository_get_c_prefix (repository, ns);
xml_start_element (xml, "namespace");
xml_printf (xml, " name=\"%s\" version=\"%s\"", ns, version);
if (shared_library)
xml_printf (xml, " shared-library=\"%s\"", shared_library);
if (c_prefix)
xml_printf (xml, " c:prefix=\"%s\"", c_prefix);
for (j = 0; j < g_irepository_get_n_infos (repository, ns); j++)
{
GIBaseInfo *info = g_irepository_get_info (repository, ns, j);
switch (g_base_info_get_type (info))
{
case GI_INFO_TYPE_FUNCTION:
write_function_info (ns, (GIFunctionInfo *)info, xml);
break;
case GI_INFO_TYPE_CALLBACK:
write_callback_info (ns, (GICallbackInfo *)info, xml);
break;
case GI_INFO_TYPE_STRUCT:
case GI_INFO_TYPE_BOXED:
write_struct_info (ns, (GIStructInfo *)info, xml);
break;
case GI_INFO_TYPE_UNION:
write_union_info (ns, (GIUnionInfo *)info, xml);
break;
case GI_INFO_TYPE_ENUM:
case GI_INFO_TYPE_FLAGS:
write_enum_info (ns, (GIEnumInfo *)info, xml);
break;
case GI_INFO_TYPE_CONSTANT:
write_constant_info (ns, (GIConstantInfo *)info, xml);
break;
case GI_INFO_TYPE_OBJECT:
write_object_info (ns, (GIObjectInfo *)info, xml);
break;
case GI_INFO_TYPE_INTERFACE:
write_interface_info (ns, (GIInterfaceInfo *)info, xml);
break;
case GI_INFO_TYPE_ERROR_DOMAIN:
write_error_domain_info (ns, (GIErrorDomainInfo *)info, xml);
break;
default:
g_error ("unknown info type %d\n", g_base_info_get_type (info));
}
g_base_info_unref (info);
}
xml_end_element (xml, "namespace");
}
xml_end_element (xml, "repository");
xml_free (xml);
}
static const guchar *
load_typelib (const gchar *filename,
GModule **dlhandle,
gsize *len)
{
guchar *typelib;
gsize *typelib_size;
GModule *handle;
handle = g_module_open (filename, G_MODULE_BIND_LOCAL|G_MODULE_BIND_LAZY);
if (handle == NULL)
{
g_printerr ("Could not load typelib from '%s': %s\n",
filename, g_module_error ());
return NULL;
}
if (!g_module_symbol (handle, "_G_TYPELIB", (gpointer *) &typelib))
{
g_printerr ("Could not load typelib from '%s': %s\n",
filename, g_module_error ());
return NULL;
}
if (!g_module_symbol (handle, "_G_TYPELIB_SIZE", (gpointer *) &typelib_size))
{
g_printerr ("Could not load typelib from '%s': %s\n",
filename, g_module_error ());
return NULL;
}
*len = *typelib_size;
if (dlhandle)
*dlhandle = handle;
return typelib;
}
int
main (int argc, char *argv[])
{
gboolean shlib = FALSE;
gchar **input = NULL;
GOptionContext *context;
GError *error = NULL;
gboolean needs_prefix;
gint i;
GTypelib *data;
GOptionEntry options[] =
{
{ "shlib", 0, 0, G_OPTION_ARG_NONE, &shlib, "handle typelib embedded in shlib", NULL },
{ "output", 'o', 0, G_OPTION_ARG_FILENAME, &output, "output file", "FILE" },
{ "includedir", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &includedirs, "include directories in GIR search path", NULL },
{ "all", 0, 0, G_OPTION_ARG_NONE, &show_all, "show all available information", NULL, },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &input, NULL, NULL },
{ NULL, }
};
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
g_type_init ();
g_typelib_check_sanity ();
context = g_option_context_new ("");
g_option_context_add_main_entries (context, options, NULL);
g_option_context_parse (context, &argc, &argv, &error);
if (!input)
{
g_fprintf (stderr, "no input files\n");
return 1;
}
if (includedirs != NULL)
for (i = 0; includedirs[i]; i++)
g_irepository_prepend_search_path (includedirs[i]);
for (i = 0; input[i]; i++)
{
GModule *dlhandle = NULL;
const guchar *typelib;
gsize len;
const char *namespace;
if (!shlib)
{
if (!g_file_get_contents (input[i], (gchar **)&typelib, &len, &error))
{
g_fprintf (stderr, "failed to read '%s': %s\n",
input[i], error->message);
g_clear_error (&error);
continue;
}
}
else
{
typelib = load_typelib (input[i], &dlhandle, &len);
if (!typelib)
{
g_fprintf (stderr, "failed to load typelib from '%s'\n",
input[i]);
continue;
}
}
if (input[i + 1] && output)
needs_prefix = TRUE;
else
needs_prefix = FALSE;
data = g_typelib_new_from_const_memory (typelib, len);
{
GError *error = NULL;
if (!g_typelib_validate (data, &error)) {
g_printerr ("typelib not valid: %s\n", error->message);
g_clear_error (&error);
return 1;
}
}
namespace = g_irepository_load_typelib (g_irepository_get_default (), data, 0,
&error);
if (namespace == NULL)
{
g_printerr ("failed to load typelib: %s\n", error->message);
return 1;
}
write_repository (namespace, needs_prefix);
if (dlhandle)
{
g_module_close (dlhandle);
dlhandle = NULL;
}
/* when writing to stdout, stop after the first module */
if (input[i + 1] && !output)
{
g_fprintf (stderr, "warning, %d modules omitted\n",
g_strv_length (input) - 1);
break;
}
}
return 0;
}