Logo Search packages:      
Sourcecode: libnih version File versions  Download package

property.c

/* nih-dbus-tool
 *
 * property.c - property parsing and generation
 *
 * Copyright © 2009 Scott James Remnant <scott@netsplit.com>.
 * Copyright © 2009 Canonical Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2, as
 * published by the Free Software Foundation.
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */

#include <string.h>

#include <nih/macros.h>
#include <nih/alloc.h>
#include <nih/list.h>
#include <nih/string.h>
#include <nih/logging.h>
#include <nih/error.h>

#include <nih-dbus/dbus_object.h>

#include "symbol.h"
#include "indent.h"
#include "type.h"
#include "marshal.h"
#include "demarshal.h"
#include "property.h"
#include "interface.h"
#include "property.h"
#include "parse.h"
#include "errors.h"


/**
 * property_name_valid:
 * @name: Member name to verify.
 *
 * Verifies whether @name matches the specification for a D-Bus interface
 * member name, and thus is valid for a property.
 *
 * Returns: TRUE if valid, FALSE if not.
 **/
int
property_name_valid (const char *name)
{
      nih_assert (name != NULL);

      /* We can get away with just using strlen() here even through name
       * is in UTF-8 because all the valid characters are ASCII.
       */
      for (size_t i = 0; i < strlen (name); i++) {
            /* Names may contain digits, but not at the beginning. */
            if ((name[i] >= '0') && (name[i] <= '9')) {
                  if (i == 0)
                        return FALSE;

                  continue;
            }

            /* Valid characters anywhere are [A-Za-z_] */
            if (   ((name[i] < 'A') || (name[i] > 'Z'))
                && ((name[i] < 'a') || (name[i] > 'z'))
                && (name[i] != '_'))
                  return FALSE;
      }

      /* Name must be at least 1 character and no more than 255 characters */
      if ((strlen (name) < 1) || (strlen (name) > 255))
            return FALSE;

      return TRUE;
}


/**
 * property_new:
 * @parent: parent object for new property,
 * @name: D-Bus name of property,
 * @type: D-Bus type signature,
 * @access: access to property.
 *
 * Allocates a new D-Bus object Property data structure, with the D-Bus name
 * set to @name and the D-Bus type signature set to @type.  The returned
 * structure is not placed into any list.
 *
 * If @parent is not NULL, it should be a pointer to another object which
 * will be used as a parent for the returned property.  When all parents
 * of the returned property are freed, the returned property will also be
 * freed.
 *
 * Returns: the new property or NULL if the allocation failed.
 **/
Property *
property_new (const void *  parent,
            const char *  name,
            const char *  type,
            NihDBusAccess access)
{
      Property *property;

      nih_assert (name != NULL);

      property = nih_new (parent, Property);
      if (! property)
            return NULL;

      nih_list_init (&property->entry);

      nih_alloc_set_destructor (property, nih_list_destroy);

      property->name = nih_strdup (property, name);
      if (! property->name) {
            nih_free (property);
            return NULL;
      }

      property->symbol = NULL;

      property->type = nih_strdup (property, type);
      if (! property->type) {
            nih_free (property);
            return NULL;
      }

      property->access = access;
      property->deprecated = FALSE;

      return property;
}


/**
 * property_start_tag:
 * @xmlp: XML parser,
 * @tag: name of XML tag being parsed,
 * @attr: NULL-terminated array of attribute name and value pairs.
 *
 * This function is called by parse_start_tag() for a "property"
 * start tag, a child of the "interface" tag that defines a property the
 * D-Bus interface specifies.
 *
 * If the property does not appear within an interface tag a warning is
 * emitted and the tag will be ignored.
 *
 * Properties must have a "name" attribute containing the D-Bus name
 * of the interface, a "type" attribute containing the D-Bus type
 * signature and an "access" attribute specifying whether the property
 * is read-only, write-only or read/write.
 *
 * Any unknown attributes result in a warning and will be ignored; an
 * unknown value for the "access" attribute results in an error.
 *
 * A Property object will be allocated and pushed onto the stack, this is
 * not added to the interface until the end tag is found.
 *
 * Returns: zero on success, negative value on raised error.
 **/
int
property_start_tag (XML_Parser    xmlp,
                const char *  tag,
                char * const *attr)
{
      ParseContext *      context;
      ParseStack *        parent;
      nih_local Property *property = NULL;
      char * const *      key;
      char * const *      value;
      const char *        name = NULL;
      const char *        type = NULL;
      const char *        access_str = NULL;
      NihDBusAccess       access;
      DBusError           error;

      nih_assert (xmlp != NULL);
      nih_assert (tag != NULL);
      nih_assert (attr != NULL);

      context = XML_GetUserData (xmlp);
      nih_assert (context != NULL);

      /* Properties should only appear inside interfaces. */
      parent = parse_stack_top (&context->stack);
      if ((! parent) || (parent->type != PARSE_INTERFACE)) {
            nih_warn ("%s:%zu:%zu: %s", context->filename,
                    (size_t)XML_GetCurrentLineNumber (xmlp),
                    (size_t)XML_GetCurrentColumnNumber (xmlp),
                    _("Ignored unexpected <property> tag"));

            if (! parse_stack_push (NULL, &context->stack,
                              PARSE_IGNORED, NULL))
                  nih_return_system_error (-1);

            return 0;
      }

      /* Retrieve the name, type and access from the attributes */
      for (key = attr; key && *key; key += 2) {
            value = key + 1;
            nih_assert (value && *value);

            if (! strcmp (*key, "name")) {
                  name = *value;
            } else if (! strcmp (*key, "type")) {
                  type = *value;
            } else if (! strcmp (*key, "access")) {
                  access_str = *value;
            } else {
                  nih_warn ("%s:%zu:%zu: %s: %s", context->filename,
                          (size_t)XML_GetCurrentLineNumber (xmlp),
                          (size_t)XML_GetCurrentColumnNumber (xmlp),
                          _("Ignored unknown <property> attribute"),
                          *key);
            }
      }

      /* Check we have a name, type and access and that they are valid */
      if (! name)
            nih_return_error (-1, PROPERTY_MISSING_NAME,
                          _(PROPERTY_MISSING_NAME_STR));
      if (! property_name_valid (name))
            nih_return_error (-1, PROPERTY_INVALID_NAME,
                          _(PROPERTY_INVALID_NAME_STR));

      if (! type)
            nih_return_error (-1, PROPERTY_MISSING_TYPE,
                          _(PROPERTY_MISSING_TYPE_STR));

      dbus_error_init (&error);
      if (! dbus_signature_validate_single (type, &error)) {
            nih_error_raise_printf (PROPERTY_INVALID_TYPE, "%s: %s",
                              _(PROPERTY_INVALID_TYPE_STR),
                              error.message);
            dbus_error_free (&error);
            return -1;
      }

      if (! access_str)
            nih_return_error (-1, PROPERTY_MISSING_ACCESS,
                          _(PROPERTY_MISSING_ACCESS_STR));

      if (! strcmp (access_str, "read")) {
            access = NIH_DBUS_READ;
      } else if (! strcmp (access_str, "write")) {
            access = NIH_DBUS_WRITE;
      } else if (! strcmp (access_str, "readwrite")) {
            access = NIH_DBUS_READWRITE;
      } else {
            nih_return_error (-1, PROPERTY_ILLEGAL_ACCESS,
                          _(PROPERTY_ILLEGAL_ACCESS_STR));
      }

      /* Allocate a Property object and push onto the stack */
      property = property_new (NULL, name, type, access);
      if (! property)
            nih_return_system_error (-1);

      if (! parse_stack_push (NULL, &context->stack,
                        PARSE_PROPERTY, property)) {
            nih_error_raise_system ();
            return -1;
      }

      return 0;
}

/**
 * property_end:
 * @xmlp: XML parser,
 * @tag: name of XML tag being parsed.
 *
 * This function is called by parse_end_tag() for a "property" end
 * tag, and matches a call to property_start_tag() made at the same
 * parsing level.
 *
 * The property is added to the list of properties defined by the parent
 * interface.
 *
 * Returns: zero on success, negative value on raised error.
 **/
int
property_end_tag (XML_Parser  xmlp,
              const char *tag)
{
      ParseContext *context;
      ParseStack *  entry;
      ParseStack *  parent;
      Property *    property;
      Property *    conflict;
      Interface *   interface;

      nih_assert (xmlp != NULL);
      nih_assert (tag != NULL);

      context = XML_GetUserData (xmlp);
      nih_assert (context != NULL);

      entry = parse_stack_top (&context->stack);
      nih_assert (entry != NULL);
      nih_assert (entry->type == PARSE_PROPERTY);
      property = entry->property;

      /* Generate a symbol from the name */
      if (! property->symbol) {
            property->symbol = symbol_from_name (property, property->name);
            if (! property->symbol)
                  nih_return_no_memory_error (-1);
      }

      nih_list_remove (&entry->entry);
      parent = parse_stack_top (&context->stack);
      nih_assert (parent != NULL);
      nih_assert (parent->type == PARSE_INTERFACE);
      interface = parent->interface;

      /* Make sure there's not a conflict before adding the property */
      conflict = property_lookup (interface, property->symbol);
      if (conflict) {
            nih_error_raise_printf (PROPERTY_DUPLICATE_SYMBOL,
                              _(PROPERTY_DUPLICATE_SYMBOL_STR),
                              property->symbol, conflict->name);
            return -1;
      }

      nih_debug ("Add %s property to %s interface",
               property->name, interface->name);
      nih_list_add (&interface->properties, &property->entry);
      nih_ref (property, interface);

      nih_free (entry);

      return 0;
}


/**
 * property_annotation:
 * @property: property object annotation applies to,
 * @name: annotation name,
 * @value: annotation value.
 *
 * Handles applying the annotation @name with value @value to the property
 * @property.  Properties may be annotated as deprecated or may have an
 * alternate symbol name specified.
 *
 * Unknown annotations or illegal values to the known annotations result
 * in an error being raised.
 *
 * Returns: zero on success, negative value on raised error.
 **/
int
property_annotation (Property *  property,
                 const char *name,
                 const char *value)
{
      nih_assert (property != NULL);
      nih_assert (name != NULL);
      nih_assert (value != NULL);

      if (! strcmp (name, "org.freedesktop.DBus.Deprecated")) {
            if (! strcmp (value, "true")) {
                  nih_debug ("Marked %s property as deprecated",
                           property->name);
                  property->deprecated = TRUE;
            } else if (! strcmp (value, "false")) {
                  nih_debug ("Marked %s property as not deprecated",
                           property->name);
                  property->deprecated = FALSE;
            } else {
                  nih_return_error (-1, PROPERTY_ILLEGAL_DEPRECATED,
                                _(PROPERTY_ILLEGAL_DEPRECATED_STR));
            }

      } else if (! strcmp (name, "com.netsplit.Nih.Symbol")) {
            if (symbol_valid (value)) {
                  if (property->symbol)
                        nih_unref (property->symbol, property);

                  property->symbol = nih_strdup (property, value);
                  if (! property->symbol)
                        nih_return_no_memory_error (-1);

                  nih_debug ("Set %s property symbol to %s",
                           property->name, property->symbol);
            } else {
                  nih_return_error (-1, PROPERTY_INVALID_SYMBOL,
                                _(PROPERTY_INVALID_SYMBOL_STR));
            }

      } else {
            nih_error_raise_printf (PROPERTY_UNKNOWN_ANNOTATION,
                              "%s: %s: %s",
                              _(PROPERTY_UNKNOWN_ANNOTATION_STR),
                              property->name, name);
            return -1;
      }

      return 0;
}


/**
 * property_lookup:
 * @interface: interface to search,
 * @symbol: property symbol to find.
 *
 * Finds a property in @interface's properties list which has the generated
 * or supplied C symbol @symbol.
 *
 * Returns: property found or NULL if no property matches.
 **/
Property *
property_lookup (Interface * interface,
             const char *symbol)
{
      nih_assert (interface != NULL);
      nih_assert (symbol != NULL);

      NIH_LIST_FOREACH (&interface->properties, iter) {
            Property *property = (Property *)iter;

            if (property->symbol
                && (! strcmp (property->symbol, symbol)))
                  return property;
      }

      return NULL;
}


/**
 * property_object_get_function:
 * @parent: parent object for new string,
 * @prefix: prefix for function name,
 * @interface: interface of @property,
 * @property: property to generate function for,
 * @prototypes: list to append function prototypes to,
 * @handlers: list to append definitions of required handlers to,
 * @structs: list to append structure definitions to.
 *
 * Generates C code for a function that will append a variant containing
 * the value of property @property on @interface to a D-Bus message iterator,
 * the value being obtained from a handler function.
 *
 * The prototype of the returned function is returned as a TypeFunc object
 * appended to the @prototypes list.
 *
 * The prototype for the handler function is returned as a TypeFunc object
 * added to the @handlers list.
 *
 * The names of both the returned function and handled function prototype
 * will be generated using information in @interface and @property, prefixed
 * with @prefix.
 *
 * If the property type requires a structure to be defined, the
 * definition is returned as a TypeStruct object appended to the @structs
 * list.  The name is generated from @prefix, @interface and @property.
 *
 * If @parent is not NULL, it should be a pointer to another object which
 * will be used as a parent for the returned string.  When all parents
 * of the returned string are freed, the return string will also be
 * freed.
 *
 * Returns: newly allocated string or NULL if insufficient memory.
 **/
char *
property_object_get_function (const void *parent,
                        const char *prefix,
                        Interface * interface,
                        Property *  property,
                        NihList *   prototypes,
                        NihList *   handlers,
                        NihList *   structs)
{
      DBusSignatureIter   iter;
      NihList             inputs;
      NihList             locals;
      NihList             property_structs;
      nih_local char *    name = NULL;
      nih_local TypeFunc *func = NULL;
      TypeVar *           arg;
      nih_local TypeVar * iter_var = NULL;
      nih_local char *    code_block = NULL;
      nih_local char *    oom_error_code = NULL;
      nih_local char *    block = NULL;
      nih_local char *    handler_name = NULL;
      nih_local TypeFunc *handler_func = NULL;
      NihListEntry *      attrib;
      nih_local char *    vars_block = NULL;
      nih_local char *    body = NULL;
      char *              code;

      nih_assert (prefix != NULL);
      nih_assert (interface != NULL);
      nih_assert (property != NULL);
      nih_assert (prototypes != NULL);
      nih_assert (handlers != NULL);
      nih_assert (structs != NULL);

      dbus_signature_iter_init (&iter, property->type);

      nih_list_init (&inputs);
      nih_list_init (&locals);
      nih_list_init (&property_structs);

      /* The function returns an integer, and accepts an arguments for
       * the D-Bus object, message and a message iterator.
       */
      name = symbol_impl (NULL, prefix, interface->name,
                      property->name, "get");
      if (! name)
            return NULL;

      func = type_func_new (NULL, "int", name);
      if (! func)
            return NULL;

      arg = type_var_new (func, "NihDBusObject *", "object");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      arg = type_var_new (func, "NihDBusMessage *", "message");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      arg = type_var_new (func, "DBusMessageIter *", "iter");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      /* The function requires a local iterator for the variant.  Rather
       * than deal with it by hand, it's far easier to put it on the
       * locals list and deal with it along with the rest.
       */
      iter_var = type_var_new (NULL, "DBusMessageIter", "variter");
      if (! iter_var)
            return NULL;

      nih_list_add (&locals, &iter_var->entry);

      /* In case of out of memory, simply return and let the caller
       * decide what to do.
       */
      oom_error_code = nih_strdup (NULL,
                             "dbus_message_iter_abandon_container (iter, &variter);\n"
                             "nih_error_raise_no_memory ();\n"
                             "return -1;\n");
      if (! oom_error_code)
            return NULL;

      block = marshal (NULL, &iter, "variter", "value",
                   oom_error_code,
                   &inputs, &locals,
                   prefix, interface->symbol,
                   property->symbol, NULL,
                   &property_structs);
      if (! block)
            return NULL;

      /* Begin the handler calling block */
      handler_name = symbol_extern (NULL, prefix, interface->symbol, "get",
                              property->symbol, NULL);
      if (! handler_name)
            return NULL;

      if (! nih_strcat_sprintf (&code_block, NULL,
                          "/* Call the handler function */\n"
                          "if (%s (object->data, message",
                          handler_name))
            return NULL;

      handler_func = type_func_new (NULL, "int", handler_name);
      if (! handler_func)
            return NULL;

      attrib = nih_list_entry_new (handler_func);
      if (! attrib)
            return NULL;

      attrib->str = nih_strdup (attrib, "warn_unused_result");
      if (! attrib->str)
            return NULL;

      nih_list_add (&handler_func->attribs, &attrib->entry);

      arg = type_var_new (handler_func, "void *", "data");
      if (! arg)
            return NULL;

      nih_list_add (&handler_func->args, &arg->entry);

      arg = type_var_new (handler_func, "NihDBusMessage *", "message");
      if (! arg)
            return NULL;

      nih_list_add (&handler_func->args, &arg->entry);

      /* Each of the inputs to the marshalling code becomes a local
       * variable to our function that we pass the address of to the
       * implementation function.
       */
      NIH_LIST_FOREACH_SAFE (&inputs, iter) {
            TypeVar *var = (TypeVar *)iter;

            if (! nih_strcat_sprintf (&code_block, NULL,
                                ", &%s",
                                var->name))
                  return NULL;

            nih_list_add (&locals, &var->entry);

            /* Handler argument is pointer */
            arg = type_var_new (handler_func, var->type, var->name);
            if (! arg)
                  return NULL;

            if (! type_to_pointer (&arg->type, arg))
                  return NULL;

            nih_list_add (&handler_func->args, &arg->entry);
      }

      /* Finish up the calling block, in case of error we again just
       * return and let our caller deal with it.
       */
      if (! nih_strcat_sprintf (&code_block, NULL, ") < 0)\n"
                          "\treturn -1;\n"
                          "\n"))
            return NULL;

      /* Surround the marshalling code by appending a variant onto the
       * passed-in message iterator, and closing it once complete.
       */
      if (! nih_strcat_sprintf (&code_block, NULL,
                          "/* Append a variant onto the message to contain the property value. */\n"
                          "if (! dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, \"%s\", &variter)) {\n"
                          "\tnih_error_raise_no_memory ();\n"
                          "\treturn -1;\n"
                          "}\n"
                          "\n"
                          "%s"
                          "\n"
                          "/* Finish the variant */\n"
                          "if (! dbus_message_iter_close_container (iter, &variter)) {\n"
                          "\tnih_error_raise_no_memory ();\n"
                          "\treturn -1;\n"
                          "}\n",
                          property->type,
                          block))
            return NULL;

      /* Lay out the function body, indenting it all before placing it
       * in the function code.
       */
      vars_block = type_var_layout (NULL, &locals);
      if (! vars_block)
            return NULL;

      if (! nih_strcat_sprintf (&body, NULL,
                          "%s"
                          "\n"
                          "nih_assert (object != NULL);\n"
                          "nih_assert (message != NULL);\n"
                          "nih_assert (iter != NULL);\n"
                          "\n"
                          "%s"
                          "\n"
                          "return 0;\n",
                          vars_block,
                          code_block))
            return NULL;

      if (! indent (&body, NULL, 1))
            return NULL;

      /* Function header */
      code = type_func_to_string (parent, func);
      if (! code)
            return NULL;

      if (! nih_strcat_sprintf (&code, parent,
                          "{\n"
                          "%s"
                          "}\n",
                          body)) {
            nih_free (code);
            return NULL;
      }

      /* Append the functions to the prototypes and handlers lists */
      nih_list_add (prototypes, &func->entry);
      nih_ref (func, code);

      nih_list_add (handlers, &handler_func->entry);
      nih_ref (handler_func, code);

      NIH_LIST_FOREACH_SAFE (&property_structs, iter) {
            TypeStruct *structure = (TypeStruct *)iter;

            nih_ref (structure, code);
            nih_list_add (structs, &structure->entry);
      }

      return code;
}

/**
 * property_object_set_function:
 * @parent: parent object for new string,
 * @prefix: prefix for function name,
 * @interface: interface of @property,
 * @property: property to generate function for,
 * @prototypes: list to append function prototypes to,
 * @handlers: list to append definitions of required handlers to,
 * @structs: list to append structure definitions to.
 *
 * Generates C code for a function that will extract the new value of
 * property @property on @interface from a variant at the D-Bus message
 * iterator passed.  The new value of the property is then passed to
 * a handler function.
 *
 * The prototype of the returned function is returned as a TypeFunc object
 * appended to the @prototypes list.
 *
 * The prototype for the handler function is returned as a TypeFunc object
 * added to the @handlers list.
 *
 * The names of both the returned function and handled function prototype
 * will be generated using information in @interface and @property, prefixed
 * with @prefix.
 *
 * If the property type requires a structure to be defined, the
 * definition is returned as a TypeStruct object appended to the @structs
 * list.  The name is generated from @prefix, @interface and @property.
 *
 * If @parent is not NULL, it should be a pointer to another object which
 * will be used as a parent for the returned string.  When all parents
 * of the returned string are freed, the return string will also be
 * freed.
 *
 * Returns: newly allocated string or NULL if insufficient memory.
 **/
char *
property_object_set_function (const void *parent,
                        const char *prefix,
                        Interface * interface,
                        Property *  property,
                        NihList *   prototypes,
                        NihList *   handlers,
                        NihList *   structs)
{
      DBusSignatureIter   iter;
      NihList             outputs;
      NihList             locals;
      NihList             property_structs;
      nih_local char *    name = NULL;
      nih_local TypeFunc *func = NULL;
      TypeVar *           arg;
      nih_local TypeVar * iter_var = NULL;
      nih_local TypeVar * reply_var = NULL;
      nih_local char *    demarshal_block = NULL;
      nih_local char *    oom_error_code = NULL;
      nih_local char *    type_error_code = NULL;
      nih_local char *    block = NULL;
      nih_local char *    call_block = NULL;
      nih_local char *    handler_name = NULL;
      nih_local TypeFunc *handler_func = NULL;
      NihListEntry *      attrib;
      nih_local char *    vars_block = NULL;
      nih_local char *    body = NULL;
      char *              code;

      nih_assert (prefix != NULL);
      nih_assert (interface != NULL);
      nih_assert (property != NULL);
      nih_assert (prototypes != NULL);
      nih_assert (handlers != NULL);
      nih_assert (structs != NULL);

      dbus_signature_iter_init (&iter, property->type);

      nih_list_init (&outputs);
      nih_list_init (&locals);
      nih_list_init (&property_structs);

      /* The function returns an integer, which means success when zero
       * or a raised error when non-zero and accepts arguments for the
       * D-Bus object, message and a message iterator.
       */
      name = symbol_impl (NULL, prefix, interface->name,
                      property->name, "set");
      if (! name)
            return NULL;

      func = type_func_new (NULL, "int", name);
      if (! func)
            return NULL;

      arg = type_var_new (func, "NihDBusObject *", "object");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      arg = type_var_new (func, "NihDBusMessage *", "message");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      arg = type_var_new (func, "DBusMessageIter *", "iter");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      /* The function requires a local iterator for the variant.  Rather
       * than deal with this by hand, it's far easier to put it on the
       * locals list and deal with them along with the rest.
       */
      iter_var = type_var_new (NULL, "DBusMessageIter", "variter");
      if (! iter_var)
            return NULL;

      nih_list_add (&locals, &iter_var->entry);

      /* Make sure that the iterator points to a variant, then open the
       * variant.
       */
      if (! nih_strcat_sprintf (&demarshal_block, NULL,
                          "/* Recurse into the variant */\n"
                          "if (dbus_message_iter_get_arg_type (iter) != DBUS_TYPE_VARIANT) {\n"
                          "\tnih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,\n"
                          "\t                             \"Invalid arguments to %s property\");\n"
                          "\treturn -1;\n"
                          "}\n"
                          "\n"
                          "dbus_message_iter_recurse (iter, &variter);\n"
                          "\n",
                          property->name))
            return NULL;

      /* In case of out of memory, or type error, return a raised error
       * to the caller.
       */
      oom_error_code = nih_strdup (NULL,
                             "nih_error_raise_no_memory ();\n"
                             "return -1;\n");
      if (! oom_error_code)
            return NULL;

      type_error_code = nih_sprintf (NULL,
                               "nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,\n"
                               "                             \"Invalid arguments to %s property\");\n"
                               "return -1;\n",
                               property->name);
      if (! type_error_code)
            return NULL;

      block = demarshal (NULL, &iter, "message", "variter", "value",
                     oom_error_code,
                     type_error_code,
                     &outputs, &locals,
                     prefix, interface->symbol,
                     property->symbol, NULL,
                     &property_structs);
      if (! block)
            return NULL;

      /* Complete the demarshalling block, checking for any unexpected
       * arguments which we also want to error on and begin the handler
       * calling block.
       */
      handler_name = symbol_extern (NULL, prefix, interface->symbol, "set",
                              property->symbol, NULL);
      if (! handler_name)
            return NULL;

      if (! nih_strcat_sprintf (&call_block, NULL,
                          "dbus_message_iter_next (iter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (iter) != DBUS_TYPE_INVALID) {\n"
                          "\tnih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,\n"
                          "\t                             \"Invalid arguments to %s property\");\n"
                          "\treturn -1;\n"
                          "}\n"
                          "\n"
                          "/* Call the handler function */\n"
                          "if (%s (object->data, message",
                          property->name,
                          handler_name))
            return NULL;

      handler_func = type_func_new (NULL, "int", handler_name);
      if (! handler_func)
            return NULL;

      attrib = nih_list_entry_new (handler_func);
      if (! attrib)
            return NULL;

      attrib->str = nih_strdup (attrib, "warn_unused_result");
      if (! attrib->str)
            return NULL;

      nih_list_add (&handler_func->attribs, &attrib->entry);

      arg = type_var_new (handler_func, "void *", "data");
      if (! arg)
            return NULL;

      nih_list_add (&handler_func->args, &arg->entry);

      arg = type_var_new (handler_func, "NihDBusMessage *", "message");
      if (! arg)
            return NULL;

      nih_list_add (&handler_func->args, &arg->entry);

      /* Each of the outputs from the demarshalling code becomes a local
       * variable to our function that we pass to the implementation
       * function.
       */
      NIH_LIST_FOREACH_SAFE (&outputs, iter) {
            TypeVar *var = (TypeVar *)iter;

            if (! nih_strcat_sprintf (&call_block, NULL,
                                ", %s",
                                var->name))
                  return NULL;

            nih_list_add (&locals, &var->entry);

            /* Handler argument is const */
            arg = type_var_new (handler_func, var->type, var->name);
            if (! arg)
                  return NULL;

            if (! type_to_const (&arg->type, arg))
                  return NULL;

            nih_list_add (&handler_func->args, &arg->entry);
      }

      /* Finish up the calling block, in case of out of memory error we
       * return and let D-Bus deal with it, other errors generate an
       * error reply.
       */
      if (! nih_strcat_sprintf (&call_block, NULL, ") < 0)\n"
                          "\treturn -1;\n"))
            return NULL;

      /* Lay out the function body, indenting it all before placing it
       * in the function code.
       */
      vars_block = type_var_layout (NULL, &locals);
      if (! vars_block)
            return NULL;

      if (! nih_strcat_sprintf (&body, NULL,
                          "%s"
                          "\n"
                          "nih_assert (object != NULL);\n"
                          "nih_assert (message != NULL);\n"
                          "nih_assert (iter != NULL);\n"
                          "\n"
                          "%s"
                          "%s"
                          "\n"
                          "%s"
                          "\n"
                          "return 0;\n",
                          vars_block,
                          demarshal_block,
                          block,
                          call_block))
            return NULL;

      if (! indent (&body, NULL, 1))
            return NULL;

      /* Function header */
      code = type_func_to_string (parent, func);
      if (! code)
            return NULL;

      if (! nih_strcat_sprintf (&code, parent,
                          "{\n"
                          "%s"
                          "}\n",
                          body)) {
            nih_free (code);
            return NULL;
      }

      /* Append the functions to the prototypes and handlers lists */
      nih_list_add (prototypes, &func->entry);
      nih_ref (func, code);

      nih_list_add (handlers, &handler_func->entry);
      nih_ref (handler_func, code);

      NIH_LIST_FOREACH_SAFE (&property_structs, iter) {
            TypeStruct *structure = (TypeStruct *)iter;

            nih_ref (structure, code);
            nih_list_add (structs, &structure->entry);
      }

      return code;
}


/**
 * property_proxy_get_function:
 * @parent: parent object for new string.
 * @prefix: prefix for function name,
 * @interface: interface of @property,
 * @property: property to generate function for,
 * @prototypes: list to append function prototypes to,
 * @structs: list to append structure definitions to.
 *
 * Generates C code for a function that will make an asynchronous method
 * call to obtain the value of the property @property on @interface,
 * calling a notify function when the method call completes.
 *
 * The prototype of the returned function is returned as a TypeFunc object
 * appended to the @prototypes list.
 *
 * The names of both the returned function and notify function prototype
 * will be generated using information in @interface and @property, prefixed
 * with @prefix.
 *
 * The notify function will call a handler function passed in if the
 * reply is valid.  The name and type for this can be obtained from
 * property_proxy_get_notify_function().
 *
 * If the property type requires a structure to be defined, the
 * definition is returned as a TypeStruct object appended to the @structs
 * list.  The name is generated from @prefix, @interface and @property.
 *
 * If @parent is not NULL, it should be a pointer to another object which
 * will be used as a parent for the returned string.  When all parents
 * of the returned string are freed, the return string will also be
 * freed.
 *
 * Returns: newly allocated string or NULL if insufficient memory.
 **/
char *
property_proxy_get_function (const void *parent,
                       const char *prefix,
                       Interface * interface,
                       Property *  property,
                       NihList *   prototypes,
                       NihList *   structs)
{
      NihList             locals;
      nih_local char *    name = NULL;
      nih_local TypeFunc *func = NULL;
      TypeVar *           arg;
      NihListEntry *      attrib;
      nih_local char *    assert_block = NULL;
      nih_local char *    handler_type = NULL;
      nih_local TypeVar * message_var = NULL;
      nih_local TypeVar * iter_var = NULL;
      nih_local TypeVar * pending_var = NULL;
      nih_local TypeVar * data_var = NULL;
      nih_local TypeVar * interface_var = NULL;
      nih_local TypeVar * property_var = NULL;
      nih_local char *    call_block = NULL;
      nih_local char *    notify_name = NULL;
      nih_local char *    block = NULL;
      nih_local char *    vars_block = NULL;
      nih_local char *    body = NULL;
      char *              code = NULL;

      nih_assert (prefix != NULL);
      nih_assert (interface != NULL);
      nih_assert (property != NULL);
      nih_assert (prototypes != NULL);
      nih_assert (structs != NULL);

      nih_list_init (&locals);

      /* The function returns a pending call, and takes the proxy object
       * as the only argument.  The pending call also indicates whether
       * an error occurred, so we want warning if the result isn't used.
       * We don't have a malloc attribute, since we can't guarantee that
       * D-Bus doesn't cache them.  Since this is used by the client, we
       * also add a deprecated attribute if the property is deprecated.
       */
      name = symbol_extern (NULL, prefix, interface->symbol, "get",
                        property->symbol, NULL);
      if (! name)
            return NULL;

      func = type_func_new (NULL, "DBusPendingCall *", name);
      if (! func)
            return NULL;

      attrib = nih_list_entry_new (func);
      if (! attrib)
            return NULL;

      attrib->str = nih_strdup (attrib, "warn_unused_result");
      if (! attrib->str)
            return NULL;

      nih_list_add (&func->attribs, &attrib->entry);

      if (property->deprecated) {
            attrib = nih_list_entry_new (func);
            if (! attrib)
                  return NULL;

            attrib->str = nih_strdup (attrib, "deprecated");
            if (! attrib->str)
                  return NULL;

            nih_list_add (&func->attribs, &attrib->entry);
      }

      arg = type_var_new (func, "NihDBusProxy *", "proxy");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      if (! nih_strcat (&assert_block, NULL,
                    "nih_assert (proxy != NULL);\n"))
            return NULL;

      /* We also require a handler (which receives the property value),
       * error handler (in case of error) and data arguments to pass to
       * both as well as a timeout for the method call.  Unlike the
       * method call case, we don't allow for no-reply calls since
       * they're nonsensical.
       */
      handler_type = symbol_typedef (NULL, prefix, interface->symbol,
                               "Get", property->symbol, "Reply");
      if (! handler_type)
            return NULL;

      arg = type_var_new (func, handler_type, "handler");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      arg = type_var_new (func, "NihDBusErrorHandler", "error_handler");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      arg = type_var_new (func, "void *", "data");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      if (! nih_strcat (&assert_block, NULL,
                    "nih_assert ((handler != NULL) && (error_handler != NULL));\n"))
            return NULL;

      arg = type_var_new (func, "int", "timeout");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);


      /* The function requires a message pointer, which we allocate,
       * and an iterator for it to append the arguments.  We also need
       * a return pending call pointer and data structure as well.
       * Rather than deal with these by hand, it's far easier to put them
       * on the locals list and deal with them along with the rest.
       */
      message_var = type_var_new (NULL, "DBusMessage *", "method_call");
      if (! message_var)
            return NULL;

      nih_list_add (&locals, &message_var->entry);

      iter_var = type_var_new (NULL, "DBusMessageIter", "iter");
      if (! iter_var)
            return NULL;

      nih_list_add (&locals, &iter_var->entry);

      pending_var = type_var_new (NULL, "DBusPendingCall *", "pending_call");
      if (! pending_var)
            return NULL;

      nih_list_add (&locals, &pending_var->entry);

      data_var = type_var_new (NULL, "NihDBusPendingData *", "pending_data");
      if (! data_var)
            return NULL;

      nih_list_add (&locals, &data_var->entry);

      /* Annoyingly we also need variables for the interface and
       * property names, since D-Bus wants their address and can't just
       * take a constant string.
       */
      interface_var = type_var_new (NULL, "const char *", "interface");
      if (! interface_var)
            return NULL;

      nih_list_add (&locals, &interface_var->entry);

      property_var = type_var_new (NULL, "const char *", "property");
      if (! property_var)
            return NULL;

      nih_list_add (&locals, &property_var->entry);


      /* Create the method call to get the property, the property
       * interface gets specified as an argument - the method call
       * interface is the D-Bus properties one.
       */
      if (! nih_strcat_sprintf (&call_block, NULL,
                          "/* Construct the method call message. */\n"
                          "method_call = dbus_message_new_method_call (proxy->name, proxy->path, \"%s\", \"Get\");\n"
                          "if (! method_call)\n"
                          "\tnih_return_no_memory_error (NULL);\n"
                          "\n"
                          "dbus_message_set_auto_start (method_call, proxy->auto_start);\n"
                          "\n"
                          "dbus_message_iter_init_append (method_call, &iter);\n"
                          "\n"
                          "interface = \"%s\";\n"
                          "if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface)) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_return_no_memory_error (NULL);\n"
                          "}\n"
                          "\n"
                          "property = \"%s\";\n"
                          "if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &property)) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_return_no_memory_error (NULL);\n"
                          "}\n"
                          "\n",
                          DBUS_INTERFACE_PROPERTIES,
                          interface->name,
                          property->name))
            return NULL;

      /* FIXME autostart? */

      /* Complete the marshalling block by sending the message and checking
       * for error replies.
       */
      notify_name = symbol_impl (NULL, prefix, interface->name,
                           property->name, "get_notify");
      if (! notify_name)
            return NULL;

      if (! nih_strcat_sprintf (&call_block, NULL,
                          "/* Send the message and set up the reply notification. */\n"
                          "pending_data = nih_dbus_pending_data_new (NULL, proxy->connection,\n"
                          "                                          (NihDBusReplyHandler)handler,\n"
                          "                                          error_handler, data);\n"
                          "if (! pending_data) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_return_no_memory_error (NULL);\n"
                          "}\n"
                          "\n"
                          "pending_call = NULL;\n"
                          "if (! dbus_connection_send_with_reply (proxy->connection, method_call,\n"
                          "                                       &pending_call, timeout)) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_free (pending_data);\n"
                          "\tnih_return_no_memory_error (NULL);\n"
                          "}\n"
                          "\n"
                          "dbus_message_unref (method_call);\n"
                          "\n"
                          "if (! pending_call) {\n"
                          "\tnih_dbus_error_raise (DBUS_ERROR_DISCONNECTED,\n"
                          "\t                      \"Connection is closed\");\n"
                          "\tnih_free (pending_data);\n"
                          "\treturn NULL;\n"
                          "}\n"
                          "\n"
                          "NIH_MUST (dbus_pending_call_set_notify (pending_call, (DBusPendingCallNotifyFunction)%s,\n"
                          "                                        pending_data, (DBusFreeFunction)nih_discard));\n",
                          notify_name))
            return NULL;

      /* Lay out the function body, indenting it all before placing it
       * in the function code.
       */
      vars_block = type_var_layout (NULL, &locals);
      if (! vars_block)
            return NULL;

      if (! nih_strcat_sprintf (&body, NULL,
                          "%s"
                          "\n"
                          "%s"
                          "\n"
                          "%s"
                          "\n"
                          "return pending_call;\n",
                          vars_block,
                          assert_block,
                          call_block))
            return NULL;

      if (! indent (&body, NULL, 1))
            return NULL;

      /* Function header */
      code = type_func_to_string (parent, func);
      if (! code)
            return NULL;

      if (! nih_strcat_sprintf (&code, parent,
                          "{\n"
                          "%s"
                          "}\n",
                          body)) {
            nih_free (code);
            return NULL;
      }

      /* Append the function to the prototypes list */
      nih_list_add (prototypes, &func->entry);
      nih_ref (func, code);

      return code;
}

/**
 * property_proxy_get_notify_function:
 * @parent: parent object for new string.
 * @prefix: prefix for function name,
 * @interface: interface of @property,
 * @property: property to generate function for,
 * @prototypes: list to append function prototypes to,
 * @typedefs: list to append function pointer typedef definitions to,
 * @structs: list to append structure definitions to.
 *
 * Generates C code for a function to handle the notification of
 * a complete pending call to obtain the value of the property @property
 * on @interface by calling either the handler function on success or
 * error function on failure.
 *
 * The notify function will call a handler function passed in if the
 * reply is valid, the typedef name for this handler must be passed as
 * @handler_type.  The actual type for this can be obtained from the
 * entry added to @typedefs.
 *
 * The prototype of the returned function is returned as a TypeFunc object
 * appended to the @prototypes list.
 *
 * The typedef for the handler function is returned as a TypeFunc object
 * added to the @typedefs list.
 *
 * If the property type requires a structure to be defined, the
 * definition is returned as a TypeStruct object appended to the @structs
 * list.  The name is generated from @prefix, @interface and @property.
 *
 * If @parent is not NULL, it should be a pointer to another object which
 * will be used as a parent for the returned string.  When all parents
 * of the returned string are freed, the return string will also be
 * freed.
 *
 * Returns: newly allocated string or NULL if insufficient memory.
 **/
char *
property_proxy_get_notify_function (const void *parent,
                            const char *prefix,
                            Interface * interface,
                            Property *  property,
                            NihList *   prototypes,
                            NihList *   typedefs,
                            NihList *   structs)
{
      DBusSignatureIter   iter;
      NihList             outputs;
      NihList             locals;
      NihList             property_structs;
      nih_local char *    name = NULL;
      nih_local TypeFunc *func = NULL;
      TypeVar *           arg;
      nih_local char *    assert_block = NULL;
      nih_local TypeVar * reply_var = NULL;
      nih_local TypeVar * iter_var = NULL;
      nih_local TypeVar * variter_var = NULL;
      nih_local TypeVar * error_var = NULL;
      nih_local TypeVar * parent_var = NULL;
      nih_local char *    steal_block = NULL;
      nih_local char *    demarshal_block = NULL;
      nih_local char *    call_block = NULL;
      nih_local char *    handler_type = NULL;
      nih_local char *    handler_name = NULL;
      nih_local TypeFunc *handler_func = NULL;
      nih_local char *    oom_error_code = NULL;
      nih_local char *    type_error_code = NULL;
      nih_local char *    block = NULL;
      nih_local char *    vars_block = NULL;
      nih_local char *    body = NULL;
      char *              code = NULL;

      nih_assert (prefix != NULL);
      nih_assert (interface != NULL);
      nih_assert (property != NULL);
      nih_assert (prototypes != NULL);
      nih_assert (typedefs != NULL);
      nih_assert (structs != NULL);

      dbus_signature_iter_init (&iter, property->type);

      nih_list_init (&outputs);
      nih_list_init (&locals);
      nih_list_init (&property_structs);

      /* The function takes the pending call being notified and the
       * associated data structure.  We don't mark the function deprecated
       * since it's used internally, it's enough to mark the method
       * call function deprecated.
       */
      name = symbol_impl (NULL, prefix, interface->name,
                      property->name, "get_notify");
      if (! name)
            return NULL;

      func = type_func_new (NULL, "void", name);
      if (! func)
            return NULL;

      arg = type_var_new (func, "DBusPendingCall *", "pending_call");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      if (! nih_strcat (&assert_block, NULL,
                    "nih_assert (pending_call != NULL);\n"))
            return NULL;

      arg = type_var_new (func, "NihDBusPendingData *", "pending_data");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      if (! nih_strcat (&assert_block, NULL,
                    "nih_assert (pending_data != NULL);\n"))
            return NULL;


      /* The function requires a message pointer, stolen from the
       * pending call and iterators for the message and variant.  We
       * also need a parent message context for any allocations we make,
       * as well as an error object.
       */
      reply_var = type_var_new (NULL, "DBusMessage *", "reply");
      if (! reply_var)
            return NULL;

      nih_list_add (&locals, &reply_var->entry);

      iter_var = type_var_new (NULL, "DBusMessageIter", "iter");
      if (! iter_var)
            return NULL;

      nih_list_add (&locals, &iter_var->entry);

      variter_var = type_var_new (NULL, "DBusMessageIter", "variter");
      if (! variter_var)
            return NULL;

      nih_list_add (&locals, &variter_var->entry);

      parent_var = type_var_new (NULL, "NihDBusMessage *", "message");
      if (! parent_var)
            return NULL;

      nih_list_add (&locals, &parent_var->entry);

      error_var = type_var_new (NULL, "DBusError", "error");
      if (! error_var)
            return NULL;

      nih_list_add (&locals, &error_var->entry);

      /* Assert that the pending call is, in fact, complete then
       * steal the message from it; handling it immediately if it's an
       * error.
       */
      if (! nih_strcat (&steal_block, NULL,
                    "nih_assert (dbus_pending_call_get_completed (pending_call));\n"
                    "\n"
                    "/* Steal the reply from the pending call. */\n"
                    "reply = dbus_pending_call_steal_reply (pending_call);\n"
                    "nih_assert (reply != NULL);\n"
                    "\n"
                    "/* Handle error replies */\n"
                    "if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR) {\n"
                    "\tmessage = NIH_MUST (nih_dbus_message_new (pending_data, pending_data->connection, reply));\n"
                    "\n"
                    "\tdbus_error_init (&error);\n"
                    "\tdbus_set_error_from_message (&error, message->message);\n"
                    "\n"
                    "\tnih_error_push_context ();\n"
                    "\tnih_dbus_error_raise (error.name, error.message);\n"
                    "\tpending_data->error_handler (pending_data->data, message);\n"
                    "\tnih_error_pop_context ();\n"
                    "\n"
                    "\tdbus_error_free (&error);\n"
                    "\tnih_free (message);\n"
                    "\tdbus_message_unref (reply);\n"
                    "\treturn;\n"
                    "}\n"
                    "\n"
                    "nih_assert (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN);\n"
                    "\n"))
            return NULL;

      /* To deal with out-of-memory situations, we have to loop until we've
       * extracted all of the arguments, so this now happens in a different
       * code block.  Create a message context and initialise the iterator,
       * recursing into the variant.
       */
      if (! nih_strcat (&demarshal_block, NULL,
                    "/* Create a message context for the reply, and iterate\n"
                    " * over and recurse into the arguments.\n"
                    " */\n"
                    "message = nih_dbus_message_new (pending_data, pending_data->connection, reply);\n"
                    "if (! message)\n"
                    "\tgoto enomem;\n"
                    "\n"
                    "dbus_message_iter_init (message->message, &iter);\n"
                    "\n"
                    "if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_VARIANT) {\n"
                    "\tnih_error_push_context ();\n"
                    "\tnih_error_raise (NIH_DBUS_INVALID_ARGS,\n"
                    "\t                 _(NIH_DBUS_INVALID_ARGS_STR));\n"
                    "\tpending_data->error_handler (pending_data->data, message);\n"
                    "\tnih_error_pop_context ();\n"
                    "\n"
                    "\tnih_free (message);\n"
                    "\tdbus_message_unref (reply);\n"
                    "\treturn;\n"
                    "}\n"
                    "\n"
                    "dbus_message_iter_recurse (&iter, &variter);\n"
                    "\n"))
            return NULL;

      /* Begin the handler calling block, the handler is not permitted
       * to reply.
       */
      handler_type = symbol_typedef (NULL, prefix, interface->symbol, "Get",
                               property->symbol, "Reply");
      if (! handler_type)
            return NULL;

      if (! nih_strcat_sprintf (&call_block, NULL,
                          "/* Call the handler function */\n"
                          "nih_error_push_context ();\n"
                          "((%s)pending_data->handler) (pending_data->data, message",
                          handler_type))
            return NULL;

      handler_name = nih_sprintf (NULL, "(*%s)", handler_type);
      if (! handler_name)
            return NULL;

      handler_func = type_func_new (NULL, "typedef void", handler_name);
      if (! handler_func)
            return NULL;

      arg = type_var_new (handler_func, "void *", "data");
      if (! arg)
            return NULL;

      nih_list_add (&handler_func->args, &arg->entry);

      arg = type_var_new (handler_func, "NihDBusMessage *", "message");
      if (! arg)
            return NULL;

      nih_list_add (&handler_func->args, &arg->entry);

      /* In case of out of memory, we can't just return because we've
       * already made the method call so we loop over the code instead.
       * But in case of type error in the returned arguments, all we
       * can do is return an error.
       */
      oom_error_code = nih_sprintf (NULL,
                              "nih_free (message);\n"
                              "message = NULL;\n"
                              "goto enomem;\n");
      if (! oom_error_code)
            return NULL;

      type_error_code = nih_strdup (NULL,
                              "nih_error_push_context ();\n"
                              "nih_error_raise (NIH_DBUS_INVALID_ARGS,\n"
                              "                 _(NIH_DBUS_INVALID_ARGS_STR));\n"
                              "pending_data->error_handler (pending_data->data, message);\n"
                              "nih_error_pop_context ();\n"
                              "\n"
                              "nih_free (message);\n"
                              "dbus_message_unref (reply);\n"
                              "return;\n");
      if (! type_error_code)
            return NULL;

      block = demarshal (NULL, &iter, "message", "variter", "value",
                     oom_error_code,
                     type_error_code,
                     &outputs, &locals,
                     prefix, interface->symbol,
                     property->symbol, NULL,
                     &property_structs);
      if (! block)
            return NULL;

      if (! nih_strcat_sprintf (&demarshal_block, NULL,
                          "%s"
                          "\n",
                          block))
            return NULL;

      /* Each of the outputs from the demarshalling code becomes a local
       * variable to our function that we store the value in, and passed
       * to the handler function.
       */
      NIH_LIST_FOREACH_SAFE (&outputs, iter) {
            TypeVar *var = (TypeVar *)iter;
            TypeVar *arg;

            if (! nih_strcat_sprintf (&call_block, NULL,
                                ", %s",
                                var->name))
                  return NULL;

            nih_list_add (&locals, &var->entry);
            nih_ref (var, demarshal_block);

            /* Handler arg is const */
            arg = type_var_new (handler_func, var->type, var->name);
            if (! arg)
                  return NULL;

            if (! type_to_const (&arg->type, arg))
                  return NULL;

            nih_list_add (&handler_func->args, &arg->entry);
      }

      /* Complete the demarshalling block, checking for any unexpected
       * reply arguments which we also want to error on.
       */
      if (! nih_strcat_sprintf (&demarshal_block, NULL,
                          "dbus_message_iter_next (&iter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) {\n"
                          "\tnih_error_push_context ();\n"
                          "\tnih_error_raise (NIH_DBUS_INVALID_ARGS,\n"
                          "\t                 _(NIH_DBUS_INVALID_ARGS_STR));\n"
                          "\tpending_data->error_handler (pending_data->data, message);\n"
                          "\tnih_error_pop_context ();\n"
                          "\n"
                          "\tnih_free (message);\n"
                          "\tdbus_message_unref (reply);\n"
                          "\treturn;\n"
                          "}\n"
                          "\n"))
            return NULL;

      /* Complete the call block. */
      if (! nih_strcat (&call_block, NULL, ");\n"
                    "nih_error_pop_context ();\n"
                    "\n"
                    "nih_free (message);\n"
                    "dbus_message_unref (reply);\n"))
            return NULL;

      /* Lay out the function body, indenting it all before placing it
       * in the function code.
       */
      vars_block = type_var_layout (NULL, &locals);
      if (! vars_block)
            return NULL;

      if (! indent (&demarshal_block, NULL, 1))
            return NULL;

      if (! nih_strcat_sprintf (&body, NULL,
                          "%s"
                          "\n"
                          "%s"
                          "\n"
                          "%s"
                          "do {\n"
                          "\t__label__ enomem;\n"
                          "\n"
                          "%s"
                          "enomem: __attribute__ ((unused));\n"
                          "} while (! message);\n"
                          "\n"
                          "%s",
                          vars_block,
                          assert_block,
                          steal_block,
                          demarshal_block,
                          call_block))
            return NULL;

      if (! indent (&body, NULL, 1))
            return NULL;

      /* Function header */
      code = type_func_to_string (parent, func);
      if (! code)
            return NULL;

      if (! nih_strcat_sprintf (&code, parent,
                          "{\n"
                          "%s"
                          "}\n",
                          body)) {
            nih_free (code);
            return NULL;
      }

      /* Append the functions to the prototypes and typedefs list */
      nih_list_add (prototypes, &func->entry);
      nih_ref (func, code);

      nih_list_add (typedefs, &handler_func->entry);
      nih_ref (handler_func, code);

      NIH_LIST_FOREACH_SAFE (&property_structs, iter) {
            TypeStruct *structure = (TypeStruct *)iter;

            nih_ref (structure, code);
            nih_list_add (structs, &structure->entry);
      }

      return code;
}


/**
 * property_proxy_set_function:
 * @parent: parent object for new string.
 * @prefix: prefix for function name,
 * @interface: interface of @property,
 * @property: property to generate function for,
 * @prototypes: list to append function prototypes to,
 * @structs: list to append structure definitions to.
 *
 * Generates C code for a function that will make an asynchronous method
 * call to set the value of the property @property on @interface,
 * calling a notify function when the method call completes.
 *
 * The prototype of the returned function is returned as a TypeFunc object
 * appended to the @prototypes list.
 *
 * The names of both the returned function and notify function prototype
 * will be generated using information in @interface and @property, prefixed
 * with @prefix.
 *
 * The notify function will call a handler function passed in if the
 * reply is valid.  The name and type for this can be obtained from
 * property_proxy_set_notify_function().
 *
 * If the property type requires a structure to be defined, the
 * definition is returned as a TypeStruct object appended to the @structs
 * list.  The name is generated from @prefix, @interface and @property.
 *
 * If @parent is not NULL, it should be a pointer to another object which
 * will be used as a parent for the returned string.  When all parents
 * of the returned string are freed, the return string will also be
 * freed.
 *
 * Returns: newly allocated string or NULL if insufficient memory.
 **/
char *
property_proxy_set_function (const void *parent,
                       const char *prefix,
                       Interface * interface,
                       Property *  property,
                       NihList *   prototypes,
                       NihList *   structs)
{
      DBusSignatureIter   iter;
      NihList             inputs;
      NihList             locals;
      NihList             property_structs;
      nih_local char *    name = NULL;
      nih_local TypeFunc *func = NULL;
      TypeVar *           arg;
      NihListEntry *      attrib;
      nih_local char *    assert_block = NULL;
      nih_local char *    handler_type = NULL;
      nih_local TypeVar * message_var = NULL;
      nih_local TypeVar * iter_var = NULL;
      nih_local TypeVar * variter_var = NULL;
      nih_local TypeVar * pending_var = NULL;
      nih_local TypeVar * data_var = NULL;
      nih_local TypeVar * interface_var = NULL;
      nih_local TypeVar * property_var = NULL;
      nih_local char *    marshal_block = NULL;
      nih_local char *    call_block = NULL;
      nih_local char *    notify_name = NULL;
      nih_local char *    block = NULL;
      nih_local char *    vars_block = NULL;
      nih_local char *    oom_error_code = NULL;
      nih_local char *    body = NULL;
      char *              code = NULL;

      nih_assert (prefix != NULL);
      nih_assert (interface != NULL);
      nih_assert (property != NULL);
      nih_assert (prototypes != NULL);
      nih_assert (structs != NULL);

      dbus_signature_iter_init (&iter, property->type);

      nih_list_init (&inputs);
      nih_list_init (&locals);
      nih_list_init (&property_structs);

      /* The function returns a pending call, and takes the proxy object
       * as argument along with the new property value.  The pending call
       * also indicates whether an error occurred, so we want warning if
       * the result isn't used.  We don't have a malloc attribute, since
       * we can't guarantee that D-Bus doesn't cache them.  Since this is
       * used by the client, we also add a deprecated attribute if the
       * property is deprecated.
       */
      name = symbol_extern (NULL, prefix, interface->symbol, "set",
                        property->symbol, NULL);
      if (! name)
            return NULL;

      func = type_func_new (NULL, "DBusPendingCall *", name);
      if (! func)
            return NULL;

      attrib = nih_list_entry_new (func);
      if (! attrib)
            return NULL;

      attrib->str = nih_strdup (attrib, "warn_unused_result");
      if (! attrib->str)
            return NULL;

      nih_list_add (&func->attribs, &attrib->entry);

      if (property->deprecated) {
            attrib = nih_list_entry_new (func);
            if (! attrib)
                  return NULL;

            attrib->str = nih_strdup (attrib, "deprecated");
            if (! attrib->str)
                  return NULL;

            nih_list_add (&func->attribs, &attrib->entry);
      }

      arg = type_var_new (func, "NihDBusProxy *", "proxy");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      if (! nih_strcat (&assert_block, NULL,
                    "nih_assert (proxy != NULL);\n"))
            return NULL;

      /* The function requires a message pointer, which we allocate,
       * and an iterator for it to append the arguments, as well as an
       * iterator for the variant.  We also need a return pending call
       * pointer and data structure as well.
       */
      message_var = type_var_new (NULL, "DBusMessage *", "method_call");
      if (! message_var)
            return NULL;

      nih_list_add (&locals, &message_var->entry);

      iter_var = type_var_new (NULL, "DBusMessageIter", "iter");
      if (! iter_var)
            return NULL;

      nih_list_add (&locals, &iter_var->entry);

      variter_var = type_var_new (NULL, "DBusMessageIter", "variter");
      if (! variter_var)
            return NULL;

      nih_list_add (&locals, &variter_var->entry);

      pending_var = type_var_new (NULL, "DBusPendingCall *", "pending_call");
      if (! pending_var)
            return NULL;

      nih_list_add (&locals, &pending_var->entry);

      data_var = type_var_new (NULL, "NihDBusPendingData *", "pending_data");
      if (! data_var)
            return NULL;

      nih_list_add (&locals, &data_var->entry);

      /* Annoyingly we also need variables for the interface and
       * property names, since D-Bus wants their address and can't just
       * take a constant string.
       */
      interface_var = type_var_new (NULL, "const char *", "interface");
      if (! interface_var)
            return NULL;

      nih_list_add (&locals, &interface_var->entry);

      property_var = type_var_new (NULL, "const char *", "property");
      if (! property_var)
            return NULL;

      nih_list_add (&locals, &property_var->entry);


      /* Create the method call to get the property, the property
       * interface gets specified as an argument - the method call
       * interface is the D-Bus properties one.
       */
      if (! nih_strcat_sprintf (&marshal_block, NULL,
                          "/* Construct the method call message. */\n"
                          "method_call = dbus_message_new_method_call (proxy->name, proxy->path, \"%s\", \"Set\");\n"
                          "if (! method_call)\n"
                          "\tnih_return_no_memory_error (NULL);\n"
                          "\n"
                          "dbus_message_set_auto_start (method_call, proxy->auto_start);\n"
                          "\n"
                          "dbus_message_iter_init_append (method_call, &iter);\n"
                          "\n"
                          "interface = \"%s\";\n"
                          "if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface)) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_return_no_memory_error (NULL);\n"
                          "}\n"
                          "\n"
                          "property = \"%s\";\n"
                          "if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &property)) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_return_no_memory_error (NULL);\n"
                          "}\n"
                          "\n"
                          "if (! dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, \"%s\", &variter)) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_return_no_memory_error (NULL);\n"
                          "}\n"
                          "\n",
                          DBUS_INTERFACE_PROPERTIES,
                          interface->name,
                          property->name,
                          property->type))
            return NULL;

      /* FIXME autostart? */

      /* In case of out of memory, we just return the error to the caller
       * since we haven't made the method call yet.
       */
      oom_error_code = nih_sprintf (NULL,
                              "dbus_message_iter_abandon_container (&iter, &variter);\n"
                              "dbus_message_unref (method_call);\n"
                              "nih_return_no_memory_error (NULL);\n");
      if (! oom_error_code)
            return NULL;

      block = marshal (NULL, &iter, "variter", "value",
                   oom_error_code,
                   &inputs, &locals,
                   prefix, interface->symbol,
                   property->symbol, NULL,
                   &property_structs);
      if (! block)
            return NULL;

      if (! nih_strcat_sprintf (&marshal_block, NULL,
                          "%s"
                          "\n",
                          block))
            return NULL;

      /* Each of the inputs of the marshalling code becomes a const
       * argument to our function that we obtain the value from.
       */
      NIH_LIST_FOREACH_SAFE (&inputs, iter) {
            TypeVar *var = (TypeVar *)iter;

            if (! type_to_const (&var->type, var))
                  return NULL;

            if (! type_strcat_assert (&assert_block, NULL, var,
                                func->args.prev != &func->args ? (TypeVar *)func->args.prev : NULL,
                                _iter.next != &inputs ? (TypeVar *)_iter.next : NULL))
                  return NULL;

            nih_list_add (&func->args, &var->entry);
            nih_ref (var, func);
      }

      /* Complete the marshalling block by closing the container. */
      if (! nih_strcat_sprintf (&marshal_block, NULL,
                          "if (! dbus_message_iter_close_container (&iter, &variter)) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_return_no_memory_error (NULL);\n"
                          "}\n"
                          "\n"))
            return NULL;

      /* We also have an argument for an optional handler that notifies
       * of a successful property set and an error handler which notifies
       * of an error.  The error handler is optional only if the handler
       * itself is NULL.  A data argument is passed to both, and we also
       * have the timeout for the method call.
       */
      handler_type = symbol_typedef (NULL, prefix, interface->symbol,
                               "Set", property->symbol, "Reply");
      if (! handler_type)
            return NULL;

      arg = type_var_new (func, handler_type, "handler");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      arg = type_var_new (func, "NihDBusErrorHandler", "error_handler");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      arg = type_var_new (func, "void *", "data");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      if (! nih_strcat (&assert_block, NULL,
                    "nih_assert ((handler == NULL) || (error_handler != NULL));\n"))
            return NULL;

      arg = type_var_new (func, "int", "timeout");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      /* Send the message and check for error replies, or arguments
       * in the reply (which is an error).
       */
      notify_name = symbol_impl (NULL, prefix, interface->name,
                           property->name, "set_notify");
      if (! notify_name)
            return NULL;

      if (! nih_strcat_sprintf (&call_block, NULL,
                          "/* Handle a fire-and-forget message */\n"
                          "if (! error_handler) {\n"
                          "\tdbus_message_set_no_reply (method_call, TRUE);\n"
                          "\tif (! dbus_connection_send (proxy->connection, method_call, NULL)) {\n"
                          "\t\tdbus_message_unref (method_call);\n"
                          "\t\tnih_return_no_memory_error (NULL);\n"
                          "\t}\n"
                          "\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\treturn (DBusPendingCall *)TRUE;\n"
                          "}\n"
                          "\n"
                          "/* Send the message and set up the reply notification. */\n"
                          "pending_data = nih_dbus_pending_data_new (NULL, proxy->connection,\n"
                          "                                          (NihDBusReplyHandler)handler,\n"
                          "                                          error_handler, data);\n"
                          "if (! pending_data) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_return_no_memory_error (NULL);\n"
                          "}\n"
                          "\n"
                          "pending_call = NULL;\n"
                          "if (! dbus_connection_send_with_reply (proxy->connection, method_call,\n"
                          "                                       &pending_call, timeout)) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_free (pending_data);\n"
                          "\tnih_return_no_memory_error (NULL);\n"
                          "}\n"
                          "\n"
                          "dbus_message_unref (method_call);\n"
                          "\n"
                          "if (! pending_call) {\n"
                          "\tnih_dbus_error_raise (DBUS_ERROR_DISCONNECTED,\n"
                          "\t                      \"Connection is closed\");\n"
                          "\tnih_free (pending_data);\n"
                          "\treturn NULL;\n"
                          "}\n"
                          "\n"
                          "NIH_MUST (dbus_pending_call_set_notify (pending_call, (DBusPendingCallNotifyFunction)%s,\n"
                          "                                        pending_data, (DBusFreeFunction)nih_discard));\n",
                          notify_name))
            return NULL;

      /* Lay out the function body, indenting it all before placing it
       * in the function code.
       */
      vars_block = type_var_layout (NULL, &locals);
      if (! vars_block)
            return NULL;

      if (! nih_strcat_sprintf (&body, NULL,
                          "%s"
                          "\n"
                          "%s"
                          "\n"
                          "%s"
                          "%s"
                          "\n"
                          "return pending_call;\n",
                          vars_block,
                          assert_block,
                          marshal_block,
                          call_block))
            return NULL;

      if (! indent (&body, NULL, 1))
            return NULL;

      /* Function header */
      code = type_func_to_string (parent, func);
      if (! code)
            return NULL;

      if (! nih_strcat_sprintf (&code, parent,
                          "{\n"
                          "%s"
                          "}\n",
                          body)) {
            nih_free (code);
            return NULL;
      }

      /* Append the function to the prototypes list */
      nih_list_add (prototypes, &func->entry);
      nih_ref (func, code);

      NIH_LIST_FOREACH_SAFE (&property_structs, iter) {
            TypeStruct *structure = (TypeStruct *)iter;

            nih_ref (structure, code);
            nih_list_add (structs, &structure->entry);
      }

      return code;
}

/**
 * property_proxy_set_notify_function:
 * @parent: parent object for new string.
 * @prefix: prefix for function name,
 * @interface: interface of @property,
 * @property: property to generate function for,
 * @prototypes: list to append function prototypes to,
 * @typedefs: list to append function pointer typedef definitions to,
 * @structs: list to append structure definitions to.
 *
 * Generates C code for a function to handle the notification of
 * a complete pending call to set the value of the property @property
 * on @interface by calling either the handler function on success or
 * error function on failure.
 *
 * The notify function will call a handler function passed in if the
 * reply is valid, the typedef name for this handler must be passed as
 * @handler_type.  The actual type for this can be obtained from the
 * entry added to @typedefs.
 *
 * The prototype of the returned function is returned as a TypeFunc object
 * appended to the @prototypes list.
 *
 * The typedef for the handler function is returned as a TypeFunc object
 * added to the @typedefs list.
 *
 * If the property type requires a structure to be defined, the
 * definition is returned as a TypeStruct object appended to the @structs
 * list.  The name is generated from @prefix, @interface and @property.
 *
 * If @parent is not NULL, it should be a pointer to another object which
 * will be used as a parent for the returned string.  When all parents
 * of the returned string are freed, the return string will also be
 * freed.
 *
 * Returns: newly allocated string or NULL if insufficient memory.
 **/
char *
property_proxy_set_notify_function (const void *parent,
                            const char *prefix,
                            Interface * interface,
                            Property *  property,
                            NihList *   prototypes,
                            NihList *   typedefs,
                            NihList *   structs)
{
      DBusSignatureIter   iter;
      NihList             outputs;
      NihList             locals;
      nih_local char *    name = NULL;
      nih_local TypeFunc *func = NULL;
      TypeVar *           arg;
      nih_local char *    assert_block = NULL;
      nih_local TypeVar * reply_var = NULL;
      nih_local TypeVar * iter_var = NULL;
      nih_local TypeVar * error_var = NULL;
      nih_local TypeVar * parent_var = NULL;
      nih_local char *    steal_block = NULL;
      nih_local char *    call_block = NULL;
      nih_local char *    handler_type = NULL;
      nih_local char *    handler_name = NULL;
      nih_local TypeFunc *handler_func = NULL;
      nih_local char *    oom_error_code = NULL;
      nih_local char *    type_error_code = NULL;
      nih_local char *    block = NULL;
      nih_local char *    vars_block = NULL;
      nih_local char *    body = NULL;
      char *              code = NULL;

      nih_assert (prefix != NULL);
      nih_assert (interface != NULL);
      nih_assert (property != NULL);
      nih_assert (prototypes != NULL);
      nih_assert (typedefs != NULL);
      nih_assert (structs != NULL);

      dbus_signature_iter_init (&iter, property->type);

      nih_list_init (&outputs);
      nih_list_init (&locals);

      /* The function takes the pending call being notified and the
       * associated data structure.  We don't mark the function deprecated
       * since it's used internally, it's enough to mark the method
       * call function deprecated.
       */
      name = symbol_impl (NULL, prefix, interface->name,
                      property->name, "set_notify");
      if (! name)
            return NULL;

      func = type_func_new (NULL, "void", name);
      if (! func)
            return NULL;

      arg = type_var_new (func, "DBusPendingCall *", "pending_call");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      if (! nih_strcat (&assert_block, NULL,
                    "nih_assert (pending_call != NULL);\n"))
            return NULL;

      arg = type_var_new (func, "NihDBusPendingData *", "pending_data");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      if (! nih_strcat (&assert_block, NULL,
                    "nih_assert (pending_data != NULL);\n"))
            return NULL;


      /* The function requires a message pointer, stolen from the
       * pending call and an iterator for the message.  We also need
       * a parent message context for any allocations we make, as well
       * as an error object.
       */
      reply_var = type_var_new (NULL, "DBusMessage *", "reply");
      if (! reply_var)
            return NULL;

      nih_list_add (&locals, &reply_var->entry);

      iter_var = type_var_new (NULL, "DBusMessageIter", "iter");
      if (! iter_var)
            return NULL;

      nih_list_add (&locals, &iter_var->entry);

      parent_var = type_var_new (NULL, "NihDBusMessage *", "message");
      if (! parent_var)
            return NULL;

      nih_list_add (&locals, &parent_var->entry);

      error_var = type_var_new (NULL, "DBusError", "error");
      if (! error_var)
            return NULL;

      nih_list_add (&locals, &error_var->entry);

      /* Assert that the pending call is, in fact, complete then
       * steal the message from it; handling it immediately if it's an
       * error.
       */
      if (! nih_strcat (&steal_block, NULL,
                    "nih_assert (dbus_pending_call_get_completed (pending_call));\n"
                    "\n"
                    "/* Steal the reply from the pending call. */\n"
                    "reply = dbus_pending_call_steal_reply (pending_call);\n"
                    "nih_assert (reply != NULL);\n"
                    "\n"
                    "/* Handle error replies */\n"
                    "if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR) {\n"
                    "\tmessage = NIH_MUST (nih_dbus_message_new (pending_data, pending_data->connection, reply));\n"
                    "\n"
                    "\tdbus_error_init (&error);\n"
                    "\tdbus_set_error_from_message (&error, message->message);\n"
                    "\n"
                    "\tnih_error_push_context ();\n"
                    "\tnih_dbus_error_raise (error.name, error.message);\n"
                    "\tpending_data->error_handler (pending_data->data, message);\n"
                    "\tnih_error_pop_context ();\n"
                    "\n"
                    "\tdbus_error_free (&error);\n"
                    "\tnih_free (message);\n"
                    "\tdbus_message_unref (reply);\n"
                    "\treturn;\n"
                    "}\n"
                    "\n"
                    "nih_assert (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN);\n"
                    "\n"))
            return NULL;

      /* Create a message context, and check that the reply had no
       * arguments before calling the handler.
       */
      handler_type = symbol_typedef (NULL, prefix, interface->symbol, "Set",
                               property->symbol, "Reply");
      if (! handler_type)
            return NULL;

      if (! nih_strcat_sprintf (&call_block, NULL,
                          "/* Create a message context for the reply, and check\n"
                          " * there are no arguments.\n"
                          " */\n"
                          "message = NIH_MUST (nih_dbus_message_new (pending_data, pending_data->connection, reply));\n"
                          "dbus_message_iter_init (message->message, &iter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) {\n"
                          "\tnih_error_push_context ();\n"
                          "\tnih_error_raise (NIH_DBUS_INVALID_ARGS,\n"
                          "\t                 _(NIH_DBUS_INVALID_ARGS_STR));\n"
                          "\tpending_data->error_handler (pending_data->data, message);\n"
                          "\tnih_error_pop_context ();\n"
                          "\n"
                          "\tnih_free (message);\n"
                          "\tdbus_message_unref (reply);\n"
                          "\treturn;\n"
                          "}\n"
                          "\n"
                          "/* Call the handler function */\n"
                          "if (pending_data->handler) {\n"
                          "\tnih_error_push_context ();\n"
                          "\t((%s)pending_data->handler) (pending_data->data, message);\n"
                          "\tnih_error_pop_context ();\n"
                          "}\n"
                          "\n"
                          "nih_free (message);\n"
                          "dbus_message_unref (reply);\n",
                          handler_type))
            return NULL;

      handler_name = nih_sprintf (NULL, "(*%s)", handler_type);
      if (! handler_name)
            return NULL;

      handler_func = type_func_new (NULL, "typedef void", handler_name);
      if (! handler_func)
            return NULL;

      arg = type_var_new (handler_func, "void *", "data");
      if (! arg)
            return NULL;

      nih_list_add (&handler_func->args, &arg->entry);

      arg = type_var_new (handler_func, "NihDBusMessage *", "message");
      if (! arg)
            return NULL;

      nih_list_add (&handler_func->args, &arg->entry);


      /* Lay out the function body, indenting it all before placing it
       * in the function code.
       */
      vars_block = type_var_layout (NULL, &locals);
      if (! vars_block)
            return NULL;

      if (! nih_strcat_sprintf (&body, NULL,
                          "%s"
                          "\n"
                          "%s"
                          "\n"
                          "%s"
                          "%s",
                          vars_block,
                          assert_block,
                          steal_block,
                          call_block))
            return NULL;

      if (! indent (&body, NULL, 1))
            return NULL;

      /* Function header */
      code = type_func_to_string (parent, func);
      if (! code)
            return NULL;

      if (! nih_strcat_sprintf (&code, parent,
                          "{\n"
                          "%s"
                          "}\n",
                          body)) {
            nih_free (code);
            return NULL;
      }

      /* Append the functions to the prototypes and typedefs list */
      nih_list_add (prototypes, &func->entry);
      nih_ref (func, code);

      nih_list_add (typedefs, &handler_func->entry);
      nih_ref (handler_func, code);

      return code;
}


/**
 * property_proxy_get_sync_function:
 * @parent: parent object for new string.
 * @prefix: prefix for function name,
 * @interface: interface of @property,
 * @property: property to generate function for,
 * @prototypes: list to append function prototypes to,
 * @structs: list to append structure definitions to.
 *
 * Generates C code for a function that will make a synchronous method
 * call to obtain the value of the property @property on @interface.
 *
 * The prototype of the returned function is returned as a TypeFunc object
 * appended to the @prototypes list, with the name as @name itself.
 *
 * If the property type requires a structure to be defined, the
 * definition is returned as a TypeStruct object appended to the @structs
 * list.  The name is generated from @prefix, @interface and @property.
 *
 * If @parent is not NULL, it should be a pointer to another object which
 * will be used as a parent for the returned string.  When all parents
 * of the returned string are freed, the return string will also be
 * freed.
 *
 * Returns: newly allocated string or NULL if insufficient memory.
 **/
char *
property_proxy_get_sync_function (const void *parent,
                          const char *prefix,
                          Interface * interface,
                          Property *  property,
                          NihList *   prototypes,
                          NihList *   structs)
{
      DBusSignatureIter   iter;
      NihList             outputs;
      NihList             locals;
      NihList             property_structs;
      nih_local char *    name = NULL;
      nih_local TypeFunc *func = NULL;
      TypeVar *           arg;
      NihListEntry *      attrib;
      nih_local char *    assert_block = NULL;
      nih_local TypeVar * message_var = NULL;
      nih_local TypeVar * iter_var = NULL;
      nih_local TypeVar * variter_var = NULL;
      nih_local TypeVar * error_var = NULL;
      nih_local TypeVar * reply_var = NULL;
      nih_local TypeVar * interface_var = NULL;
      nih_local TypeVar * property_var = NULL;
      nih_local char *    call_block = NULL;
      nih_local char *    demarshal_block = NULL;
      nih_local char *    oom_error_code = NULL;
      nih_local char *    type_error_code = NULL;
      nih_local char *    block = NULL;
      nih_local char *    vars_block = NULL;
      nih_local char *    body = NULL;
      char *              code = NULL;

      nih_assert (prefix != NULL);
      nih_assert (interface != NULL);
      nih_assert (property != NULL);
      nih_assert (prototypes != NULL);
      nih_assert (structs != NULL);

      dbus_signature_iter_init (&iter, property->type);

      nih_list_init (&outputs);
      nih_list_init (&locals);
      nih_list_init (&property_structs);

      /* The function returns an integer, and takes a parent object and
       * the proxy object as the argument along with an output argument
       * for the property value.  The integer is negative if a raised
       * error occurred, so we want warning if the result isn't used.
       * Since this is used by the client, we also add a deprecated
       * attribute if the property is deprecated.
       */
      name = symbol_extern (NULL, prefix, interface->symbol, "get",
                        property->symbol, "sync");
      if (! name)
            return NULL;

      func = type_func_new (NULL, "int", name);
      if (! func)
            return NULL;

      attrib = nih_list_entry_new (func);
      if (! attrib)
            return NULL;

      attrib->str = nih_strdup (attrib, "warn_unused_result");
      if (! attrib->str)
            return NULL;

      nih_list_add (&func->attribs, &attrib->entry);

      if (property->deprecated) {
            attrib = nih_list_entry_new (func);
            if (! attrib)
                  return NULL;

            attrib->str = nih_strdup (attrib, "deprecated");
            if (! attrib->str)
                  return NULL;

            nih_list_add (&func->attribs, &attrib->entry);
      }

      arg = type_var_new (func, "const void *", "parent");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      arg = type_var_new (func, "NihDBusProxy *", "proxy");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      if (! nih_strcat (&assert_block, NULL,
                    "nih_assert (proxy != NULL);\n"))
            return NULL;


      /* The function requires a message pointer, which we allocate,
       * and an iterator for it to append the arguments.  We also need
       * a reply message pointer as well and an error object.
       * Rather than deal with these by hand, it's far easier to put them
       * on the locals list and deal with them along with the rest.
       */
      message_var = type_var_new (NULL, "DBusMessage *", "method_call");
      if (! message_var)
            return NULL;

      nih_list_add (&locals, &message_var->entry);

      iter_var = type_var_new (NULL, "DBusMessageIter", "iter");
      if (! iter_var)
            return NULL;

      nih_list_add (&locals, &iter_var->entry);

      variter_var = type_var_new (NULL, "DBusMessageIter", "variter");
      if (! variter_var)
            return NULL;

      nih_list_add (&locals, &variter_var->entry);

      error_var = type_var_new (NULL, "DBusError", "error");
      if (! error_var)
            return NULL;

      nih_list_add (&locals, &error_var->entry);

      reply_var = type_var_new (NULL, "DBusMessage *", "reply");
      if (! reply_var)
            return NULL;

      nih_list_add (&locals, &reply_var->entry);

      /* Annoyingly we also need variables for the interface and
       * property names, since D-Bus wants their address and can't just
       * take a constant string.
       */
      interface_var = type_var_new (NULL, "const char *", "interface");
      if (! interface_var)
            return NULL;

      nih_list_add (&locals, &interface_var->entry);

      property_var = type_var_new (NULL, "const char *", "property");
      if (! property_var)
            return NULL;

      nih_list_add (&locals, &property_var->entry);


      /* Create the method call to get the property, the property
       * interface gets specified as an argument - the method call
       * interface is the D-Bus properties one.
       */
      if (! nih_strcat_sprintf (&call_block, NULL,
                          "/* Construct the method call message. */\n"
                          "method_call = dbus_message_new_method_call (proxy->name, proxy->path, \"%s\", \"Get\");\n"
                          "if (! method_call)\n"
                          "\tnih_return_no_memory_error (-1);\n"
                          "\n"
                          "dbus_message_set_auto_start (method_call, proxy->auto_start);\n"
                          "\n"
                          "dbus_message_iter_init_append (method_call, &iter);\n"
                          "\n"
                          "interface = \"%s\";\n"
                          "if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface)) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_return_no_memory_error (-1);\n"
                          "}\n"
                          "\n"
                          "property = \"%s\";\n"
                          "if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &property)) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_return_no_memory_error (-1);\n"
                          "}\n"
                          "\n",
                          DBUS_INTERFACE_PROPERTIES,
                          interface->name,
                          property->name))
            return NULL;

      /* FIXME autostart? */

      /* Complete the marshalling block by sending the message and checking
       * for error replies.
       */
      if (! nih_strcat_sprintf (&call_block, NULL,
                          "/* Send the message, and wait for the reply. */\n"
                          "dbus_error_init (&error);\n"
                          "\n"
                          "reply = dbus_connection_send_with_reply_and_block (proxy->connection, method_call, -1, &error);\n"
                          "if (! reply) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\n"
                          "\tif (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) {\n"
                          "\t\tnih_error_raise_no_memory ();\n"
                          "\t} else {\n"
                          "\t\tnih_dbus_error_raise (error.name, error.message);\n"
                          "\t}\n"
                          "\n"
                          "\tdbus_error_free (&error);\n"
                          "\treturn -1;\n"
                          "}\n"
                          "\n"))
            return NULL;

      /* Begin the demarshalling block, making sure the first argument
       * is a variant and recursing into it and also making sure that
       * there are no subsequent arguments before we allocate the
       * return value.
       */
      if (! nih_strcat_sprintf (&demarshal_block, NULL,
                          "dbus_message_unref (method_call);\n"
                          "\n"
                          "/* Iterate the method arguments, recursing into the variant */\n"
                          "dbus_message_iter_init (reply, &iter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_VARIANT) {\n"
                          "\tdbus_message_unref (reply);\n"
                          "\tnih_return_error (-1, NIH_DBUS_INVALID_ARGS,\n"
                          "\t                  _(NIH_DBUS_INVALID_ARGS_STR));\n"
                          "}\n"
                          "\n"
                          "dbus_message_iter_recurse (&iter, &variter);\n"
                          "\n"
                          "dbus_message_iter_next (&iter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) {\n"
                          "\tdbus_message_unref (reply);\n"
                          "\tnih_return_error (-1, NIH_DBUS_INVALID_ARGS,\n"
                          "\t                  _(NIH_DBUS_INVALID_ARGS_STR));\n"
                          "}\n"
                          "\n"))
            return NULL;

      /* In case of out of memory, we can't just return because we've
       * already made the method call so we loop over the code instead.
       * But in case of type error in the returned arguments, all we
       * can do is return an error.
       */
      oom_error_code = nih_sprintf (NULL,
                              "*value = NULL;\n"
                              "goto enomem;\n");
      if (! oom_error_code)
            return NULL;

      type_error_code = nih_strdup (NULL,
                              "dbus_message_unref (reply);\n"
                              "nih_return_error (-1, NIH_DBUS_INVALID_ARGS,\n"
                              "                  _(NIH_DBUS_INVALID_ARGS_STR));\n");
      if (! type_error_code)
            return NULL;

      block = demarshal (NULL, &iter, "parent", "variter", "local",
                     oom_error_code,
                     type_error_code,
                     &outputs, &locals,
                     prefix, interface->symbol,
                     property->symbol, NULL,
                     &property_structs);
      if (! block)
            return NULL;

      if (! nih_strcat (&block, NULL, "\n"))
            return NULL;

      /* Each of the outputs from the demarshalling code becomes a local
       * variable to our function that we store the value in, and an
       * argument to the function that we set when done.
       */
      NIH_LIST_FOREACH_SAFE (&outputs, iter) {
            TypeVar *       var = (TypeVar *)iter;
            nih_local char *arg_type = NULL;
            const char *    suffix;
            nih_local char *arg_name = NULL;
            TypeVar *       arg;

            /* Output variable */
            arg_type = nih_strdup (NULL, var->type);
            if (! arg_type)
                  return NULL;

            if (! type_to_pointer (&arg_type, NULL))
                  return NULL;

            nih_assert (! strncmp (var->name, "local", 5));
            suffix = var->name + 5;

            arg_name = nih_sprintf (NULL, "value%s", suffix);
            if (! arg_name)
                  return NULL;

            arg = type_var_new (func, arg_type, arg_name);
            if (! arg)
                  return NULL;

            nih_list_add (&func->args, &arg->entry);

            if (! nih_strcat_sprintf (&assert_block, NULL,
                                "nih_assert (%s != NULL);\n",
                                arg->name))
                  return NULL;

            /* Copy from local variable to output */
            if (! nih_strcat_sprintf (&block, NULL,
                                "*%s = %s;\n",
                                arg->name, var->name))
                  return NULL;

            nih_list_add (&locals, &var->entry);
            nih_ref (var, demarshal_block);
      }

      /* Loop over the demarshalling code for out-of-memory situations */
      if (! indent (&block, NULL, 1))
            return NULL;

      if (! nih_strcat_sprintf (&demarshal_block, NULL,
                          "do {\n"
                          "\t__label__ enomem;\n"
                          "\n"
                          "%s"
                          "enomem: __attribute__ ((unused));\n"
                          "} while (! *value);\n"
                          "\n"
                          "dbus_message_unref (reply);\n",
                          block))
            return NULL;

      /* Lay out the function body, indenting it all before placing it
       * in the function code.
       */
      vars_block = type_var_layout (NULL, &locals);
      if (! vars_block)
            return NULL;

      if (! nih_strcat_sprintf (&body, NULL,
                          "%s"
                          "\n"
                          "%s"
                          "\n"
                          "%s"
                          "%s"
                          "\n"
                          "return 0;\n",
                          vars_block,
                          assert_block,
                          call_block,
                          demarshal_block))
            return NULL;

      if (! indent (&body, NULL, 1))
            return NULL;

      /* Function header */
      code = type_func_to_string (parent, func);
      if (! code)
            return NULL;

      if (! nih_strcat_sprintf (&code, parent,
                          "{\n"
                          "%s"
                          "}\n",
                          body)) {
            nih_free (code);
            return NULL;
      }

      /* Append the function to the prototypes list */
      nih_list_add (prototypes, &func->entry);
      nih_ref (func, code);

      NIH_LIST_FOREACH_SAFE (&property_structs, iter) {
            TypeStruct *structure = (TypeStruct *)iter;

            nih_ref (structure, code);
            nih_list_add (structs, &structure->entry);
      }

      return code;
}

/**
 * property_proxy_set_sync_function:
 * @parent: parent object for new string.
 * @prefix: prefix for function name,
 * @interface: interface of @property,
 * @property: property to generate function for,
 * @prototypes: list to append function prototypes to,
 * @structs: list to append structure definitions to.
 *
 * Generates C code for a function that will make a synchronous method
 * call to set the value of the property @property on @interface.
 *
 * The prototype of the returned function is returned as a TypeFunc object
 * appended to the @prototypes list, with the name as @name itself.
 *
 * If the property type requires a structure to be defined, the
 * definition is returned as a TypeStruct object appended to the @structs
 * list.  The name is generated from @prefix, @interface and @property.
 *
 * If @parent is not NULL, it should be a pointer to another object which
 * will be used as a parent for the returned string.  When all parents
 * of the returned string are freed, the return string will also be
 * freed.
 *
 * Returns: newly allocated string or NULL if insufficient memory.
 **/
char *
property_proxy_set_sync_function (const void *parent,
                          const char *prefix,
                          Interface * interface,
                          Property *  property,
                          NihList *   prototypes,
                          NihList *   structs)
{
      DBusSignatureIter   iter;
      NihList             inputs;
      NihList             locals;
      NihList             property_structs;
      nih_local char *    name = NULL;
      nih_local TypeFunc *func = NULL;
      TypeVar *           arg;
      NihListEntry *      attrib;
      nih_local char *    assert_block = NULL;
      nih_local TypeVar * message_var = NULL;
      nih_local TypeVar * iter_var = NULL;
      nih_local TypeVar * variter_var = NULL;
      nih_local TypeVar * error_var = NULL;
      nih_local TypeVar * reply_var = NULL;
      nih_local TypeVar * interface_var = NULL;
      nih_local TypeVar * property_var = NULL;
      nih_local char *    marshal_block = NULL;
      nih_local char *    call_block = NULL;
      nih_local char *    oom_error_code = NULL;
      nih_local char *    block = NULL;
      nih_local char *    vars_block = NULL;
      nih_local char *    body = NULL;
      char *              code = NULL;

      nih_assert (prefix != NULL);
      nih_assert (interface != NULL);
      nih_assert (property != NULL);
      nih_assert (prototypes != NULL);
      nih_assert (structs != NULL);

      dbus_signature_iter_init (&iter, property->type);

      nih_list_init (&inputs);
      nih_list_init (&locals);
      nih_list_init (&property_structs);

      /* The function returns an integer, and takes the proxy object
       * as the argument along with an input argument for the property
       * value.  The integer is negative if a raised error occurred,
       * so we want warning if the result isn't used.  Since this is
       * used by the client, we also add a deprecated attribute if
       * the property is deprecated.
       */
      name = symbol_extern (NULL, prefix, interface->symbol, "set",
                        property->symbol, "sync");
      if (! name)
            return NULL;

      func = type_func_new (NULL, "int", name);
      if (! func)
            return NULL;

      attrib = nih_list_entry_new (func);
      if (! attrib)
            return NULL;

      attrib->str = nih_strdup (attrib, "warn_unused_result");
      if (! attrib->str)
            return NULL;

      nih_list_add (&func->attribs, &attrib->entry);

      if (property->deprecated) {
            attrib = nih_list_entry_new (func);
            if (! attrib)
                  return NULL;

            attrib->str = nih_strdup (attrib, "deprecated");
            if (! attrib->str)
                  return NULL;

            nih_list_add (&func->attribs, &attrib->entry);
      }

      arg = type_var_new (func, "const void *", "parent");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      arg = type_var_new (func, "NihDBusProxy *", "proxy");
      if (! arg)
            return NULL;

      nih_list_add (&func->args, &arg->entry);

      if (! nih_strcat (&assert_block, NULL,
                    "nih_assert (proxy != NULL);\n"))
            return NULL;


      /* The function requires a message pointer, which we allocate,
       * and an iterator for it to append the arguments.  We also need
       * a reply message pointer as well and an error object.
       * Rather than deal with these by hand, it's far easier to put them
       * on the locals list and deal with them along with the rest.
       */
      message_var = type_var_new (NULL, "DBusMessage *", "method_call");
      if (! message_var)
            return NULL;

      nih_list_add (&locals, &message_var->entry);

      iter_var = type_var_new (NULL, "DBusMessageIter", "iter");
      if (! iter_var)
            return NULL;

      nih_list_add (&locals, &iter_var->entry);

      variter_var = type_var_new (NULL, "DBusMessageIter", "variter");
      if (! variter_var)
            return NULL;

      nih_list_add (&locals, &variter_var->entry);

      error_var = type_var_new (NULL, "DBusError", "error");
      if (! error_var)
            return NULL;

      nih_list_add (&locals, &error_var->entry);

      reply_var = type_var_new (NULL, "DBusMessage *", "reply");
      if (! reply_var)
            return NULL;

      nih_list_add (&locals, &reply_var->entry);

      /* Annoyingly we also need variables for the interface and
       * property names, since D-Bus wants their address and can't just
       * take a constant string.
       */
      interface_var = type_var_new (NULL, "const char *", "interface");
      if (! interface_var)
            return NULL;

      nih_list_add (&locals, &interface_var->entry);

      property_var = type_var_new (NULL, "const char *", "property");
      if (! property_var)
            return NULL;

      nih_list_add (&locals, &property_var->entry);


      /* Create the method call to set the property, the property
       * interface gets specified as an argument - the method call
       * interface is the D-Bus properties one.  Append a variant
       * which is where we put the new value.
       */
      if (! nih_strcat_sprintf (&marshal_block, NULL,
                          "/* Construct the method call message. */\n"
                          "method_call = dbus_message_new_method_call (proxy->name, proxy->path, \"%s\", \"Set\");\n"
                          "if (! method_call)\n"
                          "\tnih_return_no_memory_error (-1);\n"
                          "\n"
                          "dbus_message_set_auto_start (method_call, proxy->auto_start);\n"
                          "\n"
                          "dbus_message_iter_init_append (method_call, &iter);\n"
                          "\n"
                          "interface = \"%s\";\n"
                          "if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface)) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_return_no_memory_error (-1);\n"
                          "}\n"
                          "\n"
                          "property = \"%s\";\n"
                          "if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &property)) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_return_no_memory_error (-1);\n"
                          "}\n"
                          "\n"
                          "if (! dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, \"%s\", &variter)) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_return_no_memory_error (-1);\n"
                          "}\n"
                          "\n",
                          DBUS_INTERFACE_PROPERTIES,
                          interface->name,
                          property->name,
                          property->type))
            return NULL;

      /* FIXME autostart? */

      /* In case of out of memory, we just return the error to the caller
       * since we haven't made the method call yet.
       */
      oom_error_code = nih_sprintf (NULL,
                              "dbus_message_iter_abandon_container (&iter, &variter);\n"
                              "dbus_message_unref (method_call);\n"
                              "nih_return_no_memory_error (-1);\n");
      if (! oom_error_code)
            return NULL;

      block = marshal (NULL, &iter, "variter", "value",
                   oom_error_code,
                   &inputs, &locals,
                   prefix, interface->symbol,
                   property->symbol, NULL,
                   &property_structs);
      if (! block)
            return NULL;

      if (! nih_strcat_sprintf (&marshal_block, NULL,
                          "%s"
                          "\n",
                          block))
            return NULL;

      /* Each of the inputs of the marshalling code becomes a const
       * argument to our function that we obtain the value from.
       */
      NIH_LIST_FOREACH_SAFE (&inputs, iter) {
            TypeVar *var = (TypeVar *)iter;

            if (! type_to_const (&var->type, var))
                  return NULL;

            if (! type_strcat_assert (&assert_block, NULL, var,
                                func->args.prev != &func->args ? (TypeVar *)func->args.prev : NULL,
                                _iter.next != &inputs ? (TypeVar *)_iter.next : NULL))
                  return NULL;

            nih_list_add (&func->args, &var->entry);
            nih_ref (var, func);
      }

      /* Complete the marshalling block by closing the container. */
      if (! nih_strcat_sprintf (&marshal_block, NULL,
                          "if (! dbus_message_iter_close_container (&iter, &variter)) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\tnih_return_no_memory_error (-1);\n"
                          "}\n"
                          "\n"))
            return NULL;

      /* Send the message and check for error replies, or arguments
       * in the reply (which is an error).
       */
      if (! nih_strcat_sprintf (&call_block, NULL,
                          "/* Send the message, and wait for the reply. */\n"
                          "dbus_error_init (&error);\n"
                          "\n"
                          "reply = dbus_connection_send_with_reply_and_block (proxy->connection, method_call, -1, &error);\n"
                          "if (! reply) {\n"
                          "\tdbus_message_unref (method_call);\n"
                          "\n"
                          "\tif (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) {\n"
                          "\t\tnih_error_raise_no_memory ();\n"
                          "\t} else {\n"
                          "\t\tnih_dbus_error_raise (error.name, error.message);\n"
                          "\t}\n"
                          "\n"
                          "\tdbus_error_free (&error);\n"
                          "\treturn -1;\n"
                          "}\n"
                          "\n"
                          "/* Check the reply has no arguments */\n"
                          "dbus_message_unref (method_call);\n"
                          "dbus_message_iter_init (reply, &iter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) {\n"
                          "\tdbus_message_unref (reply);\n"
                          "\tnih_return_error (-1, NIH_DBUS_INVALID_ARGS,\n"
                          "\t                  _(NIH_DBUS_INVALID_ARGS_STR));\n"
                          "}\n"
                          "\n"
                          "dbus_message_unref (reply);\n"))
            return NULL;

      /* Lay out the function body, indenting it all before placing it
       * in the function code.
       */
      vars_block = type_var_layout (NULL, &locals);
      if (! vars_block)
            return NULL;

      if (! nih_strcat_sprintf (&body, NULL,
                          "%s"
                          "\n"
                          "%s"
                          "\n"
                          "%s"
                          "%s"
                          "\n"
                          "return 0;\n",
                          vars_block,
                          assert_block,
                          marshal_block,
                          call_block))
            return NULL;

      if (! indent (&body, NULL, 1))
            return NULL;

      /* Function header */
      code = type_func_to_string (parent, func);
      if (! code)
            return NULL;

      if (! nih_strcat_sprintf (&code, parent,
                          "{\n"
                          "%s"
                          "}\n",
                          body)) {
            nih_free (code);
            return NULL;
      }

      /* Append the function to the prototypes list */
      nih_list_add (prototypes, &func->entry);
      nih_ref (func, code);

      NIH_LIST_FOREACH_SAFE (&property_structs, iter) {
            TypeStruct *structure = (TypeStruct *)iter;

            nih_ref (structure, code);
            nih_list_add (structs, &structure->entry);
      }

      return code;
}

Generated by  Doxygen 1.6.0   Back to index