| /* nih-dbus-tool |
| * |
| * demarshal.c - type demarshalling |
| * |
| * 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 <dbus/dbus.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 "indent.h" |
| #include "symbol.h" |
| #include "type.h" |
| #include "demarshal.h" |
| |
| |
| /* Prototypes for static functions */ |
| static char *demarshal_basic (const void *parent, |
| DBusSignatureIter *iter, |
| const char *parent_name, |
| const char *iter_name, const char *name, |
| const char *oom_error_code, |
| const char *type_error_code, |
| NihList *outputs, NihList *locals, |
| const char *prefix, const char *interface_symbol, |
| const char *member_symbol, const char *symbol, |
| NihList *structs) |
| __attribute__ ((warn_unused_result, malloc)); |
| static char *demarshal_array (const void *parent, |
| DBusSignatureIter *iter, |
| const char *parent_name, |
| const char *iter_name, const char *name, |
| const char *oom_error_code, |
| const char *type_error_code, |
| NihList *outputs, NihList *locals, |
| const char *prefix, const char *interface_symbol, |
| const char *member_symbol, const char *symbol, |
| NihList *structs) |
| __attribute__ ((warn_unused_result, malloc)); |
| static char *demarshal_struct (const void *parent, |
| DBusSignatureIter *iter, |
| const char *parent_name, |
| const char *iter_name, const char *name, |
| const char *oom_error_code, |
| const char *type_error_code, |
| NihList *outputs, NihList *locals, |
| const char *prefix, const char *interface_symbol, |
| const char *member_symbol, const char *symbol, |
| NihList *structs) |
| __attribute__ ((warn_unused_result, malloc)); |
| |
| |
| /** |
| * demarshal: |
| * @parent: parent object for new string, |
| * @signature: signature of type, |
| * @parent_name: name of parent variable, |
| * @iter_name: name of iterator variable, |
| * @name: name of variable, |
| * @oom_error_code: code to execute on OOM Error, |
| * @type_error_code: code to exectute on type error, |
| * @outputs: list to append output variables to, |
| * @locals: list to append local variables to, |
| * @interface_symbol: symbol of interface for structure names, |
| * @member_symbol: symbol of interface member for structure names, |
| * @symbol: symbol of argument or variable for structure names, |
| * @structs: list to append structure definitions to. |
| * |
| * Generates C code to demarshal any D-Bus type from the D-Bus iterator |
| * variable named @iter_name into an appropriately typed variable named |
| * @name. |
| * |
| * The type should be the current element of the signature iterator @iter. |
| * This then simply calls demarshal_fixed(), demarshal_string(), |
| * demarshal_fixed_array(), demarshal_flexible_array() or demarshal_struct() |
| * as appropriate. |
| * |
| * The generated code detects out-of-memory conditions but does not know |
| * how to handle them, therefore you need to pass the appropriate handling |
| * code in @oom_error_code. This code will be inserted wherever an OOM |
| * condition is detected. |
| * |
| * Likewise the code detects an invalid type in the iterator, but requires |
| * that you pass the appropriate handling code in @type_error_code. |
| * |
| * The expected output variable types and names are given as TypeVar objects |
| * appended to the @outputs list, each name is guaranteed to begin with @name |
| * and the first member will always be @name itself. Should the C code |
| * require local variables, similar TypeVar objects will be appended to |
| * the @locals list. |
| * |
| * If the variable requires a structure to be defined, the definition is |
| * returned as a TypeStruct object appended to the @structs list. The name |
| * is generated from @prefix, @interface_symbol, @member_symbol and @symbol. |
| * |
| * 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 returned string will also be |
| * freed. |
| * |
| * Demarshalling may require that memory is allocated, the parent object |
| * is the variable named in @parent_name which may of course be "NULL". |
| * |
| * Returns: newly allocated string or NULL if insufficient memory. |
| **/ |
| char * |
| demarshal (const void * parent, |
| DBusSignatureIter *iter, |
| const char * parent_name, |
| const char * iter_name, |
| const char * name, |
| const char * oom_error_code, |
| const char * type_error_code, |
| NihList * outputs, |
| NihList * locals, |
| const char * prefix, |
| const char * interface_symbol, |
| const char * member_symbol, |
| const char * symbol, |
| NihList * structs) |
| { |
| int dbus_type; |
| |
| nih_assert (iter != NULL); |
| nih_assert (parent_name != NULL); |
| nih_assert (iter_name != NULL); |
| nih_assert (name != NULL); |
| nih_assert (oom_error_code != NULL); |
| nih_assert (type_error_code != NULL); |
| nih_assert (outputs != NULL); |
| nih_assert (locals != NULL); |
| nih_assert (prefix != NULL); |
| nih_assert (member_symbol != NULL); |
| nih_assert (structs != NULL); |
| |
| dbus_type = dbus_signature_iter_get_current_type (iter); |
| |
| if (dbus_type_is_basic (dbus_type)) { |
| return demarshal_basic (parent, iter, |
| parent_name, |
| iter_name, name, |
| oom_error_code, type_error_code, |
| outputs, locals, |
| prefix, interface_symbol, |
| member_symbol, symbol, |
| structs); |
| } else if (dbus_type == DBUS_TYPE_ARRAY) { |
| return demarshal_array (parent, iter, |
| parent_name, |
| iter_name, name, |
| oom_error_code, |
| type_error_code, |
| outputs, locals, |
| prefix, interface_symbol, |
| member_symbol, symbol, |
| structs); |
| } else if ((dbus_type == DBUS_TYPE_STRUCT) |
| || (dbus_type == DBUS_TYPE_DICT_ENTRY)) { |
| return demarshal_struct (parent, iter, |
| parent_name, |
| iter_name, name, |
| oom_error_code, type_error_code, |
| outputs, locals, |
| prefix, interface_symbol, |
| member_symbol, symbol, |
| structs); |
| } else { |
| nih_assert_not_reached (); |
| } |
| } |
| |
| |
| /** |
| * demarshal_basic: |
| * @parent: parent object for new string, |
| * @iter: D-Bus signature iterator, |
| * @parent_name: name of parent variable, |
| * @name: name of variable, |
| * @oom_error_code: code to execute on OOM Error, |
| * @type_error_code: code to exectute on type error, |
| * @outputs: list to append output variables to, |
| * @locals: list to append local variables to, |
| * @interface_symbol: symbol of interface for structure names, |
| * @member_symbol: symbol of interface member for structure names, |
| * @symbol: symbol of argument or variable for structure names, |
| * @structs: list to append structure definitions to. |
| * |
| * Generates C code to demarshal a D-Bus basic type (ie. numerics and |
| * strings) from the D-Bus iterator variable named @iter_name into an |
| * appropriately typed variable pointer named @name. |
| * |
| * The type should be the current element of the signature iterator @iter. |
| * |
| * The generated code detects out-of-memory conditions but does not know |
| * how to handle them, therefore you need to pass the appropriate handling |
| * code in @oom_error_code. This code will be inserted wherever an OOM |
| * condition is detected. |
| * |
| * Likewise the code detects an invalid type in the iterator, but requires |
| * that you pass the appropriate handling code in @type_error_code. |
| * |
| * The expected output variable types and names are given as TypeVar objects |
| * appended to the @outputs list, each name is guaranteed to begin with @name |
| * and the first member will always be @name itself. Should the C code |
| * require local variables, similar TypeVar objects will be appended to |
| * the @locals list. |
| * |
| * If the variable requires a structure to be defined, the definition is |
| * returned as a TypeStruct object appended to the @structs list. The name |
| * is generated from @prefix, @interface_symbol, @member_symbol and @symbol. |
| * |
| * 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 returned string will also be |
| * freed. |
| * |
| * Demarshalling may require that memory is allocated, the parent object |
| * is the variable named in @parent_name which may of course be "NULL". |
| * |
| * Returns: newly allocated string or NULL if insufficient memory. |
| **/ |
| static char * |
| demarshal_basic (const void * parent, |
| DBusSignatureIter *iter, |
| const char * parent_name, |
| const char * iter_name, |
| const char * name, |
| const char * oom_error_code, |
| const char * type_error_code, |
| NihList * outputs, |
| NihList * locals, |
| const char * prefix, |
| const char * interface_symbol, |
| const char * member_symbol, |
| const char * symbol, |
| NihList * structs) |
| { |
| int dbus_type; |
| const char * dbus_const; |
| nih_local char *oom_error_block = NULL; |
| nih_local char *type_error_block = NULL; |
| nih_local char *c_type = NULL; |
| char * code = NULL; |
| TypeVar * var; |
| |
| nih_assert (iter != NULL); |
| nih_assert (parent_name != NULL); |
| nih_assert (iter_name != NULL); |
| nih_assert (name != NULL); |
| nih_assert (oom_error_code != NULL); |
| nih_assert (type_error_code != NULL); |
| nih_assert (outputs != NULL); |
| nih_assert (locals != NULL); |
| nih_assert (prefix != NULL); |
| nih_assert (member_symbol != NULL); |
| nih_assert (structs != NULL); |
| |
| dbus_type = dbus_signature_iter_get_current_type (iter); |
| dbus_const = type_const (dbus_type); |
| |
| oom_error_block = nih_strdup (NULL, oom_error_code); |
| if (! oom_error_block) |
| return NULL; |
| if (! indent (&oom_error_block, NULL, 1)) |
| return NULL; |
| |
| type_error_block = nih_strdup (NULL, type_error_code); |
| if (! type_error_block) |
| return NULL; |
| if (! indent (&type_error_block, NULL, 1)) |
| return NULL; |
| |
| c_type = type_of (NULL, iter); |
| if (! c_type) |
| return NULL; |
| |
| if (! nih_strcat_sprintf (&code, parent, |
| "/* Demarshal a %s from the message */\n" |
| "if (dbus_message_iter_get_arg_type (&%s) != %s) {\n" |
| "%s" |
| "}\n" |
| "\n", |
| c_type, |
| iter_name, dbus_const, |
| type_error_block)) |
| return NULL; |
| |
| if (dbus_type_is_fixed (dbus_type)) { |
| if (! nih_strcat_sprintf (&code, parent, |
| "dbus_message_iter_get_basic (&%s, &%s);\n" |
| "\n", |
| iter_name, name)) { |
| nih_free (code); |
| return NULL; |
| } |
| } else { |
| nih_local char *local_name = NULL; |
| nih_local char *local_type = NULL; |
| |
| /* We need a local variable to store the const value we get |
| * from D-Bus before we allocate the copy that we return. |
| */ |
| local_name = nih_sprintf (NULL, "%s_dbus", name); |
| if (! local_name) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| local_type = nih_strdup (NULL, c_type); |
| if (! local_type) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| if (! type_to_const (&local_type, NULL)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| if (! nih_strcat_sprintf (&code, parent, |
| "dbus_message_iter_get_basic (&%s, &%s);\n" |
| "\n" |
| "%s = nih_strdup (%s, %s);\n" |
| "if (! %s) {\n" |
| "%s" |
| "}\n" |
| "\n", |
| iter_name, local_name, |
| name, parent_name, local_name, |
| name, |
| oom_error_block)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| var = type_var_new (code, local_type, local_name); |
| if (! var) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| nih_list_add (locals, &var->entry); |
| } |
| |
| if (! nih_strcat_sprintf (&code, parent, |
| "dbus_message_iter_next (&%s);\n", |
| iter_name)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| /* Append our required output variable */ |
| var = type_var_new (code, c_type, name); |
| if (! var) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| nih_list_add (outputs, &var->entry); |
| |
| return code; |
| } |
| |
| |
| /** |
| * demarshal_array: |
| * @parent: parent object for new string, |
| * @iter: D-Bus signature iterator, |
| * @parent_name: name of parent variable, |
| * @name: name of variable, |
| * @oom_error_code: code to execute on OOM Error, |
| * @type_error_code: code to exectute on type error, |
| * @outputs: list to append output variables to, |
| * @locals: list to append local variables to, |
| * @interface_symbol: symbol of interface for structure names, |
| * @member_symbol: symbol of interface member for structure names, |
| * @symbol: symbol of argument or variable for structure names, |
| * @structs: list to append structure definitions to. |
| * |
| * Generates C code to demarshal a D-Bus array type from the D-Bus |
| * iterator variable named @iter_name into an appropriately typed, |
| * NULL-terminated, array variable pointer named @name. In the case |
| * of arrays (of any number of levels) ultimately to a fixed type, an |
| * additional input named "@name"_len is required of size_t type or an |
| * appropriate number of pointers to it. |
| * |
| * The type should be the current element of the signature iterator @iter. |
| * |
| * The generated code detects out-of-memory conditions but does not know |
| * how to handle them, therefore you need to pass the appropriate handling |
| * code in @oom_error_code. This code will be inserted wherever an OOM |
| * condition is detected. |
| * |
| * Likewise the code detects an invalid type in the iterator, but requires |
| * that you pass the appropriate handling code in @type_error_code. |
| * |
| * The expected output variable types and names are given as TypeVar objects |
| * appended to the @outputs list, each name is guaranteed to begin with @name |
| * and the first member will always be @name itself. Should the C code |
| * require local variables, similar TypeVar objects will be appended to |
| * the @locals list. |
| * |
| * If the variable requires a structure to be defined, the definition is |
| * returned as a TypeStruct object appended to the @structs list. The name |
| * is generated from @prefix, @interface_symbol, @member_symbol and @symbol. |
| * |
| * 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 returned string will also be |
| * freed. |
| * |
| * Demarshalling may require that memory is allocated, the parent object |
| * is the variable named in @parent_name which may of course be "NULL". |
| * |
| * Returns: newly allocated string or NULL if insufficient memory. |
| **/ |
| static char * |
| demarshal_array (const void * parent, |
| DBusSignatureIter *iter, |
| const char * parent_name, |
| const char * iter_name, |
| const char * name, |
| const char * oom_error_code, |
| const char * type_error_code, |
| NihList * outputs, |
| NihList * locals, |
| const char * prefix, |
| const char * interface_symbol, |
| const char * member_symbol, |
| const char * symbol, |
| NihList * structs) |
| { |
| nih_local char * array_iter_name = NULL; |
| nih_local char * element_name = NULL; |
| nih_local char * element_symbol = NULL; |
| nih_local char * size_name = NULL; |
| nih_local char * oom_error_block = NULL; |
| nih_local char * child_oom_error_code = NULL; |
| nih_local char * child_oom_error_block = NULL; |
| nih_local char * type_error_block = NULL; |
| nih_local char * child_type_error_code = NULL; |
| nih_local char * child_type_error_block = NULL; |
| DBusSignatureIter subiter; |
| int element_type; |
| char * code = NULL; |
| TypeVar * array_iter_var; |
| TypeVar * size_var; |
| NihList element_outputs; |
| NihList element_locals; |
| NihList element_structs; |
| nih_local char * element_block = NULL; |
| nih_local char * alloc_block = NULL; |
| nih_local char * block = NULL; |
| nih_local char * vars_block = NULL; |
| |
| nih_assert (iter != NULL); |
| nih_assert (parent_name != NULL); |
| nih_assert (iter_name != NULL); |
| nih_assert (name != NULL); |
| nih_assert (oom_error_code != NULL); |
| nih_assert (outputs != NULL); |
| nih_assert (locals != NULL); |
| nih_assert (prefix != NULL); |
| nih_assert (member_symbol != NULL); |
| nih_assert (structs != NULL); |
| |
| dbus_signature_iter_recurse (iter, &subiter); |
| element_type = dbus_signature_iter_get_current_type (&subiter); |
| |
| array_iter_name = nih_sprintf (NULL, "%s_iter", name); |
| if (! array_iter_name) |
| return NULL; |
| |
| element_name = nih_sprintf (NULL, "%s_element", name); |
| if (! element_name) |
| return NULL; |
| |
| element_symbol = (symbol |
| ? nih_sprintf (NULL, "%s_element", symbol) |
| : nih_strdup (NULL, "element")); |
| if (! element_symbol) |
| return NULL; |
| |
| if (dbus_type_is_fixed (element_type)) { |
| size_name = nih_sprintf (NULL, "%s_len", name); |
| if (! size_name) |
| return NULL; |
| } else { |
| size_name = nih_sprintf (NULL, "%s_size", name); |
| if (! size_name) |
| return NULL; |
| } |
| |
| oom_error_block = nih_strdup (NULL, oom_error_code); |
| if (! oom_error_block) |
| return NULL; |
| if (! indent (&oom_error_block, NULL, 1)) |
| return NULL; |
| |
| child_oom_error_code = nih_sprintf (NULL, ("if (%s)\n" |
| "\tnih_free (%s);\n" |
| "%s"), |
| name, name, oom_error_code); |
| if (! child_oom_error_code) |
| return NULL; |
| |
| child_oom_error_block = nih_strdup (NULL, child_oom_error_code); |
| if (! child_oom_error_block) |
| return NULL; |
| if (! indent (&child_oom_error_block, NULL, 1)) |
| return NULL; |
| |
| type_error_block = nih_strdup (NULL, type_error_code); |
| if (! type_error_block) |
| return NULL; |
| if (! indent (&type_error_block, NULL, 1)) |
| return NULL; |
| |
| child_type_error_code = nih_sprintf (NULL, ("if (%s)\n" |
| "\tnih_free (%s);\n" |
| "%s"), |
| name, name, type_error_code); |
| if (! child_type_error_code) |
| return NULL; |
| |
| child_type_error_block = nih_strdup (NULL, child_type_error_code); |
| if (! child_type_error_block) |
| return NULL; |
| if (! indent (&child_type_error_block, NULL, 1)) |
| return NULL; |
| |
| /* Recurse into the array */ |
| if (! nih_strcat_sprintf (&code, parent, |
| "/* Demarshal an array from the message */\n" |
| "if (dbus_message_iter_get_arg_type (&%s) != DBUS_TYPE_ARRAY) {\n" |
| "%s" |
| "}\n" |
| "\n" |
| "dbus_message_iter_recurse (&%s, &%s);\n" |
| "\n", |
| iter_name, |
| type_error_block, |
| iter_name, array_iter_name)) |
| return NULL; |
| |
| array_iter_var = type_var_new (code, "DBusMessageIter", |
| array_iter_name); |
| if (! array_iter_var) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| nih_list_add (locals, &array_iter_var->entry); |
| |
| /* We need a variable to keep track of the array sizes for |
| * allocation; for fixed types, this is an output but for non-fixed |
| * types it's a local. |
| */ |
| size_var = type_var_new (code, "size_t", size_name); |
| if (! size_var) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| if (! dbus_type_is_fixed (element_type)) |
| nih_list_add (locals, &size_var->entry); |
| |
| |
| if (! nih_strcat_sprintf (&code, parent, |
| "%s = 0;\n", |
| size_name)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| /* Get the code that will demarshal the individual elements, and |
| * any output and local variables it needs. |
| */ |
| nih_list_init (&element_outputs); |
| nih_list_init (&element_locals); |
| nih_list_init (&element_structs); |
| element_block = demarshal (NULL, &subiter, |
| name, array_iter_name, element_name, |
| child_oom_error_code, |
| child_type_error_code, |
| &element_outputs, &element_locals, |
| prefix, interface_symbol, |
| member_symbol, element_symbol, |
| &element_structs); |
| if (! element_block) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| /* Each of the outputs of the demarshalling code equates to one |
| * of our own outputs, except that we add another level of pointers |
| * for the array; at the same time, we keep the suffix and append |
| * it to our own name. |
| * |
| * Since the outputs are all arrays, they need to be initialised |
| * or allocated before demarshalling begins. Those of fixed types |
| * simply need to be set to NULL, those of pointer types need |
| * to be allocated with a single element containing the terminating |
| * NULL pointer. |
| * |
| * Instead of mucking around with pointers and structure members, |
| * we also append the outputs onto the local lists and fill in our |
| * variable from this. |
| */ |
| NIH_LIST_FOREACH_SAFE (&element_outputs, iter) { |
| TypeVar * output_var = (TypeVar *)iter; |
| char * ptr; |
| nih_local char *var_type = NULL; |
| const char * suffix; |
| nih_local char *var_name = NULL; |
| TypeVar * var; |
| nih_local char *tmp_name = NULL; |
| const char * var_parent; |
| |
| /* Output variable */ |
| var_type = nih_strdup (NULL, output_var->type); |
| if (! var_type) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| if (! type_to_pointer (&var_type, NULL)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| nih_assert (! strncmp (output_var->name, element_name, |
| strlen (element_name))); |
| suffix = output_var->name + strlen (element_name); |
| |
| var_name = nih_sprintf (NULL, "%s%s", name, suffix); |
| if (! var_name) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| var = type_var_new (code, var_type, var_name); |
| if (! var) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| nih_list_add (outputs, &var->entry); |
| |
| /* For array reallocation we need yet another variable to |
| * capture the output and make sure it didn't fail, we could |
| * just cheat and use void * but instead we use the proper |
| * type which is the same as the output variable. |
| */ |
| tmp_name = nih_sprintf (NULL, "%s_tmp", var_name); |
| if (! tmp_name) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| var = type_var_new (element_block, var_type, tmp_name); |
| if (! var) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| nih_list_add (&element_locals, &var->entry); |
| |
| /* Code to allocate and reallocate */ |
| var_parent = (*suffix ? name : parent_name); |
| |
| if (! nih_strcat_sprintf (&code, parent, |
| "%s = NULL;\n", |
| var_name)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| ptr = output_var->type + strlen (output_var->type) - 1; |
| if (*ptr != '*') { |
| if (! nih_strcat_sprintf (&block, NULL, |
| "if (%s + 1 > SIZE_MAX / sizeof (%s)) {\n" |
| "%s" |
| "}\n" |
| "\n" |
| "%s = nih_realloc (%s, %s, sizeof (%s) * (%s + 1));\n" |
| "if (! %s) {\n" |
| "%s" |
| "}\n" |
| "\n" |
| "%s = %s;\n" |
| "%s[%s] = %s;\n" |
| "\n", |
| size_name, output_var->type, |
| child_type_error_block, |
| tmp_name, var_name, var_parent, output_var->type, size_name, |
| tmp_name, |
| child_oom_error_block, |
| var_name, tmp_name, |
| var_name, size_name, output_var->name)) { |
| nih_free (code); |
| return NULL; |
| } |
| } else { |
| if (! nih_strcat_sprintf (&alloc_block, NULL, |
| "%s = nih_alloc (%s, sizeof (%s));\n" |
| "if (! %s) {\n" |
| "%s" |
| "}\n" |
| "\n" |
| "%s[%s] = NULL;\n" |
| "\n", |
| var_name, var_parent, output_var->type, |
| var_name, |
| (*suffix ? child_oom_error_block : oom_error_block), |
| var_name, size_name)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| if (! nih_strcat_sprintf (&block, NULL, |
| "if (%s + 2 > SIZE_MAX / sizeof (%s)) {\n" |
| "%s" |
| "}\n" |
| "\n" |
| "%s = nih_realloc (%s, %s, sizeof (%s) * (%s + 2));\n" |
| "if (! %s) {\n" |
| "%s" |
| "}\n" |
| "\n" |
| "%s = %s;\n" |
| "%s[%s] = %s;\n" |
| "%s[%s + 1] = NULL;\n" |
| "\n", |
| size_name, output_var->type, |
| child_type_error_block, |
| tmp_name, var_name, var_parent, output_var->type, size_name, |
| tmp_name, |
| child_oom_error_block, |
| var_name, tmp_name, |
| var_name, size_name, output_var->name, |
| var_name, size_name)) { |
| nih_free (code); |
| return NULL; |
| } |
| } |
| |
| nih_list_add (&element_locals, &output_var->entry); |
| } |
| |
| if (! nih_strcat_sprintf (&code, parent, |
| "\n" |
| "%s", |
| alloc_block ?: "")) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| if (! nih_strcat_sprintf (&block, parent, |
| "%s++;\n", |
| size_name)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| vars_block = type_var_layout (NULL, &element_locals); |
| if (! vars_block) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| NIH_LIST_FOREACH_SAFE (&element_structs, iter) { |
| TypeStruct *structure = (TypeStruct *)iter; |
| |
| nih_ref (structure, code); |
| nih_list_add (structs, &structure->entry); |
| } |
| |
| /* Iterate over the incoming message */ |
| if (! nih_strcat_sprintf (&code, parent, |
| "while (dbus_message_iter_get_arg_type (&%s) != DBUS_TYPE_INVALID) {\n", |
| array_iter_name)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| /* Lay all that out in an indented block inside the while loop. |
| * Make sure that we increase the size of the array as we go, |
| * which varies depending on whether we are using a fixed type or |
| * not. |
| */ |
| if (! indent (&vars_block, NULL, 1)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| if (! indent (&element_block, NULL, 1)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| if (! indent (&block, NULL, 1)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| if (! nih_strcat_sprintf (&code, parent, |
| "%s" |
| "\n" |
| "%s" |
| "\n" |
| "%s", |
| vars_block, |
| element_block, |
| block)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| /* Finish off the iteration and move onto the next message item */ |
| if (! nih_strcat_sprintf (&code, parent, |
| "}\n" |
| "\n" |
| "dbus_message_iter_next (&%s);\n", |
| iter_name)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| if (dbus_type_is_fixed (element_type)) |
| nih_list_add (outputs, &size_var->entry); |
| |
| return code; |
| } |
| |
| |
| /** |
| * demarshal_struct: |
| * @parent: parent object for new string, |
| * @iter: D-Bus signature iterator, |
| * @parent_name: name of parent variable, |
| * @name: name of variable, |
| * @oom_error_code: code to execute on OOM Error, |
| * @type_error_code: code to exectute on type error, |
| * @outputs: list to append output variables to, |
| * @locals: list to append local variables to, |
| * @interface_symbol: symbol of interface for structure names, |
| * @member_symbol: symbol of interface member for structure names, |
| * @symbol: symbol of argument or variable for structure names, |
| * @structs: list to append structure definitions to. |
| * |
| * Generates C code to demarshal a D-Bus structure type, and its members, |
| * from the D-Bus iterator variable named @iter_name into an appropriately |
| * typed variable pointer named @name. |
| * |
| * The type should be the current element of the signature iterator @iter. |
| * |
| * The generated code detects out-of-memory conditions but does not know |
| * how to handle them, therefore you need to pass the appropriate handling |
| * code in @oom_error_code. This code will be inserted wherever an OOM |
| * condition is detected. |
| * |
| * Likewise the code detects an invalid type in the iterator, but requires |
| * that you pass the appropriate handling code in @type_error_code. |
| * |
| * The expected output variable types and names are given as TypeVar objects |
| * appended to the @outputs list, each name is guaranteed to begin with @name |
| * and the first member will always be @name itself. Should the C code |
| * require local variables, similar TypeVar objects will be appended to |
| * the @locals list. |
| * |
| * If the variable requires a structure to be defined, the definition is |
| * returned as a TypeStruct object appended to the @structs list. The name |
| * is generated from @prefix, @interface_symbol, @member_symbol and @symbol. |
| * |
| * 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 returned string will also be |
| * freed. |
| * |
| * Demarshalling may require that memory is allocated, the parent object |
| * is the variable named in @parent_name which may of course be "NULL". |
| * |
| * Returns: newly allocated string or NULL if insufficient memory. |
| **/ |
| static char * |
| demarshal_struct (const void * parent, |
| DBusSignatureIter *iter, |
| const char * parent_name, |
| const char * iter_name, |
| const char * name, |
| const char * oom_error_code, |
| const char * type_error_code, |
| NihList * outputs, |
| NihList * locals, |
| const char * prefix, |
| const char * interface_symbol, |
| const char * member_symbol, |
| const char * symbol, |
| NihList * structs) |
| { |
| nih_local char * struct_iter_name = NULL; |
| nih_local char * oom_error_block = NULL; |
| nih_local char * child_oom_error_code = NULL; |
| nih_local char * child_oom_error_block = NULL; |
| nih_local char * type_error_block = NULL; |
| nih_local char * child_type_error_code = NULL; |
| nih_local char * child_type_error_block = NULL; |
| int dbus_type; |
| const char * dbus_const; |
| DBusSignatureIter subiter; |
| char * code = NULL; |
| TypeVar * struct_iter_var; |
| nih_local char * c_type = NULL; |
| nih_local char * alloc_type = NULL; |
| TypeStruct * structure; |
| size_t count = 0; |
| TypeVar * var; |
| |
| nih_assert (iter != NULL); |
| nih_assert (parent_name != NULL); |
| nih_assert (iter_name != NULL); |
| nih_assert (name != NULL); |
| nih_assert (oom_error_code != NULL); |
| nih_assert (type_error_code != NULL); |
| nih_assert (outputs != NULL); |
| nih_assert (locals != NULL); |
| nih_assert (prefix != NULL); |
| nih_assert (member_symbol != NULL); |
| nih_assert (structs != NULL); |
| |
| struct_iter_name = nih_sprintf (NULL, "%s_iter", name); |
| if (! struct_iter_name) |
| return NULL; |
| |
| oom_error_block = nih_strdup (NULL, oom_error_code); |
| if (! oom_error_block) |
| return NULL; |
| if (! indent (&oom_error_block, NULL, 1)) |
| return NULL; |
| |
| child_oom_error_code = nih_sprintf (NULL, ("nih_free (%s);\n" |
| "%s"), |
| name, oom_error_code); |
| if (! child_oom_error_code) |
| return NULL; |
| |
| child_oom_error_block = nih_strdup (NULL, child_oom_error_code); |
| if (! child_oom_error_block) |
| return NULL; |
| if (! indent (&child_oom_error_block, NULL, 1)) |
| return NULL; |
| |
| type_error_block = nih_strdup (NULL, type_error_code); |
| if (! type_error_block) |
| return NULL; |
| if (! indent (&type_error_block, NULL, 1)) |
| return NULL; |
| |
| child_type_error_code = nih_sprintf (NULL, ("nih_free (%s);\n" |
| "%s"), |
| name, type_error_code); |
| if (! child_type_error_code) |
| return NULL; |
| |
| child_type_error_block = nih_strdup (NULL, child_type_error_code); |
| if (! child_type_error_block) |
| return NULL; |
| if (! indent (&child_type_error_block, NULL, 1)) |
| return NULL; |
| |
| /* Open the struct container, for that we need to know whether this |
| * is a struct or a dictionary entry even through we handled the two |
| * identically. We'll obviously need a local variable for the |
| * recursed iterator. |
| */ |
| dbus_type = dbus_signature_iter_get_current_type (iter); |
| dbus_const = type_const (dbus_type); |
| |
| dbus_signature_iter_recurse (iter, &subiter); |
| |
| if (! nih_strcat_sprintf (&code, parent, |
| "/* Demarshal a structure from the message */\n" |
| "if (dbus_message_iter_get_arg_type (&%s) != %s) {\n" |
| "%s" |
| "}\n" |
| "\n" |
| "dbus_message_iter_recurse (&%s, &%s);\n" |
| "\n", |
| iter_name, dbus_const, |
| type_error_block, |
| iter_name, struct_iter_name)) |
| return NULL; |
| |
| struct_iter_var = type_var_new (code, "DBusMessageIter", |
| struct_iter_name); |
| if (! struct_iter_var) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| nih_list_add (locals, &struct_iter_var->entry); |
| |
| /* FIXME there should be a way to override this to a different type |
| * name by annotation. |
| */ |
| alloc_type = symbol_typedef (NULL, prefix, interface_symbol, NULL, |
| member_symbol, symbol); |
| if (! alloc_type) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| c_type = nih_strdup (NULL, alloc_type); |
| if (! c_type) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| structure = type_struct_new (code, c_type); |
| if (! structure) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| nih_list_add (structs, &structure->entry); |
| |
| if (! type_to_pointer (&c_type, NULL)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| /* Allocate the new structure */ |
| if (! nih_strcat_sprintf (&code, parent, |
| "%s = nih_new (%s, %s);\n" |
| "if (! %s) {\n" |
| "%s" |
| "}\n" |
| "\n", |
| name, parent_name, alloc_type, |
| name, |
| oom_error_block)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| /* Deal with each structure element individually, however we have |
| * to end up with just one set of locals and one block so we |
| * append directly onto our locals. |
| */ |
| do { |
| nih_local char *item_member = NULL; |
| nih_local char *item_name = NULL; |
| nih_local char *item_symbol = NULL; |
| NihList item_outputs; |
| NihList item_locals; |
| NihList item_structs; |
| nih_local char *item_code = NULL; |
| |
| /* FIXME there should be a way to override the item names |
| * via an annotation, which would also show up in the |
| * structure definition itself. |
| */ |
| item_member = nih_sprintf (NULL, "item%zu", count); |
| if (! item_member) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| item_name = nih_sprintf (NULL, "%s_%s", name, item_member); |
| if (! item_name) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| item_symbol = (symbol |
| ? nih_sprintf (NULL, "%s_%s", symbol, item_member) |
| : nih_strdup (NULL, item_member)); |
| if (! item_symbol) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| /* Get the code to do the demarshalling of this item */ |
| nih_list_init (&item_outputs); |
| nih_list_init (&item_locals); |
| nih_list_init (&item_structs); |
| item_code = demarshal (NULL, &subiter, |
| name, struct_iter_name, item_name, |
| child_oom_error_code, |
| child_type_error_code, |
| &item_outputs, &item_locals, |
| prefix, interface_symbol, |
| member_symbol, item_symbol, |
| &item_structs); |
| if (! item_code) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| /* Append the item locals onto our locals list, we have |
| * to reference these as we go. |
| */ |
| NIH_LIST_FOREACH_SAFE (&item_locals, iter) { |
| TypeVar *item_local_var = (TypeVar *)iter; |
| |
| nih_list_add (locals, &item_local_var->entry); |
| nih_ref (item_local_var, code); |
| } |
| |
| /* Append item demarshalling code block */ |
| if (! nih_strcat_sprintf (&code, parent, |
| "%s" |
| "\n", |
| item_code)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| /* Instead of mucking around with pointers and structure |
| * members, each of the marshalling code outputs is appended |
| * onto the local list and we copy from the local into |
| * our output variable. |
| */ |
| NIH_LIST_FOREACH_SAFE (&item_outputs, iter) { |
| TypeVar * output_var = (TypeVar *)iter; |
| char * suffix; |
| nih_local char *member_name = NULL; |
| TypeVar * member_var; |
| |
| nih_assert (! strncmp (output_var->name, item_name, |
| strlen (item_name))); |
| suffix = output_var->name + strlen (item_name); |
| |
| /* Create the structure member entry */ |
| member_name = nih_sprintf (NULL, "%s%s", |
| item_member, suffix); |
| if (! member_name) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| member_var = type_var_new (structure, |
| output_var->type, |
| member_name); |
| if (! member_var) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| nih_list_add (&structure->members, &member_var->entry); |
| |
| /* Add code to copy from local variable */ |
| if (! nih_strcat_sprintf (&code, parent, |
| "%s->%s = %s;\n", |
| name, member_name, |
| output_var->name)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| /* Add to locals */ |
| nih_list_add (locals, &output_var->entry); |
| nih_ref (output_var, code); |
| } |
| |
| NIH_LIST_FOREACH_SAFE (&item_structs, iter) { |
| TypeStruct *structure = (TypeStruct *)iter; |
| |
| nih_ref (structure, code); |
| nih_list_add (structs, &structure->entry); |
| } |
| |
| if (! nih_strcat (&code, parent, "\n")) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| nih_assert (++count > 0); |
| } while (dbus_signature_iter_next (&subiter)); |
| |
| /* Close the container again */ |
| if (! nih_strcat_sprintf (&code, parent, |
| "if (dbus_message_iter_get_arg_type (&%s) != DBUS_TYPE_INVALID) {\n" |
| "%s" |
| "}\n" |
| "\n" |
| "dbus_message_iter_next (&%s);\n", |
| struct_iter_name, |
| child_type_error_block, |
| iter_name)) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| /* Append our required output variable */ |
| var = type_var_new (code, c_type, name); |
| if (! var) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| nih_list_add (outputs, &var->entry); |
| |
| return code; |
| } |