| /* nih-dbus-tool |
| * |
| * interface.c - interface parsing and handling |
| * |
| * Copyright © 2009 Scott James Remnant <scott@netsplit.com>. |
| * Copyright © 2009 Canonical Ltd. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files (the |
| * "Software"), to deal in the Software without restriction, including |
| * without limitation the rights to use, copy, modify, merge, publish, |
| * distribute, sublicense, and/or sell copies of the Software, and to |
| * permit persons to whom the Software is furnished to do so, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR |
| * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
| * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #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 "node.h" |
| #include "interface.h" |
| #include "method.h" |
| #include "parse.h" |
| #include "errors.h" |
| |
| |
| /** |
| * interface_name_valid: |
| * @name: Interface name to verify. |
| * |
| * Verifies whether @name matches the specification for D-Bus interface |
| * names. |
| * |
| * Returns: TRUE if valid, FALSE if not. |
| **/ |
| int |
| interface_name_valid (const char *name) |
| { |
| size_t parts = 1; |
| |
| nih_assert (name != NULL); |
| |
| /* Name must not begin with a '.' */ |
| if (name[0] == '.') |
| return FALSE; |
| |
| /* We can get away with just using strlen() here even through name |
| * is in UTF-8 because all the valid characters are ASCII. |
| */ |
| for (size_t i = 0; i < strlen (name); i++) { |
| /* Name components may be separated by single '.' characters, |
| * multiple ones are not allowed. Keep a count of how many |
| * parts we have, since there's a defined minimum. |
| */ |
| if (name[i] == '.') { |
| if (name[i-1] == '.') |
| return FALSE; |
| |
| parts++; |
| continue; |
| } |
| |
| /* Names may contain digits, but not at the beginning of the |
| * name or any part of it. |
| */ |
| if ((name[i] >= '0') && (name[i] <= '9')) { |
| if (i == 0) |
| return FALSE; |
| if (name[i-1] == '.') |
| return FALSE; |
| |
| continue; |
| } |
| |
| /* Valid characters anywhere are [A-Za-z_] */ |
| if ( ((name[i] < 'A') || (name[i] > 'Z')) |
| && ((name[i] < 'a') || (name[i] > 'z')) |
| && (name[i] != '_')) |
| return FALSE; |
| } |
| |
| /* Name must consist of at least two parts */ |
| if (parts < 2) |
| return FALSE; |
| |
| /* Final character may not be '.' */ |
| if (name[strlen (name) - 1] == '.') |
| return FALSE; |
| |
| /* Name must be no more than 255 characters */ |
| if (strlen (name) > 255) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| |
| /** |
| * interface_new: |
| * @parent: parent object for new interface, |
| * @name: D-Bus name of interface. |
| * |
| * Allocates a new D-Bus object Interface data structure, with the D-Bus |
| * name set to @name. The returned structure is not placed into any list. |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned interface. When all parents |
| * of the returned interface are freed, the returned interface will also be |
| * freed. |
| * |
| * Returns: the new interface or NULL if the allocation failed. |
| **/ |
| Interface * |
| interface_new (const void *parent, |
| const char *name) |
| { |
| Interface *interface; |
| |
| nih_assert (name != NULL); |
| |
| interface = nih_new (parent, Interface); |
| if (! interface) |
| return NULL; |
| |
| nih_list_init (&interface->entry); |
| |
| nih_alloc_set_destructor (interface, nih_list_destroy); |
| |
| interface->name = nih_strdup (interface, name); |
| if (! interface->name) { |
| nih_free (interface); |
| return NULL; |
| } |
| |
| interface->symbol = NULL; |
| interface->deprecated = FALSE; |
| |
| nih_list_init (&interface->methods); |
| nih_list_init (&interface->signals); |
| nih_list_init (&interface->properties); |
| |
| return interface; |
| } |
| |
| |
| /** |
| * interface_start_tag: |
| * @xmlp: XML parser, |
| * @tag: name of XML tag being parsed, |
| * @attr: NULL-terminated array of attribute name and value pairs. |
| * |
| * This function is called by parse_start_tag() for an "interface" |
| * start tag, a child of the "node" tag that defines a D-Bus interface |
| * implemented by that object. |
| * |
| * If the interface does not appear within a node tag a warning is emitted |
| * and the tag will be ignored. |
| * |
| * Interfaces must have a "name" attribute containing the D-Bus name |
| * of the interface. |
| * |
| * Any unknown attributes result in a warning and will be ignored. |
| * |
| * An Interface object will be allocated and pushed onto the stack, this is |
| * not added to the node until the end tag is found. |
| * |
| * Returns: zero on success, negative value on raised error. |
| **/ |
| int |
| interface_start_tag (XML_Parser xmlp, |
| const char * tag, |
| char * const *attr) |
| { |
| ParseContext *context; |
| ParseStack * parent; |
| Interface * interface; |
| char * const *key; |
| char * const *value; |
| const char * name = NULL; |
| |
| nih_assert (xmlp != NULL); |
| nih_assert (tag != NULL); |
| nih_assert (attr != NULL); |
| |
| context = XML_GetUserData (xmlp); |
| nih_assert (context != NULL); |
| |
| /* Interfaces should only appear inside nodes. */ |
| parent = parse_stack_top (&context->stack); |
| if ((! parent) || (parent->type != PARSE_NODE)) { |
| nih_warn ("%s:%zu:%zu: %s", context->filename, |
| (size_t)XML_GetCurrentLineNumber (xmlp), |
| (size_t)XML_GetCurrentColumnNumber (xmlp), |
| _("Ignored unexpected <interface> tag")); |
| |
| if (! parse_stack_push (NULL, &context->stack, |
| PARSE_IGNORED, NULL)) |
| nih_return_system_error (-1); |
| |
| return 0; |
| } |
| |
| /* Retrieve the name from the attributes */ |
| for (key = attr; key && *key; key += 2) { |
| value = key + 1; |
| nih_assert (value && *value); |
| |
| if (! strcmp (*key, "name")) { |
| name = *value; |
| } else { |
| nih_warn ("%s:%zu:%zu: %s: %s", context->filename, |
| (size_t)XML_GetCurrentLineNumber (xmlp), |
| (size_t)XML_GetCurrentColumnNumber (xmlp), |
| _("Ignored unknown <interface> attribute"), |
| *key); |
| } |
| } |
| |
| /* Check we have a name and that it's valid */ |
| if (! name) |
| nih_return_error (-1, INTERFACE_MISSING_NAME, |
| _(INTERFACE_MISSING_NAME_STR)); |
| if (! interface_name_valid (name)) |
| nih_return_error (-1, INTERFACE_INVALID_NAME, |
| _(INTERFACE_INVALID_NAME_STR)); |
| |
| /* Allocate an Interface object and push onto the stack */ |
| interface = interface_new (NULL, name); |
| if (! interface) |
| nih_return_system_error (-1); |
| |
| if (! parse_stack_push (NULL, &context->stack, |
| PARSE_INTERFACE, interface)) { |
| nih_error_raise_system (); |
| nih_free (interface); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * interface_end_tag: |
| * @xmlp: XML parser, |
| * @tag: name of XML tag being parsed. |
| * |
| * This function is called by parse_end_tag() for an "interface" end |
| * tag, and matches a call to interface_start_tag() made at the same |
| * parsing level. |
| * |
| * The interface is added to the list of interfaces defined by its parent |
| * node. |
| * |
| * Returns: zero on success, negative value on raised error. |
| **/ |
| int |
| interface_end_tag (XML_Parser xmlp, |
| const char *tag) |
| { |
| ParseContext *context; |
| ParseStack * entry; |
| ParseStack * parent; |
| Interface * interface; |
| Interface * conflict; |
| Node * node; |
| |
| nih_assert (xmlp != NULL); |
| nih_assert (tag != NULL); |
| |
| context = XML_GetUserData (xmlp); |
| nih_assert (context != NULL); |
| |
| entry = parse_stack_top (&context->stack); |
| nih_assert (entry != NULL); |
| nih_assert (entry->type == PARSE_INTERFACE); |
| interface = entry->interface; |
| |
| /* Generate a symbol from the trailing part of the name */ |
| if (! interface->symbol) { |
| char *trail; |
| |
| trail = strrchr (interface->name, '.'); |
| nih_assert (trail != NULL); |
| trail++; |
| |
| interface->symbol = symbol_from_name (interface, trail); |
| if (! interface->symbol) |
| nih_return_no_memory_error (-1); |
| } else if (! strlen (interface->symbol)) { |
| nih_unref (interface->symbol, interface); |
| interface->symbol = NULL; |
| } |
| |
| nih_list_remove (&entry->entry); |
| parent = parse_stack_top (&context->stack); |
| nih_assert (parent != NULL); |
| nih_assert (parent->type == PARSE_NODE); |
| node = parent->node; |
| |
| /* Make sure there's not a conflict before adding the interface */ |
| conflict = node_lookup_interface (node, interface->symbol); |
| if (conflict) { |
| nih_error_raise_printf (INTERFACE_DUPLICATE_SYMBOL, |
| _(INTERFACE_DUPLICATE_SYMBOL_STR), |
| interface->symbol, conflict->name); |
| return -1; |
| } |
| |
| nih_debug ("Add %s interface to %s node", |
| interface->name, node->path ?: "(unknown)"); |
| nih_ref (interface, node); |
| nih_list_add (&node->interfaces, &interface->entry); |
| |
| nih_free (entry); |
| |
| return 0; |
| } |
| |
| |
| /** |
| * interface_annotation: |
| * @interface: interface object annotation applies to, |
| * @name: annotation name, |
| * @value: annotation value. |
| * |
| * Handles applying the annotation @name with value @value to the interface |
| * @interface. Interfaces may be annotated as deprecated or may have an |
| * alternate symbol name specified. |
| * |
| * Unknown annotations or illegal values to the known annotations result |
| * in an error being raised. |
| * |
| * Returns: zero on success, negative value on raised error. |
| **/ |
| int |
| interface_annotation (Interface * interface, |
| const char *name, |
| const char *value) |
| { |
| nih_assert (interface != NULL); |
| nih_assert (name != NULL); |
| nih_assert (value != NULL); |
| |
| if (! strcmp (name, "org.freedesktop.DBus.Deprecated")) { |
| if (! strcmp (value, "true")) { |
| nih_debug ("Marked %s interface as deprecated", |
| interface->name); |
| interface->deprecated = TRUE; |
| } else if (! strcmp (value, "false")) { |
| nih_debug ("Marked %s interface as not deprecated", |
| interface->name); |
| interface->deprecated = FALSE; |
| } else { |
| nih_return_error (-1, INTERFACE_ILLEGAL_DEPRECATED, |
| _(INTERFACE_ILLEGAL_DEPRECATED_STR)); |
| } |
| |
| } else if (! strcmp (name, "com.netsplit.Nih.Symbol")) { |
| if ((strlen (value) == 0) || symbol_valid (value)) { |
| if (interface->symbol) |
| nih_unref (interface->symbol, interface); |
| |
| interface->symbol = nih_strdup (interface, value); |
| if (! interface->symbol) |
| nih_return_no_memory_error (-1); |
| |
| nih_debug ("Set %s interface symbol to %s", |
| interface->name, interface->symbol); |
| } else { |
| nih_return_error (-1, INTERFACE_INVALID_SYMBOL, |
| _(INTERFACE_INVALID_SYMBOL_STR)); |
| } |
| |
| } else { |
| nih_error_raise_printf (INTERFACE_UNKNOWN_ANNOTATION, |
| "%s: %s: %s", |
| _(INTERFACE_UNKNOWN_ANNOTATION_STR), |
| interface->name, name); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| |
| /** |
| * interface_methods_array: |
| * @parent: parent object for new string, |
| * @prefix: prefix for array name, |
| * @interface: interface to generate array for, |
| * @with_handlers: whether to include handler pointers, |
| * @prototypes: list to append prototype to. |
| * |
| * Generates C code to declare an array of NihDBusMethod variables |
| * containing information about the methods of the interface @interface; |
| * this will also include array definitions for the arguments of each |
| * method, since these are referred to by the returned array. |
| * |
| * If @with_handlers is TRUE the returned array will contain pointers to |
| * handler functions that should be already defined (or at least prototyped); |
| * when FALSE this member will be NULL. |
| * |
| * The prototype of the returned variable declaration is returned as a |
| * TypeVar object appended to the @prototypes list. The arguments array |
| * prototypes are not returned since they are made static. |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned string. When all parents |
| * of the returned string are freed, the return string will also be |
| * freed. |
| * |
| * Returns: newly allocated string or NULL if insufficient memory. |
| **/ |
| char * |
| interface_methods_array (const void *parent, |
| const char *prefix, |
| Interface * interface, |
| int with_handlers, |
| NihList * prototypes) |
| { |
| nih_local char *name = NULL; |
| NihList vars; |
| size_t max_name = 0; |
| size_t max_args = 0; |
| size_t max_handler = 0; |
| nih_local char *args = NULL; |
| TypeVar * var; |
| nih_local char *block = NULL; |
| char * code; |
| |
| nih_assert (prefix != NULL); |
| nih_assert (interface != NULL); |
| nih_assert (prototypes != NULL); |
| |
| name = symbol_impl (NULL, prefix, interface->name, NULL, "methods"); |
| if (! name) |
| return NULL; |
| |
| nih_list_init (&vars); |
| |
| /* Figure out the longest method name, arguments array variable name |
| * and handler function name. |
| */ |
| NIH_LIST_FOREACH (&interface->methods, iter) { |
| Method * method = (Method *)iter; |
| NihList args_prototypes; |
| nih_local char *args_array = NULL; |
| nih_local char *handler_name = NULL; |
| |
| /* Obtain the arguments array for the method, giving us the |
| * name of the array. Append it as a static to the block |
| * we prepend to our code. |
| */ |
| nih_list_init (&args_prototypes); |
| args_array = method_args_array (NULL, prefix, interface, |
| method, &args_prototypes); |
| if (! args_array) |
| return NULL; |
| |
| if (! nih_strcat_sprintf (&args, NULL, |
| "static %s" |
| "\n", |
| args_array)) |
| return NULL; |
| |
| nih_assert (! NIH_LIST_EMPTY (&args_prototypes)); |
| |
| var = (TypeVar *)args_prototypes.next; |
| nih_list_add (&vars, &var->entry); |
| nih_ref (var, args); |
| |
| /* Calculate size of method name and args var name */ |
| if (strlen (method->name) > max_name) |
| max_name = strlen (method->name); |
| |
| if (strlen (var->name) > max_args) |
| max_args = strlen (var->name); |
| |
| /* Work out name of handler or leave space for "NULL" */ |
| if (with_handlers) { |
| handler_name = symbol_impl (NULL, prefix, |
| interface->name, |
| method->name, "method"); |
| if (! handler_name) |
| return NULL; |
| |
| if (strlen (handler_name) > max_handler) |
| max_handler = strlen (handler_name); |
| } else { |
| if (max_handler < 4) |
| max_handler = 4; |
| } |
| } |
| |
| /* Append each method such that the names, args variable names and |
| * handler function names are all lined up with each other. |
| */ |
| var = (TypeVar *)vars.next; |
| NIH_LIST_FOREACH (&interface->methods, iter) { |
| Method * method = (Method *)iter; |
| nih_local char *line = NULL; |
| char * dest; |
| nih_local char *handler_name = NULL; |
| |
| nih_assert (&var->entry != &vars); |
| |
| /* Allocate the line and fill in the values, padding out |
| * where necessary. |
| */ |
| line = nih_alloc (NULL, max_name + max_args + max_handler + 13); |
| if (! line) |
| return NULL; |
| |
| dest = line; |
| |
| memcpy (dest, "{ \"", 3); |
| dest += 3; |
| |
| memcpy (dest, method->name, strlen (method->name)); |
| dest += strlen (method->name); |
| |
| memcpy (dest, "\", ", 3); |
| dest += 3; |
| |
| memset (dest, ' ', max_name - strlen (method->name)); |
| dest += max_name - strlen (method->name); |
| |
| memcpy (dest, var->name, strlen (var->name)); |
| dest += strlen (var->name); |
| |
| memcpy (dest, ", ", 2); |
| dest += 2; |
| |
| memset (dest, ' ', max_args - strlen (var->name)); |
| dest += max_args - strlen (var->name); |
| |
| if (with_handlers) { |
| handler_name = symbol_impl (NULL, prefix, |
| interface->name, |
| method->name, "method"); |
| if (! handler_name) |
| return NULL; |
| |
| memcpy (dest, handler_name, strlen (handler_name)); |
| dest += strlen (handler_name); |
| |
| memset (dest, ' ', max_handler - strlen (handler_name)); |
| dest += max_handler - strlen (handler_name); |
| } else { |
| memcpy (dest, "NULL", 4); |
| dest += 4; |
| |
| memset (dest, ' ', max_handler - 4); |
| dest += max_handler - 4; |
| } |
| |
| memcpy (dest, " },\n", 4); |
| dest += 4; |
| |
| *dest = '\0'; |
| |
| if (! nih_strcat (&block, NULL, line)) |
| return NULL; |
| |
| var = (TypeVar *)var->entry.next; |
| } |
| |
| /* Append the final element to the block of elements, indent and |
| * surround with the structure definition. |
| */ |
| if (! nih_strcat (&block, NULL, "{ NULL }\n")) |
| return NULL; |
| |
| if (! indent (&block, NULL, 1)) |
| return NULL; |
| |
| code = nih_sprintf (parent, |
| "%s" |
| "const NihDBusMethod %s[] = {\n" |
| "%s" |
| "};\n", |
| args ?: "", |
| name, |
| block); |
| if (! code) |
| return NULL; |
| |
| /* Append the prototype to the list */ |
| var = type_var_new (code, "const NihDBusMethod", name); |
| if (! var) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| var->array = TRUE; |
| |
| nih_list_add (prototypes, &var->entry); |
| |
| return code; |
| } |
| |
| /** |
| * interface_signals_array: |
| * @parent: parent object for new string, |
| * @prefix: prefix for array name, |
| * @interface: interface to generate array for, |
| * @with_filter: whether to include filter function pointers, |
| * @prototypes: list to append prototype to. |
| * |
| * Generates C code to declare an array of NihDBusSignal variables |
| * containing information about the signals of the interface @interface; |
| * this will also include array definitions for the arguments of each |
| * signals, since these are referred to by the returned array. |
| * |
| * If @with_filters is TRUE the returned array will contain pointers to |
| * filter functions that should be already defined (or at least prototyped); |
| * when FALSE this member will be NULL. |
| * |
| * The prototype of the returned variable declaration is returned as a |
| * TypeVar object appended to the @prototypes list. The arguments array |
| * prototypes are not returned since they are made static. |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned string. When all parents |
| * of the returned string are freed, the return string will also be |
| * freed. |
| * |
| * Returns: newly allocated string or NULL if insufficient memory. |
| **/ |
| char * |
| interface_signals_array (const void *parent, |
| const char *prefix, |
| Interface * interface, |
| int with_filters, |
| NihList * prototypes) |
| { |
| nih_local char *name = NULL; |
| NihList vars; |
| size_t max_name = 0; |
| size_t max_args = 0; |
| size_t max_filter = 0; |
| nih_local char *args = NULL; |
| TypeVar * var; |
| nih_local char *block = NULL; |
| char * code; |
| |
| nih_assert (prefix != NULL); |
| nih_assert (interface != NULL); |
| nih_assert (prototypes != NULL); |
| |
| name = symbol_impl (NULL, prefix, interface->name, NULL, "signals"); |
| if (! name) |
| return NULL; |
| |
| nih_list_init (&vars); |
| |
| /* Figure out the longest signal name, arguments array variable name |
| * and filter function name. |
| */ |
| NIH_LIST_FOREACH (&interface->signals, iter) { |
| Signal * signal = (Signal *)iter; |
| NihList args_prototypes; |
| nih_local char *args_array = NULL; |
| nih_local char *filter_name = NULL; |
| |
| /* Obtain the arguments array for the signal, giving us the |
| * name of the array. Append it as a static to the block |
| * we prepend to our code. |
| */ |
| nih_list_init (&args_prototypes); |
| args_array = signal_args_array (NULL, prefix, interface, |
| signal, &args_prototypes); |
| if (! args_array) |
| return NULL; |
| |
| if (! nih_strcat_sprintf (&args, NULL, |
| "static %s" |
| "\n", |
| args_array)) |
| return NULL; |
| |
| nih_assert (! NIH_LIST_EMPTY (&args_prototypes)); |
| |
| var = (TypeVar *)args_prototypes.next; |
| nih_list_add (&vars, &var->entry); |
| nih_ref (var, args); |
| |
| /* Calculate size of signal name and args var name */ |
| if (strlen (signal->name) > max_name) |
| max_name = strlen (signal->name); |
| |
| if (strlen (var->name) > max_args) |
| max_args = strlen (var->name); |
| |
| /* Work out name of filter or leave space for "NULL" */ |
| if (with_filters) { |
| filter_name = symbol_impl (NULL, prefix, |
| interface->name, |
| signal->name, "signal"); |
| if (! filter_name) |
| return NULL; |
| |
| if (strlen (filter_name) > max_filter) |
| max_filter = strlen (filter_name); |
| } else { |
| if (max_filter < 4) |
| max_filter = 4; |
| } |
| } |
| |
| /* Append each signal such that the names, args variable names and |
| * filter function names are all lined up with each other. |
| */ |
| var = (TypeVar *)vars.next; |
| NIH_LIST_FOREACH (&interface->signals, iter) { |
| Signal * signal = (Signal *)iter; |
| nih_local char *line = NULL; |
| char * dest; |
| nih_local char *filter_name = NULL; |
| |
| nih_assert (&var->entry != &vars); |
| |
| /* Allocate the line and fill in the values, padding out |
| * where necessary. |
| */ |
| line = nih_alloc (NULL, max_name + max_args + max_filter + 13); |
| if (! line) |
| return NULL; |
| |
| dest = line; |
| |
| memcpy (dest, "{ \"", 3); |
| dest += 3; |
| |
| memcpy (dest, signal->name, strlen (signal->name)); |
| dest += strlen (signal->name); |
| |
| memcpy (dest, "\", ", 3); |
| dest += 3; |
| |
| memset (dest, ' ', max_name - strlen (signal->name)); |
| dest += max_name - strlen (signal->name); |
| |
| memcpy (dest, var->name, strlen (var->name)); |
| dest += strlen (var->name); |
| |
| memcpy (dest, ", ", 2); |
| dest += 2; |
| |
| memset (dest, ' ', max_args - strlen (var->name)); |
| dest += max_args - strlen (var->name); |
| |
| if (with_filters) { |
| filter_name = symbol_impl (NULL, prefix, |
| interface->name, |
| signal->name, "signal"); |
| if (! filter_name) |
| return NULL; |
| |
| memcpy (dest, filter_name, strlen (filter_name)); |
| dest += strlen (filter_name); |
| |
| memset (dest, ' ', max_filter - strlen (filter_name)); |
| dest += max_filter - strlen (filter_name); |
| } else { |
| memcpy (dest, "NULL", 4); |
| dest += 4; |
| |
| memset (dest, ' ', max_filter - 4); |
| dest += max_filter - 4; |
| } |
| |
| memcpy (dest, " },\n", 4); |
| dest += 4; |
| |
| *dest = '\0'; |
| |
| if (! nih_strcat (&block, NULL, line)) |
| return NULL; |
| |
| var = (TypeVar *)var->entry.next; |
| } |
| |
| /* Append the final element to the block of elements, indent and |
| * surround with the structure definition. |
| */ |
| if (! nih_strcat (&block, NULL, "{ NULL }\n")) |
| return NULL; |
| |
| if (! indent (&block, NULL, 1)) |
| return NULL; |
| |
| code = nih_sprintf (parent, |
| "%s" |
| "const NihDBusSignal %s[] = {\n" |
| "%s" |
| "};\n", |
| args ?: "", |
| name, |
| block); |
| if (! code) |
| return NULL; |
| |
| /* Append the prototype to the list */ |
| var = type_var_new (code, "const NihDBusSignal", name); |
| if (! var) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| var->array = TRUE; |
| |
| nih_list_add (prototypes, &var->entry); |
| |
| return code; |
| } |
| |
| /** |
| * interface_properties_array: |
| * @parent: parent object for new string, |
| * @prefix: prefix for array name, |
| * @interface: interface to generate array for, |
| * @with_handlers: whether to include handler pointers, |
| * @prototypes: list to append prototype to. |
| * |
| * Generates C code to declare an array of NihDBusProperty variables |
| * containing information about the properties of the interface @interface. |
| * |
| * If @with_handlers is TRUE the returned array will contain pointers to |
| * getter and setter functions that should be already defined (or at least |
| * prototyped); when FALSE these members will be NULL. |
| * |
| * The prototype of the returned variable declaration is returned as a |
| * TypeVar object appended to the @prototypes list. |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned string. When all parents |
| * of the returned string are freed, the return string will also be |
| * freed. |
| * |
| * Returns: newly allocated string or NULL if insufficient memory. |
| **/ |
| char * |
| interface_properties_array (const void *parent, |
| const char *prefix, |
| Interface * interface, |
| int with_handlers, |
| NihList * prototypes) |
| { |
| nih_local char *name = NULL; |
| size_t max_name = 0; |
| size_t max_type = 0; |
| size_t max_access = 0; |
| size_t max_getter = 0; |
| size_t max_setter = 0; |
| nih_local char *block = NULL; |
| char * code; |
| TypeVar * var; |
| |
| nih_assert (prefix != NULL); |
| nih_assert (interface != NULL); |
| nih_assert (prototypes != NULL); |
| |
| name = symbol_impl (NULL, prefix, interface->name, NULL, "properties"); |
| if (! name) |
| return NULL; |
| |
| /* Figure out the longest property name, type, access variable, |
| * getter and setter function names. |
| */ |
| NIH_LIST_FOREACH (&interface->properties, iter) { |
| Property * property = (Property *)iter; |
| nih_local char *getter_name = NULL; |
| nih_local char *setter_name = NULL; |
| |
| if (strlen (property->name) > max_name) |
| max_name = strlen (property->name); |
| |
| if (strlen (property->type) > max_type) |
| max_type = strlen (property->type); |
| |
| switch (property->access) { |
| case NIH_DBUS_READ: |
| if (max_access < 13) |
| max_access = 13; |
| break; |
| case NIH_DBUS_WRITE: |
| if (max_access < 14) |
| max_access = 14; |
| break; |
| case NIH_DBUS_READWRITE: |
| if (max_access < 18) |
| max_access = 18; |
| break; |
| default: |
| nih_assert_not_reached (); |
| } |
| |
| if (with_handlers && (property->access != NIH_DBUS_WRITE)) { |
| getter_name = symbol_impl (NULL, prefix, |
| interface->name, |
| property->name, "get"); |
| if (! getter_name) |
| return NULL; |
| |
| if (strlen (getter_name) > max_getter) |
| max_getter = strlen (getter_name); |
| } else { |
| if (max_getter < 4) |
| max_getter = 4; |
| } |
| |
| if (with_handlers && (property->access != NIH_DBUS_READ)) { |
| setter_name = symbol_impl (NULL, prefix, |
| interface->name, |
| property->name, "set"); |
| if (! setter_name) |
| return NULL; |
| |
| if (strlen (setter_name) > max_setter) |
| max_setter = strlen (setter_name); |
| } else { |
| if (max_setter < 4) |
| max_setter = 4; |
| } |
| } |
| |
| /* Append each property such that the names, types, access enum, |
| * getter and setter function names are all lined up with each other. |
| */ |
| NIH_LIST_FOREACH (&interface->properties, iter) { |
| Property * property = (Property *)iter; |
| nih_local char *line = NULL; |
| char * dest; |
| nih_local char *getter_name = NULL; |
| nih_local char *setter_name = NULL; |
| |
| line = nih_alloc (NULL, (max_name + max_type + max_access |
| + max_getter + max_setter + 19)); |
| if (! line) |
| return NULL; |
| |
| dest = line; |
| |
| memcpy (dest, "{ \"", 3); |
| dest += 3; |
| |
| memcpy (dest, property->name, strlen (property->name)); |
| dest += strlen (property->name); |
| |
| memcpy (dest, "\", ", 3); |
| dest += 3; |
| |
| memset (dest, ' ', max_name - strlen (property->name)); |
| dest += max_name - strlen (property->name); |
| |
| |
| memcpy (dest, "\"", 1); |
| dest += 1; |
| |
| memcpy (dest, property->type, strlen (property->type)); |
| dest += strlen (property->type); |
| |
| memcpy (dest, "\", ", 3); |
| dest += 3; |
| |
| memset (dest, ' ', max_type - strlen (property->type)); |
| dest += max_type - strlen (property->type); |
| |
| switch (property->access) { |
| case NIH_DBUS_READ: |
| memcpy (dest, "NIH_DBUS_READ, ", 15); |
| dest += 15; |
| |
| memset (dest, ' ', max_access - 13); |
| dest += max_access - 13; |
| break; |
| case NIH_DBUS_WRITE: |
| memcpy (dest, "NIH_DBUS_WRITE, ", 16); |
| dest += 16; |
| |
| memset (dest, ' ', max_access - 14); |
| dest += max_access - 14; |
| break; |
| case NIH_DBUS_READWRITE: |
| memcpy (dest, "NIH_DBUS_READWRITE, ", 20); |
| dest += 20; |
| |
| memset (dest, ' ', max_access - 18); |
| dest += max_access - 18; |
| break; |
| default: |
| nih_assert_not_reached (); |
| } |
| |
| if (with_handlers && (property->access != NIH_DBUS_WRITE)) { |
| getter_name = symbol_impl (NULL, prefix, |
| interface->name, |
| property->name, "get"); |
| if (! getter_name) |
| return NULL; |
| |
| memcpy (dest, getter_name, strlen (getter_name)); |
| dest += strlen (getter_name); |
| |
| memcpy (dest, ", ", 2); |
| dest += 2; |
| |
| memset (dest, ' ', max_getter - strlen (getter_name)); |
| dest += max_getter - strlen (getter_name); |
| } else { |
| memcpy (dest, "NULL, ", 6); |
| dest += 6; |
| |
| memset (dest, ' ', max_getter - 4); |
| dest += max_getter - 4; |
| } |
| |
| if (with_handlers && (property->access != NIH_DBUS_READ)) { |
| setter_name = symbol_impl (NULL, prefix, |
| interface->name, |
| property->name, "set"); |
| if (! setter_name) |
| return NULL; |
| |
| memcpy (dest, setter_name, strlen (setter_name)); |
| dest += strlen (setter_name); |
| |
| memset (dest, ' ', max_setter - strlen (setter_name)); |
| dest += max_setter - strlen (setter_name); |
| } else { |
| memcpy (dest, "NULL", 4); |
| dest += 4; |
| |
| memset (dest, ' ', max_setter - 4); |
| dest += max_setter - 4; |
| } |
| |
| memcpy (dest, " },\n", 4); |
| dest += 4; |
| |
| *dest = '\0'; |
| |
| if (! nih_strcat (&block, NULL, line)) |
| return NULL; |
| } |
| |
| /* Append the final element to the block of elements, indent and |
| * surround with the structure definition. |
| */ |
| if (! nih_strcat (&block, NULL, "{ NULL }\n")) |
| return NULL; |
| |
| if (! indent (&block, NULL, 1)) |
| return NULL; |
| |
| code = nih_sprintf (parent, |
| "const NihDBusProperty %s[] = {\n" |
| "%s" |
| "};\n", |
| name, |
| block); |
| if (! code) |
| return NULL; |
| |
| /* Append the prototype to the list */ |
| var = type_var_new (code, "const NihDBusProperty", name); |
| if (! var) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| var->array = TRUE; |
| |
| nih_list_add (prototypes, &var->entry); |
| |
| return code; |
| } |
| |
| |
| /** |
| * interface_struct: |
| * @parent: parent object for new string, |
| * @prefix: prefix for struct name, |
| * @interface: interface to generate struct for, |
| * @object: whether struct is for an object or proxy, |
| * @prototypes: list to append prototype to. |
| * |
| * Generates C code to declare an NihDBusInterface structure variable for |
| * the given interface @interface, the code includes the array definitions |
| * for methods, signals, properties and their arguments. |
| * |
| * If @object is TRUE, the struct will be for an object definition so method |
| * handler function and property getter and setter function pointers will |
| * be filled in. If @object is FALSE, the struct will be for a proxy |
| * definition so the signal filter function pointers will be filled in. |
| * |
| * The prototype of the returned variable declaration is returned as a |
| * TypeVar object appended to the @prototypes list. The methods, signals |
| * and properties array prototypes are not returned since they are made |
| * static. |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned string. When all parents |
| * of the returned string are freed, the return string will also be |
| * freed. |
| * |
| * Returns: newly allocated string or NULL if insufficient memory. |
| **/ |
| char * |
| interface_struct (const void *parent, |
| const char *prefix, |
| Interface * interface, |
| int object, |
| NihList * prototypes) |
| { |
| nih_local char *name = NULL; |
| nih_local char *block = NULL; |
| nih_local char *arrays = NULL; |
| TypeVar * var; |
| char * ptr; |
| NihList methods_prototypes; |
| nih_local char *methods_array = NULL; |
| NihList signals_prototypes; |
| nih_local char *signals_array = NULL; |
| NihList properties_prototypes; |
| nih_local char *properties_array = NULL; |
| char * code; |
| |
| nih_assert (prefix != NULL); |
| nih_assert (interface != NULL); |
| nih_assert (prototypes != NULL); |
| |
| /* Work out the structure name, and append the interface name to the |
| * definition. |
| */ |
| name = symbol_impl (NULL, prefix, interface->name, NULL, NULL); |
| if (! name) |
| return NULL; |
| |
| if (! nih_strcat_sprintf (&block, NULL, "\"%s\",\n", interface->name)) |
| return NULL; |
| |
| /* Append the methods array to the arrays block, making it static |
| * in the process. |
| */ |
| nih_list_init (&methods_prototypes); |
| |
| methods_array = interface_methods_array (NULL, prefix, interface, |
| object ? TRUE : FALSE, |
| &methods_prototypes); |
| if (! methods_array) |
| return NULL; |
| |
| nih_assert (! NIH_LIST_EMPTY (&methods_prototypes)); |
| |
| var = (TypeVar *)methods_prototypes.next; |
| ptr = strstr (methods_array, var->type); |
| nih_assert (ptr != NULL); |
| |
| if (! nih_strncat (&arrays, NULL, methods_array, ptr - methods_array)) |
| return NULL; |
| |
| if (! nih_strcat_sprintf (&arrays, NULL, "static %s\n", ptr)) |
| return NULL; |
| |
| if (! nih_strcat_sprintf (&block, NULL, "%s,\n", var->name)) |
| return NULL; |
| |
| /* Append the signals array to the arrays block, making it static |
| * in the process. |
| */ |
| nih_list_init (&signals_prototypes); |
| |
| signals_array = interface_signals_array (NULL, prefix, interface, |
| object ? FALSE : TRUE, |
| &signals_prototypes); |
| if (! signals_array) |
| return NULL; |
| |
| nih_assert (! NIH_LIST_EMPTY (&signals_prototypes)); |
| |
| var = (TypeVar *)signals_prototypes.next; |
| ptr = strstr (signals_array, var->type); |
| nih_assert (ptr != NULL); |
| |
| if (! nih_strncat (&arrays, NULL, signals_array, ptr - signals_array)) |
| return NULL; |
| |
| if (! nih_strcat_sprintf (&arrays, NULL, "static %s\n", ptr)) |
| return NULL; |
| |
| if (! nih_strcat_sprintf (&block, NULL, "%s,\n", var->name)) |
| return NULL; |
| |
| /* Append the properties array to the arrays block, making it static |
| * in the process. |
| */ |
| nih_list_init (&properties_prototypes); |
| |
| properties_array = interface_properties_array (NULL, prefix, interface, |
| object ? TRUE : FALSE, |
| &properties_prototypes); |
| if (! properties_array) |
| return NULL; |
| |
| nih_assert (! NIH_LIST_EMPTY (&properties_prototypes)); |
| |
| var = (TypeVar *)properties_prototypes.next; |
| |
| if (! nih_strcat_sprintf (&arrays, NULL, "static %s\n", |
| properties_array)) |
| return NULL; |
| |
| if (! nih_strcat_sprintf (&block, NULL, "%s\n", var->name)) |
| return NULL; |
| |
| /* Output the code */ |
| if (! indent (&block, NULL, 1)) |
| return NULL; |
| |
| code = nih_sprintf (parent, |
| "%s" |
| "const NihDBusInterface %s = {\n" |
| "%s" |
| "};\n", |
| arrays, |
| name, |
| block); |
| if (! code) |
| return NULL; |
| |
| /* Append the prototype to the list */ |
| var = type_var_new (code, "const NihDBusInterface", name); |
| if (! var) { |
| nih_free (code); |
| return NULL; |
| } |
| |
| nih_list_add (prototypes, &var->entry); |
| |
| return code; |
| } |