diff -r e20de85af2ee -r ce057bb09d0b ofdbus/dbus-glib/dbus/dbus-binding-tool-glib.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ofdbus/dbus-glib/dbus/dbus-binding-tool-glib.c Fri Jun 04 16:20:51 2010 +0100 @@ -0,0 +1,1640 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-binding-tool-glib.c: Output C glue + * + * Copyright (C) 2003, 2004, 2005 Red Hat, Inc. + * Copyright (C) 2005 Nokia + * Portion Copyright © 2008 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SYMBIAN32__ +#include +#else +#include "config.h" +#endif //__SYMBIAN32__ +#include "dbus/dbus-glib.h" +#include "dbus-gidl.h" +#include "dbus-gparser.h" +#include "dbus-gutils.h" +#include "dbus-gtype-specialized.h" +#include "dbus-gsignature.h" +#include "dbus-gvalue-utils.h" +#include "dbus-glib-tool.h" +#include "dbus-binding-tool-glib.h" + +#ifndef __SYMBIAN32__ +#include +#include +#define _(x) dgettext (GETTEXT_PACKAGE, x) +#define N_(x) x +#else + +#define _(x) x +#define N_(x) x +#endif + +#include +#include +#include +#include + +#define MARSHAL_PREFIX "dbus_glib_marshal_" + +typedef struct +{ + gboolean ignore_unsupported; + const char* prefix; + GIOChannel *channel; + + GError **error; + + GHashTable *generated; + GString *blob; + GString *signal_blob; + GString *property_blob; + guint count; +} DBusBindingToolCData; + +static gboolean gather_marshallers (BaseInfo *base, DBusBindingToolCData *data, GError **error); +static gboolean generate_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error); +static gboolean generate_client_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error); + +static const char * +dbus_g_type_get_marshal_name (GType gtype) +{ + switch (G_TYPE_FUNDAMENTAL (gtype)) + { + case G_TYPE_NONE: + return "NONE"; + case G_TYPE_BOOLEAN: + return "BOOLEAN"; + case G_TYPE_UCHAR: + return "UCHAR"; + case G_TYPE_INT: + return "INT"; + case G_TYPE_UINT: + return "UINT"; + case G_TYPE_INT64: + return "INT64"; + case G_TYPE_UINT64: + return "UINT64"; + case G_TYPE_DOUBLE: + return "DOUBLE"; + case G_TYPE_STRING: + return "STRING"; + case G_TYPE_POINTER: + return "POINTER"; + case G_TYPE_BOXED: + return "BOXED"; + case G_TYPE_OBJECT: + return "OBJECT"; + default: + return NULL; + } +} + +/* This entire function is kind of...ugh. */ +static const char * +dbus_g_type_get_c_name (GType gtype) +{ + GType subtype; + if (dbus_g_type_is_struct (gtype)) + { + return "GValueArray"; + } + if (dbus_g_type_is_collection (gtype)) + { + subtype = dbus_g_type_get_collection_specialization(gtype); + if (_dbus_g_type_is_fixed (subtype)) + return "GArray"; + else + return "GPtrArray"; + } + + if (dbus_g_type_is_map (gtype)) + return "GHashTable"; + + if (g_type_is_a (gtype, G_TYPE_STRING)) + return "char *"; + + /* This one is even more hacky...we get an extra * + * because G_TYPE_STRV is a G_TYPE_BOXED + */ + if (g_type_is_a (gtype, G_TYPE_STRV)) + return "char *"; + + if (g_type_is_a (gtype, DBUS_TYPE_G_OBJECT_PATH)) + return "char"; + + return g_type_name (gtype); +} + +static gboolean +compute_gsignature (MethodInfo *method, GType *rettype, GArray **params, GError **error) +{ + GSList *elt; + GType retval_type; + GArray *ret; + gboolean is_async; + const char *arg_type; + gboolean retval_signals_error; + + is_async = method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_ASYNC) != NULL; + retval_signals_error = FALSE; + + ret = g_array_new (TRUE, TRUE, sizeof (GType)); + + if (is_async) + retval_type = G_TYPE_NONE; + else + { + gboolean found_retval; + + /* Look for return value */ + found_retval = FALSE; + for (elt = method_info_get_args (method); elt; elt = elt->next) + { + ArgInfo *arg = elt->data; + const char *returnval_annotation; + + returnval_annotation = arg_info_get_annotation (arg, DBUS_GLIB_ANNOTATION_RETURNVAL); + if (returnval_annotation != NULL) + { + arg_type = arg_info_get_type (arg); + retval_type = _dbus_gtype_from_signature (arg_type, FALSE); + if (retval_type == G_TYPE_INVALID) + goto invalid_type; + found_retval = TRUE; + if (!strcmp (returnval_annotation, "error")) + retval_signals_error = TRUE; + break; + } + } + if (!found_retval) + { + retval_type = G_TYPE_BOOLEAN; + retval_signals_error = TRUE; + } + } + + *rettype = retval_type; + + /* Handle all input arguments */ + for (elt = method_info_get_args (method); elt; elt = elt->next) + { + ArgInfo *arg = elt->data; + if (arg_info_get_direction (arg) == ARG_IN) + { + GType gtype; + + arg_type = arg_info_get_type (arg); + gtype = _dbus_gtype_from_signature (arg_type, FALSE); + if (gtype == G_TYPE_INVALID) + goto invalid_type; + + g_array_append_val (ret, gtype); + } + } + + if (!is_async) + { + /* Append pointer for each out arg storage */ + for (elt = method_info_get_args (method); elt; elt = elt->next) + { + ArgInfo *arg = elt->data; + + /* Skip return value */ + if (arg_info_get_annotation (arg, DBUS_GLIB_ANNOTATION_RETURNVAL) != NULL) + continue; + + if (arg_info_get_direction (arg) == ARG_OUT) + { + GType gtype; + arg_type = arg_info_get_type (arg); + gtype = _dbus_gtype_from_signature (arg_type, FALSE); + if (gtype == G_TYPE_INVALID) + goto invalid_type; + /* We actually just need a pointer for the return value + storage */ + gtype = G_TYPE_POINTER; + g_array_append_val (ret, gtype); + } + } + + if (retval_signals_error) + { + /* Final GError parameter */ + GType gtype = G_TYPE_POINTER; + g_array_append_val (ret, gtype); + } + } + else + { + /* Context pointer */ + GType gtype = G_TYPE_POINTER; + g_array_append_val (ret, gtype); + } + + *params = ret; + return TRUE; + + invalid_type: + g_set_error (error, + DBUS_BINDING_TOOL_ERROR, + DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION, + _("Unsupported conversion from D-BUS type %s to glib-genmarshal type"), + arg_type); + return FALSE; +} + + +static char * +compute_marshaller (MethodInfo *method, GError **error) +{ + GArray *signature; + GType rettype; + const char *marshal_name; + GString *ret; + guint i; + + if (!compute_gsignature (method, &rettype, &signature, error)) + return NULL; + + ret = g_string_new (""); + marshal_name = dbus_g_type_get_marshal_name (rettype); + g_assert (marshal_name != NULL); + g_string_append (ret, marshal_name); + g_string_append_c (ret, ':'); + for (i = 0; i < signature->len; i++) + { + marshal_name = dbus_g_type_get_marshal_name (g_array_index (signature, GType, i)); + g_assert (marshal_name != NULL); + g_string_append (ret, marshal_name); + if (i < signature->len - 1) + g_string_append_c (ret, ','); + } + if (signature->len == 0) + { + marshal_name = dbus_g_type_get_marshal_name (G_TYPE_NONE); + g_assert (marshal_name != NULL); + g_string_append (ret, marshal_name); + } + g_array_free (signature, TRUE); + return g_string_free (ret, FALSE); +} + +static char * +compute_marshaller_name (MethodInfo *method, const char *prefix, GError **error) +{ + GString *ret; + GArray *signature; + GType rettype; + const char *marshal_name; + guint i; + + if (!compute_gsignature (method, &rettype, &signature, error)) + return NULL; + + ret = g_string_new (MARSHAL_PREFIX); + g_string_append (ret, prefix); + g_string_append_c (ret, '_'); + + marshal_name = dbus_g_type_get_marshal_name (rettype); + g_assert (marshal_name != NULL); + g_string_append (ret, marshal_name); + g_string_append (ret, "__"); + for (i = 0; i < signature->len; i++) + { + marshal_name = dbus_g_type_get_marshal_name (g_array_index (signature, GType, i)); + g_assert (marshal_name != NULL); + g_string_append (ret, marshal_name); + if (i < signature->len - 1) + g_string_append_c (ret, '_'); + } + if (signature->len == 0) + { + marshal_name = dbus_g_type_get_marshal_name (G_TYPE_NONE); + g_assert (marshal_name != NULL); + g_string_append (ret, marshal_name); + } + g_array_free (signature, TRUE); + return g_string_free (ret, FALSE); +} + +static gboolean +gather_marshallers_list (GSList *list, DBusBindingToolCData *data, GError **error) +{ + GSList *tmp; + + tmp = list; + while (tmp != NULL) + { + if (!gather_marshallers (tmp->data, data, error)) + return FALSE; + tmp = tmp->next; + } + return TRUE; +} + +static gboolean +gather_marshallers (BaseInfo *base, DBusBindingToolCData *data, GError **error) +{ + if (base_info_get_type (base) == INFO_TYPE_NODE) + { + if (!gather_marshallers_list (node_info_get_nodes ((NodeInfo *) base), + data, error)) + return FALSE; + if (!gather_marshallers_list (node_info_get_interfaces ((NodeInfo *) base), + data, error)) + return FALSE; + } + else + { + InterfaceInfo *interface; + GSList *methods; + GSList *tmp; + const char *interface_c_name; + + interface = (InterfaceInfo *) base; + interface_c_name = interface_info_get_annotation (interface, DBUS_GLIB_ANNOTATION_C_SYMBOL); + if (interface_c_name == NULL) + { + if (!data->prefix) + return TRUE; + } + + methods = interface_info_get_methods (interface); + + /* Generate the necessary marshallers for the methods. */ + + for (tmp = methods; tmp != NULL; tmp = g_slist_next (tmp)) + { + MethodInfo *method; + char *marshaller_name; + + method = (MethodInfo *) tmp->data; + + marshaller_name = compute_marshaller (method, error); + if (!marshaller_name) + return FALSE; + + if (g_hash_table_lookup (data->generated, marshaller_name)) + { + g_free (marshaller_name); + continue; + } + + g_hash_table_insert (data->generated, marshaller_name, NULL); + } + + } + return TRUE; +} + +static gboolean +generate_glue_list (GSList *list, DBusBindingToolCData *data, GError **error) +{ + GSList *tmp; + + tmp = list; + while (tmp != NULL) + { + if (!generate_glue (tmp->data, data, error)) + return FALSE; + tmp = tmp->next; + } + return TRUE; +} + +#define WRITE_OR_LOSE(x) do { gsize bytes_written; if (!g_io_channel_write_chars (channel, x, -1, &bytes_written, error)) goto io_lose; } while (0) + +static gboolean +write_printf_to_iochannel (const char *fmt, GIOChannel *channel, GError **error, ...) +{ + char *str; + va_list args; + GIOStatus status; + gsize written; + gboolean ret; + + va_start (args, error); + + str = g_strdup_vprintf (fmt, args); + if ((status = g_io_channel_write_chars (channel, str, -1, &written, error)) == G_IO_STATUS_NORMAL) + ret = TRUE; + else + ret = FALSE; + + g_free (str); + + va_end (args); + + return ret; +} + +static gboolean +write_quoted_string (GIOChannel *channel, GString *string, GError **error) +{ + guint i; + + WRITE_OR_LOSE ("\""); + for (i = 0; i < string->len; i++) + { + if (string->str[i] != '\0') + { + if (!g_io_channel_write_chars (channel, string->str + i, 1, NULL, error)) + return FALSE; + } + else + { + if (!g_io_channel_write_chars (channel, "\\0", -1, NULL, error)) + return FALSE; + } + } + WRITE_OR_LOSE ("\\0\""); + return TRUE; + io_lose: + return FALSE; +} + +static gboolean +generate_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error) +{ + if (base_info_get_type (base) == INFO_TYPE_NODE) + { + GString *object_introspection_data_blob; + GIOChannel *channel; + + channel = data->channel; + + object_introspection_data_blob = g_string_new_len ("", 0); + + data->blob = object_introspection_data_blob; + data->count = 0; + + data->signal_blob = g_string_new_len ("", 0); + data->property_blob = g_string_new_len ("", 0); + + if (!write_printf_to_iochannel ("static const DBusGMethodInfo dbus_glib_%s_methods[] = {\n", channel, error, data->prefix)) + goto io_lose; + + if (!generate_glue_list (node_info_get_nodes ((NodeInfo *) base), + data, error)) + return FALSE; + if (!generate_glue_list (node_info_get_interfaces ((NodeInfo *) base), + data, error)) + return FALSE; + + WRITE_OR_LOSE ("};\n\n"); + + /* Information about the object. */ + + if (!write_printf_to_iochannel ("const DBusGObjectInfo dbus_glib_%s_object_info = {\n", + channel, error, data->prefix)) + goto io_lose; + WRITE_OR_LOSE (" 0,\n"); + if (!write_printf_to_iochannel (" dbus_glib_%s_methods,\n", channel, error, data->prefix)) + goto io_lose; + if (!write_printf_to_iochannel (" %d,\n", channel, error, data->count)) + goto io_lose; + + if (!write_quoted_string (channel, object_introspection_data_blob, error)) + goto io_lose; + WRITE_OR_LOSE (",\n"); + if (!write_quoted_string (channel, data->signal_blob, error)) + goto io_lose; + WRITE_OR_LOSE (",\n"); + if (!write_quoted_string (channel, data->property_blob, error)) + goto io_lose; + WRITE_OR_LOSE ("\n};\n\n"); + + g_string_free (object_introspection_data_blob, TRUE); + g_string_free (data->signal_blob, TRUE); + g_string_free (data->property_blob, TRUE); + } + else + { + GIOChannel *channel; + InterfaceInfo *interface; + GSList *methods; + GSList *signals; + GSList *properties; + GSList *tmp; + const char *interface_c_name; + GString *object_introspection_data_blob; + + channel = data->channel; + object_introspection_data_blob = data->blob; + + interface = (InterfaceInfo *) base; + interface_c_name = interface_info_get_annotation (interface, DBUS_GLIB_ANNOTATION_C_SYMBOL); + if (interface_c_name == NULL) + { + if (data->prefix == NULL) + return TRUE; + interface_c_name = data->prefix; + } + + methods = interface_info_get_methods (interface); + + /* Table of marshalled methods. */ + + for (tmp = methods; tmp != NULL; tmp = g_slist_next (tmp)) + { + MethodInfo *method; + char *marshaller_name; + char *method_c_name; + gboolean async = FALSE; + GSList *args; + gboolean found_retval = FALSE; + + method = (MethodInfo *) tmp->data; + method_c_name = g_strdup (method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_C_SYMBOL)); + if (method_c_name == NULL) + { + char *method_name_uscored; + method_name_uscored = _dbus_gutils_wincaps_to_uscore (method_info_get_name (method)); + method_c_name = g_strdup_printf ("%s_%s", + interface_c_name, + method_name_uscored); + g_free (method_name_uscored); + } + + if (!write_printf_to_iochannel (" { (GCallback) %s, ", channel, error, + method_c_name)) + goto io_lose; + + marshaller_name = compute_marshaller_name (method, data->prefix, error); + if (!marshaller_name) + goto io_lose; + + if (!write_printf_to_iochannel ("%s, %d },\n", channel, error, + marshaller_name, + object_introspection_data_blob->len)) + { + g_free (marshaller_name); + goto io_lose; + } + + if (method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_ASYNC) != NULL) + async = TRUE; + + /* Object method data blob format: + * \0\0(\0\0\0)*\0 + */ + + g_string_append (object_introspection_data_blob, interface_info_get_name (interface)); + g_string_append_c (object_introspection_data_blob, '\0'); + + g_string_append (object_introspection_data_blob, method_info_get_name (method)); + g_string_append_c (object_introspection_data_blob, '\0'); + + g_string_append_c (object_introspection_data_blob, async ? 'A' : 'S'); + g_string_append_c (object_introspection_data_blob, '\0'); + + for (args = method_info_get_args (method); args; args = args->next) + { + ArgInfo *arg; + char direction; + const char *returnval_annotation; + + arg = args->data; + + g_string_append (object_introspection_data_blob, arg_info_get_name (arg)); + g_string_append_c (object_introspection_data_blob, '\0'); + + switch (arg_info_get_direction (arg)) + { + case ARG_IN: + direction = 'I'; + break; + case ARG_OUT: + direction = 'O'; + break; + case ARG_INVALID: + default: + g_assert_not_reached (); + direction = 0; /* silence gcc */ + break; + } + g_string_append_c (object_introspection_data_blob, direction); + g_string_append_c (object_introspection_data_blob, '\0'); + + if (arg_info_get_annotation (arg, DBUS_GLIB_ANNOTATION_CONST) != NULL) + { + if (arg_info_get_direction (arg) == ARG_IN) + { + g_set_error (error, + DBUS_BINDING_TOOL_ERROR, + DBUS_BINDING_TOOL_ERROR_INVALID_ANNOTATION, + "Input argument \"%s\" cannot have const annotation in method \"%s\" of interface \"%s\"\n", + arg_info_get_name (arg), + method_info_get_name (method), + interface_info_get_name (interface)); + return FALSE; + } + g_string_append_c (object_introspection_data_blob, 'C'); + g_string_append_c (object_introspection_data_blob, '\0'); + } + else if (arg_info_get_direction (arg) == ARG_OUT) + { + g_string_append_c (object_introspection_data_blob, 'F'); + g_string_append_c (object_introspection_data_blob, '\0'); + } + + returnval_annotation = arg_info_get_annotation (arg, DBUS_GLIB_ANNOTATION_RETURNVAL); + if (returnval_annotation != NULL) + { + GType gtype; + + if (found_retval) + { + g_set_error (error, + DBUS_BINDING_TOOL_ERROR, + DBUS_BINDING_TOOL_ERROR_INVALID_ANNOTATION, + "Multiple arguments with return value annotation in method \"%s\" of interface \"%s\"\n", + method_info_get_name (method), + interface_info_get_name (interface)); + return FALSE; + } + found_retval = TRUE; + if (arg_info_get_direction (arg) == ARG_IN) + { + g_set_error (error, + DBUS_BINDING_TOOL_ERROR, + DBUS_BINDING_TOOL_ERROR_INVALID_ANNOTATION, + "Input argument \"%s\" cannot have return value annotation in method \"%s\" of interface \"%s\"\n", + arg_info_get_name (arg), + method_info_get_name (method), + interface_info_get_name (interface)); + return FALSE; + } + if (!strcmp ("", returnval_annotation)) + g_string_append_c (object_introspection_data_blob, 'R'); + else if (!strcmp ("error", returnval_annotation)) + { + gtype = _dbus_gtype_from_signature (arg_info_get_type (arg), TRUE); + if (!_dbus_gtype_can_signal_error (gtype)) + { + g_set_error (error, + DBUS_BINDING_TOOL_ERROR, + DBUS_BINDING_TOOL_ERROR_INVALID_ANNOTATION, + "Output argument \"%s\" cannot signal error with type \"%s\" in method \"%s\" of interface \"%s\"\n", + arg_info_get_name (arg), + g_type_name (gtype), + method_info_get_name (method), + interface_info_get_name (interface)); + return FALSE; + } + g_string_append_c (object_introspection_data_blob, 'E'); + } + else + { + g_set_error (error, + DBUS_BINDING_TOOL_ERROR, + DBUS_BINDING_TOOL_ERROR_INVALID_ANNOTATION, + "Invalid ReturnVal annotation for argument \"%s\" in method \"%s\" of interface \"%s\"\n", + arg_info_get_name (arg), + method_info_get_name (method), + interface_info_get_name (interface)); + return FALSE; + } + + g_string_append_c (object_introspection_data_blob, '\0'); + } + else if (arg_info_get_direction (arg) == ARG_OUT) + { + g_string_append_c (object_introspection_data_blob, 'N'); + g_string_append_c (object_introspection_data_blob, '\0'); + } + + g_string_append (object_introspection_data_blob, arg_info_get_type (arg)); + g_string_append_c (object_introspection_data_blob, '\0'); + } + + g_string_append_c (object_introspection_data_blob, '\0'); + + data->count++; + } + + signals = interface_info_get_signals (interface); + + for (tmp = signals; tmp != NULL; tmp = g_slist_next (tmp)) + { + SignalInfo *sig; + + sig = tmp->data; + + g_string_append (data->signal_blob, interface_info_get_name (interface)); + g_string_append_c (data->signal_blob, '\0'); + g_string_append (data->signal_blob, signal_info_get_name (sig)); + g_string_append_c (data->signal_blob, '\0'); + } + + properties = interface_info_get_properties (interface); + + for (tmp = properties; tmp != NULL; tmp = g_slist_next (tmp)) + { + PropertyInfo *prop; + + prop = tmp->data; + + g_string_append (data->property_blob, interface_info_get_name (interface)); + g_string_append_c (data->property_blob, '\0'); + g_string_append (data->property_blob, property_info_get_name (prop)); + g_string_append_c (data->property_blob, '\0'); + } + } + return TRUE; + io_lose: + return FALSE; +} + +static void +write_marshaller (gpointer key, gpointer value, gpointer user_data) +{ + DBusBindingToolCData *data; + const char *marshaller; + gsize bytes_written; + + data = user_data; + marshaller = key; + + if (data->error && *data->error) + return; + + if (g_io_channel_write_chars (data->channel, marshaller, -1, &bytes_written, data->error) == G_IO_STATUS_NORMAL) + g_io_channel_write_chars (data->channel, "\n", -1, &bytes_written, data->error); +} + +gboolean +dbus_binding_tool_output_glib_server (BaseInfo *info, GIOChannel *channel, const char *prefix, GError **error) +{ + gboolean ret; + GPtrArray *argv; + gint child_stdout; + GIOChannel *genmarshal_stdout; + GPid child_pid; + DBusBindingToolCData data; + char *tempfile_name; + gint tempfile_fd; + GIOStatus iostatus; + char buf[4096]; + gsize bytes_read, bytes_written; + + memset (&data, 0, sizeof (data)); + + dbus_g_type_specialized_init (); + _dbus_g_type_specialized_builtins_init (); + + data.prefix = prefix; + data.generated = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL); + data.error = error; + genmarshal_stdout = NULL; + tempfile_name = NULL; + + if (!gather_marshallers (info, &data, error)) + goto io_lose; + + tempfile_fd = g_file_open_tmp ("dbus-binding-tool-c-marshallers.XXXXXX", + &tempfile_name, error); + if (tempfile_fd < 0) + goto io_lose; + + data.channel = g_io_channel_unix_new (tempfile_fd); + if (!g_io_channel_set_encoding (data.channel, NULL, error)) + goto io_lose; + g_hash_table_foreach (data.generated, write_marshaller, &data); + if (error && *error != NULL) + { + ret = FALSE; + g_io_channel_close (data.channel); + g_io_channel_unref (data.channel); + goto io_lose; + } + + g_io_channel_close (data.channel); + g_io_channel_unref (data.channel); + + /* Now spawn glib-genmarshal to insert all our required marshallers */ + argv = g_ptr_array_new (); + g_ptr_array_add (argv, "glib-genmarshal"); + g_ptr_array_add (argv, "--header"); + g_ptr_array_add (argv, "--body"); + g_ptr_array_add (argv, g_strdup_printf ("--prefix=%s%s", MARSHAL_PREFIX, prefix)); + g_ptr_array_add (argv, tempfile_name); + g_ptr_array_add (argv, NULL); + if (!g_spawn_async_with_pipes (NULL, (char**)argv->pdata, NULL, + G_SPAWN_SEARCH_PATH, + NULL, NULL, + &child_pid, + NULL, + &child_stdout, NULL, error)) + { + g_ptr_array_free (argv, TRUE); + goto io_lose; + } + g_ptr_array_free (argv, TRUE); + + genmarshal_stdout = g_io_channel_unix_new (child_stdout); + if (!g_io_channel_set_encoding (genmarshal_stdout, NULL, error)) + goto io_lose; + + WRITE_OR_LOSE ("/* Generated by dbus-binding-tool; do not edit! */\n\n"); + + while ((iostatus = g_io_channel_read_chars (genmarshal_stdout, buf, sizeof (buf), + &bytes_read, error)) == G_IO_STATUS_NORMAL) + if (g_io_channel_write_chars (channel, buf, bytes_read, &bytes_written, error) != G_IO_STATUS_NORMAL) + goto io_lose; + if (iostatus != G_IO_STATUS_EOF) + goto io_lose; + + g_io_channel_close (genmarshal_stdout); + + WRITE_OR_LOSE ("#include \n"); + + data.channel = channel; + g_io_channel_ref (data.channel); + if (!generate_glue (info, &data, error)) + goto io_lose; + + ret = TRUE; + cleanup: + if (tempfile_name) + unlink (tempfile_name); + g_free (tempfile_name); + if (genmarshal_stdout) + g_io_channel_unref (genmarshal_stdout); + if (data.channel) + g_io_channel_unref (data.channel); + g_hash_table_destroy (data.generated); + + return ret; + io_lose: + ret = FALSE; + goto cleanup; +} + +static char * +iface_to_c_prefix (const char *iface) +{ + char **components; + char **component; + GString *ret; + gboolean first; + + components = g_strsplit (iface, ".", 0); + + first = TRUE; + ret = g_string_new (""); + for (component = components; *component; component++) + { + if (!first) + g_string_append_c (ret, '_'); + else + first = FALSE; + g_string_append (ret, *component); + } + g_strfreev (components); + return g_string_free (ret, FALSE); +} + +static char * +compute_client_method_name (const char *iface_prefix, MethodInfo *method) +{ + char *method_name_uscored, *ret; + + method_name_uscored = _dbus_gutils_wincaps_to_uscore (method_info_get_name (method)); + ret = g_strdup_printf ("%s_%s", iface_prefix, method_name_uscored); + g_free (method_name_uscored); + + return ret; +} + +static gboolean +write_formal_parameters (InterfaceInfo *iface, MethodInfo *method, GIOChannel *channel, GError **error) +{ + GSList *args; + + for (args = method_info_get_args (method); args; args = args->next) + { + ArgInfo *arg; + const char *type_str; + const char *type_suffix; + GType gtype; + int direction; + + arg = args->data; + + WRITE_OR_LOSE (", "); + + direction = arg_info_get_direction (arg); + + gtype = _dbus_gtype_from_signature (arg_info_get_type (arg), TRUE); + if (gtype == G_TYPE_INVALID) + { + g_set_error (error, + DBUS_BINDING_TOOL_ERROR, + DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION, + _("Unsupported conversion from D-BUS type signature \"%s\" to glib C type in method \"%s\" of interface \"%s\""), + arg_info_get_type (arg), + method_info_get_name (method), + interface_info_get_name (iface)); + return FALSE; + } + type_str = dbus_g_type_get_c_name (gtype); + g_assert (type_str); + /* Variants are special...*/ + if (gtype == G_TYPE_VALUE) + { + if (direction == ARG_IN) + type_suffix = "*"; + else + type_suffix = ""; + } + else if ((g_type_is_a (gtype, G_TYPE_BOXED) + || g_type_is_a (gtype, G_TYPE_OBJECT) + || g_type_is_a (gtype, G_TYPE_POINTER))) + type_suffix = "*"; + else + type_suffix = ""; + + + switch (direction) + { + case ARG_IN: + if (!write_printf_to_iochannel ("const %s%s IN_%s", channel, error, + type_str, + type_suffix, + arg_info_get_name (arg))) + goto io_lose; + break; + case ARG_OUT: + if (!write_printf_to_iochannel ("%s%s* OUT_%s", channel, error, + type_str, + type_suffix, + arg_info_get_name (arg))) + goto io_lose; + break; + case ARG_INVALID: + break; + } + } + + return TRUE; + io_lose: + return FALSE; +} + +#define MAP_FUNDAMENTAL(NAME) \ + case G_TYPE_ ## NAME: \ + return g_strdup ("G_TYPE_" #NAME); +#define MAP_KNOWN(NAME) \ + if (gtype == NAME) \ + return g_strdup (#NAME) +static char * +dbus_g_type_get_lookup_function (GType gtype) +{ + char *type_lookup; + switch (gtype) + { + MAP_FUNDAMENTAL(CHAR); + MAP_FUNDAMENTAL(UCHAR); + MAP_FUNDAMENTAL(BOOLEAN); + MAP_FUNDAMENTAL(LONG); + MAP_FUNDAMENTAL(ULONG); + MAP_FUNDAMENTAL(INT); + MAP_FUNDAMENTAL(UINT); + MAP_FUNDAMENTAL(INT64); + MAP_FUNDAMENTAL(UINT64); + MAP_FUNDAMENTAL(FLOAT); + MAP_FUNDAMENTAL(DOUBLE); + MAP_FUNDAMENTAL(STRING); + } + if (dbus_g_type_is_collection (gtype)) + { + GType elt_gtype; + char *sublookup; + + elt_gtype = dbus_g_type_get_collection_specialization (gtype); + sublookup = dbus_g_type_get_lookup_function (elt_gtype); + g_assert (sublookup); + + if (_dbus_g_type_is_fixed (elt_gtype)) + { + type_lookup = g_strdup_printf ("dbus_g_type_get_collection " + "(\"GArray\", %s)", sublookup); + } + else + { + type_lookup = g_strdup_printf ("dbus_g_type_get_collection " + "(\"GPtrArray\", %s)", sublookup); + } + + g_free (sublookup); + + return type_lookup; + } + else if (dbus_g_type_is_map (gtype)) + { + GType key_gtype; + char *key_lookup; + GType value_gtype; + char *value_lookup; + + key_gtype = dbus_g_type_get_map_key_specialization (gtype); + value_gtype = dbus_g_type_get_map_value_specialization (gtype); + key_lookup = dbus_g_type_get_lookup_function (key_gtype); + g_assert (key_lookup); + value_lookup = dbus_g_type_get_lookup_function (value_gtype); + g_assert (value_lookup); + type_lookup = g_strdup_printf ("dbus_g_type_get_map (\"GHashTable\", %s, %s)", + key_lookup, value_lookup); + g_free (key_lookup); + g_free (value_lookup); + return type_lookup; + } + else if (dbus_g_type_is_struct (gtype)) + { + GType value_gtype; + GString *string; + char *value_lookup = NULL; + guint size, i; + + string = g_string_new ("dbus_g_type_get_struct (\"GValueArray\""); + + size = dbus_g_type_get_struct_size (gtype); + for (i=0; i < size; i++) + { + value_gtype = dbus_g_type_get_struct_member_type(gtype, i); + value_lookup = dbus_g_type_get_lookup_function (value_gtype); + g_assert (value_lookup); + g_string_append_printf (string, ", %s", value_lookup); + g_free (value_lookup); + } + g_string_append (string, ", G_TYPE_INVALID)"); + return g_string_free (string, FALSE); + } + + MAP_KNOWN(G_TYPE_VALUE); + MAP_KNOWN(G_TYPE_STRV); + MAP_KNOWN(G_TYPE_VALUE_ARRAY); + MAP_KNOWN(DBUS_TYPE_G_PROXY); + MAP_KNOWN(DBUS_TYPE_G_OBJECT_PATH); + return NULL; +} +#undef MAP_FUNDAMENTAL +#undef MAP_KNOWN + +static gboolean +write_args_for_direction (InterfaceInfo *iface, MethodInfo *method, GIOChannel *channel, int direction, GError **error) +{ + GSList *args; + + for (args = method_info_get_args (method); args; args = args->next) + { + ArgInfo *arg; + GType gtype; + char *type_lookup; + + arg = args->data; + + if (direction != arg_info_get_direction (arg)) + continue; + + gtype = _dbus_gtype_from_signature (arg_info_get_type (arg), TRUE); + g_assert (gtype != G_TYPE_INVALID); + type_lookup = dbus_g_type_get_lookup_function (gtype); + g_assert (type_lookup != NULL); + + switch (direction) + { + + case ARG_IN: + if (!write_printf_to_iochannel ("%s, IN_%s, ", channel, error, + type_lookup, + arg_info_get_name (arg))) + goto io_lose; + break; + case ARG_OUT: + if (!write_printf_to_iochannel ("%s, OUT_%s, ", channel, error, + type_lookup, + arg_info_get_name (arg))) + goto io_lose; + break; + case ARG_INVALID: + break; + } + g_free (type_lookup); + } + + return TRUE; + io_lose: + return FALSE; +} + +static gboolean +check_supported_parameters (MethodInfo *method) +{ + GSList *args; + + for (args = method_info_get_args (method); args; args = args->next) + { + ArgInfo *arg; + GType gtype; + + arg = args->data; + gtype = _dbus_gtype_from_signature (arg_info_get_type (arg), TRUE); + if (gtype == G_TYPE_INVALID) + return FALSE; + } + return TRUE; +} + +static gboolean +write_untyped_out_args (InterfaceInfo *iface, MethodInfo *method, GIOChannel *channel, GError **error) +{ + GSList *args; + + for (args = method_info_get_args (method); args; args = args->next) + { + ArgInfo *arg; + + arg = args->data; + if (arg_info_get_direction (arg) != ARG_OUT) + continue; + + if (!write_printf_to_iochannel ("OUT_%s, ", channel, error, + arg_info_get_name (arg))) + goto io_lose; + } + + return TRUE; + io_lose: + return FALSE; +} + +static gboolean +write_formal_declarations_for_direction (InterfaceInfo *iface, MethodInfo *method, GIOChannel *channel, const int direction, GError **error) + { + GSList *args; + + for (args = method_info_get_args (method); args; args = args->next) + { + ArgInfo *arg; + GType gtype; + const char *type_str, *type_suffix; + int dir; + + arg = args->data; + + dir = arg_info_get_direction (arg); + + gtype = _dbus_gtype_from_signature (arg_info_get_type (arg), TRUE); + type_str = dbus_g_type_get_c_name (gtype); + + if (!type_str) + { + g_set_error (error, + DBUS_BINDING_TOOL_ERROR, + DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION, + _("Unsupported conversion from D-BUS type signature \"%s\" to glib C type in method \"%s\" of interface \"%s\""), + arg_info_get_type (arg), + method_info_get_name (method), + interface_info_get_name (iface)); + return FALSE; + } + + /* Variants are special...*/ + if (gtype == G_TYPE_VALUE) + { + if (direction == ARG_IN) + type_suffix = "*"; + else + type_suffix = ""; + } + else if ((g_type_is_a (gtype, G_TYPE_BOXED) + || g_type_is_a (gtype, G_TYPE_OBJECT) + || g_type_is_a (gtype, G_TYPE_POINTER))) + type_suffix = "*"; + else + type_suffix = ""; + + if (direction != dir) + continue; + + switch (dir) + { + case ARG_IN: + if (!write_printf_to_iochannel (" %s%s IN_%s;\n", channel, error, + type_str, type_suffix, + arg_info_get_name (arg))) + goto io_lose; + break; + case ARG_OUT: + if (!write_printf_to_iochannel (" %s%s OUT_%s;\n", channel, error, + type_str, type_suffix, + arg_info_get_name (arg))) + goto io_lose; + break; + case ARG_INVALID: + break; + } + } + return TRUE; + io_lose: + return FALSE; + } + +static gboolean +write_formal_parameters_for_direction (InterfaceInfo *iface, MethodInfo *method, int dir, GIOChannel *channel, GError **error) +{ + GSList *args; + + for (args = method_info_get_args (method); args; args = args->next) + { + ArgInfo *arg; + const char *type_str; + const char *type_suffix; + GType gtype; + int direction; + + arg = args->data; + + direction = arg_info_get_direction (arg); + if (dir != direction) continue; + + WRITE_OR_LOSE (", "); + + gtype = _dbus_gtype_from_signature (arg_info_get_type (arg), TRUE); + type_str = dbus_g_type_get_c_name (gtype); + /* Variants are special...*/ + if (gtype == G_TYPE_VALUE) + { + if (direction == ARG_IN) + type_suffix = "*"; + else + type_suffix = ""; + } + else if ((g_type_is_a (gtype, G_TYPE_BOXED) + || g_type_is_a (gtype, G_TYPE_OBJECT) + || g_type_is_a (gtype, G_TYPE_POINTER))) + type_suffix = "*"; + else + type_suffix = ""; + + if (!type_str) + { + g_set_error (error, + DBUS_BINDING_TOOL_ERROR, + DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION, + _("Unsupported conversion from D-BUS type signature \"%s\" to glib C type in method \"%s\" of interface \"%s\""), + arg_info_get_type (arg), + method_info_get_name (method), + interface_info_get_name (iface)); + return FALSE; + } + + switch (direction) + { + case ARG_IN: + if (!write_printf_to_iochannel ("const %s%s IN_%s", channel, error, + type_str, + type_suffix, + arg_info_get_name (arg))) + goto io_lose; + break; + case ARG_OUT: + if (!write_printf_to_iochannel ("%s%s* OUT_%s", channel, error, + type_str, + type_suffix, + arg_info_get_name (arg))) + goto io_lose; + break; + case ARG_INVALID: + break; + } + } + return TRUE; + io_lose: + return FALSE; +} + +static gboolean +write_typed_args_for_direction (InterfaceInfo *iface, MethodInfo *method, GIOChannel *channel, const int direction, GError **error) + { + GSList *args; + + for (args = method_info_get_args (method); args; args = args->next) + { + ArgInfo *arg; + int dir; + GType gtype; + const char *type_lookup; + + arg = args->data; + + dir = arg_info_get_direction (arg); + + if (dir != direction) + continue; + + gtype = _dbus_gtype_from_signature (arg_info_get_type (arg), TRUE); + type_lookup = dbus_g_type_get_lookup_function (gtype); + + if (!write_printf_to_iochannel ("%s, &%s_%s, ", channel, error, type_lookup, direction == ARG_IN ? "IN" : "OUT", arg_info_get_name (arg))) + goto io_lose; + } + return TRUE; + io_lose: + return FALSE; +} + +static gboolean +write_async_method_client (GIOChannel *channel, InterfaceInfo *interface, MethodInfo *method, GError **error) +{ + char *method_name, *iface_prefix; + const char *interface_c_name; + + iface_prefix = iface_to_c_prefix (interface_info_get_name (interface)); + interface_c_name = interface_info_get_annotation (interface, DBUS_GLIB_ANNOTATION_CLIENT_C_SYMBOL); + if (interface_c_name == NULL) + { + interface_c_name = (const char *) iface_prefix; + } + + method_name = g_strdup (method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_CLIENT_C_SYMBOL)); + if (method_name == NULL) + { + method_name = compute_client_method_name (interface_c_name, method); + } + g_free(iface_prefix); + + /* Write the typedef for the client callback */ + if (!write_printf_to_iochannel ("typedef void (*%s_reply) (DBusGProxy *proxy, ", channel, error, method_name)) + goto io_lose; + { + GSList *args; + for (args = method_info_get_args (method); args; args = args->next) + { + ArgInfo *arg; + const char *type_suffix, *type_str; + GType gtype; + + arg = args->data; + + if (arg_info_get_direction (arg) != ARG_OUT) + continue; + gtype = _dbus_gtype_from_signature (arg_info_get_type (arg), TRUE); + if (gtype != G_TYPE_VALUE && (g_type_is_a (gtype, G_TYPE_BOXED) + || g_type_is_a (gtype, G_TYPE_OBJECT) + || g_type_is_a (gtype, G_TYPE_POINTER))) + type_suffix = "*"; + else + type_suffix = ""; + type_str = dbus_g_type_get_c_name (_dbus_gtype_from_signature (arg_info_get_type (arg), TRUE)); + if (!write_printf_to_iochannel ("%s %sOUT_%s, ", channel, error, type_str, type_suffix, arg_info_get_name (arg))) + goto io_lose; + } + } + WRITE_OR_LOSE ("GError *error, gpointer userdata);\n\n"); + + + /* Write the callback when the call returns */ + WRITE_OR_LOSE ("static void\n"); + if (!write_printf_to_iochannel ("%s_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)\n", channel, error, method_name)) + goto io_lose; + WRITE_OR_LOSE ("{\n"); + WRITE_OR_LOSE (" DBusGAsyncData *data = (DBusGAsyncData*) user_data;\n GError *error = NULL;\n"); + if (!write_formal_declarations_for_direction (interface, method, channel, ARG_OUT, error)) + goto io_lose; + /* TODO: handle return boolean of end_call */ + WRITE_OR_LOSE (" dbus_g_proxy_end_call (proxy, call, &error, "); + if (!write_typed_args_for_direction (interface, method, channel, ARG_OUT, error)) + goto io_lose; + WRITE_OR_LOSE("G_TYPE_INVALID);\n"); + if (!write_printf_to_iochannel (" (*(%s_reply)data->cb) (proxy, ", channel, error, method_name)) + goto io_lose; + if (!write_untyped_out_args (interface, method, channel, error)) + goto io_lose; + WRITE_OR_LOSE ("error, data->userdata);\n"); + WRITE_OR_LOSE (" return;\n}\n\n"); + + + /* Write the main wrapper function */ + WRITE_OR_LOSE ("static\n#ifdef G_HAVE_INLINE\ninline\n#endif\nDBusGProxyCall*\n"); + if (!write_printf_to_iochannel ("%s_async (DBusGProxy *proxy", channel, error, + method_name)) + goto io_lose; + if (!write_formal_parameters_for_direction (interface, method, ARG_IN, channel, error)) + goto io_lose; + + if (!write_printf_to_iochannel (", %s_reply callback, gpointer userdata)\n\n", channel, error, method_name)) + goto io_lose; + + WRITE_OR_LOSE ("{\n"); + WRITE_OR_LOSE (" DBusGAsyncData *stuff;\n stuff = g_new (DBusGAsyncData, 1);\n stuff->cb = G_CALLBACK (callback);\n stuff->userdata = userdata;\n"); + if (!write_printf_to_iochannel (" return dbus_g_proxy_begin_call (proxy, \"%s\", %s_async_callback, stuff, g_free, ", channel, error, method_info_get_name (method), method_name)) + goto io_lose; + if (!write_args_for_direction (interface, method, channel, ARG_IN, error)) + goto io_lose; + WRITE_OR_LOSE ("G_TYPE_INVALID);\n}\n"); + + g_free (method_name); + return TRUE; + io_lose: + g_free (method_name); + return FALSE; + } + +static gboolean +generate_client_glue_list (GSList *list, DBusBindingToolCData *data, GError **error) +{ + GSList *tmp; + + tmp = list; + while (tmp != NULL) + { + if (!generate_client_glue (tmp->data, data, error)) + return FALSE; + tmp = tmp->next; + } + return TRUE; +} + +static gboolean +generate_client_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error) +{ + if (base_info_get_type (base) == INFO_TYPE_NODE) + { + if (!generate_client_glue_list (node_info_get_nodes ((NodeInfo *) base), + data, error)) + return FALSE; + if (!generate_client_glue_list (node_info_get_interfaces ((NodeInfo *) base), + data, error)) + return FALSE; + } + else + { + GIOChannel *channel; + InterfaceInfo *interface; + GSList *methods; + GSList *tmp; + char *iface_prefix; + const char *interface_c_name; + + channel = data->channel; + + interface = (InterfaceInfo *) base; + + methods = interface_info_get_methods (interface); + + iface_prefix = iface_to_c_prefix (interface_info_get_name (interface)); + interface_c_name = interface_info_get_annotation (interface, DBUS_GLIB_ANNOTATION_CLIENT_C_SYMBOL); + if (interface_c_name == NULL) + { + interface_c_name = (const char *) iface_prefix; + } + + if (!write_printf_to_iochannel ("#ifndef DBUS_GLIB_CLIENT_WRAPPERS_%s\n" + "#define DBUS_GLIB_CLIENT_WRAPPERS_%s\n\n", + channel, error, + iface_prefix, iface_prefix)) + { + g_free (iface_prefix); + goto io_lose; + } + + for (tmp = methods; tmp != NULL; tmp = g_slist_next (tmp)) + { + MethodInfo *method; + char *method_c_name; + gboolean is_noreply; + + method = (MethodInfo *) tmp->data; + method_c_name = g_strdup (method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_CLIENT_C_SYMBOL)); + if (method_c_name == NULL) + { + method_c_name = compute_client_method_name (interface_c_name, method); + } + + is_noreply = method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_NOREPLY) != NULL; + + if (data->ignore_unsupported && !check_supported_parameters (method)) + { + g_warning ("Ignoring unsupported signature in method \"%s\" of interface \"%s\"\n", + method_info_get_name (method), + interface_info_get_name (interface)); + continue; + } + + + WRITE_OR_LOSE ("static\n#ifdef G_HAVE_INLINE\ninline\n#endif\ngboolean\n"); + if (!write_printf_to_iochannel ("%s (DBusGProxy *proxy", channel, error, + method_c_name)) + goto io_lose; + g_free (method_c_name); + + if (!write_formal_parameters (interface, method, channel, error)) + goto io_lose; + + WRITE_OR_LOSE (", GError **error)\n\n"); + + WRITE_OR_LOSE ("{\n"); + + if (is_noreply) { + if (!write_printf_to_iochannel (" dbus_g_proxy_call_no_reply (proxy, \"%s\", ", channel, error, + method_info_get_name (method))) + goto io_lose; + + if (!write_args_for_direction (interface, method, channel, ARG_IN, error)) + goto io_lose; + + WRITE_OR_LOSE ("G_TYPE_INVALID, "); + + if (!write_args_for_direction (interface, method, channel, ARG_OUT, error)) + goto io_lose; + + WRITE_OR_LOSE ("G_TYPE_INVALID);\n"); + + WRITE_OR_LOSE (" return TRUE;\n}\n\n"); + } else { + if (!write_printf_to_iochannel (" return dbus_g_proxy_call (proxy, \"%s\", ", channel, error, + method_info_get_name (method))) + goto io_lose; + + WRITE_OR_LOSE ("error, "); + + if (!write_args_for_direction (interface, method, channel, ARG_IN, error)) + goto io_lose; + + WRITE_OR_LOSE ("G_TYPE_INVALID, "); + + if (!write_args_for_direction (interface, method, channel, ARG_OUT, error)) + goto io_lose; + + WRITE_OR_LOSE ("G_TYPE_INVALID);\n}\n\n"); + } + + write_async_method_client (channel, interface, method, error); + } + + if (!write_printf_to_iochannel ("#endif /* defined DBUS_GLIB_CLIENT_WRAPPERS_%s */\n\n", channel, error, iface_prefix)) + { + g_free (iface_prefix); + goto io_lose; + } + + g_free (iface_prefix); + } + return TRUE; + io_lose: + return FALSE; +} + + +gboolean +dbus_binding_tool_output_glib_client (BaseInfo *info, GIOChannel *channel, gboolean ignore_unsupported, GError **error) +{ + DBusBindingToolCData data; + gboolean ret; + + memset (&data, 0, sizeof (data)); + + data.channel = channel; + data.ignore_unsupported = ignore_unsupported; + + dbus_g_type_specialized_init (); + _dbus_g_type_specialized_builtins_init (); + + WRITE_OR_LOSE ("/* Generated by dbus-binding-tool; do not edit! */\n\n"); + WRITE_OR_LOSE ("#include \n"); + WRITE_OR_LOSE ("#include \n"); + WRITE_OR_LOSE ("#include \n\n"); + WRITE_OR_LOSE ("G_BEGIN_DECLS\n\n"); + + ret = generate_client_glue (info, &data, error); + if (!ret) + goto io_lose; + + WRITE_OR_LOSE ("G_END_DECLS\n"); + + return ret; + io_lose: + return FALSE; +}