From 32842fca2195b4a9de29cce44a650189620231b5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Roberto=20de=20Souza?= Date: Thu, 20 Dec 2012 21:47:20 +0000 Subject: [PATCH] edbus codegen: Initial commit MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add codegen tool to generate client code. Patch by: José Roberto de Souza SVN revision: 81492 --- .gitignore | 1 + Makefile.am | 12 + configure.ac | 10 + m4/efl_binary.m4 | 78 ++++++ src/codegen/client.c | 81 ++++++ src/codegen/codegen.h | 112 ++++++++ src/codegen/dbus.c | 141 ++++++++++ src/codegen/parser.c | 482 +++++++++++++++++++++++++++++++++ src/codegen/source_client.c | 629 ++++++++++++++++++++++++++++++++++++++++++++ src/codegen/utils.c | 166 ++++++++++++ 10 files changed, 1712 insertions(+) create mode 100644 m4/efl_binary.m4 create mode 100644 src/codegen/client.c create mode 100644 src/codegen/codegen.h create mode 100644 src/codegen/dbus.c create mode 100644 src/codegen/parser.c create mode 100644 src/codegen/source_client.c create mode 100644 src/codegen/utils.c diff --git a/.gitignore b/.gitignore index 51ea75c..bcfa7a8 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ doc/Doxyfile doc/html/ doc/latex/ doc/man/ +edbus_client_codegen diff --git a/Makefile.am b/Makefile.am index c1ee20d..293c087 100644 --- a/Makefile.am +++ b/Makefile.am @@ -129,6 +129,18 @@ src_examples_simple_signal_emit_LDADD = $(EXAMPLES_LIBS) src_examples_complex_types_client_eina_value_SOURCES = src/examples/complex_types_client_eina_value.c src_examples_complex_types_client_eina_value_LDADD = $(EXAMPLES_LIBS) +if BUILD_EDBUS_CODEGEN +bin_PROGRAMS = edbus_client_codegen +edbus_client_codegen_SOURCES = \ + src/codegen/utils.c \ + src/codegen/parser.c \ + src/codegen/dbus.c \ + src/codegen/source_client.c \ + src/codegen/client.c +edbus_client_codegen_CFLAGS = -I$(top_srcdir)/src/codegen @EDBUS_CODEGEN_CFLAGS@ @ECORE_CFLAGS@ +edbus_client_codegen_LDADD = @EDBUS_CODEGEN_LIBS@ @ECORE_LIBS@ +endif + .PHONY: doc examples examples: $(EXAMPLES) diff --git a/configure.ac b/configure.ac index eb863d1..4b2c038 100644 --- a/configure.ac +++ b/configure.ac @@ -105,6 +105,16 @@ VMIN=v_min AC_SUBST(VMAJ) AC_SUBST(VMIN) +EFL_ENABLE_BIN([edbus-codegen], ["yes"]) + +if test "x${have_edbus_codegen}" = "xyes" ; then + PKG_CHECK_MODULES([EDBUS_CODEGEN], + [eina >= 1.6.99], + [ecore >= 1.6.99], + [have_edbus_codegen="yes"], + [have_edbus_codegen="no"]) +fi + AC_CONFIG_FILES([ Makefile edbus.spec diff --git a/m4/efl_binary.m4 b/m4/efl_binary.m4 new file mode 100644 index 0000000..0ad38ce --- /dev/null +++ b/m4/efl_binary.m4 @@ -0,0 +1,78 @@ +dnl Copyright (C) 2010 Vincent Torri +dnl That code is public domain and can be freely used or copied. + +dnl Macro that checks if a binary is built or not + +dnl Usage: EFL_ENABLE_BIN(binary, dep[, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl Call AC_SUBST(BINARY_PRG) (BINARY is the uppercase of binary, - being transformed into _) +dnl Define have_binary (- is transformed into _) +dnl Define conditional BUILD_BINARY (BINARY is the uppercase of binary, - being transformed into _) + +AC_DEFUN([EFL_ENABLE_BIN], +[ + +m4_pushdef([UP], m4_translit([[$1]], [-a-z], [_A-Z]))dnl +m4_pushdef([DOWN], m4_translit([[$1]], [-A-Z], [_a-z]))dnl + +dnl configure option + +AC_ARG_ENABLE([$1], + [AC_HELP_STRING([--disable-$1], [disable building of ]DOWN)], + [ + if test "x${enableval}" = "xyes" ; then + have_[]m4_defn([DOWN])="yes" + else + have_[]m4_defn([DOWN])="no" + fi + ], + [have_[]m4_defn([DOWN])=$2]) + +AC_MSG_CHECKING([whether to build ]DOWN[ binary]) +AC_MSG_RESULT([$have_[]m4_defn([DOWN])]) + +if test "x$have_[]m4_defn([DOWN])" = "xyes"; then + UP[]_PRG=DOWN[${EXEEXT}] +fi + +AC_SUBST(UP[]_PRG) + +AM_CONDITIONAL(BUILD_[]UP, test "x$have_[]m4_defn([DOWN])" = "xyes") + +AS_IF([test "x$have_[]m4_defn([DOWN])" = "xyes"], [$3], [$4]) + +]) + +dnl Macro that specifies the binary to be used + +dnl Usage: EFL_WITH_BIN(binary, package, msg) +dnl Call AC_SUBST(BINARY_PRG) (BINARY is the uppercase of binary, - being transformed into _) +dnl Define with_binary (- is transformed into _) +dnl Define conditional BUILD_BINARY (BINARY is the uppercase of binary, - being transformed into _) + +AC_DEFUN([EFL_WITH_BIN], +[ + +m4_pushdef([UP], m4_translit([[$1]], [-a-z], [_A-Z]))dnl +m4_pushdef([DOWN], m4_translit([[$1]], [-A-Z], [_a-z]))dnl + +AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +AC_MSG_NOTICE([$PKG_CONFIG]) + +with_[]m4_defn([DOWN])=m4_esyscmd($PKG_CONFIG --variable=prefix $2)/bin/m4_defn([DOWN]) + +dnl configure option + +AC_ARG_WITH([$1], + [AC_HELP_STRING([--with-$1-bin=PATH], [specify a specific path to ]DOWN)], + [ + with_[]m4_defn([DOWN])=$withval + _efl_msg="( explicitely set)" + ]) + +AC_MSG_NOTICE([$msg: ]m4_defn([DOWN])[$_efl_msg]) + +AC_SUBST(with_[]m4_defn([DOWN])) + +AS_IF([test "x$have_[]m4_defn([DOWN])" = "xyes"], [$4], [$5]) + +]) diff --git a/src/codegen/client.c b/src/codegen/client.c new file mode 100644 index 0000000..742a294 --- /dev/null +++ b/src/codegen/client.c @@ -0,0 +1,81 @@ +#include "codegen.h" + +static const Ecore_Getopt optdesc = { + "edbus_codegen", + "%prog [options] ", + "1.0", + "(C) 2012 - The Enlightenment Project", + "BSD", + "Generate DBUS code, making write DBus client applications easy\n", + 0, + { + ECORE_GETOPT_STORE_STR('p', "prefix", "The prefix for the generated code."), + ECORE_GETOPT_STORE_STR('i', "interface", "To generate code of only one interface of xml."), + ECORE_GETOPT_STORE_STR('o', "output file name", "The name of output files, only used if a interface is selected."), + ECORE_GETOPT_LICENSE('L', "license"), + ECORE_GETOPT_COPYRIGHT('C', "copyright"), + ECORE_GETOPT_VERSION('V', "version"), + ECORE_GETOPT_HELP('h', "help"), + ECORE_GETOPT_SENTINEL + } +}; + +int +main(int argc, char **argv) +{ + char *xml_buf = NULL; + DBus_Object *obj = NULL; + char *prefix = NULL, *interface = NULL, *output = NULL; + Eina_Bool quit_option = EINA_FALSE; + int arg_index; + Ecore_Getopt_Value values[] = { + ECORE_GETOPT_VALUE_STR(prefix), + ECORE_GETOPT_VALUE_STR(interface), + ECORE_GETOPT_VALUE_STR(output), + ECORE_GETOPT_VALUE_BOOL(quit_option), + ECORE_GETOPT_VALUE_BOOL(quit_option), + ECORE_GETOPT_VALUE_BOOL(quit_option), + ECORE_GETOPT_VALUE_BOOL(quit_option), + ECORE_GETOPT_VALUE_NONE + }; + + eina_init(); + + arg_index = ecore_getopt_parse(&optdesc, values, argc, argv); + if (arg_index == -1) + { + printf("Error: parsing arguments.\n"); + return -1; + } + if (quit_option) + return 0; + if (argc <= arg_index) + { + printf("Error: Missing file name.\n"); + return -1; + } + + if (!file_read(argv[arg_index], &xml_buf)) + { + printf("Error reading file.\n"); + goto end; + } + + if (!eina_simple_xml_parse(xml_buf, strlen(xml_buf), EINA_TRUE, + parser, &obj)) + { + printf("Error: could not parse XML\n"); + goto end; + } + + source_client_generate(obj, prefix, interface, output); + util_h_write(); + +end: + if (obj) + object_free(obj); + + free(xml_buf); + eina_shutdown(); + return 0; +} diff --git a/src/codegen/codegen.h b/src/codegen/codegen.h new file mode 100644 index 0000000..ffcaf99 --- /dev/null +++ b/src/codegen/codegen.h @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +#define ACCESS_READ 1 << 0 +#define ACCESS_WRITE 1 << 1 + +typedef struct _DBus_Object +{ + char *name; + char *c_name; + Eina_Inlist *ifaces; +} DBus_Object; + +typedef struct _DBus_Interface +{ + EINA_INLIST; + char *name; + char *c_name; + Eina_Inlist *methods; + Eina_Inlist *signals; + Eina_Inlist *properties; + DBus_Object *obj; +} DBus_Interface; + +typedef struct _DBus_Signal +{ + EINA_INLIST; + char *name; + char *c_name; + char *cb_name; + char *struct_name; + char *free_function; + Eina_Inlist *args; + unsigned int arg_without_name; + DBus_Interface *iface; + Eina_Bool complex; + char *signal_event; +} DBus_Signal; + +typedef struct _DBus_Arg +{ + EINA_INLIST; + char *type; + char direction; + char *name; + char *c_name; +} DBus_Arg; + +typedef enum +{ + NO_REPLY = 0, + INVALID +} DBus_Annotation_Type; + +typedef struct _DBus_Annotation +{ + DBus_Annotation_Type type; + char *value; +} DBus_Annotation; + +typedef struct _DBus_Method +{ + EINA_INLIST; + char *name; + char *c_name; + char *cb_name; + char *function_cb; + Eina_Inlist *args; + unsigned int arg_without_name; + DBus_Interface *iface; + Eina_Bool in_complex:1; + Eina_Bool out_complex:1; + Eina_Bool no_reply:1; +} DBus_Method; + +typedef struct _DBus_Property +{ + EINA_INLIST; + char *name; + char *c_name; + char *cb_name; + char *type; + char access; + DBus_Interface *iface; + Eina_Bool complex; +} DBus_Property; + +Eina_Bool parser(void *data, Eina_Simple_XML_Type type, const char *content, unsigned offset, unsigned length); + +void object_free(DBus_Object *obj); +DBus_Interface *interface_new(DBus_Object *obj); +void interface_free(DBus_Interface *iface); +DBus_Signal *signal_new(DBus_Interface *iface); +void signal_free(DBus_Signal *signal); +void arg_free(DBus_Arg *arg); +void method_free(DBus_Method *method); +DBus_Method *method_new(DBus_Interface *iface); +DBus_Property *property_new(DBus_Interface *iface); +void property_free(DBus_Property *property); + +void source_client_generate(DBus_Object *path, const char *prefix, const char *interface_name, const char *output); + +//utils +Eina_Bool file_read(const char *file_name, char **buffer); +Eina_Bool file_write(const char *file_name, const char *buffer); +Eina_Bool util_h_write(void); +char *replace_string(const char *string, const char *substr, const char *replacement); +char *dbus_name_to_c(const char *dbus); +char *string_build(const char *fmt, ...); +char *get_pieces(const char *string, char break_in, int amount); diff --git a/src/codegen/dbus.c b/src/codegen/dbus.c new file mode 100644 index 0000000..b05c760 --- /dev/null +++ b/src/codegen/dbus.c @@ -0,0 +1,141 @@ +#include "codegen.h" + +void +property_free(DBus_Property *property) +{ + property->iface->properties = eina_inlist_remove(property->iface->properties, + EINA_INLIST_GET(property)); + free(property->c_name); + free(property->cb_name); + free(property->name); + free(property->type); + free(property); +} + +DBus_Property * +property_new(DBus_Interface *iface) +{ + DBus_Property *prop = calloc(1, sizeof(DBus_Property)); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop, NULL); + iface->properties = eina_inlist_append(iface->properties, + EINA_INLIST_GET(prop)); + prop->iface = iface; + return prop; +} + +DBus_Method * +method_new(DBus_Interface *iface) +{ + DBus_Method *method = calloc(1, sizeof(DBus_Method)); + EINA_SAFETY_ON_NULL_RETURN_VAL(method, NULL); + iface->methods = eina_inlist_append(iface->methods, EINA_INLIST_GET(method)); + method->iface = iface; + return method; +} + +void +method_free(DBus_Method *method) +{ + DBus_Arg *arg; + Eina_Inlist *inlist; + + EINA_INLIST_FOREACH_SAFE(method->args, inlist, arg) + arg_free(arg); + method->iface->methods = eina_inlist_remove(method->iface->methods, + EINA_INLIST_GET(method)); + free(method->c_name); + free(method->cb_name); + free(method->function_cb); + free(method->name); + free(method); +} + +void +arg_free(DBus_Arg *arg) +{ + free(arg->c_name); + free(arg->name); + free(arg->type); + free(arg); +} + +DBus_Signal * +signal_new(DBus_Interface *iface) +{ + DBus_Signal *signal = calloc(1, sizeof(DBus_Signal)); + EINA_SAFETY_ON_NULL_RETURN_VAL(signal, NULL); + iface->signals = eina_inlist_append(iface->signals, EINA_INLIST_GET(signal)); + signal->iface = iface; + return signal; +} + +void +signal_free(DBus_Signal *signal) +{ + DBus_Arg *arg; + Eina_Inlist *inlist; + + EINA_INLIST_FOREACH_SAFE(signal->args, inlist, arg) + arg_free(arg); + signal->iface->signals = eina_inlist_remove(signal->iface->signals, + EINA_INLIST_GET(signal)); + free(signal->c_name); + free(signal->struct_name); + free(signal->free_function); + free(signal->cb_name); + free(signal->name); + free(signal->signal_event); + free(signal); +} + +DBus_Interface * +interface_new(DBus_Object *obj) +{ + DBus_Interface *iface = calloc(1, sizeof(DBus_Interface)); + EINA_SAFETY_ON_NULL_RETURN_VAL(iface, NULL); + obj->ifaces = eina_inlist_append(obj->ifaces, EINA_INLIST_GET(iface)); + iface->obj = obj; + return iface; +} + +void +interface_free(DBus_Interface *iface) +{ + while (iface->signals) + { + DBus_Signal *signal = EINA_INLIST_CONTAINER_GET(iface->signals, + DBus_Signal); + signal_free(signal); + } + while (iface->methods) + { + DBus_Method *method = EINA_INLIST_CONTAINER_GET(iface->methods, + DBus_Method); + method_free(method); + } + while (iface->properties) + { + DBus_Property *property = EINA_INLIST_CONTAINER_GET(iface->properties, + DBus_Property); + property_free(property); + } + iface->obj->ifaces = eina_inlist_remove(iface->obj->ifaces, + EINA_INLIST_GET(iface)); + free(iface->c_name); + free(iface->name); + free(iface); +} + +void +object_free(DBus_Object *obj) +{ + while (obj->ifaces) + { + DBus_Interface *iface = EINA_INLIST_CONTAINER_GET(obj->ifaces, + DBus_Interface); + interface_free(iface); + } + free(obj->c_name); + free(obj->name); + free(obj); +} diff --git a/src/codegen/parser.c b/src/codegen/parser.c new file mode 100644 index 0000000..55bbab8 --- /dev/null +++ b/src/codegen/parser.c @@ -0,0 +1,482 @@ +#include "codegen.h" + +#define OBJECT_TAG "node" +#define OBJECT_TAG_LENGHT strlen(OBJECT_TAG) +#define INTERFACE_TAG "interface" +#define INTERFACE_TAG_LENGHT strlen(INTERFACE_TAG) +#define SIGNAL_TAG "signal" +#define SIGNAL_TAG_LENGHT strlen(SIGNAL_TAG) +#define METHOD_TAG "method" +#define METHOD_TAG_LENGHT strlen(METHOD_TAG) +#define PROPERTY_TAG "property" +#define PROPERTY_TAG_LENGHT strlen(PROPERTY_TAG) +#define ARG_TAG "arg" +#define ARG_TAG_LENGHT strlen(ARG_TAG) +#define ANNOTATION_TAG "annotation" +#define ANNOTATION_TAG_LENGHT strlen(ANNOTATION_TAG) + +//attributes +#define NAME_ATTR "name" +#define TYPE_ATTR "type" +#define DIRECTION_ATTR "direction" +#define ACCESS_ATTR "access" +#define VALUE_ATTR "value" + +#define ACCESS_ATTR_VALUE_WRITE "write" +#define ACCESS_ATTR_VALUE_READ "read" + +#define DBUS_INTERFACE "org.freedesktop.DBus." + +static DBus_Interface *iface; +static DBus_Signal *d_signal; +static DBus_Method *method; +static DBus_Property *property; + +static Eina_Bool attributes_parse(const char *content, unsigned length, Eina_Simple_XML_Attribute_Cb func, const void *data); + +static Eina_Bool +obj_attributes_parser(void *data, const char *key, const char *value) +{ + DBus_Object *obj = data; + + if (!strcmp(key, NAME_ATTR)) + obj->name = strdup(value); + + return EINA_TRUE; +} + +static Eina_Bool +iface_attributes_parser(void *data, const char *key, const char *value) +{ + if (!strcmp(key, NAME_ATTR)) + iface->name = strdup(value); + + return EINA_TRUE; +} + +static Eina_Bool +signal_attributes_parser(void *data, const char *key, const char *value) +{ + if (!strcmp(key, NAME_ATTR)) + d_signal->name = strdup(value); + + return EINA_TRUE; +} + +static Eina_Bool +arg_attributes_parser(void *data, const char *key, const char *value) +{ + DBus_Arg *arg = data; + if (!strcmp(key, NAME_ATTR)) + arg->name = strdup(value); + else if (!strcmp(key, TYPE_ATTR)) + arg->type = strdup(value); + else if (!strcmp(key, DIRECTION_ATTR)) + arg->direction = value[0]; + + return EINA_TRUE; +} + +static Eina_Bool +method_attributes_parser(void *data, const char *key, const char *value) +{ + if (!strcmp(key, NAME_ATTR)) + method->name = strdup(value); + + return EINA_TRUE; +} + +static Eina_Bool +property_attributes_parser(void *data, const char *key, const char *value) +{ + if (!strcmp(key, NAME_ATTR)) + property->name = strdup(value); + else if (!strcmp(key, TYPE_ATTR)) + { + property->type = strdup(value); + if (value[1] || value[0] == 'v') + property->complex = EINA_TRUE; + } + else if (!strcmp(key, ACCESS_ATTR)) + { + if (!strcmp(value, ACCESS_ATTR_VALUE_READ)) + property->access = ACCESS_READ; + else if (!strcmp(value, ACCESS_ATTR_VALUE_WRITE)) + property->access = ACCESS_WRITE; + else + property->access = (ACCESS_WRITE | ACCESS_READ); + } + + return EINA_TRUE; +} + +static Eina_Bool +open_object(const char *content, unsigned length, Eina_Bool is_open_empty, DBus_Object **ptr_obj) +{ + Eina_Bool r; + DBus_Object *obj = *ptr_obj; + + if (is_open_empty) return EINA_TRUE; + + if (obj) + { + printf("Only one object is supported per file."); + return EINA_FALSE; + } + obj = calloc(1, sizeof(DBus_Object)); + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE); + + r = attributes_parse(content, length, obj_attributes_parser, obj); + if (!obj->name) obj->name = strdup("/"); + + obj->c_name = dbus_name_to_c(obj->name); + + *ptr_obj = obj; + return r; +} + +static Eina_Bool +open_interface(const char *content, unsigned length, Eina_Bool is_open_empty, DBus_Object *obj) +{ + Eina_Bool r; + char *tmp_name; + + iface = interface_new(obj); + EINA_SAFETY_ON_NULL_RETURN_VAL(iface, EINA_FALSE); + + r = attributes_parse(content, length, iface_attributes_parser, NULL); + if (!iface->name) + { + interface_free(iface); + printf("Error interface without name.\n"); + return EINA_FALSE; + } + + tmp_name = get_pieces(iface->name, '.', 2); + iface->c_name = dbus_name_to_c(tmp_name); + free(tmp_name); + + return r; +} + +static Eina_Bool +open_signal(const char *content, unsigned length, Eina_Bool is_open_empty) +{ + Eina_Bool r; + char *tmp; + int i; + Eina_Strbuf *buf = eina_strbuf_new(); + + d_signal = signal_new(iface); + EINA_SAFETY_ON_NULL_RETURN_VAL(d_signal, EINA_FALSE); + + r = attributes_parse(content, length, signal_attributes_parser, NULL); + if (!d_signal->name) + { + signal_free(d_signal); + d_signal = NULL; + printf("Error signal without name.\n"); + return EINA_FALSE; + } + + tmp = dbus_name_to_c(d_signal->name); + d_signal->c_name = string_build("%s_%s", iface->c_name, tmp); + free(tmp); + d_signal->cb_name = string_build("on_%s", d_signal->c_name); + d_signal->free_function = string_build("%s_data_free", d_signal->c_name); + d_signal->struct_name = string_build("%s_%s_Data", iface->c_name, d_signal->name); + d_signal->struct_name[0] = toupper(d_signal->struct_name[0]); + for (i = 0; d_signal->struct_name[i]; i++) + { + if (d_signal->struct_name[i] == '_' && d_signal->struct_name[i+1]) + d_signal->struct_name[i+1] = toupper(d_signal->struct_name[i+1]); + } + for (i = 0; iface->c_name[i]; i++) + eina_strbuf_append_char(buf, toupper(iface->c_name[i])); + eina_strbuf_append_char(buf, '_'); + for (i = 0; d_signal->name[i]; i++) + { + if (i && isupper(d_signal->name[i]) && !isupper(d_signal->name[i-1])) + eina_strbuf_append_char(buf, '_'); + eina_strbuf_append_char(buf, toupper(d_signal->name[i])); + } + eina_strbuf_append(buf, "_EVENT"); + d_signal->signal_event = strdup(eina_strbuf_string_get(buf)); + eina_strbuf_free(buf); + + return r; +} + +#define ANNOTATION_NO_REPLY "org.freedesktop.DBus.Method.NoReply" + +static Eina_Bool +annotation_attributes_parser(void *data, const char *key, const char *value) +{ + DBus_Annotation *annotation = data; + if (!strcmp(key, NAME_ATTR)) + { + if (!strcmp(value, ANNOTATION_NO_REPLY)) + annotation->type = NO_REPLY; + } + else if (!strcmp(key, VALUE_ATTR)) + { + unsigned i; + annotation->value = strdup(value); + for (i = 0; annotation->value[i]; i++) + annotation->value[i] = tolower(annotation->value[i]); + } + + return EINA_TRUE; +} + +static Eina_Bool +open_annotation(const char *content, unsigned length) +{ + DBus_Annotation annotation; + Eina_Bool r; + + annotation.type = INVALID; + r = attributes_parse(content, length, annotation_attributes_parser, &annotation); + + if (annotation.type == NO_REPLY) + { + Eina_Bool value = EINA_FALSE; + if (!strcmp(annotation.value, "true")) + value = EINA_TRUE; + free(annotation.value); + + if (method) + method->no_reply = value; + } + + return r; +} + +static Eina_Bool +open_arg(const char *content, unsigned length) +{ + Eina_Bool r; + unsigned int *without_name; + DBus_Arg *arg = calloc(1, sizeof(DBus_Arg)); + EINA_SAFETY_ON_NULL_RETURN_VAL(arg, EINA_FALSE); + + r = attributes_parse(content, length, arg_attributes_parser, arg); + if (d_signal) + { + d_signal->args = eina_inlist_append(d_signal->args, EINA_INLIST_GET(arg)); + without_name = &d_signal->arg_without_name; + } + else if (method) + { + method->args = eina_inlist_append(method->args, EINA_INLIST_GET(arg)); + without_name = &method->arg_without_name; + } + else + { + printf("Error find an argument without any valid parent.\n"); + return EINA_FALSE; + } + + if (!arg->name) + { + arg->c_name = string_build("arg%d", *without_name); + (*without_name)++; + } + else + arg->c_name = dbus_name_to_c(arg->name); + + return r; +} + +static Eina_Bool +open_method(const char *content, unsigned lenght) +{ + Eina_Bool r; + char *tmp; + int i; + + method = method_new(iface); + EINA_SAFETY_ON_NULL_RETURN_VAL(method, EINA_FALSE); + + r = attributes_parse(content, lenght, method_attributes_parser, NULL); + if (!method->name) + { + method_free(method); + method = NULL; + printf("Error method without name.\n"); + return EINA_FALSE; + } + + tmp = dbus_name_to_c(method->name); + method->c_name = string_build("%s_%s", iface->c_name, tmp); + free(tmp); + method->cb_name = string_build("cb_%s", method->c_name); + method->function_cb = string_build("%s_Cb", method->c_name); + method->function_cb[0] = toupper(method->function_cb[0]); + for (i = 0; method->function_cb[i]; i++) + { + if (method->function_cb[i] == '_' && method->function_cb[i+1]) + method->function_cb[i+1] = toupper(method->function_cb[i+1]); + } + + return r; +} + +static Eina_Bool +open_property(const char *content, unsigned length, Eina_Bool is_open_empty) +{ + Eina_Bool r; + char *tmp; + + property = property_new(iface); + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EINA_FALSE); + + r = attributes_parse(content, length, property_attributes_parser, NULL); + if (!property->name) + { + property_free(property); + property = NULL; + printf("Error property without name.\n"); + return EINA_FALSE; + } + + tmp = dbus_name_to_c(property->name); + property->c_name = string_build("%s_%s", iface->c_name, tmp); + free(tmp); + property->cb_name = string_build("cb_%s", property->c_name); + + return r; +} + +static Eina_Bool +open_tag(const char *content, unsigned length, Eina_Bool is_open_empty, DBus_Object **obj) +{ + unsigned int i; + if (!strncmp(content, OBJECT_TAG, OBJECT_TAG_LENGHT)) + return open_object(content, length, is_open_empty, obj); + else if (!strncmp(content, INTERFACE_TAG, INTERFACE_TAG_LENGHT) && *obj) + return open_interface(content, length, is_open_empty, *obj); + else if (!strncmp(content, SIGNAL_TAG, SIGNAL_TAG_LENGHT) && iface) + return open_signal(content, length, is_open_empty); + else if (!strncmp(content, ARG_TAG, ARG_TAG_LENGHT) && iface) + return open_arg(content, length); + else if (!strncmp(content, ANNOTATION_TAG, ANNOTATION_TAG_LENGHT) && iface) + return open_annotation(content, length); + else if (!strncmp(content, METHOD_TAG, METHOD_TAG_LENGHT) && iface) + return open_method(content, length); + else if (!strncmp(content, PROPERTY_TAG, PROPERTY_TAG_LENGHT) && iface) + return open_property(content, length, is_open_empty); + else if (!strncmp(content, ANNOTATION_TAG, ANNOTATION_TAG_LENGHT) && iface) + return EINA_TRUE; + + printf("Warning: Tag not handled:\n"); + for (i = 0; i < length; i++) + printf("%c", content[i]); + printf("\n\n"); + + return EINA_TRUE; +} + +static void +interface_close(void) +{ + //its not necessary generate code to FreeDesktop interfaces + if (!strncmp(iface->name, DBUS_INTERFACE, strlen(DBUS_INTERFACE))) + interface_free(iface); + iface = NULL; +} + +static void +signal_close(void) +{ + DBus_Arg *arg; + EINA_INLIST_FOREACH(d_signal->args, arg) + { + if ((arg->type[1]) || (arg->type[0] == 'v')) + { + d_signal->complex = EINA_TRUE; + break; + } + } + d_signal = NULL; +} + +static void +method_close(void) +{ + DBus_Arg *arg; + EINA_INLIST_FOREACH(method->args, arg) + { + if ((arg->type[1]) || (arg->type[0] == 'v')) + { + if (arg->direction == 'o') + method->out_complex = EINA_TRUE; + else + method->in_complex = EINA_TRUE; + } + } + if (method->no_reply) + { + free(method->cb_name); + method->cb_name = strdup("NULL"); + } + method = NULL; +} + +static Eina_Bool +close_tag(const char *content, unsigned length) +{ + if (!strncmp(content, INTERFACE_TAG, INTERFACE_TAG_LENGHT)) + interface_close(); + if (!strncmp(content, SIGNAL_TAG, SIGNAL_TAG_LENGHT)) + signal_close(); + else if (!strncmp(content, METHOD_TAG, METHOD_TAG_LENGHT)) + method_close(); + else if (!strncmp(content, PROPERTY_TAG, PROPERTY_TAG_LENGHT)) + property = NULL; + + return EINA_TRUE; +} + +Eina_Bool +parser(void *data, Eina_Simple_XML_Type type, const char *content, unsigned offset, unsigned length) +{ + Eina_Bool r = EINA_TRUE; + DBus_Object **obj = data; + + switch (type) + { + case EINA_SIMPLE_XML_OPEN: + case EINA_SIMPLE_XML_OPEN_EMPTY: + { + r = open_tag(content, length, type == EINA_SIMPLE_XML_OPEN_EMPTY, + obj); + break; + } + case EINA_SIMPLE_XML_CLOSE: + { + r = close_tag(content, length); + break; + } + default: + break; + } + return r; +} + +static Eina_Bool +attributes_parse(const char *content, unsigned length, Eina_Simple_XML_Attribute_Cb func, const void *data) +{ + const char *attrs = eina_simple_xml_tag_attributes_find(content, length); + unsigned attrslen = 0; + if (attrs) + { + attrslen = length - (attrs - content); + if (!eina_simple_xml_attributes_parse(attrs, attrslen, func, data)) + { + printf("Parser error - attrs=%s | content=%s\n", attrs, content); + return EINA_FALSE; + } + } + return EINA_TRUE; +} diff --git a/src/codegen/source_client.c b/src/codegen/source_client.c new file mode 100644 index 0000000..536b47d --- /dev/null +++ b/src/codegen/source_client.c @@ -0,0 +1,629 @@ +#include "codegen.h" + +static const char *code_prefix = NULL; +static char buffer[4028]; + +static const char * +prefix_append(const char *text) +{ + if (code_prefix) + { + sprintf(buffer, "%s_%s", code_prefix, text); + return buffer; + } + return text; +} + +static const char * +dbus_type2c_type2(const char *dbus_type, Eina_Bool with_const) +{ + switch (dbus_type[0]) + { + case 's'://string + case 'o'://object path + case 'g'://signature + { + if (with_const) + return "const char *"; + else + return "char *"; + } + case 'h'://file descriptor + case 'i'://int + return "int "; + case 'y'://byte + return "unsigned char "; + case 'b'://bool + return "Eina_Bool "; + case 'n'://int16 + return "short int "; + case 'q'://uint16 + return "unsigned short int "; + case 'u'://uint32 + return "unsigned int "; + case 'x'://int64 + return "int64_t "; + case 't'://uint64 + return "uint64_t "; + case 'd'://double + return "double "; + case 'a'://array + case 'v'://variant + case '{'://dict + case '('://struct + return "EDBus_Message_Iter *"; + default: + { + printf("Error type not handled: %c\n", dbus_type[0]); + return NULL; + } + } +} + +static const char * +dbus_type2c_type(const char *dbus_type) +{ + return dbus_type2c_type2(dbus_type, EINA_TRUE); +} + +static void +source_client_complex_method_call_generate(const DBus_Method *method, Eina_Strbuf *c_code, Eina_Strbuf *h) +{ + DBus_Arg *arg; + Eina_Strbuf *full_signature = eina_strbuf_new(); + + EINA_INLIST_FOREACH(method->args, arg) + { + if (arg->direction == 'o') + continue; + eina_strbuf_append(full_signature, arg->type); + } + + if (method->no_reply) + { + eina_strbuf_append_printf(h, "void %s_call(EDBus_Proxy *proxy, Eina_Value *args);\n", prefix_append(method->c_name)); + + eina_strbuf_append_printf(c_code, "\nvoid \n%s_call(EDBus_Proxy *proxy, Eina_Value *args)\n{\n", prefix_append(method->c_name)); + eina_strbuf_append_printf(c_code, " EINA_SAFETY_ON_NULL_RETURN(proxy);\n"); + eina_strbuf_append_printf(c_code, " EDBus_Message *msg = edbus_proxy_method_call_new(proxy, \"%s\");\n", method->name); + eina_strbuf_append_printf(c_code, " if (!edbus_message_from_eina_value(\"%s\", msg, args))\n", eina_strbuf_string_get(full_signature)); + eina_strbuf_append_printf(c_code, " {\n"); + eina_strbuf_append_printf(c_code, " ERR(\"Error: Filling message from eina value.\");\n"); + eina_strbuf_append_printf(c_code, " return;\n"); + eina_strbuf_append_printf(c_code, " }\n"); + eina_strbuf_append_printf(c_code, " edbus_proxy_send(proxy, msg, %s, NULL, NULL, -1);\n", method->cb_name); + eina_strbuf_append_printf(c_code, "}\n"); + goto end; + } + + eina_strbuf_append_printf(h, "EDBus_Pending *%s_call", prefix_append(method->c_name)); + eina_strbuf_append_printf(h, "(EDBus_Proxy *proxy, %s cb, const void *data, Eina_Value *args);\n", prefix_append(method->function_cb)); + + eina_strbuf_append_printf(c_code, "\nEDBus_Pending *\n%s_call(", prefix_append(method->c_name)); + eina_strbuf_append_printf(c_code, "EDBus_Proxy *proxy, %s cb, const void *data, Eina_Value *args)\n{\n", prefix_append(method->function_cb)); + eina_strbuf_append_printf(c_code, " EDBus_Message *msg;\n"); + eina_strbuf_append_printf(c_code, " EDBus_Pending *p;\n"); + eina_strbuf_append_printf(c_code, " EINA_SAFETY_ON_NULL_RETURN_VAL(proxy, NULL);\n"); + eina_strbuf_append_printf(c_code, " msg = edbus_proxy_method_call_new(proxy, \"%s\");\n", method->name); + eina_strbuf_append_printf(c_code, " if (!edbus_message_from_eina_value(\"%s\", msg, args))\n", eina_strbuf_string_get(full_signature)); + eina_strbuf_append_printf(c_code, " {\n"); + eina_strbuf_append_printf(c_code, " ERR(\"Error: Filling message from eina value.\");\n"); + eina_strbuf_append_printf(c_code, " return NULL;\n"); + eina_strbuf_append_printf(c_code, " }\n"); + eina_strbuf_append_printf(c_code, " p = edbus_proxy_send(proxy, msg, %s, cb, -1);\n", method->cb_name); + eina_strbuf_append_printf(c_code, " if (data)\n"); + eina_strbuf_append_printf(c_code, " edbus_pending_data_set(p, \"__user_data\", data);\n"); + eina_strbuf_append_printf(c_code, " edbus_pending_data_set(p, \"__proxy\", proxy);\n"); + eina_strbuf_append_printf(c_code, " return p;\n"); + eina_strbuf_append_printf(c_code, "}\n"); + +end: + eina_strbuf_free(full_signature); +} + +static void +source_client_simple_method_call_no_reply_generate(const DBus_Method *method, Eina_Strbuf *c_code, Eina_Strbuf *h) +{ + DBus_Arg *arg; + Eina_Strbuf *full_signature = eina_strbuf_new(); + Eina_Strbuf *args_call = eina_strbuf_new(); + + eina_strbuf_append_printf(h, "void %s_call(EDBus_Proxy *proxy", prefix_append(method->c_name)); + eina_strbuf_append_printf(c_code, "\nvoid\n%s_call(EDBus_Proxy *proxy", prefix_append(method->c_name)); + + EINA_INLIST_FOREACH(method->args, arg) + { + if (arg->direction == 'o') + continue; + eina_strbuf_append(full_signature, arg->type); + eina_strbuf_append_printf(h, ", %s%s", dbus_type2c_type(arg->type), arg->c_name); + eina_strbuf_append_printf(c_code, ", %s%s", dbus_type2c_type(arg->type), arg->c_name); + eina_strbuf_append_printf(args_call, ", %s", arg->c_name); + } + eina_strbuf_append_printf(h, ");\n"); + eina_strbuf_append_printf(c_code, ")\n{\n"); + + eina_strbuf_append_printf(c_code, " EDBus_Message *msg;\n"); + eina_strbuf_append_printf(c_code, " EINA_SAFETY_ON_NULL_RETURN(proxy);\n"); + eina_strbuf_append_printf(c_code, " msg = edbus_proxy_method_call_new(proxy, \"%s\");\n", method->name); + eina_strbuf_append_printf(c_code, " if (!edbus_message_arguments_append(msg, \"%s\"%s))\n", eina_strbuf_string_get(full_signature), eina_strbuf_string_get(args_call)); + eina_strbuf_append_printf(c_code, " {\n"); + eina_strbuf_append_printf(c_code, " ERR(\"Error: Filling message.\");\n"); + eina_strbuf_append_printf(c_code, " return;\n"); + eina_strbuf_append_printf(c_code, " }\n"); + eina_strbuf_append_printf(c_code, " edbus_proxy_send(proxy, msg, NULL, NULL, -1);\n"); + eina_strbuf_append_printf(c_code, "}\n"); + + eina_strbuf_free(full_signature); + eina_strbuf_free(args_call); +} + +static void +source_client_simple_method_call_generate(const DBus_Method *method, Eina_Strbuf *c_code, Eina_Strbuf *h) +{ + DBus_Arg *arg; + Eina_Strbuf *full_signature = eina_strbuf_new(); + Eina_Strbuf *args_call = eina_strbuf_new(); + + eina_strbuf_append_printf(h, "EDBus_Pending *%s_call", prefix_append(method->c_name)); + eina_strbuf_append_printf(h, "(EDBus_Proxy *proxy, %s cb, const void *data", prefix_append(method->function_cb)); + eina_strbuf_append_printf(c_code, "\nEDBus_Pending *\n%s_call", prefix_append(method->c_name)); + eina_strbuf_append_printf(c_code, "(EDBus_Proxy *proxy, %s cb, const void *data", prefix_append(method->function_cb)); + + EINA_INLIST_FOREACH(method->args, arg) + { + if (arg->direction == 'o') + continue; + eina_strbuf_append(full_signature, arg->type); + eina_strbuf_append_printf(h, ", %s%s", dbus_type2c_type(arg->type), arg->c_name); + eina_strbuf_append_printf(c_code, ", %s%s", dbus_type2c_type(arg->type), arg->c_name); + eina_strbuf_append_printf(args_call, ", %s", arg->c_name); + } + eina_strbuf_append_printf(h, ");\n"); + eina_strbuf_append_printf(c_code,")\n{\n"); + + eina_strbuf_append_printf(c_code, " EDBus_Message *msg;\n"); + eina_strbuf_append_printf(c_code, " EDBus_Pending *p;\n"); + eina_strbuf_append_printf(c_code, " EINA_SAFETY_ON_NULL_RETURN_VAL(proxy, NULL);\n"); + eina_strbuf_append_printf(c_code, " msg = edbus_proxy_method_call_new(proxy, \"%s\");\n", method->name); + eina_strbuf_append_printf(c_code, " if (!edbus_message_arguments_append(msg, \"%s\"%s))\n", eina_strbuf_string_get(full_signature), eina_strbuf_string_get(args_call)); + eina_strbuf_append_printf(c_code, " {\n"); + eina_strbuf_append_printf(c_code, " ERR(\"Error: Filling message.\");\n"); + eina_strbuf_append_printf(c_code, " return NULL;\n"); + eina_strbuf_append_printf(c_code, " }\n"); + eina_strbuf_append_printf(c_code, " p = edbus_proxy_send(proxy, msg, %s, cb, -1);\n", method->cb_name); + eina_strbuf_append_printf(c_code, " if (data)\n"); + eina_strbuf_append_printf(c_code, " edbus_pending_data_set(p, \"__user_data\", data);\n"); + eina_strbuf_append_printf(c_code, " edbus_pending_data_set(p, \"__proxy\", proxy);\n"); + eina_strbuf_append_printf(c_code, " return p;\n"); + eina_strbuf_append_printf(c_code, "}\n"); + + eina_strbuf_free(full_signature); + eina_strbuf_free(args_call); +} + +static void +source_client_complex_method_callback_generate(const DBus_Method *method, Eina_Strbuf *c_code, Eina_Strbuf *h) +{ + eina_strbuf_append_printf(h, "typedef void (*%s)(EDBus_Proxy *proxy, void *data, EDBus_Pending *pending, EDBus_Error_Info *error, Eina_Value *args);\n", prefix_append(method->function_cb)); + + eina_strbuf_append_printf(c_code, "\nstatic void\n%s(void *data, const EDBus_Message *msg, EDBus_Pending *pending)\n{\n", method->cb_name); + eina_strbuf_append_printf(c_code, " void *user_data = edbus_pending_data_del(pending, \"__user_data\");\n"); + eina_strbuf_append_printf(c_code, " %s cb = data;\n", prefix_append(method->function_cb)); + eina_strbuf_append_printf(c_code, " const char *error, *error_msg;\n"); + eina_strbuf_append_printf(c_code, " Eina_Value *value;\n"); + eina_strbuf_append_printf(c_code, " EDBus_Proxy *proxy = edbus_pending_data_del(pending, \"__proxy\");\n"); + eina_strbuf_append_printf(c_code, " if (edbus_message_error_get(msg, &error, &error_msg))\n"); + eina_strbuf_append_printf(c_code, " {\n"); + eina_strbuf_append_printf(c_code, " EDBus_Error_Info error_info = {error, error_msg};\n"); + eina_strbuf_append_printf(c_code, " cb(proxy, user_data, pending, &error_info, NULL);\n"); + eina_strbuf_append_printf(c_code, " return;\n"); + eina_strbuf_append_printf(c_code, " }\n"); + eina_strbuf_append_printf(c_code, " value = edbus_message_to_eina_value(msg);\n"); + eina_strbuf_append_printf(c_code, " cb(proxy, user_data, pending, NULL, value);\n"); + eina_strbuf_append_printf(c_code, " eina_value_free(value);\n"); + eina_strbuf_append_printf(c_code, " return;\n"); + eina_strbuf_append_printf(c_code, "}\n"); +} + +static void +source_client_simple_method_callback_generate(const DBus_Method *method, Eina_Strbuf *c_code, Eina_Strbuf *h) +{ + Eina_Strbuf *full_signature = eina_strbuf_new(); + DBus_Arg *arg; + Eina_Strbuf *end_cb = eina_strbuf_new(); + Eina_Strbuf *arguments_get = eina_strbuf_new(); + + eina_strbuf_append_printf(h, "typedef void (*%s)(EDBus_Proxy *proxy, void *data, EDBus_Pending *pending, EDBus_Error_Info *error", prefix_append(method->function_cb)); + + eina_strbuf_append_printf(c_code, "\nstatic void\n%s(void *data, const EDBus_Message *msg, EDBus_Pending *pending)\n{\n", method->cb_name); + eina_strbuf_append_printf(c_code, " void *user_data = edbus_pending_data_del(pending, \"__user_data\");\n"); + eina_strbuf_append_printf(c_code, " %s cb = data;\n", prefix_append(method->function_cb)); + eina_strbuf_append_printf(c_code, " const char *error, *error_msg;\n"); + eina_strbuf_append_printf(c_code, " EDBus_Proxy *proxy = edbus_pending_data_del(pending, \"__proxy\");\n"); + + EINA_INLIST_FOREACH(method->args, arg) + { + if (arg->direction != 'o') + continue; + eina_strbuf_append(full_signature, arg->type); + eina_strbuf_append_printf(h, ", %s%s", dbus_type2c_type(arg->type), arg->c_name); + eina_strbuf_append_printf(c_code, " %s%s;\n", dbus_type2c_type(arg->type), arg->c_name); + eina_strbuf_append_printf(end_cb, ", %s", arg->c_name); + eina_strbuf_append_printf(arguments_get, ", &%s", arg->c_name); + } + eina_strbuf_append_printf(h, ");\n"); + + eina_strbuf_append_printf(c_code, " if (edbus_message_error_get(msg, &error, &error_msg))\n"); + eina_strbuf_append_printf(c_code, " {\n"); + eina_strbuf_append_printf(c_code, " EDBus_Error_Info error_info = {error, error_msg};\n"); + eina_strbuf_append_printf(c_code, " cb(proxy, user_data, pending, &error_info%s);\n", eina_strbuf_string_get(end_cb)); + eina_strbuf_append_printf(c_code, " return;\n"); + eina_strbuf_append_printf(c_code, " }\n"); + + eina_strbuf_append_printf(c_code, " if (!edbus_message_arguments_get(msg, \"%s\"%s))\n", eina_strbuf_string_get(full_signature), eina_strbuf_string_get(arguments_get)); + eina_strbuf_append_printf(c_code, " {\n"); + eina_strbuf_append_printf(c_code, " EDBus_Error_Info error_info = {\"\", \"\"};\n"); + eina_strbuf_append_printf(c_code, " ERR(\"Error: Getting arguments from message.\");\n"); + eina_strbuf_append_printf(c_code, " cb(proxy, user_data, pending, &error_info%s);\n", eina_strbuf_string_get(end_cb)); + eina_strbuf_append_printf(c_code, " return;\n"); + eina_strbuf_append_printf(c_code, " }\n"); + eina_strbuf_append_printf(c_code, " cb(proxy, user_data, pending, NULL%s);\n", eina_strbuf_string_get(end_cb)); + eina_strbuf_append_printf(c_code, " return;\n"); + eina_strbuf_append_printf(c_code, "}\n"); + + eina_strbuf_free(full_signature); + eina_strbuf_free(end_cb); + eina_strbuf_free(arguments_get); +} + +static void +source_client_method_generate(const DBus_Method *method, Eina_Strbuf *c_code, Eina_Strbuf *h) +{ + if (!method->no_reply) + { + if (method->out_complex) + source_client_complex_method_callback_generate(method, c_code, h); + else + source_client_simple_method_callback_generate(method, c_code, h); + } + + if (method->in_complex) + source_client_complex_method_call_generate(method, c_code, h); + else + { + if (method->no_reply) + source_client_simple_method_call_no_reply_generate(method, c_code, h); + else + source_client_simple_method_call_generate(method, c_code, h); + } +} + +static void +source_client_signal_generate(const DBus_Signal *signal, Eina_Strbuf *c_code, Eina_Strbuf * h, Eina_Strbuf *c_init_function, Eina_Strbuf *c_header) +{ + DBus_Arg *arg; + Eina_Strbuf *full_signature = eina_strbuf_new(); + Eina_Strbuf *parameters = eina_strbuf_new(); + Eina_Strbuf *string_copy = eina_strbuf_new(); + Eina_Strbuf *string_free = eina_strbuf_new(); + + eina_strbuf_append_printf(c_init_function, " edbus_proxy_signal_handler_add(proxy, \"%s\", %s, proxy);\n", signal->name, signal->cb_name); + eina_strbuf_append_printf(c_header, "int %s;\n", signal->signal_event); + eina_strbuf_append_printf(h, "extern int %s;\n", signal->signal_event); + eina_strbuf_append_printf(c_init_function, " if (!%s)\n", signal->signal_event); + eina_strbuf_append_printf(c_init_function, " %s = ecore_event_type_new();\n", signal->signal_event); + + eina_strbuf_append_printf(h, "typedef struct _%s\n", signal->struct_name); + eina_strbuf_append_printf(h, "{\n"); + eina_strbuf_append_printf(h, " EDBus_Proxy *proxy;\n"); + + if (signal->complex) + { + eina_strbuf_append_printf(h, " Eina_Value *value;\n"); + goto jump_simple_stuff; + } + + EINA_INLIST_FOREACH(signal->args, arg) + { + eina_strbuf_append(full_signature, arg->type); + eina_strbuf_append_printf(parameters, ", &s_data->%s", arg->c_name); + eina_strbuf_append_printf(h, " %s%s;\n", dbus_type2c_type2(arg->type, EINA_FALSE), arg->c_name); + + if (!strcmp(arg->type, "s") || !strcmp(arg->type, "o")) + { + eina_strbuf_append_printf(string_copy, " s_data->%s = strdup(s_data->%s);\n", arg->c_name, arg->c_name); + eina_strbuf_append_printf(string_free, " free(s_data->%s);\n", arg->c_name); + } + } + +jump_simple_stuff: + eina_strbuf_append_printf(h, "} %s;\n", signal->struct_name); + + //free function + eina_strbuf_append_printf(c_code, "\nstatic void\n%s(void *user_data, void *func_data)\n{\n", signal->free_function); + eina_strbuf_append_printf(c_code, " %s *s_data = user_data;\n", signal->struct_name); + if (signal->complex) + eina_strbuf_append(c_code, " eina_value_free(s_data->proxy);\n"); + else + eina_strbuf_append(c_code, eina_strbuf_string_get(string_free)); + eina_strbuf_append_printf(c_code, " free(s_data);\n"); + eina_strbuf_append_printf(c_code, "}\n"); + + //cb function + eina_strbuf_append_printf(c_code, "\nstatic void\n%s(void *data, const EDBus_Message *msg)\n{\n", signal->cb_name); + eina_strbuf_append_printf(c_code, " EDBus_Proxy *proxy = data;\n"); + eina_strbuf_append_printf(c_code, " %s *s_data = calloc(1, sizeof(%s));\n", signal->struct_name, signal->struct_name); + eina_strbuf_append_printf(c_code, " s_data->proxy = proxy;\n"); + if (signal->complex) + { + eina_strbuf_append_printf(c_code, " s_data->value = edbus_message_to_eina_value(msg);\n"); + goto end_signal; + } + eina_strbuf_append_printf(c_code, " if (!edbus_message_arguments_get(msg, \"%s\"%s))\n", eina_strbuf_string_get(full_signature), eina_strbuf_string_get(parameters)); + eina_strbuf_append_printf(c_code, " {\n"); + eina_strbuf_append_printf(c_code, " ERR(\"Error: Getting arguments from message.\");\n"); + eina_strbuf_append_printf(c_code, " return;\n"); + eina_strbuf_append_printf(c_code, " }\n"); + eina_strbuf_append(c_code, eina_strbuf_string_get(string_copy)); + +end_signal: + eina_strbuf_append_printf(c_code, " ecore_event_add(%s, s_data, %s, NULL);\n", signal->signal_event, signal->free_function); + eina_strbuf_append_printf(c_code, "}\n"); + + eina_strbuf_free(full_signature); + eina_strbuf_free(parameters); + eina_strbuf_free(string_copy); + eina_strbuf_free(string_free); +} + +static const char * +prop_cb_get(const DBus_Property *prop) +{ + if (prop->complex) + return "EDBus_Codegen_Property_Complex_Get_Cb"; + switch (prop->type[0]) + { + case 's': + case 'o': + return "EDBus_Codegen_Property_String_Get_Cb"; + case 'i': + case 'h': + return "EDBus_Codegen_Property_Int32_Get_Cb"; + case 'y': + return "EDBus_Codegen_Property_Byte_Get_Cb"; + case 'b': + return "EDBus_Codegen_Property_Bool_Get_Cb"; + case 'n': + return "EDBus_Codegen_Property_Int16_Get_Cb"; + case 'q': + return "EDBus_Codegen_Property_Uint16_Get_Cb"; + case 'u': + return "EDBus_Codegen_Property_Uint32_Get_Cb"; + case 'd': + return "EDBus_Codegen_Property_Double_Get_Cb"; + case 'x': + return "EDBus_Codegen_Property_Int64_Get_Cb"; + case 't': + return "EDBus_Codegen_Property_Uint64_Get_Cb"; + default: + return "Unexpected_type"; + } +} + +static const char * +null_or_zero(const char *type) +{ + if (type[0] == 's' || type[0] == 'o' || type[0] == 'v' || type[1]) + return "NULL"; + return "0"; +} + +static void +source_client_property_generate_get(const DBus_Property *prop, Eina_Strbuf *c_code, Eina_Strbuf *h) +{ + //callback + eina_strbuf_append_printf(c_code, "\nstatic void\n%s(void *data, const EDBus_Message *msg, EDBus_Pending *pending)\n{\n", prop->cb_name); + eina_strbuf_append_printf(c_code, " void *user_data = edbus_pending_data_del(pending, \"__user_data\");\n"); + eina_strbuf_append_printf(c_code, " const char *error, *error_msg;\n"); + eina_strbuf_append_printf(c_code, " %s cb = data;\n", prop_cb_get(prop)); + eina_strbuf_append_printf(c_code, " EDBus_Proxy *proxy = edbus_pending_data_del(pending, \"__proxy\");\n"); + eina_strbuf_append_printf(c_code, " EDBus_Message_Iter *variant;\n"); + if (prop->complex) + eina_strbuf_append_printf(c_code, " Eina_Value *v, stack_value;\n"); + else + eina_strbuf_append_printf(c_code, " %sv;\n", dbus_type2c_type(prop->type)); + eina_strbuf_append_printf(c_code, " if (edbus_message_error_get(msg, &error, &error_msg))\n"); + eina_strbuf_append_printf(c_code, " {\n"); + eina_strbuf_append_printf(c_code, " EDBus_Error_Info error_info = {error, error_msg};\n"); + eina_strbuf_append_printf(c_code, " cb(user_data, pending, \"%s\", proxy, &error_info, %s);\n", prop->name, null_or_zero(prop->type)); + eina_strbuf_append_printf(c_code, " return;\n"); + eina_strbuf_append_printf(c_code, " }\n"); + + eina_strbuf_append_printf(c_code, " if (!edbus_message_arguments_get(msg, \"v\", &variant))\n"); + eina_strbuf_append_printf(c_code, " {\n"); + eina_strbuf_append_printf(c_code, " EDBus_Error_Info error_info = {\"\", \"\"};\n"); + eina_strbuf_append_printf(c_code, " cb(user_data, pending, \"%s\", proxy, &error_info, %s);\n", prop->name, null_or_zero(prop->type)); + eina_strbuf_append_printf(c_code, " return;\n"); + eina_strbuf_append_printf(c_code, " }\n"); + + if (prop->complex) + { + eina_strbuf_append_printf(c_code, " v = edbus_message_iter_struct_like_to_eina_value(variant);\n"); + eina_strbuf_append_printf(c_code, " eina_value_struct_value_get(v, \"arg0\", &stack_value);\n"); + eina_strbuf_append_printf(c_code, " cb(user_data, pending, \"%s\", proxy, NULL, &stack_value);\n", prop->name); + eina_strbuf_append_printf(c_code, " eina_value_flush(&stack_value);\n"); + eina_strbuf_append_printf(c_code, " eina_value_free(v);\n"); + } + else + { + eina_strbuf_append_printf(c_code, " if (!edbus_message_iter_arguments_get(variant, \"%s\", &v))\n", prop->type); + eina_strbuf_append_printf(c_code, " {\n"); + eina_strbuf_append_printf(c_code, " EDBus_Error_Info error_info = {\"\", \"\"};\n"); + eina_strbuf_append_printf(c_code, " cb(user_data, pending, \"%s\", proxy, &error_info, %s);\n", prop->name, null_or_zero(prop->type)); + eina_strbuf_append_printf(c_code, " return;\n"); + eina_strbuf_append_printf(c_code, " }\n"); + eina_strbuf_append_printf(c_code, " cb(user_data, pending, \"%s\", proxy, NULL, v);\n", prop->name); + } + eina_strbuf_append_printf(c_code, "}\n"); + + //call + eina_strbuf_append_printf(h, "EDBus_Pending *%s_propget(EDBus_Proxy *proxy, %s cb, const void *data);\n", prefix_append(prop->c_name), prop_cb_get(prop)); + + eina_strbuf_append_printf(c_code, "\nEDBus_Pending *\n%s_propget(EDBus_Proxy *proxy, %s cb, const void *data)\n{\n", prefix_append(prop->c_name), prop_cb_get(prop)); + eina_strbuf_append_printf(c_code, " EDBus_Pending *p;\n"); + eina_strbuf_append_printf(c_code, " EINA_SAFETY_ON_NULL_RETURN_VAL(proxy, NULL);\n"); + eina_strbuf_append_printf(c_code, " p = edbus_proxy_property_get(proxy, \"%s\", %s, cb);\n", prop->name, prop->cb_name); + eina_strbuf_append_printf(c_code, " if (data)\n"); + eina_strbuf_append_printf(c_code, " edbus_pending_data_set(p, \"__user_data\", data);\n"); + eina_strbuf_append_printf(c_code, " edbus_pending_data_set(p, \"__proxy\", proxy);\n"); + eina_strbuf_append_printf(c_code, " return p;\n"); + eina_strbuf_append_printf(c_code, "}\n"); +} + +static void +source_client_property_generate_set(const DBus_Property *prop, Eina_Strbuf *c_code, Eina_Strbuf *h) +{ + //callback + eina_strbuf_append_printf(c_code, "\nstatic void\n%s_set(void *data, const EDBus_Message *msg, EDBus_Pending *pending)\n{\n", prop->cb_name); + eina_strbuf_append_printf(c_code, " const char *error, *error_msg;\n"); + eina_strbuf_append_printf(c_code, " void *user_data = edbus_pending_data_del(pending, \"__user_data\");\n"); + eina_strbuf_append_printf(c_code, " EDBus_Proxy *proxy = edbus_pending_data_del(pending, \"__proxy\");\n"); + eina_strbuf_append_printf(c_code, " EDBus_Codegen_Property_Set_Cb cb = data;\n"); + eina_strbuf_append_printf(c_code, " if (edbus_message_error_get(msg, &error, &error_msg))"); + eina_strbuf_append_printf(c_code, " {\n"); + eina_strbuf_append_printf(c_code, " EDBus_Error_Info error_info = {error, error_msg};\n\n"); + eina_strbuf_append_printf(c_code, " cb(user_data, \"%s\", proxy, pending, &error_info);\n", prop->name); + eina_strbuf_append_printf(c_code, " return;\n"); + eina_strbuf_append_printf(c_code, " }\n"); + eina_strbuf_append_printf(c_code, " cb(user_data, \"%s\", proxy, pending, NULL);\n", prop->name); + eina_strbuf_append_printf(c_code, "}\n"); + + //call + eina_strbuf_append_printf(h, "EDBus_Pending *%s_propset(EDBus_Proxy *proxy, EDBus_Codegen_Property_Set_Cb cb, const void *data, const void *value);\n", prefix_append(prop->c_name)); + + eina_strbuf_append_printf(c_code, "\nEDBus_Pending *\n%s_propset(EDBus_Proxy *proxy, EDBus_Codegen_Property_Set_Cb cb, const void *data, const void *value)\n{\n", prop->c_name); + eina_strbuf_append_printf(c_code, " EDBus_Pending *p;\n"); + eina_strbuf_append_printf(c_code, " EINA_SAFETY_ON_NULL_RETURN_VAL(proxy, NULL);\n"); + eina_strbuf_append_printf(c_code, " EINA_SAFETY_ON_NULL_RETURN_VAL(value, NULL);\n"); + eina_strbuf_append_printf(c_code, " p = edbus_proxy_property_set(proxy, \"%s\", \"%s\", value, %s_set, data);\n", prop->name, prop->type, prop->cb_name); + eina_strbuf_append_printf(c_code, " edbus_pending_data_set(p, \"__user_data\", data);\n"); + eina_strbuf_append_printf(c_code, " edbus_pending_data_set(p, \"__proxy\", proxy);\n"); + eina_strbuf_append_printf(c_code, " return p;\n"); + eina_strbuf_append_printf(c_code, "}\n"); +} + +static void +source_client_property_generate(const DBus_Property *prop, Eina_Strbuf *c_code, Eina_Strbuf *h) +{ + if ((prop->access & ACCESS_READ) == ACCESS_READ) + source_client_property_generate_get(prop, c_code, h); + if ((prop->access & ACCESS_WRITE) == ACCESS_WRITE) + source_client_property_generate_set(prop, c_code, h); +} + +void +source_client_generate(DBus_Object *path, const char *prefix, const char *interface_name, const char *output_name) +{ + DBus_Interface *iface; + Eina_Bool found = EINA_FALSE; + code_prefix = prefix; + EINA_INLIST_FOREACH(path->ifaces, iface) + { + Eina_Strbuf *h, *c_init_function, *c_header, *c_code; + DBus_Method *method; + DBus_Signal *signal; + DBus_Property *prop; + char *file_name, *aux; + int i; + + if (interface_name && strcmp(interface_name, iface->name)) + continue; + found = EINA_TRUE; + h = eina_strbuf_new();//.h file + c_init_function = eina_strbuf_new(); + c_header = eina_strbuf_new(); + c_code = eina_strbuf_new(); + + aux = string_build("EDBUS_%s_H", iface->c_name); + for (i = 0; aux[i]; i++) + aux[i] = toupper(aux[i]); + eina_strbuf_append_printf(h, "#ifndef %s\n", aux); + eina_strbuf_append_printf(h, "#define %s\n\n", aux); + free(aux); + + eina_strbuf_append_printf(h, "#include \n"); + eina_strbuf_append_printf(h, "#include \n"); + eina_strbuf_append_printf(h, "#include \n"); + eina_strbuf_append_printf(h, "#include \"edbus_utils.h\"\n\n"); + eina_strbuf_append_printf(h, "EDBus_Proxy *%s_proxy_get(EDBus_Connection *conn, const char *bus, const char *path);\n", prefix_append(iface->c_name)); + eina_strbuf_append_printf(h, "void %s_proxy_unref(EDBus_Proxy *proxy);\n", prefix_append(iface->c_name)); + eina_strbuf_append_printf(h, "void %s_log_domain_set(int id);\n", prefix_append(iface->c_name)); + + if (interface_name && output_name) + eina_strbuf_append_printf(c_header, "#include \"%s.h\"\n\n", output_name); + else + eina_strbuf_append_printf(c_header, "#include \"edbus_%s.h\"\n\n", iface->c_name); + + eina_strbuf_append_printf(c_header, "static int _log_main = -1;\n"); + eina_strbuf_append_printf(c_header, "#undef ERR\n"); + eina_strbuf_append_printf(c_header, "#define ERR(...) EINA_LOG_DOM_ERR(_log_main, __VA_ARGS__);\n"); + + eina_strbuf_append_printf(c_init_function, "void\n%s_log_domain_set(int id)\n{\n", prefix_append(iface->c_name)); + eina_strbuf_append_printf(c_init_function, " _log_main = id;\n"); + eina_strbuf_append_printf(c_init_function, "}\n"); + + eina_strbuf_append_printf(c_init_function, "\nvoid\n%s_proxy_unref(EDBus_Proxy *proxy)\n{\n", prefix_append(iface->c_name)); + eina_strbuf_append_printf(c_init_function, " EDBus_Object *obj = edbus_proxy_object_get(proxy);\n"); + eina_strbuf_append_printf(c_init_function, " edbus_proxy_unref(proxy);\n"); + eina_strbuf_append_printf(c_init_function, " edbus_object_unref(obj);\n"); + eina_strbuf_append_printf(c_init_function, "}\n"); + + eina_strbuf_append_printf(c_init_function, "\nEDBus_Proxy *\n%s_proxy_get(EDBus_Connection *conn, const char *bus, const char *path)\n{\n", prefix_append(iface->c_name)); + eina_strbuf_append_printf(c_init_function, " EDBus_Object *obj;\n"); + eina_strbuf_append_printf(c_init_function, " EDBus_Proxy *proxy;\n"); + eina_strbuf_append_printf(c_init_function, " EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);\n"); + eina_strbuf_append_printf(c_init_function, " EINA_SAFETY_ON_NULL_RETURN_VAL(bus, NULL);\n"); + eina_strbuf_append_printf(c_init_function, " if (!path) path = \"%s\";\n", path->name); + eina_strbuf_append_printf(c_init_function, " obj = edbus_object_get(conn, bus, path);\n"); + eina_strbuf_append_printf(c_init_function, " proxy = edbus_proxy_get(obj, \"%s\");\n", iface->name); + + EINA_INLIST_FOREACH(iface->methods, method) + source_client_method_generate(method, c_code, h); + + EINA_INLIST_FOREACH(iface->signals, signal) + source_client_signal_generate(signal, c_code, h, c_init_function, c_header); + + EINA_INLIST_FOREACH(iface->properties, prop) + source_client_property_generate(prop, c_code, h); + + eina_strbuf_append_printf(c_init_function, " return proxy;\n"); + eina_strbuf_append_printf(c_init_function, "}\n"); + + eina_strbuf_append(h, "\n#endif"); + + if (interface_name && output_name) + file_name = string_build("%s.h", output_name); + else + file_name = string_build("edbus_%s.h", iface->c_name); + file_write(file_name, eina_strbuf_string_get(h)); + eina_strbuf_free(h); + free(file_name); + + eina_strbuf_append(c_header, eina_strbuf_string_get(c_code)); + eina_strbuf_free(c_code); + eina_strbuf_append(c_header, "\n"); + eina_strbuf_append(c_header, eina_strbuf_string_get(c_init_function)); + eina_strbuf_free(c_init_function); + if (interface_name && output_name) + file_name = string_build("%s.c", output_name); + else + file_name = string_build("edbus_%s.c", iface->c_name); + file_write(file_name, eina_strbuf_string_get(c_header)); + eina_strbuf_free(c_header); + free(file_name); + } + + if (interface_name && !found) + printf("Error: Interface %s not found.\n", interface_name); +} diff --git a/src/codegen/utils.c b/src/codegen/utils.c new file mode 100644 index 0000000..a7d6273 --- /dev/null +++ b/src/codegen/utils.c @@ -0,0 +1,166 @@ +#include "codegen.h" +#include + +Eina_Bool +file_read(const char *file_name, char **buffer) +{ + FILE *xml_handler; + char data; + Eina_Strbuf *buf; + + xml_handler = fopen(file_name, "rt"); + if (!xml_handler) + { + printf("Error to read file: %s\n", file_name); + return EINA_FALSE; + } + buf = eina_strbuf_new(); + + while ((data = fgetc(xml_handler)) != EOF) + eina_strbuf_append_char(buf, data); + + fclose(xml_handler); + *buffer = strdup(eina_strbuf_string_get(buf)); + eina_strbuf_free(buf); + + return EINA_TRUE; +} + +Eina_Bool +file_write(const char *file_name, const char *buffer) +{ + FILE *file_handler; + + file_handler = fopen(file_name, "wt"); + if (!file_handler) + { + printf("Error to write file: %s\n", file_name); + return EINA_FALSE; + } + + fwrite(buffer, strlen(buffer), 1, file_handler); + fclose(file_handler); + + return EINA_TRUE; +} + +char * +dbus_name_to_c(const char *dbus) +{ + char *str_cpy = strdup(dbus), *pch, *ret; + Eina_Strbuf *buffer = eina_strbuf_new(); + unsigned i; + + pch = strtok(str_cpy, "/."); + if (!pch) + { + ret = strdup("root"); + goto end; + } + eina_strbuf_append(buffer, pch); + + while ((pch = strtok(NULL, "/."))) + eina_strbuf_append_printf(buffer, "_%s",pch); + + ret = strdup(eina_strbuf_string_get(buffer)); + eina_strbuf_reset(buffer); + for (i = 0; ret[i]; i++) + { + if (i > 0 && ret[i-1] != '_' && ret[i] > '@' && ret[i] < '[')//upper case + eina_strbuf_append_printf(buffer, "_%c", tolower(ret[i])); + else + eina_strbuf_append_char(buffer, tolower(ret[i])); + } + free(ret); + ret = strdup(eina_strbuf_string_get(buffer)); +end: + free(str_cpy); + eina_strbuf_free(buffer); + return ret; +} + +char * +replace_string(const char *string, const char *substr, const char *replacement) +{ + char *str_cpy = strdup(string); + char *pch; + char *ret; + Eina_Strbuf *buffer = eina_strbuf_new(); + + pch = strtok(str_cpy, substr); + eina_strbuf_append(buffer, pch); + + while ((pch = strtok(NULL, substr))) + eina_strbuf_append_printf(buffer, "%s%s", replacement, pch); + + ret = strdup(eina_strbuf_string_get(buffer)); + free(str_cpy); + eina_strbuf_free(buffer); + return ret; +} + +char * +get_pieces(const char *string, char break_in, int amount) +{ + int i; + int found = 0; + + for (i = strlen(string) - 1; i && amount > found; i--) + if (string[i] == break_in) + found++; + + if (found) + return strdup(string+i+2); + else + return strdup(string); +} + +char * +string_build(const char *fmt, ...) +{ + va_list ap; + Eina_Strbuf *buffer = eina_strbuf_new(); + char *ret; + + va_start(ap, fmt); + eina_strbuf_prepend_vprintf(buffer, fmt, ap); + va_end(ap); + + ret = strdup(eina_strbuf_string_get(buffer)); + eina_strbuf_free(buffer); + + return ret; +} + +#define UTIL_H "\ +#ifndef EDBUS_UTILS_H\n\ +#define EDBUS_UTILS_H 1\n\ +\n\ +typedef struct _EDBus_Error_Info\n\ +{\n\ + const char *error;\n\ + const char *message;\n\ +} EDBus_Error_Info;\n\ +\n\ +typedef void (*EDBus_Codegen_Property_Set_Cb)(void *data, const char *propname, EDBus_Proxy *proxy, EDBus_Pending *p, EDBus_Error_Info *error_info);\n\ +\n\ +typedef void (*EDBus_Codegen_Property_String_Get_Cb)(void *data, EDBus_Pending *p, const char *propname, EDBus_Proxy *proxy, EDBus_Error_Info *error_info, const char *value);\n\ +typedef void (*EDBus_Codegen_Property_Int32_Get_Cb)(void *data, EDBus_Pending *p, const char *propname, EDBus_Proxy *proxy, EDBus_Error_Info *error_info, int value);\n\ +typedef void (*EDBus_Codegen_Property_Byte_Get_Cb)(void *data, EDBus_Pending *p, const char *propname, EDBus_Proxy *proxy, EDBus_Error_Info *error_info, unsigned char value);\n\ +typedef void (*EDBus_Codegen_Property_Bool_Get_Cb)(void *data, EDBus_Pending *p, const char *propname, EDBus_Proxy *proxy, EDBus_Error_Info *error_info, Eina_Bool value);\n\ +typedef void (*EDBus_Codegen_Property_Int16_Get_Cb)(void *data, EDBus_Pending *p, const char *propname, EDBus_Proxy *proxy, EDBus_Error_Info *error_info, short int value);\n\ +typedef void (*EDBus_Codegen_Property_Uint16_Get_Cb)(void *data, EDBus_Pending *p, const char *propname, EDBus_Proxy *proxy, EDBus_Error_Info *error_info, unsigned short int value);\n\ +typedef void (*EDBus_Codegen_Property_Uint32_Get_Cb)(void *data, EDBus_Pending *p, const char *propname, EDBus_Proxy *proxy, EDBus_Error_Info *error_info, unsigned int value);\n\ +typedef void (*EDBus_Codegen_Property_Double_Get_Cb)(void *data, EDBus_Pending *p, const char *propname, EDBus_Proxy *proxy, EDBus_Error_Info *error_info, double value);\n\ +typedef void (*EDBus_Codegen_Property_Int64_Get_Cb)(void *data, EDBus_Pending *p, const char *propname, EDBus_Proxy *proxy, EDBus_Error_Info *error_info, int64_t value);\n\ +typedef void (*EDBus_Codegen_Property_Uint64_Get_Cb)(void *data, EDBus_Pending *p, const char *propname, EDBus_Proxy *proxy, EDBus_Error_Info *error_info, uint64_t value);\n\ +typedef void (*EDBus_Codegen_Property_Complex_Get_Cb)(void *data, EDBus_Pending *p, const char *propname, EDBus_Proxy *proxy, EDBus_Error_Info *error_info, Eina_Value *value);\n\ +\n\ +#endif\ +" + +Eina_Bool +util_h_write(void) +{ + return file_write("edbus_utils.h", UTIL_H); +} -- 2.7.4