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

interface.c

/* nih-dbus-tool
 *
 * interface.c - interface parsing and handling
 *
 * 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 "symbol.h"
#include "indent.h"
#include "type.h"
#include "demarshal.h"
#include "node.h"
#include "interface.h"
#include "method.h"
#include "property.h"
#include "parse.h"
#include "errors.h"


/**
 * interface_name_valid:
 * @name: Interface name to verify.
 *
 * Verifies whether @name matches the specification for D-Bus interface
 * names.
 *
 * Returns: TRUE if valid, FALSE if not.
 **/
int
interface_name_valid (const char *name)
{
      size_t parts = 1;

      nih_assert (name != NULL);

      /* Name must not begin with a '.' */
      if (name[0] == '.')
            return FALSE;

      /* 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++) {
            /* Name components may be separated by single '.' characters,
             * multiple ones are not allowed.  Keep a count of how many
             * parts we have, since there's a defined minimum.
             */
            if (name[i] == '.') {
                  if (name[i-1] == '.')
                        return FALSE;

                  parts++;
                  continue;
            }

            /* Names may contain digits, but not at the beginning of the
             * name or any part of it.
             */
            if ((name[i] >= '0') && (name[i] <= '9')) {
                  if (i == 0)
                        return FALSE;
                  if (name[i-1] == '.')
                        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 consist of at least two parts */
      if (parts < 2)
            return FALSE;

      /* Final character may not be '.' */
      if (name[strlen (name) - 1] == '.')
            return FALSE;

      /* Name must be no more than 255 characters */
      if (strlen (name) > 255)
            return FALSE;

      return TRUE;
}


/**
 * interface_new:
 * @parent: parent object for new interface,
 * @name: D-Bus name of interface.
 *
 * Allocates a new D-Bus object Interface data structure, with the D-Bus
 * name set to @name.  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 interface.  When all parents
 * of the returned interface are freed, the returned interface will also be
 * freed.
 *
 * Returns: the new interface or NULL if the allocation failed.
 **/
Interface *
interface_new (const void *parent,
             const char *name)
{
      Interface *interface;

      nih_assert (name != NULL);

      interface = nih_new (parent, Interface);
      if (! interface)
            return NULL;

      nih_list_init (&interface->entry);

      nih_alloc_set_destructor (interface, nih_list_destroy);

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

      interface->symbol = NULL;
      interface->deprecated = FALSE;

      nih_list_init (&interface->methods);
      nih_list_init (&interface->signals);
      nih_list_init (&interface->properties);

      return interface;
}


/**
 * interface_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 an "interface"
 * start tag, a child of the "node" tag that defines a D-Bus interface
 * implemented by that object.
 *
 * If the interface does not appear within a node tag a warning is emitted
 * and the tag will be ignored.
 *
 * Interfaces must have a "name" attribute containing the D-Bus name
 * of the interface.
 *
 * Any unknown attributes result in a warning and will be ignored.
 *
 * An Interface object will be allocated and pushed onto the stack, this is
 * not added to the node until the end tag is found.
 *
 * Returns: zero on success, negative value on raised error.
 **/
int
interface_start_tag (XML_Parser    xmlp,
                 const char *  tag,
                 char * const *attr)
{
      ParseContext *       context;
      ParseStack *         parent;
      nih_local Interface *interface = NULL;
      char * const *       key;
      char * const *       value;
      const char *         name = NULL;

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

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

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

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

            return 0;
      }

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

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

      /* Check we have a name and that it's valid */
      if (! name)
            nih_return_error (-1, INTERFACE_MISSING_NAME,
                          _(INTERFACE_MISSING_NAME_STR));
      if (! interface_name_valid (name))
            nih_return_error (-1, INTERFACE_INVALID_NAME,
                          _(INTERFACE_INVALID_NAME_STR));

      /* Allocate an Interface object and push onto the stack */
      interface = interface_new (NULL, name);
      if (! interface)
            nih_return_system_error (-1);

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

      return 0;
}

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

      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_INTERFACE);
      interface = entry->interface;

      /* Generate a symbol from the trailing part of the name */
      if (! interface->symbol) {
            char *trail;

            trail = strrchr (interface->name, '.');
            nih_assert (trail != NULL);
            trail++;

            interface->symbol = symbol_from_name (interface, trail);
            if (! interface->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_NODE);
      node = parent->node;

      /* Make sure there's not a conflict before adding the interface */
      conflict = node_lookup_interface (node, interface->symbol);
      if (conflict) {
            nih_error_raise_printf (INTERFACE_DUPLICATE_SYMBOL,
                              _(INTERFACE_DUPLICATE_SYMBOL_STR),
                              interface->symbol, conflict->name);
            return -1;
      }

      nih_debug ("Add %s interface to %s node",
               interface->name, node->path ?: "(unknown)");
      nih_ref (interface, node);
      nih_list_add (&node->interfaces, &interface->entry);

      nih_free (entry);

      return 0;
}


/**
 * interface_annotation:
 * @interface: interface object annotation applies to,
 * @name: annotation name,
 * @value: annotation value.
 *
 * Handles applying the annotation @name with value @value to the interface
 * @interface.  Interfaces 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
interface_annotation (Interface * interface,
                  const char *name,
                  const char *value)
{
      nih_assert (interface != NULL);
      nih_assert (name != NULL);
      nih_assert (value != NULL);

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

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

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

                  nih_debug ("Set %s interface symbol to %s",
                           interface->name, interface->symbol);
            } else {
                  nih_return_error (-1, INTERFACE_INVALID_SYMBOL,
                                _(INTERFACE_INVALID_SYMBOL_STR));
            }

      } else {
            nih_error_raise_printf (INTERFACE_UNKNOWN_ANNOTATION,
                              "%s: %s: %s",
                              _(INTERFACE_UNKNOWN_ANNOTATION_STR),
                              interface->name, name);
            return -1;
      }

      return 0;
}


/**
 * interface_methods_array:
 * @parent: parent object for new string,
 * @prefix: prefix for array name,
 * @interface: interface to generate array for,
 * @with_handlers: whether to include handler pointers,
 * @prototypes: list to append prototype to.
 *
 * Generates C code to declare an array of NihDBusMethod variables
 * containing information about the methods of the interface @interface;
 * this will also include array definitions for the arguments of each
 * method, since these are referred to by the returned array.
 *
 * If @with_handlers is TRUE the returned array will contain pointers to
 * handler functions that should be already defined (or at least prototyped);
 * when FALSE this member will be NULL.
 *
 * The prototype of the returned variable declaration is returned as a
 * TypeVar object appended to the @prototypes list.  The arguments array
 * prototypes are not returned since they are made static.
 *
 * 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 *
interface_methods_array (const void *parent,
                   const char *prefix,
                   Interface * interface,
                   int         with_handlers,
                   NihList *   prototypes)
{
      nih_local char *name = NULL;
      NihList         vars;
      size_t          max_name = 0;
      size_t          max_args = 0;
      size_t          max_handler = 0;
      nih_local char *args = NULL;
      TypeVar *       var;
      nih_local char *block = NULL;
      char *          code;

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

      name = symbol_impl (NULL, prefix, interface->name, NULL, "methods");
      if (! name)
            return NULL;

      nih_list_init (&vars);

      /* Figure out the longest method name, arguments array variable name
       * and handler function name.
       */
      NIH_LIST_FOREACH (&interface->methods, iter) {
            Method *        method = (Method *)iter;
            NihList         args_prototypes;
            nih_local char *args_array = NULL;
            nih_local char *handler_name = NULL;

            /* Obtain the arguments array for the method, giving us the
             * name of the array.  Append it as a static to the block
             * we prepend to our code.
             */
            nih_list_init (&args_prototypes);
            args_array = method_args_array (NULL, prefix, interface,
                                    method, &args_prototypes);
            if (! args_array)
                  return NULL;

            if (! nih_strcat_sprintf (&args, NULL,
                                "static %s"
                                "\n",
                                args_array))
                  return NULL;

            nih_assert (! NIH_LIST_EMPTY (&args_prototypes));

            var = (TypeVar *)args_prototypes.next;
            nih_list_add (&vars, &var->entry);
            nih_ref (var, args);

            /* Calculate size of method name and args var name */
            if (strlen (method->name) > max_name)
                  max_name = strlen (method->name);

            if (strlen (var->name) > max_args)
                  max_args = strlen (var->name);

            /* Work out name of handler or leave space for "NULL" */
            if (with_handlers) {
                  handler_name = symbol_impl (NULL, prefix,
                                        interface->name,
                                        method->name, "method");
                  if (! handler_name)
                        return NULL;

                  if (strlen (handler_name) > max_handler)
                        max_handler = strlen (handler_name);
            } else {
                  if (max_handler < 4)
                        max_handler = 4;
            }
      }

      /* Append each method such that the names, args variable names and
       * handler function names are all lined up with each other.
       */
      var = (TypeVar *)vars.next;
      NIH_LIST_FOREACH (&interface->methods, iter) {
            Method *        method = (Method *)iter;
            nih_local char *line = NULL;
            char *          dest;
            nih_local char *handler_name = NULL;

            nih_assert (&var->entry != &vars);

            /* Allocate the line and fill in the values, padding out
             * where necessary.
             */
            line = nih_alloc (NULL, max_name + max_args + max_handler + 13);
            if (! line)
                  return NULL;

            dest = line;

            memcpy (dest, "{ \"", 3);
            dest += 3;

            memcpy (dest, method->name, strlen (method->name));
            dest += strlen (method->name);

            memcpy (dest, "\", ", 3);
            dest += 3;

            memset (dest, ' ', max_name - strlen (method->name));
            dest += max_name - strlen (method->name);

            memcpy (dest, var->name, strlen (var->name));
            dest += strlen (var->name);

            memcpy (dest, ", ", 2);
            dest += 2;

            memset (dest, ' ', max_args - strlen (var->name));
            dest += max_args - strlen (var->name);

            if (with_handlers) {
                  handler_name = symbol_impl (NULL, prefix,
                                        interface->name,
                                        method->name, "method");
                  if (! handler_name)
                        return NULL;

                  memcpy (dest, handler_name, strlen (handler_name));
                  dest += strlen (handler_name);

                  memset (dest, ' ', max_handler - strlen (handler_name));
                  dest += max_handler - strlen (handler_name);
            } else {
                  memcpy (dest, "NULL", 4);
                  dest += 4;

                  memset (dest, ' ', max_handler - 4);
                  dest += max_handler - 4;
            }

            memcpy (dest, " },\n", 4);
            dest += 4;

            *dest = '\0';

            if (! nih_strcat (&block, NULL, line))
                  return NULL;

            var = (TypeVar *)var->entry.next;
      }

      /* Append the final element to the block of elements, indent and
       * surround with the structure definition.
       */
      if (! nih_strcat (&block, NULL, "{ NULL }\n"))
            return NULL;

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

      code = nih_sprintf (parent,
                      "%s"
                      "const NihDBusMethod %s[] = {\n"
                      "%s"
                      "};\n",
                      args ?: "",
                      name,
                      block);
      if (! code)
            return NULL;

      /* Append the prototype to the list */
      var = type_var_new (code, "const NihDBusMethod", name);
      if (! var) {
            nih_free (code);
            return NULL;
      }

      var->array = TRUE;

      nih_list_add (prototypes, &var->entry);

      return code;
}

/**
 * interface_signals_array:
 * @parent: parent object for new string,
 * @prefix: prefix for array name,
 * @interface: interface to generate array for,
 * @with_filter: whether to include filter function pointers,
 * @prototypes: list to append prototype to.
 *
 * Generates C code to declare an array of NihDBusSignal variables
 * containing information about the signals of the interface @interface;
 * this will also include array definitions for the arguments of each
 * signals, since these are referred to by the returned array.
 *
 * If @with_filters is TRUE the returned array will contain pointers to
 * filter functions that should be already defined (or at least prototyped);
 * when FALSE this member will be NULL.
 *
 * The prototype of the returned variable declaration is returned as a
 * TypeVar object appended to the @prototypes list.  The arguments array
 * prototypes are not returned since they are made static.
 *
 * 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 *
interface_signals_array (const void *parent,
                   const char *prefix,
                   Interface * interface,
                   int         with_filters,
                   NihList *   prototypes)
{
      nih_local char *name = NULL;
      NihList         vars;
      size_t          max_name = 0;
      size_t          max_args = 0;
      size_t          max_filter = 0;
      nih_local char *args = NULL;
      TypeVar *       var;
      nih_local char *block = NULL;
      char *          code;

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

      name = symbol_impl (NULL, prefix, interface->name, NULL, "signals");
      if (! name)
            return NULL;

      nih_list_init (&vars);

      /* Figure out the longest signal name, arguments array variable name
       * and filter function name.
       */
      NIH_LIST_FOREACH (&interface->signals, iter) {
            Signal *        signal = (Signal *)iter;
            NihList         args_prototypes;
            nih_local char *args_array = NULL;
            nih_local char *filter_name = NULL;

            /* Obtain the arguments array for the signal, giving us the
             * name of the array.  Append it as a static to the block
             * we prepend to our code.
             */
            nih_list_init (&args_prototypes);
            args_array = signal_args_array (NULL, prefix, interface,
                                    signal, &args_prototypes);
            if (! args_array)
                  return NULL;

            if (! nih_strcat_sprintf (&args, NULL,
                                "static %s"
                                "\n",
                                args_array))
                  return NULL;

            nih_assert (! NIH_LIST_EMPTY (&args_prototypes));

            var = (TypeVar *)args_prototypes.next;
            nih_list_add (&vars, &var->entry);
            nih_ref (var, args);

            /* Calculate size of signal name and args var name */
            if (strlen (signal->name) > max_name)
                  max_name = strlen (signal->name);

            if (strlen (var->name) > max_args)
                  max_args = strlen (var->name);

            /* Work out name of filter or leave space for "NULL" */
            if (with_filters) {
                  filter_name = symbol_impl (NULL, prefix,
                                        interface->name,
                                        signal->name, "signal");
                  if (! filter_name)
                        return NULL;

                  if (strlen (filter_name) > max_filter)
                        max_filter = strlen (filter_name);
            } else {
                  if (max_filter < 4)
                        max_filter = 4;
            }
      }

      /* Append each signal such that the names, args variable names and
       * filter function names are all lined up with each other.
       */
      var = (TypeVar *)vars.next;
      NIH_LIST_FOREACH (&interface->signals, iter) {
            Signal *        signal = (Signal *)iter;
            nih_local char *line = NULL;
            char *          dest;
            nih_local char *filter_name = NULL;

            nih_assert (&var->entry != &vars);

            /* Allocate the line and fill in the values, padding out
             * where necessary.
             */
            line = nih_alloc (NULL, max_name + max_args + max_filter + 13);
            if (! line)
                  return NULL;

            dest = line;

            memcpy (dest, "{ \"", 3);
            dest += 3;

            memcpy (dest, signal->name, strlen (signal->name));
            dest += strlen (signal->name);

            memcpy (dest, "\", ", 3);
            dest += 3;

            memset (dest, ' ', max_name - strlen (signal->name));
            dest += max_name - strlen (signal->name);

            memcpy (dest, var->name, strlen (var->name));
            dest += strlen (var->name);

            memcpy (dest, ", ", 2);
            dest += 2;

            memset (dest, ' ', max_args - strlen (var->name));
            dest += max_args - strlen (var->name);

            if (with_filters) {
                  filter_name = symbol_impl (NULL, prefix,
                                        interface->name,
                                        signal->name, "signal");
                  if (! filter_name)
                        return NULL;

                  memcpy (dest, filter_name, strlen (filter_name));
                  dest += strlen (filter_name);

                  memset (dest, ' ', max_filter - strlen (filter_name));
                  dest += max_filter - strlen (filter_name);
            } else {
                  memcpy (dest, "NULL", 4);
                  dest += 4;

                  memset (dest, ' ', max_filter - 4);
                  dest += max_filter - 4;
            }

            memcpy (dest, " },\n", 4);
            dest += 4;

            *dest = '\0';

            if (! nih_strcat (&block, NULL, line))
                  return NULL;

            var = (TypeVar *)var->entry.next;
      }

      /* Append the final element to the block of elements, indent and
       * surround with the structure definition.
       */
      if (! nih_strcat (&block, NULL, "{ NULL }\n"))
            return NULL;

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

      code = nih_sprintf (parent,
                      "%s"
                      "const NihDBusSignal %s[] = {\n"
                      "%s"
                      "};\n",
                      args ?: "",
                      name,
                      block);
      if (! code)
            return NULL;

      /* Append the prototype to the list */
      var = type_var_new (code, "const NihDBusSignal", name);
      if (! var) {
            nih_free (code);
            return NULL;
      }

      var->array = TRUE;

      nih_list_add (prototypes, &var->entry);

      return code;
}

/**
 * interface_properties_array:
 * @parent: parent object for new string,
 * @prefix: prefix for array name,
 * @interface: interface to generate array for,
 * @with_handlers: whether to include handler pointers,
 * @prototypes: list to append prototype to.
 *
 * Generates C code to declare an array of NihDBusProperty variables
 * containing information about the properties of the interface @interface.
 *
 * If @with_handlers is TRUE the returned array will contain pointers to
 * getter and setter functions that should be already defined (or at least
 * prototyped); when FALSE these members will be NULL.
 *
 * The prototype of the returned variable declaration is returned as a
 * TypeVar object appended to the @prototypes list.
 *
 * 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 *
interface_properties_array (const void *parent,
                      const char *prefix,
                      Interface * interface,
                      int         with_handlers,
                      NihList *   prototypes)
{
      nih_local char *name = NULL;
      size_t          max_name = 0;
      size_t          max_type = 0;
      size_t          max_access = 0;
      size_t          max_getter = 0;
      size_t          max_setter = 0;
      nih_local char *block = NULL;
      char *          code;
      TypeVar *       var;

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

      name = symbol_impl (NULL, prefix, interface->name, NULL, "properties");
      if (! name)
            return NULL;

      /* Figure out the longest property name, type, access variable,
       * getter and setter function names.
       */
      NIH_LIST_FOREACH (&interface->properties, iter) {
            Property *      property = (Property *)iter;
            nih_local char *getter_name = NULL;
            nih_local char *setter_name = NULL;

            if (strlen (property->name) > max_name)
                  max_name = strlen (property->name);

            if (strlen (property->type) > max_type)
                  max_type = strlen (property->type);

            switch (property->access) {
            case NIH_DBUS_READ:
                  if (max_access < 13)
                        max_access = 13;
                  break;
            case NIH_DBUS_WRITE:
                  if (max_access < 14)
                        max_access = 14;
                  break;
            case NIH_DBUS_READWRITE:
                  if (max_access < 18)
                        max_access = 18;
                  break;
            default:
                  nih_assert_not_reached ();
            }

            if (with_handlers && (property->access != NIH_DBUS_WRITE)) {
                  getter_name = symbol_impl (NULL, prefix,
                                       interface->name,
                                       property->name, "get");
                  if (! getter_name)
                        return NULL;

                  if (strlen (getter_name) > max_getter)
                        max_getter = strlen (getter_name);
            } else {
                  if (max_getter < 4)
                        max_getter = 4;
            }

            if (with_handlers && (property->access != NIH_DBUS_READ)) {
                  setter_name = symbol_impl (NULL, prefix,
                                       interface->name,
                                       property->name, "set");
                  if (! setter_name)
                        return NULL;

                  if (strlen (setter_name) > max_setter)
                        max_setter = strlen (setter_name);
            } else {
                  if (max_setter < 4)
                        max_setter = 4;
            }
      }

      /* Append each property such that the names, types, access enum,
       * getter and setter function names are all lined up with each other.
       */
      NIH_LIST_FOREACH (&interface->properties, iter) {
            Property *      property = (Property *)iter;
            nih_local char *line = NULL;
            char *          dest;
            nih_local char *getter_name = NULL;
            nih_local char *setter_name = NULL;

            line = nih_alloc (NULL, (max_name + max_type + max_access
                               + max_getter + max_setter + 19));
            if (! line)
                  return NULL;

            dest = line;

            memcpy (dest, "{ \"", 3);
            dest += 3;

            memcpy (dest, property->name, strlen (property->name));
            dest += strlen (property->name);

            memcpy (dest, "\", ", 3);
            dest += 3;

            memset (dest, ' ', max_name - strlen (property->name));
            dest += max_name - strlen (property->name);


            memcpy (dest, "\"", 1);
            dest += 1;

            memcpy (dest, property->type, strlen (property->type));
            dest += strlen (property->type);

            memcpy (dest, "\", ", 3);
            dest += 3;

            memset (dest, ' ', max_type - strlen (property->type));
            dest += max_type - strlen (property->type);

            switch (property->access) {
            case NIH_DBUS_READ:
                  memcpy (dest, "NIH_DBUS_READ, ", 15);
                  dest += 15;

                  memset (dest, ' ', max_access - 13);
                  dest += max_access - 13;
                  break;
            case NIH_DBUS_WRITE:
                  memcpy (dest, "NIH_DBUS_WRITE, ", 16);
                  dest += 16;

                  memset (dest, ' ', max_access - 14);
                  dest += max_access - 14;
                  break;
            case NIH_DBUS_READWRITE:
                  memcpy (dest, "NIH_DBUS_READWRITE, ", 20);
                  dest += 20;

                  memset (dest, ' ', max_access - 18);
                  dest += max_access - 18;
                  break;
            default:
                  nih_assert_not_reached ();
            }

            if (with_handlers && (property->access != NIH_DBUS_WRITE)) {
                  getter_name = symbol_impl (NULL, prefix,
                                       interface->name,
                                       property->name, "get");
                  if (! getter_name)
                        return NULL;

                  memcpy (dest, getter_name, strlen (getter_name));
                  dest += strlen (getter_name);

                  memcpy (dest, ", ", 2);
                  dest += 2;

                  memset (dest, ' ', max_getter - strlen (getter_name));
                  dest += max_getter - strlen (getter_name);
            } else {
                  memcpy (dest, "NULL, ", 6);
                  dest += 6;

                  memset (dest, ' ', max_getter - 4);
                  dest += max_getter - 4;
            }

            if (with_handlers && (property->access != NIH_DBUS_READ)) {
                  setter_name = symbol_impl (NULL, prefix,
                                       interface->name,
                                       property->name, "set");
                  if (! setter_name)
                        return NULL;

                  memcpy (dest, setter_name, strlen (setter_name));
                  dest += strlen (setter_name);

                  memset (dest, ' ', max_setter - strlen (setter_name));
                  dest += max_setter - strlen (setter_name);
            } else {
                  memcpy (dest, "NULL", 4);
                  dest += 4;

                  memset (dest, ' ', max_setter - 4);
                  dest += max_setter - 4;
            }

            memcpy (dest, " },\n", 4);
            dest += 4;

            *dest = '\0';

            if (! nih_strcat (&block, NULL, line))
                  return NULL;
      }

      /* Append the final element to the block of elements, indent and
       * surround with the structure definition.
       */
      if (! nih_strcat (&block, NULL, "{ NULL }\n"))
            return NULL;

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

      code = nih_sprintf (parent,
                      "const NihDBusProperty %s[] = {\n"
                      "%s"
                      "};\n",
                      name,
                      block);
      if (! code)
            return NULL;

      /* Append the prototype to the list */
      var = type_var_new (code, "const NihDBusProperty", name);
      if (! var) {
            nih_free (code);
            return NULL;
      }

      var->array = TRUE;

      nih_list_add (prototypes, &var->entry);

      return code;
}


/**
 * interface_struct:
 * @parent: parent object for new string,
 * @prefix: prefix for struct name,
 * @interface: interface to generate struct for,
 * @object: whether struct is for an object or proxy,
 * @prototypes: list to append prototype to.
 *
 * Generates C code to declare an NihDBusInterface structure variable for
 * the given interface @interface, the code includes the array definitions
 * for methods, signals, properties and their arguments.
 *
 * If @object is TRUE, the struct will be for an object definition so method
 * handler function and property getter and setter function pointers will
 * be filled in.  If @object is FALSE, the struct will be for a proxy
 * definition so the signal filter function pointers will be filled in.
 *
 * The prototype of the returned variable declaration is returned as a
 * TypeVar object appended to the @prototypes list.  The methods, signals
 * and properties array prototypes are not returned since they are made
 * static.
 *
 * 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 *
interface_struct (const void *parent,
              const char *prefix,
              Interface * interface,
              int         object,
              NihList *   prototypes)
{
      nih_local char *name = NULL;
      nih_local char *block = NULL;
      nih_local char *arrays = NULL;
      TypeVar *       var;
      char *          ptr;
      NihList         methods_prototypes;
      nih_local char *methods_array = NULL;
      NihList         signals_prototypes;
      nih_local char *signals_array = NULL;
      NihList         properties_prototypes;
      nih_local char *properties_array = NULL;
      char *          code;

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

      /* Work out the structure name, and append the interface name to the
       * definition.
       */
      name = symbol_impl (NULL, prefix, interface->name, NULL, NULL);
      if (! name)
            return NULL;

      if (! nih_strcat_sprintf (&block, NULL, "\"%s\",\n", interface->name))
            return NULL;

      /* Append the methods array to the arrays block, making it static
       * in the process.
       */
      nih_list_init (&methods_prototypes);

      methods_array = interface_methods_array (NULL, prefix, interface,
                                     object ? TRUE : FALSE,
                                     &methods_prototypes);
      if (! methods_array)
            return NULL;

      nih_assert (! NIH_LIST_EMPTY (&methods_prototypes));

      var = (TypeVar *)methods_prototypes.next;
      ptr = strstr (methods_array, var->type);
      nih_assert (ptr != NULL);

      if (! nih_strncat (&arrays, NULL, methods_array, ptr - methods_array))
            return NULL;

      if (! nih_strcat_sprintf (&arrays, NULL, "static %s\n", ptr))
            return NULL;

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

      /* Append the signals array to the arrays block, making it static
       * in the process.
       */
      nih_list_init (&signals_prototypes);

      signals_array = interface_signals_array (NULL, prefix, interface,
                                     object ? FALSE : TRUE,
                                     &signals_prototypes);
      if (! signals_array)
            return NULL;

      nih_assert (! NIH_LIST_EMPTY (&signals_prototypes));

      var = (TypeVar *)signals_prototypes.next;
      ptr = strstr (signals_array, var->type);
      nih_assert (ptr != NULL);

      if (! nih_strncat (&arrays, NULL, signals_array, ptr - signals_array))
            return NULL;

      if (! nih_strcat_sprintf (&arrays, NULL, "static %s\n", ptr))
            return NULL;

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

      /* Append the properties array to the arrays block, making it static
       * in the process.
       */
      nih_list_init (&properties_prototypes);

      properties_array = interface_properties_array (NULL, prefix, interface,
                                           object ? TRUE : FALSE,
                                           &properties_prototypes);
      if (! properties_array)
            return NULL;

      nih_assert (! NIH_LIST_EMPTY (&properties_prototypes));

      var = (TypeVar *)properties_prototypes.next;

      if (! nih_strcat_sprintf (&arrays, NULL, "static %s\n",
                          properties_array))
            return NULL;

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

      /* Output the code */
      if (! indent (&block, NULL, 1))
            return NULL;

      code = nih_sprintf (parent,
                      "%s"
                      "const NihDBusInterface %s = {\n"
                      "%s"
                      "};\n",
                      arrays,
                      name,
                      block);
      if (! code)
            return NULL;

      /* Append the prototype to the list */
      var = type_var_new (code, "const NihDBusInterface", name);
      if (! var) {
            nih_free (code);
            return NULL;
      }

      nih_list_add (prototypes, &var->entry);

      return code;
}


/**
 * interface_proxy_get_all_function:
 * @parent: parent object for new string.
 * @prefix: prefix for function name,
 * @interface: interface 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 all of the properties 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.
 *
 * If any of the arguments require 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 the
 * property.
 *
 * The names of both the returned function and notify function prototype
 * will be generated using information in @interface, 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
 * interface_proxy_get_all_notify_function().
 *
 * 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 *
interface_proxy_get_all_function (const void *parent,
                          const char *prefix,
                          Interface * interface,
                          NihList *   prototypes,
                          NihList *   structs)
{
      NihList             locals;
      NihList             all_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 * pending_var = NULL;
      nih_local TypeVar * data_var = NULL;
      nih_local TypeVar * interface_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 (prototypes != NULL);
      nih_assert (structs != NULL);

      nih_list_init (&locals);
      nih_list_init (&all_structs);

      /* 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.
       */
      name = symbol_extern (NULL, prefix, interface->symbol, NULL,
                        "get_all", 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);

      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,
                               NULL, "GetAll", "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 a variable for the interface name,
       * since D-Bus wants its 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);


      /* 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\", \"GetAll\");\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",
                          DBUS_INTERFACE_PROPERTIES,
                          interface->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,
                           "get_all", "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;
}

/**
 * interface_proxy_get_all_notify_function:
 * @parent: parent object for new string.
 * @prefix: prefix for function name,
 * @interface: interface 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 all of the properties
 * of @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 any of the arguments require 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 the
 * 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 *
interface_proxy_get_all_notify_function (const void *parent,
                               const char *prefix,
                               Interface * interface,
                               NihList *   prototypes,
                               NihList *   typedefs,
                               NihList *   structs)
{
      nih_local char *      c_type = NULL;
      nih_local TypeStruct *structure = NULL;
      NihList               locals;
      NihList               all_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 *   arrayiter_var = NULL;
      nih_local TypeVar *   dictiter_var = NULL;
      nih_local TypeVar *   variter_var = NULL;
      nih_local TypeVar *   error_var = NULL;
      nih_local TypeVar *   property_var = NULL;
      nih_local TypeVar *   parent_var = NULL;
      nih_local TypeVar *   structure_var = NULL;
      nih_local TypeVar *   count_var = NULL;
      nih_local char *      steal_block = NULL;
      nih_local char *      demarshal_block = NULL;
      nih_local char *      property_block = NULL;
      size_t                property_count;
      nih_local char *      oom_error_code = NULL;
      nih_local char *      type_error_code = 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 *      vars_block = NULL;
      nih_local char *      body = NULL;
      char *                code = NULL;

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

      nih_list_init (&locals);
      nih_list_init (&all_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,
                      "get_all", "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);

      arrayiter_var = type_var_new (NULL, "DBusMessageIter", "arrayiter");
      if (! arrayiter_var)
            return NULL;

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

      dictiter_var = type_var_new (NULL, "DBusMessageIter", "dictiter");
      if (! dictiter_var)
            return NULL;

      nih_list_add (&locals, &dictiter_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);

      /* Need a variable to hold the property name */
      property_var = type_var_new (NULL, "const char *", "property");
      if (! property_var)
            return NULL;

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

      /* We also need a local for the structure we pass to the handler */
      c_type = symbol_typedef (NULL, prefix, interface->symbol, NULL,
                         "properties", NULL);
      if (! c_type)
            return NULL;

      structure = type_struct_new (NULL, c_type);
      if (! structure)
            return NULL;

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

      structure_var = type_var_new (NULL, c_type, "properties");
      if (! structure_var)
            return NULL;

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

      count_var = type_var_new (NULL, "size_t", "property_count");
      if (! count_var)
            return NULL;

      nih_list_add (&locals, &count_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;

      /* Begin the demarshalling block, making sure that the first
       * argument is an array and recursing into it and looping over
       * the values.
       */
      if (! nih_strcat_sprintf (&demarshal_block, NULL,
                          "/* Create a message context for the reply, and iterate\n"
                          " * over and recurse into the arguments.\n"
                          " */\n"
                          "message = NIH_MUST (nih_dbus_message_new (pending_data, pending_data->connection, reply));\n"
                          "\n"
                          "/* Iterate the method arguments, recursing into the array */\n"
                          "dbus_message_iter_init (reply, &iter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) {\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"
                          "properties = NIH_MUST (nih_new (message, %s));\n"
                          "property_count = 0;\n"
                          "\n"
                          "dbus_message_iter_recurse (&iter, &arrayiter);\n"
                          "\n"
                          "while (dbus_message_iter_get_arg_type (&arrayiter) != DBUS_TYPE_INVALID) {\n"
                          "\t__label__ enomem;\n"
                          "\n",
                          structure->name))
            return NULL;

      /* Begin the property checking block, each array item should be
       * a dictionary entry which we recurse into; the first value of
       * which should be a string containing the property name.
       */
      if (! nih_strcat_sprintf (&property_block, NULL,
                          "if (dbus_message_iter_get_arg_type (&arrayiter) != DBUS_TYPE_DICT_ENTRY) {\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 (&arrayiter, &dictiter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (&dictiter) != DBUS_TYPE_STRING) {\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_get_basic (&dictiter, &property);\n"
                          "\n"
                          "dbus_message_iter_next (&dictiter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (&dictiter) != 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 (&dictiter, &variter);\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_strdup (NULL, "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;

      property_count = 0;
      NIH_LIST_FOREACH (&interface->properties, iter) {
            Property *        property = (Property *)iter;
            DBusSignatureIter iter;
            NihList           property_outputs;
            NihList           property_locals;
            NihList           property_structs;
            nih_local char *  block = NULL;

            if (property->access == NIH_DBUS_WRITE)
                  continue;

            dbus_signature_iter_init (&iter, property->type);

            nih_list_init (&property_outputs);
            nih_list_init (&property_locals);
            nih_list_init (&property_structs);

            block = demarshal (NULL, &iter, "properties", "variter",
                           property->symbol,
                           oom_error_code,
                           type_error_code,
                           &property_outputs, &property_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 that we copy into the structure.
             */
            NIH_LIST_FOREACH_SAFE (&property_outputs, iter) {
                  TypeVar *var = (TypeVar *)iter;
                  TypeVar *member_var;

                  /* Create the structure member entry */
                  member_var = type_var_new (structure,
                                       var->type, var->name);
                  if (! member_var)
                        return NULL;

                  nih_list_add (&structure->members, &member_var->entry);

                  /* Add code to copy from local variable */
                  if (! nih_strcat_sprintf (&block, NULL,
                                      "properties->%s = %s;\n",
                                      var->name, var->name))
                        return NULL;

                  /* Add to locals */
                  nih_list_add (&locals, &var->entry);
                  nih_ref (var, demarshal_block);
            }

            NIH_LIST_FOREACH_SAFE (&property_locals, iter) {
                  TypeVar *var = (TypeVar *)iter;

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

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

                  nih_list_add (&all_structs, &structure->entry);
                  nih_ref (structure, demarshal_block);
            }

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

            /* Wrap the code in a test for the property by name */
            if (! indent (&block, NULL, 1))
                  return NULL;

            if (! nih_strcat_sprintf (&property_block, NULL,
                                "\n"
                                "if (! strcmp (property, \"%s\")) {\n"
                                "%s"
                                "}\n",
                                property->name,
                                block))
                  return NULL;

            property_count++;
      }

      /* Finish the property checking block, making sure that there are
       * no further items in the dictionary entry before moving on to the
       * next array item.
       */
      if (! nih_strcat_sprintf (&property_block, NULL,
                          "\n"
                          "dbus_message_iter_next (&dictiter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (&dictiter) != 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"
                          "dbus_message_iter_next (&arrayiter);\n"))
            return NULL;

      /* Indent the property block and append to the demarshal block */
      if (! indent (&property_block, NULL, 1))
            return NULL;

      if (! nih_strcat (&demarshal_block, NULL, property_block))
            return NULL;

      /* Finish off the demarshalling block by ending the loop and
       * making sure that there are no further arguments in the reply.
       */
      if (! nih_strcat_sprintf (&demarshal_block, NULL,
                          "enomem: __attribute__ ((unused));\n"
                          "}\n"
                          "\n"
                          "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"
                          "if (property_count < %zu) {\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",
                          property_count))
            return NULL;

      /* Create the handler calling block, the handler is not permitted
       * to reply.
       */
      handler_type = symbol_typedef (NULL, prefix, interface->symbol, NULL,
                               "GetAll", "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, properties);\n"
                          "nih_error_pop_context ();\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);

      arg = type_var_new (handler_func, c_type, "properties");
      if (! arg)
            return NULL;

      if (! type_to_const (&arg->type, 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"
                          "\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 (&all_structs, iter) {
            TypeStruct *structure = (TypeStruct *)iter;

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

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

      return code;
}


/**
 * interface_proxy_get_all_sync_function:
 * @parent: parent object for new string.
 * @prefix: prefix for function name,
 * @interface: interface 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 all of the properties of @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 any of the arguments require 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 the
 * 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 *
interface_proxy_get_all_sync_function (const void *parent,
                               const char *prefix,
                               Interface * interface,
                               NihList *   prototypes,
                               NihList *   structs)
{
      nih_local char *      c_type = NULL;
      nih_local TypeStruct *structure = NULL;
      NihList               locals;
      NihList               all_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 *   arrayiter_var = NULL;
      nih_local TypeVar *   dictiter_var = NULL;
      nih_local TypeVar *   variter_var = NULL;
      nih_local TypeVar *   error_var = NULL;
      nih_local TypeVar *   reply_var = NULL;
      nih_local TypeVar *   count_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 *      property_block = NULL;
      nih_local char *      oom_error_code = NULL;
      nih_local char *      type_error_code = NULL;
      size_t                property_count;
      nih_local char *      vars_block = NULL;
      nih_local char *      body = NULL;
      char *                code = NULL;

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

      nih_list_init (&locals);
      nih_list_init (&all_structs);

      /* The function returns an integer, and takes a parent object and
       * the proxy object as the argument along with an output structure
       * argument for the property values.  The integer is negative if a
       * raised error occurred, so we want warning if the result isn't
       * used.
       */
      name = symbol_extern (NULL, prefix, interface->symbol, NULL,
                        "get_all", "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);

      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;


      c_type = symbol_typedef (NULL, prefix, interface->symbol, NULL,
                         "properties", NULL);
      if (! c_type)
            return NULL;

      structure = type_struct_new (NULL, c_type);
      if (! structure)
            return NULL;

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

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

      arg = type_var_new (func, c_type, "properties");
      if (! arg)
            return NULL;

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

      if (! nih_strcat (&assert_block, NULL,
                    "nih_assert (properties != 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);

      arrayiter_var = type_var_new (NULL, "DBusMessageIter", "arrayiter");
      if (! arrayiter_var)
            return NULL;

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

      dictiter_var = type_var_new (NULL, "DBusMessageIter", "dictiter");
      if (! dictiter_var)
            return NULL;

      nih_list_add (&locals, &dictiter_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);

      count_var = type_var_new (NULL, "size_t", "property_count");
      if (! count_var)
            return NULL;

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

      /* Annoyingly we need a variable for the interface name, since
       * D-Bus wants its address and can't just take a constant string.
       * We also need a variable in which to store the property names
       * we find.
       */
      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 properties, 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\", \"GetAll\");\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",
                          DBUS_INTERFACE_PROPERTIES,
                          interface->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 that the first
       * argument is an array and recursing into it and looping over
       * the values.
       */
      if (! nih_strcat_sprintf (&demarshal_block, NULL,
                          "dbus_message_unref (method_call);\n"
                          "\n"
                          "/* Iterate the method arguments, recursing into the array */\n"
                          "dbus_message_iter_init (reply, &iter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) {\n"
                          "\tdbus_message_unref (reply);\n"
                          "\tnih_return_error (-1, NIH_DBUS_INVALID_ARGS,\n"
                          "\t                  _(NIH_DBUS_INVALID_ARGS_STR));\n"
                          "}\n"
                          "\n"
                          "*properties = NIH_MUST (nih_new (parent, %s));\n"
                          "property_count = 0;\n"
                          "\n"
                          "dbus_message_iter_recurse (&iter, &arrayiter);\n"
                          "\n"
                          "while (dbus_message_iter_get_arg_type (&arrayiter) != DBUS_TYPE_INVALID) {\n"
                          "\t__label__ enomem;\n"
                          "\n",
                          structure->name))
            return NULL;

      /* Begin the property checking block, each array item should be
       * a dictionary entry which we recurse into; the first value of
       * which should be a string containing the property name.
       */
      if (! nih_strcat_sprintf (&property_block, NULL,
                          "if (dbus_message_iter_get_arg_type (&arrayiter) != DBUS_TYPE_DICT_ENTRY) {\n"
                          "\tnih_free (*properties);\n"
                          "\t*properties = NULL;\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 (&arrayiter, &dictiter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (&dictiter) != DBUS_TYPE_STRING) {\n"
                          "\tnih_free (*properties);\n"
                          "\t*properties = NULL;\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_get_basic (&dictiter, &property);\n"
                          "\n"
                          "dbus_message_iter_next (&dictiter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (&dictiter) != DBUS_TYPE_VARIANT) {\n"
                          "\tnih_free (*properties);\n"
                          "\t*properties = NULL;\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 (&dictiter, &variter);\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_strdup (NULL, "goto enomem;\n");
      if (! oom_error_code)
            return NULL;

      type_error_code = nih_strdup (NULL,
                              "nih_free (*properties);\n"
                              "*properties = NULL;\n"
                              "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;

      property_count = 0;
      NIH_LIST_FOREACH (&interface->properties, iter) {
            Property *        property = (Property *)iter;
            DBusSignatureIter iter;
            NihList           property_outputs;
            NihList           property_locals;
            NihList           property_structs;
            nih_local char *  block = NULL;

            if (property->access == NIH_DBUS_WRITE)
                  continue;

            dbus_signature_iter_init (&iter, property->type);

            nih_list_init (&property_outputs);
            nih_list_init (&property_locals);
            nih_list_init (&property_structs);

            block = demarshal (NULL, &iter, "*properties", "variter",
                           property->symbol,
                           oom_error_code,
                           type_error_code,
                           &property_outputs, &property_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 that we copy into the structure.
             */
            NIH_LIST_FOREACH_SAFE (&property_outputs, iter) {
                  TypeVar *var = (TypeVar *)iter;
                  TypeVar *member_var;

                  /* Create the structure member entry */
                  member_var = type_var_new (structure,
                                       var->type, var->name);
                  if (! member_var)
                        return NULL;

                  nih_list_add (&structure->members, &member_var->entry);

                  /* Add code to copy from local variable */
                  if (! nih_strcat_sprintf (&block, NULL,
                                      "(*properties)->%s = %s;\n",
                                      var->name, var->name))
                        return NULL;

                  /* Add to locals */
                  nih_list_add (&locals, &var->entry);
                  nih_ref (var, demarshal_block);
            }

            NIH_LIST_FOREACH_SAFE (&property_locals, iter) {
                  TypeVar *var = (TypeVar *)iter;

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

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

                  nih_list_add (&all_structs, &structure->entry);
                  nih_ref (structure, demarshal_block);
            }

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

            /* Wrap the code in a test for the property by name */
            if (! indent (&block, NULL, 1))
                  return NULL;

            if (! nih_strcat_sprintf (&property_block, NULL,
                                "\n"
                                "if (! strcmp (property, \"%s\")) {\n"
                                "%s"
                                "}\n",
                                property->name,
                                block))
                  return NULL;

            property_count++;
      }

      /* Finish the property checking block, making sure that there are
       * no further items in the dictionary entry before moving on to the
       * next array item.
       */
      if (! nih_strcat_sprintf (&property_block, NULL,
                          "\n"
                          "dbus_message_iter_next (&dictiter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (&dictiter) != DBUS_TYPE_INVALID) {\n"
                          "\tnih_free (*properties);\n"
                          "\t*properties = NULL;\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_next (&arrayiter);\n"))
            return NULL;

      /* Indent the property block and append to the demarshal block */
      if (! indent (&property_block, NULL, 1))
            return NULL;

      if (! nih_strcat (&demarshal_block, NULL, property_block))
            return NULL;

      /* Finish off the demarshalling block by ending the loop and
       * making sure that there are no further arguments in the reply.
       */
      if (! nih_strcat_sprintf (&demarshal_block, NULL,
                          "enomem: __attribute__ ((unused));\n"
                          "}\n"
                          "\n"
                          "dbus_message_iter_next (&iter);\n"
                          "\n"
                          "if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) {\n"
                          "\tnih_free (*properties);\n"
                          "\t*properties = NULL;\n"
                          "\tdbus_message_unref (reply);\n"
                          "\tnih_return_error (-1, NIH_DBUS_INVALID_ARGS,\n"
                          "\t                  _(NIH_DBUS_INVALID_ARGS_STR));\n"
                          "}\n"
                          "\n"
                          "if (property_count < %zu) {\n"
                          "\tnih_free (*properties);\n"
                          "\t*properties = NULL;\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",
                          property_count))
            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 (&all_structs, iter) {
            TypeStruct *structure = (TypeStruct *)iter;

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

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

      return code;
}

Generated by  Doxygen 1.6.0   Back to index