blob: 81d27eb80413e983e5ba52bcb2a11aecdf68eabe [file] [log] [blame]
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# nih_dbus_tool.py - D-Bus binding generation tool
#
# Copyright © 2009 Scott James Remnant <scott@netsplit.com>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os
import re
import sys
from optparse import OptionParser
from xml.etree import ElementTree
# Replaced by autoconf-obtained strings
PACKAGE_NAME = "@PACKAGE_NAME@"
PACKAGE_COPYRIGHT = "@PACKAGE_COPYRIGHT@"
# Prefix for external functions
extern_prefix = "dbus"
# Generator mode
mode = "object"
# Conversion for external C names
NAME_RE = re.compile(r'([a-z0-9])([A-Z])')
# Namespace for our own tags and attributes
XMLNS = "http://www.netsplit.com/nih/dbus"
# arrays of arrays with length (ends up being an array of a struct)
# - instead of using self.type.c_type and name, we should iterate
# over its vars? -- array will have foo_elem and foo_elem_len
#
# struct type
# variant type (at least fallback to requiring them to define the marshal?)
# - one way might be just to pass a container or fake message?
# "proxy" mode:
# async function dispatch
# function dispatch with no reply
# configurable timeout, autostart, etc. for function dispatch
# signal marshal
# - called when a signal we're expecting happens, will have a message
# object and will call a handler with that -- need an object equivalent,
# "proxy" likely
class DBusType(object):
"""D-Bus type.
This abstract class represents the base of all D-Bus types that we
can handle.
"""
@classmethod
def fromElement(cls, elem):
name = elem.get("name")
if name is None:
raise AttributeError, "Argument name may not be null"
type = elem.get("type")
if type is None:
raise AttributeError, "Argument type may not be null"
direction = elem.get("direction", "in")
if direction not in ( "in", "out" ):
raise AttributeError, "Direction must be 'in' or 'out'"
type_cls = cls.typeOf(type)
return type_cls.fromArgument(name, direction)
@classmethod
def fromArgument(cls, name, direction="in"):
self = cls(name)
self.direction = direction
return self
def __init__(self, name):
self.name = name
@classmethod
def typeOf(cls, code):
"""Return type for given type signature code."""
if code == "y":
return DBusByte
elif code == "b":
return DBusBoolean
elif code == "n":
return DBusInt16
elif code == "q":
return DBusUInt16
elif code == "i":
return DBusInt32
elif code == "u":
return DBusUInt32
elif code == "x":
return DBusInt64
elif code == "t":
return DBusUInt64
elif code == "d":
return DBusDouble
elif code == "s":
return DBusString
elif code == "o":
return DBusObjectPath
elif code == "g":
return DBusSignature
elif code[0] == "a":
return DBusArray.forType(cls.typeOf(code[-1]))
else:
raise AttributeError, "Unknown or unhandled type '%s'" % code
def realType(self, c_type, pointer=False, const=False):
"""Real type.
Returns a string containing the C type name for this type, with
pointer and const modifications added.
"""
if pointer:
c_type = pointerify(c_type)
if const:
c_type = constify(c_type)
return c_type
def signature(self, pointer=False, const=False):
"""Type signature.
Returns a string containing the D-Bus type signature for this type.
"""
return None
def vars(self, pointer=False, const=False):
"""Variable type and name.
Returns a list containing a single tuple of the C type used for this
type and the name given when creating the instance.
"""
return []
def locals(self, pointer=False, const=False):
"""Local variable type and name.
Returns a list containing necessary local variables for iteration
of this type.
"""
return []
def marshal(self, iter_name, parent, type_error, mem_error,
pointer=False, const=False):
"""Marshalling code.
Returns a string containing the code that will marshal from an
iterator with the name given into a local variable with the
type and name as returned by vars().
"""
return None
def dispatch(self, iter_name, mem_error,
pointer=False, const=False):
"""Dispatching code.
Returns a string containing the code that will dispatch from a
local variable with the type and name as returned by vars() to
an iterator with the name given.
"""
return None
class DBusBasicType(DBusType):
"""D-Bus basic type.
This abstract class represents the base of all basic D-Bus types that
we can handle. These share the code to marshal from a DBusMessage into
a variable and dispatch from a variable back into a DBusMessage.
"""
def signature(self, pointer=False, const=False):
"""Type signature.
Returns a string containing the D-Bus type signature for this type.
"""
return self.dbus_code
def vars(self, pointer=False, const=False):
"""Variable type and name.
Returns a list containing a single tuple of the C type used for this
type and the name given when creating the instance.
"""
return [ ( self.realType(self.c_type, pointer, const), self.name ) ]
def locals(self, pointer=False, const=False):
"""Local variable type and name.
Returns a list containing necessary local variables for iteration
of this type.
"""
return []
def marshal(self, iter_name, parent, type_error, mem_error,
pointer=False, const=False):
"""Marshalling code.
Returns a string containing the code that will marshal from an
iterator with the name given into a local variable with the
type and name as returned by vars().
"""
name = self.name
if pointer:
name = "*%s" % (name, )
return """\
if (dbus_message_iter_get_arg_type (&%s) != %s) {
%s
}
dbus_message_iter_get_basic (&%s, &%s);
dbus_message_iter_next (&%s);
""" % (iter_name, self.dbus_type, type_error,
iter_name, name,
iter_name)
def dispatch(self, iter_name, mem_error,
pointer=False, const=False):
"""Dispatching code.
Returns a string containing the code that will dispatch from a
local variable with the type and name as returned by vars() to
an iterator with the name given.
"""
name = self.name
if pointer:
name = "*%s" % (name, )
return """\
if (! dbus_message_iter_append_basic (&%s, %s, &%s)) {
%s
}
""" % (iter_name, self.dbus_type, name, mem_error)
class DBusByte(DBusBasicType):
"""D-Bus byte.
This class represents the D-Bus byte type and should be instantiated with
the name of the variable. It shares marshalling and dispatch code with
the other basic types.
"""
c_type = "uint8_t"
dbus_code = "y"
dbus_type = "DBUS_TYPE_BYTE"
class DBusBoolean(DBusBasicType):
"""D-Bus boolean.
This class represents the D-Bus boolean type and should be instantiated
with the name of the variable. It shares marshalling and dispatch code
with the other basic types.
"""
c_type = "int"
dbus_code = "b"
dbus_type = "DBUS_TYPE_BOOLEAN"
class DBusInt16(DBusBasicType):
"""D-Bus 16-bit integer.
This class represents the D-Bus int16 type and should be instantiated with
the name of the variable. It shares marshalling and dispatch code with
the other basic types.
"""
c_type = "int16_t"
dbus_code = "n"
dbus_type = "DBUS_TYPE_INT16"
class DBusUInt16(DBusBasicType):
"""D-Bus 16-bit unsigned integer.
This class represents the D-Bus uint16 type and should be instantiated with
the name of the variable. It shares marshalling and dispatch code with
the other basic types.
"""
c_type = "uint16_t"
dbus_code = "q"
dbus_type = "DBUS_TYPE_UINT16"
class DBusInt32(DBusBasicType):
"""D-Bus 32-bit integer.
This class represents the D-Bus int32 type and should be instantiated with
the name of the variable. It shares marshalling and dispatch code with
the other basic types.
"""
c_type = "int32_t"
dbus_code = "i"
dbus_type = "DBUS_TYPE_INT32"
class DBusUInt32(DBusBasicType):
"""D-Bus 32-bit unsigned integer.
This class represents the D-Bus uint32 type and should be instantiated with
the name of the variable. It shares marshalling and dispatch code with
the other basic types.
"""
c_type = "uint32_t"
dbus_code = "u"
dbus_type = "DBUS_TYPE_UINT32"
class DBusInt64(DBusBasicType):
"""D-Bus 64-bit integer.
This class represents the D-Bus int64 type and should be instantiated with
the name of the variable. It shares marshalling and dispatch code with
the other basic types.
"""
c_type = "int64_t"
dbus_code = "x"
dbus_type = "DBUS_TYPE_INT64"
class DBusUInt64(DBusBasicType):
"""D-Bus 64-bit unsigned integer.
This class represents the D-Bus uint64 type and should be instantiated with
the name of the variable. It shares marshalling and dispatch code with
the other basic types.
"""
c_type = "uint64_t"
dbus_code = "t"
dbus_type = "DBUS_TYPE_UINT64"
class DBusDouble(DBusBasicType):
"""D-Bus double.
This class represents the D-Bus double type and should be instantiated
with the name of the variable. It shares marshalling and dispatch code
with the other basic types.
"""
c_type = "double"
dbus_code = "d"
dbus_type = "DBUS_TYPE_DOUBLE"
class DBusStringType(DBusBasicType):
"""D-Bus string type.
This abstract class represents the base of all string-derived D-Bus types
that we can handle. These share the code to marshal from a DBusMessage
into a variable, the code to dispatch from a variable back into a
DBusMessage and the same basic C type.
They differ from the fundamental basic type in that the marshalled copy
of the string is not direct from the message, but an allocated copy;
this is because they cannot be simply passed by value, and may wish to
be stored.
"""
c_type = "char *"
def locals(self, pointer=False, const=False):
"""Local variable type and name.
Returns a list containing necessary local variables for iteration
of this type.
"""
return [ ( self.realType(self.c_type, const=const),
"_".join((self.name, "value")) ) ]
def marshal(self, iter_name, parent, type_error, mem_error,
pointer=False, const=False):
"""Marshalling code.
Returns a string containing the code that will marshal from an
iterator with the name given into a local variable with the
type and name as returned by vars().
"""
name = self.name
if pointer:
name = "*%s" % (name, )
value_name = "_".join((self.name, "value"))
return """\
if (dbus_message_iter_get_arg_type (&%s) != %s) {
%s
}
dbus_message_iter_get_basic (&%s, &%s);
%s = nih_strdup (%s, %s);
if (! %s) {
%s
}
dbus_message_iter_next (&%s);
""" % (iter_name, self.dbus_type, type_error,
iter_name, value_name,
name, parent, value_name,
name, mem_error,
iter_name)
def dispatch(self, iter_name, mem_error,
pointer=False, const=False):
"""Dispatching code.
Returns a string containing the code that will dispatch from a
local variable with the type and name as returned by vars() to
an iterator with the name given.
"""
name = self.name
if pointer:
name = "*%s" % (name, )
value_name = "_".join((self.name, "value"))
return """\
%s = %s;
if (! dbus_message_iter_append_basic (&%s, %s, &%s)) {
%s
}
""" % (value_name, name,
iter_name, self.dbus_type, value_name, mem_error)
class DBusString(DBusStringType):
"""D-Bus string.
This class represents the D-Bus string type and should be instantiated
with the name of the variable. It shares marshalling and dispatch code
and underlying C type with the other string types.
"""
dbus_type = "DBUS_TYPE_STRING"
dbus_code = "s"
class DBusObjectPath(DBusStringType):
"""D-Bus object path.
This class represents the D-Bus object path type and should be instantiated
with the name of the variable. It shares marshalling and dispatch code
and underlying C type with the other string types.
"""
dbus_type = "DBUS_TYPE_OBJECT_PATH"
dbus_code = "o"
class DBusSignature(DBusStringType):
"""D-Bus type signature.
This class represents the D-Bus signature type and should be instantiated
with the name of the variable. It shares marshalling and dispatch code
and underlying C type with the other string types.
"""
dbus_type = "DBUS_TYPE_SIGNATURE"
dbus_code = "g"
class DBusArray(DBusType):
"""D-Bus array.
This class represents the D-Bus array type, it should be first
sub-classed with member_type set to an appropriate other class (the
forType classmethod can do that for you) and then instantiated with
the name of the variable.
"""
dbus_type = "DBUS_TYPE_ARRAY"
dbus_code = "a"
@classmethod
def forType(cls, type):
class _DBusArray(DBusArray):
member_type = type
return _DBusArray
def __init__(self, name):
super(DBusArray, self).__init__(name)
self.iter_name = "%s_iter" % (self.name,)
self.len_name = "%s_len" % (self.name,)
self.loop_name = "%s_p" % (self.name,)
self.type = self.member_type("%s_elem" % (self.name, ))
# FIXME doesn't handle arrays of simple arrays since the length
# field is lost.
assert (len(self.type.vars()) == 1)
self.c_type = pointerify(self.type.c_type)
def signature(self, pointer=False, const=False):
"""Type signature.
Returns a string containing the D-Bus type signature for this type.
"""
return self.dbus_code + self.type.signature()
def locals(self, pointer=False, const=False):
"""Local variable type and name.
Returns a list containing necessary local variables for iteration
of this type.
"""
locals = [ ( "DBusMessageIter", self.iter_name )]
if self.type.c_type.endswith("*"):
locals.append(( "size_t", self.len_name ))
return locals
def vars(self, pointer=False, const=False):
"""Variable type and name.
Returns a list containing tuples of C type and name for each type
within this group.
"""
vars = [ ( self.realType(self.c_type, pointer, const),
self.name ) ]
if not self.type.c_type.endswith("*"):
vars.append(( self.realType("size_t", pointer, const),
self.len_name ))
return vars
def marshal(self, iter_name, parent, type_error, mem_error,
pointer=False, const=False):
"""Marshalling code.
Returns a string containing the code that will marshal from an
iterator with the name given into local variables with the
types and names as returned by vars().
"""
name = self.name
len_name = self.len_name
if pointer:
name = "*%s" % (name, )
if not self.type.c_type.endswith("*"):
len_name = "*%s" % (len_name, )
code = """\
if (dbus_message_iter_get_arg_type (&%s) != %s) {
%s
}
if (dbus_message_iter_get_element_type (&%s) != %s) {
%s
}
dbus_message_iter_recurse (&%s, &%s);
%s = NULL;
%s = 0;
while (dbus_message_iter_get_arg_type (&%s) != DBUS_TYPE_INVALID) {
""" % (iter_name, self.dbus_type, type_error,
iter_name, self.type.dbus_type, type_error,
iter_name, self.iter_name,
name, len_name,
self.iter_name)
vars = self.type.vars()
vars.extend(self.type.locals())
code += indent(''.join("%s;\n" % var for var in lineup_vars(vars)), 1)
code += "\n"
code += indent(self.type.marshal("%s_iter" % self.name, parent,
type_error, mem_error), 1)
code += "\n"
code += indent("""\
%s = nih_realloc (%s, %s, sizeof (%s) * ((%s) + 1));
if (! %s) {
%s
}
(%s)[(%s)++] = %s;
""" % (name, name, parent, self.type.c_type, len_name,
name, mem_error,
name, len_name, self.type.name), 1)
code += "\n"
code += """\
}
dbus_message_iter_next (&%s);
""" % (iter_name, )
if self.type.c_type.endswith("*"):
code += "\n"
code += """\
%s = nih_realloc (%s, %s, sizeof (%s) * ((%s) + 1));
if (! %s) {
%s
}
(%s)[(%s)] = NULL;
""" % (name, name, parent, self.type.c_type, len_name,
name, mem_error,
name, len_name)
return code
def dispatch(self, iter_name, mem_error,
pointer=False, const=False):
"""Dispatching code.
Returns a string containing the code that will dispatch from
local variables with the types and names as returned by vars() to
an iterator with the name given.
"""
name = self.name
len_name = self.len_name
if pointer:
name = "*%s" % (name, )
if not self.type.c_type.endswith("*"):
len_name = "*%s" % (len_name, )
code = """\
if (! dbus_message_iter_open_container (&%s, %s, \"%s\", &%s)) {
%s
}
""" % (iter_name, self.dbus_type, self.type.signature(),
self.iter_name, mem_error)
code += "\n"
if self.type.c_type.endswith("*"):
code += """\
%s = 0;
for (%s%s = %s; %s && *%s; %s++) {
""" % (len_name,
self.realType(self.c_type, const=const), self.loop_name, name,
self.loop_name, self.loop_name,
self.loop_name)
else:
code += """\
for (%s%s = %s; %s < %s + %s; %s++) {
""" % (self.realType(self.c_type, const=const), self.loop_name, name,
self.loop_name, name, len_name, self.loop_name)
vars = self.type.vars()
vars.extend(self.type.locals())
code += indent(''.join("%s;\n" % var for var in lineup_vars(vars)), 1)
code += "\n"
code += indent("""\
%s = *%s;
""" % (self.type.name, self.loop_name), 1)
code += "\n"
code += indent(self.type.dispatch(self.iter_name, mem_error), 1)
if self.type.c_type.endswith("*"):
code += "\n"
code += indent("""\
(%s)++;
""" % (len_name, ), 1)
code += """\
}
if (! dbus_message_iter_close_container (&%s, &%s)) {
%s
}
""" % (iter_name, self.iter_name, mem_error)
return code
class DBusGroup(object):
def __init__(self, types, pointer=False, const=False):
self.types = types
self.pointer = pointer
self.const = const
def signature(self):
"""Type signature.
Returns a string containing the D-Bus type signature for this type.
"""
signature = ""
for type in self.types:
signature += type.signature(pointer=self.pointer, const=self.const)
return signature
def vars(self):
"""Variable type and name.
Returns a list containing tuples of C type and name for each type
within this group.
"""
vars = []
for type in self.types:
vars.extend(type.vars(pointer=self.pointer, const=self.const))
return vars
def locals(self):
"""Local variable type and name.
Returns a list containing necessary local variables for iteration
of this type.
"""
locals = []
for type in self.types:
locals.extend(type.locals(pointer=self.pointer, const=self.const))
return locals
def marshal(self, iter_name, parent, type_error, mem_error):
"""Marshalling code.
Returns a string containing the code that will marshal from an
iterator with the name given into local variables with the
types and names as returned by vars().
"""
code = ""
for type in self.types:
if code:
code += "\n"
code += type.marshal(iter_name, parent, type_error, mem_error,
pointer=self.pointer, const=self.const)
if code:
code += "\n"
code += """\
if (dbus_message_iter_get_arg_type (&%s) != DBUS_TYPE_INVALID) {
%s
}
""" % (iter_name, type_error)
return code
def dispatch(self, iter_name, mem_error):
"""Dispatching code.
Returns a string containing the code that will dispatch from
local variables with the types and names as returned by vars() to
an iterator with the name given.
"""
code = ""
for type in self.types:
if code:
code += "\n"
code += type.dispatch(iter_name, mem_error,
pointer=self.pointer, const=self.const)
return code
class Generator(object):
def staticPrototypes(self):
"""Static prototypes.
Returns an array of static function prototypes which are normally
placed in a block at the top of the source file.
Each prototype is a (retval, name, args, attributes) tuple.
"""
return []
def externPrototypes(self):
"""Extern prototypes.
Returns an array of extern function prototypes which are normally
placed in a block at the top of the source file, in lieu of
missing headers.
Each prototype is a (retval, name, args, attributes) tuple.
"""
return []
def variables(self):
"""Variables.
Returns an array of both static and exported global variables
normally placed in a block at the top of the source file.
Each variable is the code to define it, including any documentation
and default value.
"""
return []
def functions(self):
"""Functions.
Returns an array of both static and exported functions which
consistute the bulk of the source file.
Each function is the code to define it, including any documentation.
"""
return []
def definitions(self):
"""Definitions.
Returns an array of structure and type definitions which are
normally placed in a block in the header file.
Each definition is the code to define it, including any documentation.
"""
return []
def exports(self):
"""Exports.
Returns an array of prototypes for exported variables which are
placed as a block inside the extern part of the header file.
Each export is a (type, name) tuple.
"""
return []
def exportPrototypes(self):
"""Function prototypes.
Returns an array of exported function prototypes which are normally
placed in a block inside the extern part of the header file.
Each prototype is a (retval, name, args, attributes) tuple.
"""
return []
class Member(Generator):
def __init__(self, interface, name):
self.interface = interface
self.name = name
@property
def c_name(self):
return self.name
@property
def extern_name(self):
return "_".join(( extern_prefix,
NAME_RE.sub("\\1_\\2", self.c_name).lower()))
class MemberWithArgs(Member):
@classmethod
def fromElement(cls, interface, elem):
name = elem.get("name")
if name is None:
raise AttributeError, "Name may not be null"
types = [ DBusType.fromElement(e) for e in elem.findall("arg") ]
self = cls(interface, name, types)
self.style = elem.get(ElementTree.QName(XMLNS, mode), self.style)
return self
def __init__(self, interface, name, types):
super(MemberWithArgs, self).__init__(interface, name)
self.style = "sync"
self.types = types
def argArray(self):
"""Argument array.
Returns a string containing code to initialise the array of arguments
used for nih_dbus_object_new().
"""
code = """\
static const NihDBusArg %s[] = {
""" % "_".join([ self.interface.c_name, self.c_name, "args" ])
array = []
for type in self.types:
if type.direction == "in":
direction = "NIH_DBUS_ARG_IN"
elif type.direction == "out":
direction = "NIH_DBUS_ARG_OUT"
array.append(( "\"%s\"" % type.name,
"\"%s\"" % type.signature(),
direction ))
for line in lineup_array(array):
code += indent("%s,\n" % line, 1)
code += """\
{ NULL }
};
"""
return code
def variables(self):
"""Variables.
Returns an array of both static and exported global variables
normally placed in a block at the top of the source file.
Each variable is the code to define it, including any documentation
and default value.
"""
variables = []
if mode == "object":
variables.append(self.argArray())
return variables
class Method(MemberWithArgs):
def __init__(self, interface, name, types):
super(Method, self).__init__(interface, name, types)
def marshalPrototype(self):
"""Marshalling function prototype.
Returns a (retval, name, args, attributes) tuple for the prototype
of the marshaller function.
"""
return ( "static DBusHandlerResult",
"_".join([ self.interface.c_name, self.c_name, "marshal" ]),
[ ( "NihDBusObject *", "object" ),
( "NihDBusMessage *", "message" ) ],
None )
def marshalFunction(self):
"""Marshalling function.
Returns a string containing a marshaller function that takes
arguments from a D-Bus message, calls a C handler function with
them passed properly, then constructs a reply if successful.
"""
in_args = DBusGroup([t for t in self.types if t.direction == "in"])
out_args = DBusGroup([t for t in self.types if t.direction == "out"])
name = "_".join([ self.interface.c_name, self.c_name, "marshal" ])
code = """\
static DBusHandlerResult
%s (NihDBusObject *object,
%s NihDBusMessage *message)
{
""" % (name, " " * len(name))
# Declare local variables for the iterator, reply and those needed
# for both input and output arguments.
vars = [ ( "DBusMessageIter", "iter" ),
( "DBusMessage *", "reply = NULL" ) ]
vars.extend(in_args.vars())
vars.extend(in_args.locals())
if self.style != "async":
vars.extend(out_args.vars())
vars.extend(out_args.locals())
code += indent(''.join("%s;\n" % var for var in lineup_vars(vars)), 1)
# Pre-amble for the function
code += "\n"
code += indent("""\
nih_assert (object != NULL);
nih_assert (message != NULL);
""", 1);
# Marshal the input arguments into local variables
code += "\n"
code += indent("""\
/* Iterate the arguments to the message and marshal into arguments
* for our own function call.
*/
dbus_message_iter_init (message->message, &iter);
""", 1)
code += "\n"
mem_error = indent("""\
return DBUS_HANDLER_RESULT_NEED_MEMORY;
""", 1)
type_error = indent("""\
reply = dbus_message_new_error (message->message, DBUS_ERROR_INVALID_ARGS,
_("Invalid arguments to %s method"));
if (! reply) {
%s
}
goto send;
""" % (self.name, mem_error), 1)
code += indent(in_args.marshal("iter", "message",
type_error, mem_error), 1)
# Construct the function call
args = [ "object->data", "message" ]
args.extend(n for t, n in in_args.vars())
if self.style != "async":
args.extend("&%s" % n for t, n in out_args.vars())
code += "\n"
code += indent("""\
/* Call the handler function. */
if (%s (%s) < 0) {
NihError *err;
err = nih_error_get ();
if (err->number == ENOMEM) {
nih_free (err);
return DBUS_HANDLER_RESULT_NEED_MEMORY;
} else if (err->number == NIH_DBUS_ERROR) {
NihDBusError *dbus_err = (NihDBusError *)err;
reply = dbus_message_new_error (message->message,
dbus_err->name,
err->message);
nih_free (err);
if (! reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
goto send;
} else {
reply = dbus_message_new_error (message->message,
DBUS_ERROR_FAILED,
err->message);
nih_free (err);
if (! reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
goto send;
}
}
""" % (self.extern_name, ", ".join(args)), 1)
if self.style == "async":
code += "\n"
code += indent("return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;\n", 1)
else:
# Be well-behaved and make the function return immediately
# if no reply is expected
code += "\n"
code += indent("""\
/* If the sender doesn't care about a reply, don't bother wasting
* effort constructing and sending one.
*/
if (dbus_message_get_no_reply (message->message))
return DBUS_HANDLER_RESULT_HANDLED;
""", 1)
# Create reply and dispatch the local variables into output
# arguments
code += "\n"
code += indent("""\
/* Construct the reply message */
reply = dbus_message_new_method_return (message->message);
if (! reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
dbus_message_iter_init_append (reply, &iter);
""", 1)
code += "\n"
mem_error = indent("""\
dbus_message_unref (reply);
return DBUS_HANDLER_RESULT_NEED_MEMORY;
""", 1)
code += indent(out_args.dispatch("iter", mem_error), 1)
# Send the reply
code += "\nsend:\n"
code += indent("""\
/* Send the reply, appending it to the outgoing queue. */
if (! dbus_connection_send (message->conn, reply, NULL)) {
dbus_message_unref (reply);
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
dbus_message_unref (reply);
return DBUS_HANDLER_RESULT_HANDLED;
""", 1)
code += "}\n"
return code
def handlerPrototype(self):
"""Handler function prototype.
Returns a (retval, name, args, attributes) tuple for the prototype
of the handler function that the user must define.
"""
in_args = DBusGroup([t for t in self.types if t.direction == "in"],
const=True)
out_args = DBusGroup([t for t in self.types if t.direction == "out"],
pointer=True)
vars = [ ("void *", "data"),
("NihDBusMessage *", "message") ]
vars.extend(in_args.vars())
if self.style != "async":
vars.extend(out_args.vars())
return ( "extern int",
self.extern_name,
vars,
None )
def replyPrototype(self):
"""Reply function prototype.
Returns a (retval, name, args, attributes) tuple for the prototype
of the reply function defined for async functions.
"""
out_args = DBusGroup([t for t in self.types if t.direction == "out"],
const=True)
vars = [("NihDBusMessage *", "message") ]
vars.extend(out_args.vars())
return ( "int",
"_".join([ self.extern_name, "reply" ]),
vars,
( "warn_unused_result", ) )
def replyFunction(self):
"""Reply function.
Returns a string containing a reply function that takes its
arguments and produces a D-Bus message reply.
"""
out_args = DBusGroup([t for t in self.types if t.direction == "out"],
const=True)
name = "_".join([ self.extern_name, "reply" ])
vars = [ ( "NihDBusMessage *", "message" ) ]
vars.extend(out_args.vars())
code = "int\n%s (" % (name, )
code += (",\n" + " " * (len(name) + 2)).join(lineup_vars(vars))
code += ")\n{\n"
# Declare local variables for the iterator and the reply
vars = [ ( "DBusMessageIter", "iter" ),
( "DBusMessage *", "reply = NULL" ) ]
vars.extend(out_args.locals())
code += indent(''.join("%s;\n" % var for var in lineup_vars(vars)), 1)
# Pre-amble for the function
code += "\n"
code += indent("""\
nih_assert (message != NULL);
""", 1);
# Be well-behaved and don't actually send the reply if one is
# not expected
code += "\n"
code += indent("""\
/* If the sender doesn't care about a reply, don't bother wasting
* effort constructing and sending one.
*/
if (dbus_message_get_no_reply (message->message))
return 0;
""", 1)
# Create reply and dispatch the local variables into output
# arguments
code += "\n"
code += indent("""\
/* Construct the reply message */
reply = dbus_message_new_method_return (message->message);
if (! reply)
return -1;
dbus_message_iter_init_append (reply, &iter);
""", 1)
code += "\n"
mem_error = indent("""\
dbus_message_unref (reply);
return -1;
""", 1)
code += indent(out_args.dispatch("iter", mem_error), 1)
# Send the reply
code += "\n"
code += indent("""\
/* Send the reply, appending it to the outgoing queue. */
if (! dbus_connection_send (message->conn, reply, NULL)) {
dbus_message_unref (reply);
return -1;
}
dbus_message_unref (reply);
return 0;
""", 1)
code += "}\n"
return code
def dispatchPrototype(self):
"""Dispatch function prototype.
Returns a (retval, name, args, attributes) tuple for the prototype
of the dispatch function.
"""
in_args = DBusGroup([t for t in self.types if t.direction == "in"],
const=True)
out_args = DBusGroup([t for t in self.types if t.direction == "out"],
pointer=True)
vars = [ ( "NihDBusProxy *", "proxy" ) ]
vars.extend(in_args.vars())
# FIXME async doesn't have these, but has a callback instead
vars.extend(out_args.vars())
return ( "int",
self.extern_name,
vars,
( "warn_unused_result", ) )
def dispatchFunction(self):
"""Marshalling function.
Returns a string containing a marshaller function that takes
arguments from a D-Bus message, calls a C handler function with
them passed properly, then constructs a reply if successful.
"""
in_args = DBusGroup([t for t in self.types if t.direction == "in"],
const=True)
out_args = DBusGroup([t for t in self.types if t.direction == "out"],
pointer=True)
vars = [ ( "NihDBusProxy *", "proxy" ) ]
vars.extend(in_args.vars())
# FIXME async doesn't have these, but has a callback instead
vars.extend(out_args.vars())
code = "int\n%s (" % (self.extern_name, )
code += (",\n" + " " * (len(self.extern_name) + 2)).join(lineup_vars(vars))
code += ")\n{\n"
# Declare local variables for the iterator, reply and those needed
# for both input and output arguments.
vars = [ ( "DBusMessage *", "message" ),
( "DBusMessageIter", "iter" ),
( "DBusMessage *", "reply = NULL" ),
( "DBusError", "error" ) ]
vars.extend(in_args.locals())
# FIXME not for async
vars.extend(out_args.locals())
code += indent(''.join("%s;\n" % var for var in lineup_vars(vars)), 1)
# Pre-amble for the function
code += "\n"
code += indent("""\
nih_assert (proxy != NULL);
""", 1);
# Dispatch the input arguments into a new local message
code += "\n"
code += indent("""\
message = dbus_message_new_method_call (proxy->name, proxy->path, "%s", "%s");
if (! message)
nih_return_no_memory_error (-1);
/* Iterate the arguments to the function and dispatch into
* message arguments.
*/
dbus_message_iter_init_append (message, &iter);
""" % (self.interface.name, self.name), 1)
code += "\n"
# FIXME autostart?
mem_error = indent("""\
dbus_message_unref (message);
nih_return_no_memory_error (-1);
""", 1)
code += indent(in_args.dispatch("iter", mem_error), 1)
# FIXME timeout should be configurable
# FIXME expect no reply?
code += "\n"
code += indent("""\
dbus_error_init (&error);
/* Send the reply, appending it to the outgoing queue and blocking. */
reply = dbus_connection_send_with_reply_and_block (proxy->conn, message, -1, &error);
if (! reply) {
dbus_message_unref (message);
if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) {
dbus_error_free (&error);
nih_return_no_memory_error (-1);
} else {
nih_dbus_error_raise (error.name, error.message);
dbus_error_free (&error);
return -1;
}
}
dbus_message_unref (message);
""", 1);
# Marshal the reply arguments into output arguments
code += "\n"
code += indent("""\
/* Iterate the arguments to the reply and marshal into output
* arguments from our own function call.
*/
dbus_message_iter_init (reply, &iter);
""", 1);
code += "\n"
mem_error = indent("""\
dbus_message_unref (reply);
nih_return_no_memory_error (-1);
""", 1)
type_error = indent("""\
dbus_message_unref (reply);
nih_return_error (-1, NIH_DBUS_INVALID_ARGS, NIH_DBUS_INVALID_ARGS_STR);
""", 1);
code += indent(out_args.marshal("iter", "proxy",
type_error, mem_error), 1)
code += "\n"
code += indent("""\
dbus_message_unref (reply);
return 0;
""", 1)
code += "}\n"
return code
def staticPrototypes(self):
"""Static prototypes.
Returns an array of static function prototypes which are normally
placed in a block at the top of the source file.
Each prototype is a (retval, name, args, attributes) tuple.
"""
prototypes = []
if mode == "object":
prototypes.append(self.marshalPrototype())
return prototypes
def externPrototypes(self):
"""Extern prototypes.
Returns an array of extern function prototypes which are normally
placed in a block at the top of the source file, in lieu of
missing headers.
Each prototype is a (retval, name, args, attributes) tuple.
"""
prototypes = []
if mode =="object":
prototypes.append(self.handlerPrototype())
return prototypes
def functions(self):
"""Functions.
Returns an array of both static and exported functions which
consistute the bulk of the source file.
Each function is the code to define it, including any documentation.
"""
functions = []
if mode == "object":
functions.append(self.marshalFunction())
if self.style == "async":
functions.append(self.replyFunction())
else:
functions.append(self.dispatchFunction())
return functions
def exportPrototypes(self):
"""Function prototypes.
Returns an array of exported function prototypes which are normally
placed in a block inside the extern part of the header file.
Each prototype is a (retval, name, args, attributes) tuple.
"""
prototypes = []
if mode == "object":
if self.style == "async":
prototypes.append(self.replyPrototype())
else:
prototypes.append(self.dispatchPrototype())
return prototypes
class Signal(MemberWithArgs):
def __init__(self, interface, name, types):
super(Signal, self).__init__(interface, name, types)
def dispatchPrototype(self):
"""Dispatch function prototype.
Returns a (retval, name, args, attributes) tuple for the prototype
of the dispatch function.
"""
args = DBusGroup(self.types, const=True)
vars = [ ( "DBusConnection *", "connection" ),
( "const char *", "origin_path" ) ]
vars.extend(args.vars())
return ( "int",
self.extern_name,
vars,
( "warn_unused_result", ) )
def dispatchFunction(self):
"""Dispatch function.
Returns a string containing a dispatch function that takes puts
its arguments into a D-Bus message and sends it.
"""
args = DBusGroup(self.types, const=True)
vars = [ ( "DBusConnection *", "connection" ),
( "const char *", "origin_path" ) ]
vars.extend(args.vars())
code = "int\n%s (" % (self.extern_name, )
code += (",\n" + " " * (len(self.extern_name) + 2)).join(lineup_vars(vars))
code += ")\n{\n"
# Declare local variables for the message and iterator
vars = [ ( "DBusMessage *", "message"),
( "DBusMessageIter", "iter" ) ]
vars.extend(args.locals())
code += indent(''.join("%s;\n" % var for var in lineup_vars(vars)), 1)
# Pre-amble for the function
code += "\n"
code += indent("""\
nih_assert (connection != NULL);
nih_assert (origin_path != NULL);
""", 1)
# Marshal the arguments into a new local message
code += "\n"
code += indent("""\
message = dbus_message_new_signal (origin_path, "%s", "%s");
if (! message)
return -1;
/* Iterate the arguments to the function and dispatch into
* message arguments.
*/
dbus_message_iter_init_append (message, &iter);
""" % (self.interface.name, self.name), 1)
code += "\n"
mem_error = indent("""\
dbus_message_unref (message);
return -1;
""", 1)
code += indent(args.dispatch("iter", mem_error), 1)
code += "\n"
code += indent("""\
/* Send the reply, appending it to the outgoing queue. */
if (! dbus_connection_send (connection, message, NULL)) {
dbus_message_unref (message);
return -1;
}
dbus_message_unref (message);
return 0;
""", 1)
code += "}\n"
return code
def functions(self):
"""Functions.
Returns an array of both static and exported functions which
consistute the bulk of the source file.
Each function is the code to define it, including any documentation.
"""
functions = []
if mode == "object":
functions.append(self.dispatchFunction())
return functions
def exportPrototypes(self):
"""Function prototypes.
Returns an array of exported function prototypes which are normally
placed in a block inside the extern part of the header file.
Each prototype is a (retval, name, args, attributes) tuple.
"""
prototypes = []
if mode == "object":
prototypes.append(self.dispatchPrototype())
return prototypes
class Group(Generator):
def __init__(self, members):
self.members = members
def staticPrototypes(self):
"""Static prototypes.
Returns an array of static function prototypes which are normally
placed in a block at the top of the source file.
Each prototype is a (retval, name, args, attributes) tuple.
"""
prototypes = []
for member in self.members:
prototypes.extend(member.staticPrototypes())
return prototypes
def externPrototypes(self):
"""Extern prototypes.
Returns an array of extern function prototypes which are normally
placed in a block at the top of the source file, in lieu of
missing headers.
Each prototype is a (retval, name, args, attributes) tuple.
"""
prototypes = []
for member in self.members:
prototypes.extend(member.externPrototypes())
return prototypes
def variables(self):
"""Variables.
Returns an array of both static and exported global variables
normally placed in a block at the top of the source file.
Each variable is the code to define it, including any documentation
and default value.
"""
variables = []
for member in self.members:
variables.extend(member.variables())
return variables
def functions(self):
"""Functions.
Returns an array of both static and exported functions which
consistute the bulk of the source file.
Each function is the code to define it, including any documentation.
"""
functions = []
for member in self.members:
functions.extend(member.functions())
return functions
def definitions(self):
"""Definitions.
Returns an array of structure and type definitions which are
normally placed in a block in the header file.
Each definition is the code to define it, including any documentation.
"""
definitions = []
for member in self.members:
definitions.extend(member.definitions())
return definitions
def exports(self):
"""Exports.
Returns an array of prototypes for exported variables which are
placed as a block inside the extern part of the header file.
Each export is a (type, name) tuple.
"""
exports = []
for member in self.members:
exports.extend(member.exports())
return exports
def exportPrototypes(self):
"""Function prototypes.
Returns an array of exported function prototypes which are normally
placed in a block inside the extern part of the header file.
Each prototype is a (retval, name, args, attributes) tuple.
"""
prototypes = []
for member in self.members:
prototypes.extend(member.exportPrototypes())
return prototypes
class Interface(Group):
@classmethod
def fromElement(cls, elem):
name = elem.get("name")
if name is None:
raise AttributeError, "Interface name may not be null"
self = cls(name, [])
for e in elem.findall("method"):
method = Method.fromElement(self, e)
self.methods.append(method)
self.members.append(method)
for e in elem.findall("signal"):
signal = Signal.fromElement(self, e)
self.signals.append(signal)
self.members.append(signal)
return self
def __init__(self, name, members):
super(Interface, self).__init__(members)
self.name = name
self.methods = []
self.signals = []
@property
def c_name(self):
return self.name.replace(".", "_")
def methodsArray(self):
"""Methods array.
Returns a string containing code to initialise the array of methods
used for nih_dbus_object_new().
"""
code = """\
const NihDBusMethod %s_methods[] = {
""" % self.c_name
array = []
for method in self.methods:
if mode == "object":
func = "%s_marshal" % "_".join([ self.c_name, method.c_name ])
else:
func = "NULL"
array.append(( "\"%s\"" % method.name,
func,
"%s_args" % "_".join([ self.c_name, method.c_name ])))
for line in lineup_array(array):
code += indent("%s,\n" % line, 1)
code += """\
{ NULL }
};
"""
return code
def signalsArray(self):
"""Signals array.
Returns a string containing code to initialise the array of signals
used for nih_dbus_object_new().
"""
code = """\
const NihDBusSignal %s_signals[] = {
""" % self.c_name
array = []
for signal in self.signals:
array.append(( "\"%s\"" % signal.name,
"%s_args" % "_".join([ self.c_name, signal.c_name ])))
for line in lineup_array(array):
code += indent("%s,\n" % line, 1)
code += """\
{ NULL }
};
"""
return code
def interfacePrototype(self):
"""Interface structure prototype.
Returns a (type, name) tuple for the prototype of the interface
struct.
"""
return ( "const NihDBusInterface", "%s" % self.c_name )
def interfaceStruct(self):
"""Interface structure.
Returns a string containing code to define a structure containing
information about the interface.
"""
code = """\
const NihDBusInterface %s = {
\"%s\",
%s_methods,
%s_signals,
NULL
""" % (self.c_name, self.name, self.c_name, self.c_name)
code += """\
};
"""
return code
def variables(self):
"""Variables.
Returns an array of both static and exported global variables
normally placed in a block at the top of the source file.
Each variable is the code to define it, including any documentation
and default value.
"""
variables = super(Interface, self).variables()
if mode == "object":
variables.append(self.methodsArray())
variables.append(self.signalsArray())
variables.append(self.interfaceStruct())
return variables
def exports(self):
"""Exports.
Returns an array of prototypes for exported variables which are
placed as a block inside the extern part of the header file.
Each export is a (type, name) tuple.
"""
exports = super(Interface, self).exports()
if mode == "object":
exports.append(self.interfacePrototype())
return exports
class Output(Group):
def __init__(self, basename, members):
super(Output, self).__init__(members)
self.basename = basename
def sourceFile(self):
"""Generate source (.c) file.
Returns a string containing the code for the source (.c) file
of this code group.
"""
code = """\
/* %s
*
* %s - %s
*
* %s
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
""" % (PACKAGE_NAME, "%s.c" % self.basename, "Auto-generated d-bus bindings",
PACKAGE_COPYRIGHT)
# Usual includes
if code:
code += "\n"
code += """\
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <dbus/dbus.h>
#include <errno.h>
#include <nih/macros.h>
#include <nih/alloc.h>
#include <nih/string.h>
#include <nih/logging.h>
#include <nih/error.h>
#include <nih/errors.h>
#include <nih-dbus/dbus_error.h>
#include <nih-dbus/dbus_message.h>
#include <nih-dbus/dbus_object.h>
#include <nih-dbus/dbus_proxy.h>
#include "%s.h"
""" % (self.basename, )
# Extern function prototypes
protos = self.externPrototypes()
if protos:
if code:
code += "\n\n"
code += """\
/* Prototypes for handler functions */
"""
for line in lineup_protos(protos):
code += line
code += ";\n"
# Static function prototypes
protos = self.staticPrototypes()
if protos:
if code:
code += "\n\n"
code += """\
/* Prototypes for static functions */
"""
for line in lineup_protos(protos):
code += line
code += ";\n"
# Global variables
variables = self.variables()
if variables:
if code:
code += "\n"
for g in variables:
if code:
code += "\n"
code += g
# Function definitions
functions = self.functions()
if functions:
if code:
code += "\n"
for function in functions:
if code:
code += "\n"
code += function
return code
def headerFile(self):
"""Generate header (.h) file.
Returns a string containing the code for the header (.h) file
of this code group.
"""
code = """\
/* %s
*
* %s
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
""" % (PACKAGE_NAME, PACKAGE_COPYRIGHT)
# FIXME include sub-directory name in sentry?
sentry = "DBUS__%s_H" % self.basename.replace(".", "_").upper()
if code:
code += "\n"
code += """\
#ifndef %s
#define %s
#include <dbus/dbus.h>
#include <nih/macros.h>
#include <nih-dbus/dbus_message.h>
#include <nih-dbus/dbus_object.h>
#include <nih-dbus/dbus_proxy.h>
""" % (sentry, sentry)
# Append structure definitions
definitions = self.definitions()
if definitions:
if code:
code += "\n"
for definition in definitions:
if code:
code += "\n"
code += definition
# Guard extern prototypes for C++ inclusion
if code:
code += "\n\n"
code += """\
NIH_BEGIN_EXTERN
"""
# Line up the variable prototypes together and place in a block
globals = self.exports()
if globals:
if code:
code += "\n"
for line in lineup_vars(globals):
code += line
code += ";\n"
# Line up the function prototypes together and place in a block
protos = self.exportPrototypes()
if protos:
if code:
code += "\n"
for line in lineup_protos(protos):
code += line
code += ";\n"
# End the extern guard and header sentry
if code:
code += "\n"
code += """\
NIH_END_EXTERN
#endif /* %s */
""" % (sentry)
return code
# Complex:
# s (struct) -> pointer to a struct of a defined type (named after func + arg)
# e.g. typedef struct job_find_by_name_what {
# ...
# } JobFindByNameWhat;
#
# dbus_message_iter_recurse (ITER, SUB)
# OK = dbus_message_iter_close_container (ITER, SUB)
#
# dbus_message_iter_open_container (ITER, TYPE, NULL, SUB)
# OK = dbus_message_iter_close_container (ITER, SUB)
#
# thus we can make arrays of it too :-)
# struct-in-struct can just get arbitrary names
#
#
# Annoying"
# v (variant) -> seems to be a problem
# has a type signature then the data
#
# e (dict) -> problem for now (should be NihHash really)
# always arrays a{..}
# first type is basic
# second type is any (including struct)
def lineup_vars(vars):
"""Lineup variable lists.
Returns an array of lines of C code to declare each of the variables
in vars, which should be an array of (type, name) tuples. The
declarations will be lined up in a pretty fashion.
It is up to the caller to add appropriate line-endings.
"""
exp_vars = []
for type, name in vars:
basic = type.rstrip("*")
pointers = type[len(basic):]
basic = basic.rstrip()
exp_vars.append(( basic, pointers, name ))
if not exp_vars:
return []
max_basic = max(len(b) for b,p,n in exp_vars)
max_pointers = max(len(p) for b,p,n in exp_vars)
lines = []
for basic, pointers, name in exp_vars:
code = basic.ljust(max_basic)
code += pointers.rjust(max_pointers + 1)
code += name
lines.append(code)
return lines
def lineup_protos(protos):
"""Lineup prototype lists.
Returns an array of lines of C code to declare each of the prototypes
in protos, which should be an array of (retval, name, args, attributes)
tuples. The declarations will be lined up in a pretty fashion.
It is up to the caller to add appropriate line-endings.
"""
if not protos:
return []
max_retval = max(len(r) for r,n,a,at in protos)
max_name = max(len(n) for r,n,a,at in protos)
if not [ True for r,n,a,at in protos if r.endswith("*") ]:
max_retval += 1
lines = []
for retval, name, args, attributes in protos:
code = retval.ljust(max_retval)
code += name.ljust(max_name + 1)
code += "("
# FIXME split arguments over multiple lines maybe?
code += ", ".join("%s%s%s" % (type, not type.endswith("*") and " " or "",
name) for type,name in args)
code += ")"
if attributes:
code += "\n\t__attribute__ ((" + ", ".join(attributes) + "))"
lines.append(code)
return lines
def lineup_array(array):
"""Lineup array definitions.
Returns an array of lines of C code to declare each of the array struct
definitions in array, which should be an array of tuples for each structure
member. The declarations will be lined up in a pretty fashion.
It is up to the caller to add appropriate line-endings.
"""
if not array:
return []
max_len = [ max(len(entry[i]) for entry in array)
for i in range(0, len(array[0])) ]
lines = []
for entry in array:
code = "{ "
for i, str in enumerate(entry):
if i < len(array[0]) - 1:
code += (str + ", ").ljust(max_len[i] + 2)
else:
code += str.ljust(max_len[i])
code += " }"
lines.append(code)
return lines
def indent(str, level):
"""Increase indent of string.
Returns the string with each line indented to the given level of tabs.
"""
output = ""
for line in str.splitlines(True):
if len(line.strip()):
output += ("\t" * level) + line
else:
output += line
return output
def pointerify(type):
"""Turn C type into a pointer.
Returns the string for a pointer to the given C type.
"""
if type.endswith("*"):
return type + "*"
else:
return type + " *"
def constify(type):
"""Type C pointer type into a const pointer.
Returns the string modified so that the pointer is a const pointer.
"""
if not type.endswith("*"):
return type
if type[:-1].endswith("*"):
return type[:-1] + " const *"
else:
return "const " + type
def main():
global options
global extern_prefix
global mode
usage = "%prog [OPTION]... XMLFILE"
description = """\
XMLFILE is a valid XML file containing information about one or more interfaces
in the D-Bus Introspection format, except that the top-level node may be
interface as well as node.
C code to marshal methods and dispatch signals (if --mode is object) or to
dispatch methods and marshal signals (if --mode is proxy) is written to
a .c and .h file in the current directory with the same base name as XMLFILE,
or to that specified by --output.
"""
parser = OptionParser(usage, description=description)
parser.add_option("--mode", type="string", metavar="MODE",
default="object",
help="Output mode: object, or proxy [default: %default]")
parser.add_option("-o", "--output", type="string", metavar="FILENAME",
help="Write C source to FILENAME, header alongside")
parser.add_option("--prefix", type="string", metavar="PREFIX",
default="dbus",
help="Prefix for externally supplied C functions [default: %default]")
(options, args) = parser.parse_args()
if len(args) != 1:
parser.error("incorrect number of arguments")
if options.mode not in ("object", "proxy"):
parser.error("invalid mode")
extern_prefix = options.prefix
mode = options.mode
# Figure out input and output filenames based on arguments; try and
# do the right thing in most circumstances
xml_filename = args.pop(0)
if options.output:
(root, ext) = os.path.splitext(options.output)
if ext and ext != ".h":
source_filename = options.output
else:
source_filename = os.path.extsep.join(( root, "c" ))
header_filename = os.path.extsep.join (( root, "h"))
basename = os.path.basename(root)
else:
basename = os.path.splitext(os.path.basename(xml_filename))[0]
source_filename = os.path.extsep.join(( basename, "c" ))
header_filename = os.path.extsep.join(( basename, "h" ))
(head, tail) = os.path.split(source_filename)
tmp_source_filename = os.path.join(head, ".%s.tmp" % tail)
(head, tail) = os.path.split(header_filename)
tmp_header_filename = os.path.join(head, ".%s.tmp" % tail)
# Parse the XML file into an ElementTree
tree = ElementTree.parse(xml_filename)
elem = tree.getroot()
# Walk the tree to find interfaces
interfaces = []
if elem.tag == "interface":
interfaces.append(Interface.fromElement(elem))
else:
for iface_e in elem.findall("interface"):
interfaces.append(Interface.fromElement(iface_e))
# Generate and write output
o = Output(basename, interfaces)
try:
f = open(tmp_source_filename, "w")
try:
print >>f, o.sourceFile()
finally:
f.close()
f = open(tmp_header_filename, "w")
try:
print >>f, o.headerFile()
finally:
f.close()
os.rename(tmp_source_filename, source_filename)
os.rename(tmp_header_filename, header_filename)
except:
if os.path.exists(tmp_source_filename):
os.unlink(tmp_source_filename)
if os.path.exists(tmp_header_filename):
os.unlink(tmp_header_filename)
raise
if __name__ == "__main__":
main()