blob: f1da735372b7b65a9a36e930c4b269c15627071c [file] [log] [blame]
/* nih-dbus-tool
*
* signal.c - signal parsing and generation
*
* Copyright © 2009 Scott James Remnant <scott@netsplit.com>.
* Copyright © 2009 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include <nih/macros.h>
#include <nih/alloc.h>
#include <nih/list.h>
#include <nih/string.h>
#include <nih/logging.h>
#include <nih/error.h>
#include "symbol.h"
#include "indent.h"
#include "type.h"
#include "marshal.h"
#include "demarshal.h"
#include "interface.h"
#include "signal.h"
#include "argument.h"
#include "parse.h"
#include "errors.h"
/**
* signal_name_valid:
* @name: Member name to verify.
*
* Verifies whether @name matches the specification for a D-Bus interface
* member name, and thus is valid for a signal.
*
* Returns: TRUE if valid, FALSE if not.
**/
int
signal_name_valid (const char *name)
{
nih_assert (name != NULL);
/* We can get away with just using strlen() here even through name
* is in UTF-8 because all the valid characters are ASCII.
*/
for (size_t i = 0; i < strlen (name); i++) {
/* Names may contain digits, but not at the beginning. */
if ((name[i] >= '0') && (name[i] <= '9')) {
if (i == 0)
return FALSE;
continue;
}
/* Valid characters anywhere are [A-Za-z_] */
if ( ((name[i] < 'A') || (name[i] > 'Z'))
&& ((name[i] < 'a') || (name[i] > 'z'))
&& (name[i] != '_'))
return FALSE;
}
/* Name must be at least 1 character and no more than 255 characters */
if ((strlen (name) < 1) || (strlen (name) > 255))
return FALSE;
return TRUE;
}
/**
* signal_new:
* @parent: parent object for new signal,
* @name: D-Bus name of signal.
*
* Allocates a new D-Bus object Signal data structure, with the D-Bus name
* set to @name. The returned structure is not placed into any list and
* has no arguments.
*
* If @parent is not NULL, it should be a pointer to another object which
* will be used as a parent for the returned signal. When all parents
* of the returned signal are freed, the returned signal will also be
* freed.
*
* Returns: the new signal or NULL if the allocation failed.
**/
Signal *
signal_new (const void *parent,
const char *name)
{
Signal *signal;
nih_assert (name != NULL);
signal = nih_new (parent, Signal);
if (! signal)
return NULL;
nih_list_init (&signal->entry);
nih_alloc_set_destructor (signal, nih_list_destroy);
signal->name = nih_strdup (signal, name);
if (! signal->name) {
nih_free (signal);
return NULL;
}
signal->symbol = NULL;
signal->deprecated = FALSE;
nih_list_init (&signal->arguments);
return signal;
}
/**
* signal_start_tag:
* @xmlp: XML parser,
* @tag: name of XML tag being parsed,
* @attr: NULL-terminated array of attribute name and value pairs.
*
* This function is called by parse_start_tag() for a "signal"
* start tag, a child of the "interface" tag that defines a signal the
* D-Bus interface specifies.
*
* If the method does not appear within an interface tag a warning is
* emitted and the tag will be ignored.
*
* Signals must have a "name" attribute containing the D-Bus name
* of the signal.
*
* Any unknown attributes result in a warning and will be ignored.
*
* A Signal object will be allocated and pushed onto the stack, this is
* not added to the interface until the end tag is found.
*
* Returns: zero on success, negative value on raised error.
**/
int
signal_start_tag (XML_Parser xmlp,
const char * tag,
char * const *attr)
{
ParseContext *context;
ParseStack * parent;
Signal * signal;
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);
/* Signals should only appear inside interfaces. */
parent = parse_stack_top (&context->stack);
if ((! parent) || (parent->type != PARSE_INTERFACE)) {
nih_warn ("%s:%zu:%zu: %s", context->filename,
(size_t)XML_GetCurrentLineNumber (xmlp),
(size_t)XML_GetCurrentColumnNumber (xmlp),
_("Ignored unexpected <signal> 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 <signal> attribute"),
*key);
}
}
/* Check we have a name and that it's valid */
if (! name)
nih_return_error (-1, SIGNAL_MISSING_NAME,
_(SIGNAL_MISSING_NAME_STR));
if (! signal_name_valid (name))
nih_return_error (-1, SIGNAL_INVALID_NAME,
_(SIGNAL_INVALID_NAME_STR));
/* Allocate a Signal object and push onto the stack */
signal = signal_new (NULL, name);
if (! signal)
nih_return_system_error (-1);
if (! parse_stack_push (NULL, &context->stack, PARSE_SIGNAL, signal)) {
nih_error_raise_system ();
nih_free (signal);
return -1;
}
return 0;
}
/**
* signal_end_tag:
* @xmlp: XML parser,
* @tag: name of XML tag being parsed.
*
* This function is called by parse_end_tag() for a "signal" end
* tag, and matches a call to signal_start_tag() made at the same parsing
* level.
*
* The signal is added to the list of signals defined by the parent
* interface.
*
* Returns: zero on success, negative value on raised error.
**/
int
signal_end_tag (XML_Parser xmlp,
const char *tag)
{
ParseContext *context;
ParseStack * entry;
ParseStack * parent;
Signal * signal;
Signal * conflict;
Interface * interface;
nih_assert (xmlp != NULL);
nih_assert (tag != NULL);
context = XML_GetUserData (xmlp);
nih_assert (context != NULL);
entry = parse_stack_top (&context->stack);
nih_assert (entry != NULL);
nih_assert (entry->type == PARSE_SIGNAL);
signal = entry->signal;
/* Generate a symbol from the name */
if (! signal->symbol) {
signal->symbol = symbol_from_name (signal, signal->name);
if (! signal->symbol)
nih_return_no_memory_error (-1);
}
nih_list_remove (&entry->entry);
parent = parse_stack_top (&context->stack);
nih_assert (parent != NULL);
nih_assert (parent->type == PARSE_INTERFACE);
interface = parent->interface;
/* Make sure there's not a conflict before adding the signal */
conflict = signal_lookup (interface, signal->symbol);
if (conflict) {
nih_error_raise_printf (SIGNAL_DUPLICATE_SYMBOL,
_(SIGNAL_DUPLICATE_SYMBOL_STR),
signal->symbol, conflict->name);
return -1;
}
nih_debug ("Add %s signal to %s interface",
signal->name, interface->name);
nih_list_add (&interface->signals, &signal->entry);
nih_ref (signal, interface);
nih_free (entry);
return 0;
}
/**
* signal_annotation:
* @signal: signal object annotation applies to,
* @name: annotation name,
* @value: annotation value.
*
* Handles applying the annotation @name with value @value to the signal
* @signal. Signals 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
signal_annotation (Signal * signal,
const char *name,
const char *value)
{
nih_assert (signal != NULL);
nih_assert (name != NULL);
nih_assert (value != NULL);
if (! strcmp (name, "org.freedesktop.DBus.Deprecated")) {
if (! strcmp (value, "true")) {
nih_debug ("Marked %s signal as deprecated",
signal->name);
signal->deprecated = TRUE;
} else if (! strcmp (value, "false")) {
nih_debug ("Marked %s signal as not deprecated",
signal->name);
signal->deprecated = FALSE;
} else {
nih_return_error (-1, SIGNAL_ILLEGAL_DEPRECATED,
_(SIGNAL_ILLEGAL_DEPRECATED_STR));
}
} else if (! strcmp (name, "com.netsplit.Nih.Symbol")) {
if (symbol_valid (value)) {
if (signal->symbol)
nih_unref (signal->symbol, signal);
signal->symbol = nih_strdup (signal, value);
if (! signal->symbol)
nih_return_no_memory_error (-1);
nih_debug ("Set %s signal symbol to %s",
signal->name, signal->symbol);
} else {
nih_return_error (-1, SIGNAL_INVALID_SYMBOL,
_(SIGNAL_INVALID_SYMBOL_STR));
}
} else {
nih_error_raise_printf (SIGNAL_UNKNOWN_ANNOTATION,
"%s: %s: %s",
_(SIGNAL_UNKNOWN_ANNOTATION_STR),
signal->name, name);
return -1;
}
return 0;
}
/**
* signal_lookup:
* @interface: interface to search,
* @symbol: signal symbol to find.
*
* Finds a signal in @interface's signals list which has the generated
* or supplied C symbol @symbol.
*
* Returns: signal found or NULL if no signal matches.
**/
Signal *
signal_lookup (Interface * interface,
const char *symbol)
{
nih_assert (interface != NULL);
nih_assert (symbol != NULL);
NIH_LIST_FOREACH (&interface->signals, iter) {
Signal *signal = (Signal *)iter;
if (signal->symbol
&& (! strcmp (signal->symbol, symbol)))
return signal;
}
return NULL;
}
/**
* signal_lookup_argument:
* @signal: signal to search,
* @symbol: argument symbol to find.
*
* Finds a argument in @signal's arguments list which has the generated
* or supplied C symbol @symbol.
*
* Returns: argument found or NULL if no argument matches.
**/
Argument *
signal_lookup_argument (Signal * signal,
const char *symbol)
{
nih_assert (signal != NULL);
nih_assert (symbol != NULL);
NIH_LIST_FOREACH (&signal->arguments, iter) {
Argument *argument = (Argument *)iter;
if (argument->symbol
&& (! strcmp (argument->symbol, symbol)))
return argument;
}
return NULL;
}
/**
* signal_object_function:
* @parent: parent object for new string.
* @prefix: prefix for function name,
* @interface: interface of @signal,
* @signal: signal to generate function for,
* @prototypes: list to append function prototypes to,
* @structs: list to append structure definitions to.
*
* Generates C code for a function to emit a signal @signal on @interface by
* marshalling the arguments.
*
* 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 @signal.
*
* 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 *
signal_object_function (const void *parent,
const char *prefix,
Interface * interface,
Signal * signal,
NihList * prototypes,
NihList * structs)
{
NihList locals;
NihList signal_structs;
nih_local char * name = NULL;
nih_local TypeFunc *func = NULL;
TypeVar * arg;
NihListEntry * attrib;
nih_local char * assert_block = NULL;
nih_local TypeVar * signal_var = NULL;
nih_local TypeVar * iter_var = NULL;
nih_local char * marshal_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 (signal != NULL);
nih_assert (prototypes != NULL);
nih_assert (structs != NULL);
nih_list_init (&locals);
nih_list_init (&signal_structs);
/* The function returns an integer, and accepts an argument for
* the connection and origin path. The integer indicates whether
* an error occurred, so we want a warning if the result isn't used.
*/
name = symbol_extern (NULL, prefix, interface->symbol, "emit",
signal->symbol, NULL);
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, "DBusConnection *", "connection");
if (! arg)
return NULL;
nih_list_add (&func->args, &arg->entry);
if (! nih_strcat (&assert_block, NULL,
"nih_assert (connection != NULL);\n"))
return NULL;
arg = type_var_new (func, "const char *", "origin_path");
if (! arg)
return NULL;
nih_list_add (&func->args, &arg->entry);
if (! nih_strcat (&assert_block, NULL,
"nih_assert (origin_path != NULL);\n"))
return NULL;
/* The function requires a message pointer, which we allocate,
* and an iterator for it to append the arguments. 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.
*/
signal_var = type_var_new (NULL, "DBusMessage *", "signal");
if (! signal_var)
return NULL;
nih_list_add (&locals, &signal_var->entry);
iter_var = type_var_new (NULL, "DBusMessageIter", "iter");
if (! iter_var)
return NULL;
nih_list_add (&locals, &iter_var->entry);
/* Create the signal and set up the iterator to append to it. */
if (! nih_strcat_sprintf (&marshal_block, NULL,
"/* Construct the message. */\n"
"signal = dbus_message_new_signal (origin_path, \"%s\", \"%s\");\n"
"if (! signal)\n"
"\treturn -1;\n"
"\n"
"dbus_message_iter_init_append (signal, &iter);\n"
"\n",
interface->name, signal->name))
return NULL;
/* Iterate over the signal's output arguments, for each one we
* append the code to the marshalling code and at the same time
* build up our own expected arguments themselves.
*/
NIH_LIST_FOREACH (&signal->arguments, iter) {
Argument * argument = (Argument *)iter;
NihList arg_vars;
NihList arg_locals;
NihList arg_structs;
DBusSignatureIter iter;
nih_local char * oom_error_code = NULL;
nih_local char * type_error_code = NULL;
nih_local char * block = NULL;
if (argument->direction != NIH_DBUS_ARG_OUT)
continue;
nih_list_init (&arg_vars);
nih_list_init (&arg_locals);
nih_list_init (&arg_structs);
dbus_signature_iter_init (&iter, argument->type);
/* In case of out of memory, simply return; the caller
* can try again.
*/
oom_error_code = nih_strdup (NULL,
"dbus_message_unref (signal);\n"
"return -1;\n");
if (! oom_error_code)
return NULL;
block = marshal (NULL, &iter, "iter", argument->symbol,
oom_error_code,
&arg_vars, &arg_locals,
prefix, interface->symbol,
signal->symbol, argument->symbol,
&arg_structs);
if (! block)
return NULL;
if (! nih_strcat_sprintf (&marshal_block, NULL,
"%s"
"\n",
block))
return NULL;
/* We take a parameter of the expected type and name of
* the marshal input variable; if it's a pointer, we
* assert that it's not NULL and make sure it's const.
*/
NIH_LIST_FOREACH_SAFE (&arg_vars, iter) {
TypeVar *var = (TypeVar *)iter;
if (! type_to_const (&var->type, var))
return NULL;
if (! type_strcat_assert (&assert_block, NULL, var,
func->args.prev != &func->args ? (TypeVar *)func->args.prev : NULL,
_iter.next != &arg_vars ? (TypeVar *)_iter.next : NULL))
return NULL;
nih_list_add (&func->args, &var->entry);
nih_ref (var, func);
}
NIH_LIST_FOREACH_SAFE (&arg_locals, iter) {
TypeVar *var = (TypeVar *)iter;
nih_list_add (&locals, &var->entry);
nih_ref (var, marshal_block);
}
NIH_LIST_FOREACH_SAFE (&arg_structs, iter) {
TypeStruct *structure = (TypeStruct *)iter;
nih_list_add (&signal_structs, &structure->entry);
nih_ref (structure, marshal_block);
}
}
/* 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"
"/* Send the signal, appending it to the outgoing queue. */\n"
"if (! dbus_connection_send (connection, signal, NULL)) {\n"
"\tdbus_message_unref (signal);\n"
"\treturn -1;\n"
"}\n"
"\n"
"dbus_message_unref (signal);\n"
"\n"
"return 0;\n",
vars_block,
assert_block,
marshal_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 (&signal_structs, iter) {
TypeStruct *structure = (TypeStruct *)iter;
nih_ref (structure, code);
nih_list_add (structs, &structure->entry);
}
return code;
}
/**
* signal_proxy_function:
* @parent: parent object for new string.
* @prefix: prefix for function name,
* @interface: interface of @signal,
* @signal: signal 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 that acts as a D-Bus connection filter
* function checking that the incoming message matches @signal on @itnerface
* and calls a handler function after demarshalling the arguments.
*
* The prototype of the returned function is returned as a TypeFunc object
* appended to the @prototypes list.
*
* The typedef of the handler function is returned as a TypeFunc object
* appended 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 @signal.
*
* 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 *
signal_proxy_function (const void *parent,
const char *prefix,
Interface * interface,
Signal * signal,
NihList * prototypes,
NihList * typedefs,
NihList * structs)
{
NihList locals;
NihList signal_structs;
nih_local char * name = NULL;
nih_local TypeFunc *func = NULL;
TypeVar * arg;
nih_local char * assert_block = NULL;
nih_local TypeVar * iter_var = NULL;
nih_local TypeVar * parent_var = NULL;
nih_local char * demarshal_block = NULL;
nih_local char * call_block = NULL;
nih_local char * handler_type = NULL;
nih_local char * handler_name = NULL;
nih_local TypeFunc *handler_func = NULL;
NihListEntry * attrib;
nih_local char * vars_block = NULL;
nih_local char * body = NULL;
char * code;
nih_assert (prefix != NULL);
nih_assert (interface != NULL);
nih_assert (signal != NULL);
nih_assert (prototypes != NULL);
nih_assert (typedefs != NULL);
nih_assert (structs != NULL);
nih_list_init (&locals);
nih_list_init (&signal_structs);
/* The function returns a D-Bus handler result, accepting arguments
* for the connection, received message and proxied signal structure.
*/
name = symbol_impl (NULL, prefix, interface->name,
signal->name, "signal");
if (! name)
return NULL;
func = type_func_new (NULL, "DBusHandlerResult", name);
if (! func)
return NULL;
arg = type_var_new (func, "DBusConnection *", "connection");
if (! arg)
return NULL;
nih_list_add (&func->args, &arg->entry);
if (! nih_strcat (&assert_block, NULL,
"nih_assert (connection != NULL);\n"))
return NULL;
arg = type_var_new (func, "DBusMessage *", "signal");
if (! arg)
return NULL;
nih_list_add (&func->args, &arg->entry);
if (! nih_strcat (&assert_block, NULL,
"nih_assert (signal != NULL);\n"))
return NULL;
arg = type_var_new (func, "NihDBusProxySignal *", "proxied");
if (! arg)
return NULL;
nih_list_add (&func->args, &arg->entry);
if (! nih_strcat (&assert_block, NULL,
"nih_assert (proxied != NULL);\n"))
return NULL;
if (! nih_strcat (&assert_block, NULL,
"nih_assert (connection == proxied->proxy->connection);\n"))
return NULL;
/* The function requires a message context to act as the parent of
* arguments and is passed to the handler function; we also need
* an iterator for it.
*/
iter_var = type_var_new (NULL, "DBusMessageIter", "iter");
if (! iter_var)
return NULL;
nih_list_add (&locals, &iter_var->entry);
parent_var = type_var_new (NULL, "NihDBusMessage *", "message");
if (! parent_var)
return NULL;
nih_list_add (&locals, &parent_var->entry);
/* Begin the demarshalling block by checking that the message is
* the signal we're looking for, and then if it is, allocating a
* message context and iterating the arguments.
*/
if (! nih_strcat_sprintf (&demarshal_block, NULL,
"if (! dbus_message_is_signal (signal, proxied->interface->name, proxied->signal->name))\n"
"\treturn DBUS_HANDLER_RESULT_NOT_YET_HANDLED;\n"
"\n"
"if (! dbus_message_has_path (signal, proxied->proxy->path))\n"
"\treturn DBUS_HANDLER_RESULT_NOT_YET_HANDLED;\n"
"\n"
"if (proxied->proxy->name)\n"
"\tif (! dbus_message_has_sender (signal, proxied->proxy->owner))\n"
"\t\treturn DBUS_HANDLER_RESULT_NOT_YET_HANDLED;\n"
"\n"
"message = nih_dbus_message_new (NULL, connection, signal);\n"
"if (! message)\n"
"\treturn DBUS_HANDLER_RESULT_NEED_MEMORY;\n"
"\n"
"/* Iterate the arguments to the signal and demarshal into arguments\n"
" * for our own function call.\n"
" */\n"
"dbus_message_iter_init (message->message, &iter);\n"
"\n"))
return NULL;
/* Begin the handler calling block, which is not permitted to
* reply. Build up the typedef for it, which we mostly use for
* type passing reasons.
*/
handler_type = symbol_typedef (NULL, prefix, interface->symbol, NULL,
signal->symbol, "Handler");
if (! handler_type)
return NULL;
if (! nih_strcat_sprintf (&call_block, NULL,
"/* Call the handler function */\n"
"nih_error_push_context ();\n"
"((%s)proxied->handler) (proxied->data, message",
handler_type))
return NULL;
handler_name = nih_sprintf (NULL, "(*%s)", handler_type);
if (! handler_name)
return NULL;
handler_func = type_func_new (NULL, "typedef void", handler_name);
if (! handler_func)
return NULL;
if (signal->deprecated) {
attrib = nih_list_entry_new (handler_func);
if (! attrib)
return NULL;
attrib->str = nih_strdup (attrib, "deprecated");
if (! attrib->str)
return NULL;
nih_list_add (&handler_func->attribs, &attrib->entry);
}
arg = type_var_new (handler_func, "void *", "data");
if (! arg)
return NULL;
nih_list_add (&handler_func->args, &arg->entry);
arg = type_var_new (handler_func, "NihDBusMessage *", "message");
if (! arg)
return NULL;
nih_list_add (&handler_func->args, &arg->entry);
/* Iterate over the signal arguments, for each output argument
* we append the code to the the demarshalling code. At the
* same time, we build up the handler call itself and transfer
* the actual arguments to the locals list.
*/
NIH_LIST_FOREACH (&signal->arguments, iter) {
Argument * argument = (Argument *)iter;
NihList arg_vars;
NihList arg_locals;
NihList arg_structs;
DBusSignatureIter iter;
nih_local char * local_name = NULL;
nih_local char * oom_error_code = NULL;
nih_local char * type_error_code = NULL;
nih_local char * block = NULL;
if (argument->direction != NIH_DBUS_ARG_OUT)
continue;
nih_list_init (&arg_vars);
nih_list_init (&arg_locals);
nih_list_init (&arg_structs);
dbus_signature_iter_init (&iter, argument->type);
/* In case of out of memory, we just return and D-Bus will
* call us again. In case of type error, just ignore the
* signal entirely.
*/
oom_error_code = nih_sprintf (NULL,
"nih_free (message);\n"
"return DBUS_HANDLER_RESULT_NEED_MEMORY;\n");
if (! oom_error_code)
return NULL;
type_error_code = nih_strdup (NULL,
"nih_free (message);\n"
"return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;\n");
if (! type_error_code)
return NULL;
block = demarshal (NULL, &iter, "message", "iter",
argument->symbol,
oom_error_code,
type_error_code,
&arg_vars, &arg_locals,
prefix, interface->symbol,
signal->symbol, argument->symbol,
&arg_structs);
if (! block)
return NULL;
if (! nih_strcat (&block, NULL, "\n"))
return NULL;
NIH_LIST_FOREACH_SAFE (&arg_vars, iter) {
TypeVar *var = (TypeVar *)iter;
TypeVar *arg;
if (! nih_strcat_sprintf (&call_block, NULL,
", %s",
var->name))
return NULL;
nih_list_add (&locals, &var->entry);
nih_ref (var, call_block);
/* Handler argument is const */
arg = type_var_new (handler_func,
var->type, var->name);
if (! arg)
return NULL;
if (! type_to_const (&arg->type, arg))
return NULL;
nih_list_add (&handler_func->args,
&arg->entry);
}
NIH_LIST_FOREACH_SAFE (&arg_locals, iter) {
TypeVar *var = (TypeVar *)iter;
nih_list_add (&locals, &var->entry);
nih_ref (var, demarshal_block);
}
NIH_LIST_FOREACH_SAFE (&arg_structs, iter) {
TypeStruct *structure = (TypeStruct *)iter;
nih_list_add (&signal_structs, &structure->entry);
nih_ref (structure, demarshal_block);
}
if (! nih_strcat (&demarshal_block, NULL, block))
return NULL;
}
/* Complete the demarshalling block, checking for any unexpected
* arguments which we also want to error on.
*/
if (! nih_strcat_sprintf (&demarshal_block, NULL,
"if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) {\n"
"\tnih_free (message);\n"
"\treturn DBUS_HANDLER_RESULT_NOT_YET_HANDLED;\n"
"}\n"
"\n"))
return NULL;
/* Complete the call block */
if (! nih_strcat (&call_block, NULL, ");\n"
"nih_error_pop_context ();\n"
"nih_free (message);\n"
"\n"))
return NULL;
/* Lay out the function body, indenting it all before placing it
* in the function code.
*/
vars_block = type_var_layout (NULL, &locals);
if (! vars_block)
return NULL;
if (! nih_strcat_sprintf (&body, NULL,
"%s"
"\n"
"%s"
"\n"
"%s"
"%s"
"return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;\n",
vars_block,
assert_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 lists */
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 (&signal_structs, iter) {
TypeStruct *structure = (TypeStruct *)iter;
nih_ref (structure, code);
nih_list_add (structs, &structure->entry);
}
return code;
}
/**
* signal_args_array:
* @parent: parent object for new string,
* @prefix: prefix for array name,
* @interface: interface of @signal,
* @signal: signal to generate array for,
* @prototypes: list to append prototype to.
*
* Generates C code to declare an array of NihDBusArg variables containing
* information about the arguments of the signal @signal on @interface.
*
* 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 *
signal_args_array (const void *parent,
const char *prefix,
Interface * interface,
Signal * signal,
NihList * prototypes)
{
nih_local char *name = NULL;
size_t max_name = 0;
size_t max_type = 0;
nih_local char *block = NULL;
char * code;
TypeVar * var;
nih_assert (prefix != NULL);
nih_assert (interface != NULL);
nih_assert (signal != NULL);
nih_assert (prototypes != NULL);
name = symbol_impl (NULL, prefix, interface->name,
signal->name, "signal_args");
if (! name)
return NULL;
/* Figure out the longest argument name and signature */
NIH_LIST_FOREACH (&signal->arguments, iter) {
Argument *argument = (Argument *)iter;
if (argument->name) {
if (strlen (argument->name) > max_name)
max_name = strlen (argument->name);
} else {
if (max_name < 4)
max_name = 4;
}
if (strlen (argument->type) > max_type)
max_type = strlen (argument->type);
}
/* Append each argument such that the names, types and directions
* are all lined up with each other.
*/
NIH_LIST_FOREACH (&signal->arguments, iter) {
Argument * argument = (Argument *)iter;
nih_local char *line = NULL;
char * dest;
line = nih_alloc (NULL, max_name + max_type + 31);
if (! line)
return NULL;
dest = line;
memcpy (dest, "{ ", 2);
dest += 2;
if (argument->name) {
memcpy (dest, "\"", 1);
dest += 1;
memcpy (dest, argument->name, strlen (argument->name));
dest += strlen (argument->name);
memcpy (dest, "\", ", 3);
dest += 3;
memset (dest, ' ', max_name - strlen (argument->name));
dest += max_name - strlen (argument->name);
} else {
memcpy (dest, "NULL, ", 6);
dest += 6;
memset (dest, ' ', max_name - 2);
dest += max_name - 2;
}
memcpy (dest, "\"", 1);
dest += 1;
memcpy (dest, argument->type, strlen (argument->type));
dest += strlen (argument->type);
memcpy (dest, "\", ", 3);
dest += 3;
memset (dest, ' ', max_type - strlen (argument->type));
dest += max_type - strlen (argument->type);
memcpy (dest, "NIH_DBUS_ARG_OUT", 16);
dest += 16;
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 NihDBusArg %s[] = {\n"
"%s"
"};\n",
name,
block);
if (! code)
return NULL;
/* Append the prototype to the list */
var = type_var_new (code, "const NihDBusArg", name);
if (! var) {
nih_free (code);
return NULL;
}
var->array = TRUE;
nih_list_add (prototypes, &var->entry);
return code;
}