From d021cfae6695f0f44102edf758abfc42e2f3c093 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Wed, 3 Sep 2003 02:08:25 +0000 Subject: 2003-09-01 Havoc Pennington * glib/dbus-gparser.c: implement * glib/dbus-gobject.c: start implementing skeletons support * configure.in: when disabling checks/assert, also define G_DISABLE_ASSERT and G_DISABLE_CHECKS --- ChangeLog | 9 + configure.in | 9 +- dbus/dbus-marshal.c | 4 +- dbus/dbus-protocol.h | 23 +- glib/Makefile.am | 6 +- glib/dbus-gidl.c | 90 +++++- glib/dbus-gidl.h | 10 + glib/dbus-glib.h | 26 +- glib/dbus-gloader-expat.c | 254 +++++++++++++++ glib/dbus-gobject.c | 780 ++++++++++++++++++++++++++++++++++++++++++++++ glib/dbus-gparser.c | 549 +++++++++++++++++++++++++++++++- glib/dbus-gparser.h | 8 +- 12 files changed, 1740 insertions(+), 28 deletions(-) create mode 100644 glib/dbus-gloader-expat.c create mode 100644 glib/dbus-gobject.c diff --git a/ChangeLog b/ChangeLog index f57c964a..d9557bed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2003-09-01 Havoc Pennington + + * glib/dbus-gparser.c: implement + + * glib/dbus-gobject.c: start implementing skeletons support + + * configure.in: when disabling checks/assert, also define + G_DISABLE_ASSERT and G_DISABLE_CHECKS + 2003-09-01 Havoc Pennington * glib/Makefile.am: rearrange a bunch of files and get "make diff --git a/configure.in b/configure.in index 3963a5de..1125ef60 100644 --- a/configure.in +++ b/configure.in @@ -54,9 +54,11 @@ if test x$enable_verbose_mode = xyes; then fi if test x$enable_asserts = xno; then AC_DEFINE(DBUS_DISABLE_ASSERT,1,[Disable assertion checking]) + AC_DEFINE(G_DISABLE_ASSERT,1,[Disable GLib assertion macros]) fi if test x$enable_checks = xno; then AC_DEFINE(DBUS_DISABLE_CHECKS,1,[Disable public API sanity checking]) + AC_DEFINE(G_DISABLE_CHECKS,1,[Disable GLib public API sanity checking]) fi #### gcc warning flags @@ -545,7 +547,7 @@ AC_SUBST(DBUS_TEST_CFLAGS) AC_SUBST(DBUS_TEST_LIBS) # Glib detection -PKG_CHECK_MODULES(DBUS_GLIB, glib-2.0, have_glib=yes, have_glib=no) +PKG_CHECK_MODULES(DBUS_GLIB, gobject-2.0, have_glib=yes, have_glib=no) PKG_CHECK_MODULES(DBUS_GLIB_THREADS, glib-2.0 gthread-2.0, have_glib_threads=yes, have_glib_threads=no) if test x$have_glib = xno ; then @@ -570,6 +572,11 @@ AC_SUBST(DBUS_GLIB_CFLAGS) AC_SUBST(DBUS_GLIB_LIBS) AC_SUBST(DBUS_GLIB_THREADS_LIBS) +DBUS_GLIB_TOOL_CFLAGS=$XML_CFLAGS +DBUS_GLIB_TOOL_LIBS=$XML_LIBS +AC_SUBST(DBUS_GLIB_TOOL_CFLAGS) +AC_SUBST(DBUS_GLIB_TOOL_LIBS) + # Qt detection have_qt=no AC_MSG_CHECKING([for qglobal.h]) diff --git a/dbus/dbus-marshal.c b/dbus/dbus-marshal.c index 5297cb67..c542ee8b 100644 --- a/dbus/dbus-marshal.c +++ b/dbus/dbus-marshal.c @@ -1473,7 +1473,8 @@ _dbus_demarshal_string_array (const DBusString *str, #define VERBOSE_DECOMPOSE 0 /** - * Demarshals an object path. + * Demarshals an object path. A path of just "/" is + * represented as an empty vector of strings. * * @param str the string containing the data * @param byte_order the byte order @@ -1556,7 +1557,6 @@ _dbus_demarshal_object_path (const DBusString *str, i = j; } _dbus_assert (i == len); - _dbus_assert (retval[0] != NULL); *path = retval; if (path_len) diff --git a/dbus/dbus-protocol.h b/dbus/dbus-protocol.h index e56ab756..473a1051 100644 --- a/dbus/dbus-protocol.h +++ b/dbus/dbus-protocol.h @@ -87,6 +87,18 @@ extern "C" { #define DBUS_PATH_ORG_FREEDESKTOP_DBUS "/org/freedesktop/DBus" #define DBUS_PATH_ORG_FREEDESKTOP_LOCAL "/org/freedesktop/Local" +/* Interfaces, these #define don't do much other than + * catch typos at compile time + */ +#define DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS "org.freedesktop.DBus" +#define DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE "org.freedesktop.Introspectable" + +/* This is a special interface whose methods can only be invoked + * by the local implementation (messages from remote apps aren't + * allowed to specify this interface). + */ +#define DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL "org.freedesktop.Local" + /* Service owner flags */ #define DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT 0x1 #define DBUS_SERVICE_FLAG_REPLACE_EXISTING 0x2 @@ -101,17 +113,6 @@ extern "C" { #define DBUS_ACTIVATION_REPLY_ACTIVATED 0x0 #define DBUS_ACTIVATION_REPLY_ALREADY_ACTIVE 0x1 -/* Interfaces, these #define don't do much other than - * catch typos at compile time - */ -#define DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS "org.freedesktop.DBus" - -/* This is a special interface whose methods can only be invoked - * by the local implementation (messages from remote apps aren't - * allowed to specify this interface). - */ -#define DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL "org.freedesktop.Local" - #ifdef __cplusplus } #endif diff --git a/glib/Makefile.am b/glib/Makefile.am index 3d67db2f..f3b43ed3 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) -DDBUS_COMPILATION=1 +INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_GLIB_TOOL_CFLAGS) -DDBUS_COMPILATION=1 dbusincludedir=$(includedir)/dbus-1.0/dbus @@ -10,6 +10,7 @@ dbusinclude_HEADERS= \ libdbus_glib_1_la_SOURCES = \ dbus-gmain.c \ + dbus-gobject.c \ dbus-gproxy.c \ dbus-gtest.c \ dbus-gtest.h \ @@ -23,12 +24,13 @@ dbus_glib_tool_SOURCES = \ dbus-gidl.c \ dbus-gidl.h \ dbus-glib-tool.c \ + dbus-gloader-expat.c \ dbus-gparser.c \ dbus-gparser.h \ dbus-gtool-test.h -dbus_glib_tool_LDADD= libdbus-glib-1.la $(DBUS_GLIB_LIBS) $(top_builddir)/dbus/libdbus-1.la +dbus_glib_tool_LDADD= $(DBUS_GLIB_TOOL_LIBS) libdbus-glib-1.la $(top_builddir)/dbus/libdbus-1.la if DBUS_BUILD_TESTS diff --git a/glib/dbus-gidl.c b/glib/dbus-gidl.c index d64e95a7..12468abb 100644 --- a/glib/dbus-gidl.c +++ b/glib/dbus-gidl.c @@ -24,6 +24,13 @@ #include "dbus-gidl.h" +struct NodeInfo +{ + int refcount; + char *name; + GSList *interfaces; +}; + struct InterfaceInfo { int refcount; @@ -54,6 +61,20 @@ struct ArgInfo ArgDirection direction; }; +static void +free_interface_list (GSList **interfaces_p) +{ + GSList *tmp; + tmp = *interfaces_p; + while (tmp != NULL) + { + interface_info_unref (tmp->data); + tmp = tmp->next; + } + g_slist_free (*interfaces_p); + *interfaces_p = NULL; +} + static void free_method_list (GSList **methods_p) { @@ -82,6 +103,59 @@ free_signal_list (GSList **signals_p) *signals_p = NULL; } +NodeInfo* +node_info_new (const char *name) +{ + NodeInfo *info; + + /* name can be NULL */ + + info = g_new0 (NodeInfo, 1); + info->refcount = 1; + info->name = g_strdup (name); + + return info; +} + +void +node_info_ref (NodeInfo *info) +{ + info->refcount += 1; +} + +void +node_info_unref (NodeInfo *info) +{ + info->refcount -= 1; + if (info->refcount == 0) + { + free_interface_list (&info->interfaces); + g_free (info->name); + g_free (info); + } +} + +const char* +node_info_get_name (NodeInfo *info) +{ + return info->name; +} + +GSList* +node_info_get_interfaces (NodeInfo *info) +{ + return info->interfaces; +} + +void +node_info_add_interface (NodeInfo *info, + InterfaceInfo *interface) +{ + interface_info_ref (interface); + info->interfaces = g_slist_append (info->interfaces, interface); +} + + InterfaceInfo* interface_info_new (const char *name) { @@ -90,7 +164,7 @@ interface_info_new (const char *name) info = g_new0 (InterfaceInfo, 1); info->refcount = 1; info->name = g_strdup (name); - + return info; } @@ -113,6 +187,12 @@ interface_info_unref (InterfaceInfo *info) } } +const char* +interface_info_get_name (InterfaceInfo *info) +{ + return info->name; +} + GSList* interface_info_get_methods (InterfaceInfo *info) { @@ -163,7 +243,7 @@ method_info_new (const char *name) info = g_new0 (MethodInfo, 1); info->refcount = 1; info->name = g_strdup (name); - + return info; } @@ -213,7 +293,7 @@ signal_info_new (const char *name) info = g_new0 (SignalInfo, 1); info->refcount = 1; info->name = g_strdup (name); - + return info; } @@ -264,10 +344,12 @@ arg_info_new (const char *name, info = g_new0 (ArgInfo, 1); info->refcount = 1; + + /* name can be NULL */ info->name = g_strdup (name); info->direction = direction; info->type = type; - + return info; } diff --git a/glib/dbus-gidl.h b/glib/dbus-gidl.h index 68649cf3..6e4e207a 100644 --- a/glib/dbus-gidl.h +++ b/glib/dbus-gidl.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS +typedef struct NodeInfo NodeInfo; typedef struct InterfaceInfo InterfaceInfo; typedef struct MethodInfo MethodInfo; typedef struct SignalInfo SignalInfo; @@ -40,9 +41,18 @@ typedef enum ARG_OUT } ArgDirection; +NodeInfo* node_info_new (const char *name); +void node_info_ref (NodeInfo *info); +void node_info_unref (NodeInfo *info); +const char* node_info_get_name (NodeInfo *info); +GSList* node_info_get_interfaces (NodeInfo *info); +void node_info_add_interface (NodeInfo *info, + InterfaceInfo *interface); + InterfaceInfo* interface_info_new (const char *name); void interface_info_ref (InterfaceInfo *info); void interface_info_unref (InterfaceInfo *info); +const char* interface_info_get_name (InterfaceInfo *info); GSList* interface_info_get_methods (InterfaceInfo *info); GSList* interface_info_get_signals (InterfaceInfo *info); void interface_info_add_method (InterfaceInfo *info, diff --git a/glib/dbus-glib.h b/glib/dbus-glib.h index 7ca22417..3a87dec2 100644 --- a/glib/dbus-glib.h +++ b/glib/dbus-glib.h @@ -24,7 +24,7 @@ #define DBUS_GLIB_H #include -#include +#include G_BEGIN_DECLS @@ -36,6 +36,30 @@ void dbus_connection_setup_with_g_main (DBusConnection *connection, void dbus_server_setup_with_g_main (DBusServer *server, GMainContext *context); +typedef struct DBusGObjectInfo DBusGObjectInfo; +typedef struct DBusGMethodInfo DBusGMethodInfo; + +struct DBusGMethodInfo +{ + GCallback function; + DBusHandleMessageFunction marshaller; + int data_offset; +}; + +struct DBusGObjectInfo +{ + const DBusGMethodInfo *infos; + const unsigned char *data; + void *dbus_internal_padding1; + void *dbus_internal_padding2; +}; + +void dbus_gobject_class_install_info (GObjectClass *object_class, + const DBusGObjectInfo *info); +void dbus_connection_register_gobject (DBusConnection *connection, + const char *at_path, + GObject *object); + #undef DBUS_INSIDE_DBUS_GLIB_H G_END_DECLS diff --git a/glib/dbus-gloader-expat.c b/glib/dbus-gloader-expat.c new file mode 100644 index 00000000..050d3532 --- /dev/null +++ b/glib/dbus-gloader-expat.c @@ -0,0 +1,254 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-gloader-expat.c expat XML loader + * + * Copyright (C) 2003 Red Hat, Inc. + * + * Licensed under the Academic Free License version 1.2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-gparser.h" +#include + +static void* +expat_g_malloc (size_t sz) +{ + return g_malloc (sz); +} + +static void* +expat_g_realloc (void *mem, size_t sz) +{ + return g_realloc (mem, sz); +} + +static XML_Memory_Handling_Suite memsuite = +{ + expat_g_malloc, + expat_g_realloc, + g_free +}; + +typedef struct +{ + Parser *parser; + const char *filename; + GString *content; + GError **error; + gboolean failed; +} ExpatParseContext; + +static dbus_bool_t +process_content (ExpatParseContext *context) +{ + if (context->failed) + return FALSE; + + if (context->content->len > 0) + { + if (!parser_content (context->parser, + context->content->str, + context->content->len, + context->error)) + { + context->failed = TRUE; + return FALSE; + } + g_string_set_size (context->content, 0); + } + + return TRUE; +} + +static void +expat_StartElementHandler (void *userData, + const XML_Char *name, + const XML_Char **atts) +{ + ExpatParseContext *context = userData; + int i; + char **names; + char **values; + + /* Expat seems to suck and can't abort the parse if we + * throw an error. Expat 2.0 is supposed to fix this. + */ + if (context->failed) + return; + + if (!process_content (context)) + return; + + /* "atts" is key, value, key, value, NULL */ + for (i = 0; atts[i] != NULL; ++i) + ; /* nothing */ + + g_assert (i % 2 == 0); + names = g_new0 (char *, i / 2 + 1); + values = g_new0 (char *, i / 2 + 1); + + i = 0; + while (atts[i] != NULL) + { + g_assert (i % 2 == 0); + names [i / 2] = (char*) atts[i]; + values[i / 2] = (char*) atts[i+1]; + + i += 2; + } + + if (!parser_start_element (context->parser, + name, + (const char **) names, + (const char **) values, + context->error)) + { + g_free (names); + g_free (values); + context->failed = TRUE; + return; + } + + g_free (names); + g_free (values); +} + +static void +expat_EndElementHandler (void *userData, + const XML_Char *name) +{ + ExpatParseContext *context = userData; + + if (!process_content (context)) + return; + + if (!parser_end_element (context->parser, + name, + context->error)) + { + context->failed = TRUE; + return; + } +} + +/* s is not 0 terminated. */ +static void +expat_CharacterDataHandler (void *userData, + const XML_Char *s, + int len) +{ + ExpatParseContext *context = userData; + + if (context->failed) + return; + + g_string_append_len (context->content, + s, len); +} + +Parser* +description_load_from_file (const char *filename, + GError **error) +{ + char *contents; + gsize len; + Parser *parser; + + contents = NULL; + if (!g_file_get_contents (filename, &contents, &len, error)) + return NULL; + + parser = description_load_from_string (contents, len, error); + g_free (contents); + + return parser; +} + +Parser* +description_load_from_string (const char *str, + int len, + GError **error) +{ + XML_Parser expat; + ExpatParseContext context; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + expat = NULL; + context.parser = NULL; + context.error = error; + context.failed = FALSE; + + expat = XML_ParserCreate_MM ("UTF-8", &memsuite, NULL); + if (expat == NULL) + g_error ("No memory to create XML parser\n"); + + context.parser = parser_new (); + context.content = g_string_new (NULL); + + XML_SetUserData (expat, &context); + XML_SetElementHandler (expat, + expat_StartElementHandler, + expat_EndElementHandler); + XML_SetCharacterDataHandler (expat, + expat_CharacterDataHandler); + + if (!XML_Parse (expat, str, len, TRUE)) + { + if (context.error != NULL && + *context.error == NULL) + { + enum XML_Error e; + + e = XML_GetErrorCode (expat); + if (e == XML_ERROR_NO_MEMORY) + g_error ("Not enough memory to parse XML document"); + else + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + "Error in D-BUS description XML, line %d, column %d: %s\n", + XML_GetCurrentLineNumber (expat), + XML_GetCurrentColumnNumber (expat), + XML_ErrorString (e)); + } + + goto failed; + } + + if (context.failed) + goto failed; + + if (!parser_finished (context.parser, error)) + goto failed; + + XML_ParserFree (expat); + g_string_free (context.content, TRUE); + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return context.parser; + + failed: + g_return_val_if_fail (error == NULL || *error != NULL, NULL); + + g_string_free (context.content, TRUE); + if (expat) + XML_ParserFree (expat); + if (context.parser) + parser_unref (context.parser); + return NULL; +} diff --git a/glib/dbus-gobject.c b/glib/dbus-gobject.c new file mode 100644 index 00000000..aa53265b --- /dev/null +++ b/glib/dbus-gobject.c @@ -0,0 +1,780 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-gobject.c Exporting a GObject remotely + * + * Copyright (C) 2003 Red Hat, Inc. + * + * Licensed under the Academic Free License version 1.2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "dbus-glib.h" +#include "dbus-gtest.h" +#include + +/** + * @addtogroup DBusGLibInternals + * @{ + */ + +static GStaticMutex info_hash_mutex = G_STATIC_MUTEX_INIT; +static GHashTable *info_hash = NULL; + +static char* +javacaps_to_uscore (const char *caps) +{ + const char *p; + GString *str; + + str = g_string_new (NULL); + p = caps; + while (*p) + { + if (g_ascii_isupper (*p)) + { + if (str->len > 0 && + (str->len < 2 || str->str[str->len-2] != '_')) + g_string_append_c (str, '_'); + g_string_append_c (str, g_ascii_tolower (*p)); + } + else + { + g_string_append_c (str, *p); + } + ++p; + } + + return g_string_free (str, FALSE); +} + +static char* +uscore_to_javacaps (const char *uscore) +{ + const char *p; + GString *str; + gboolean last_was_uscore; + + last_was_uscore = TRUE; + + str = g_string_new (NULL); + p = uscore; + while (*p) + { + if (*p == '-' || *p == '_') + { + last_was_uscore = TRUE; + } + else + { + if (last_was_uscore) + { + g_string_append_c (str, g_ascii_toupper (*p)); + last_was_uscore = FALSE; + } + else + g_string_append_c (str, *p); + } + ++p; + } + + return g_string_free (str, FALSE); +} + +static void +gobject_unregister_function (DBusConnection *connection, + void *user_data) +{ + GObject *object; + + object = G_OBJECT (user_data); + + +} + +static int +gtype_to_dbus_type (GType type) +{ + switch (type) + { + case G_TYPE_CHAR: + case G_TYPE_UCHAR: + return DBUS_TYPE_BYTE; + + case G_TYPE_BOOLEAN: + return DBUS_TYPE_BOOLEAN; + + /* long gets cut to 32 bits so the remote API is consistent + * on all architectures + */ + + case G_TYPE_LONG: + case G_TYPE_INT: + return DBUS_TYPE_INT32; + case G_TYPE_ULONG: + case G_TYPE_UINT: + return DBUS_TYPE_UINT32; + + case G_TYPE_INT64: + return DBUS_TYPE_INT64; + + case G_TYPE_UINT64: + return DBUS_TYPE_UINT64; + + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + return DBUS_TYPE_DOUBLE; + + case G_TYPE_STRING: + return DBUS_TYPE_STRING; + + default: + return DBUS_TYPE_INVALID; + } +} + +static const char * +dbus_type_to_string (int type) +{ + switch (type) + { + case DBUS_TYPE_INVALID: + return "invalid"; + case DBUS_TYPE_NIL: + return "nil"; + case DBUS_TYPE_BOOLEAN: + return "boolean"; + case DBUS_TYPE_INT32: + return "int32"; + case DBUS_TYPE_UINT32: + return "uint32"; + case DBUS_TYPE_DOUBLE: + return "double"; + case DBUS_TYPE_STRING: + return "string"; + case DBUS_TYPE_NAMED: + return "named"; + case DBUS_TYPE_ARRAY: + return "array"; + case DBUS_TYPE_DICT: + return "dict"; + default: + return "unknown"; + } +} + +static DBusHandlerResult +handle_introspect (DBusConnection *connection, + DBusMessage *message, + GObject *object) +{ + GString *xml; + GParamSpec **specs; + unsigned int n_specs; + unsigned int i; + GType last_type; + DBusMessage *ret; + + xml = g_string_new (NULL); + + g_string_append (xml, "\n"); + + last_type = G_TYPE_INVALID; + + specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object), + &n_specs); + + i = 0; + while (i < n_specs) + { + GParamSpec *spec = specs[i]; + gboolean can_set; + gboolean can_get; + char *s; + int dbus_type; + + dbus_type = gtype_to_dbus_type (G_PARAM_SPEC_VALUE_TYPE (spec)); + if (dbus_type == DBUS_TYPE_INVALID) + goto next; + + if (spec->owner_type != last_type) + { + if (last_type != G_TYPE_INVALID) + g_string_append (xml, " \n"); + + + /* FIXME what should the namespace on the interface be in + * general? should people be able to set it for their + * objects? + */ + + g_string_append (xml, " owner_type)); + g_string_append (xml, "\">\n"); + + last_type = spec->owner_type; + } + + can_set = ((spec->flags & G_PARAM_WRITABLE) != 0 && + (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0); + + can_get = (spec->flags & G_PARAM_READABLE) != 0; + + s = uscore_to_javacaps (spec->name); + + if (can_set) + { + g_string_append (xml, " \n"); + + g_string_append (xml, " \n"); + } + + if (can_get) + { + g_string_append (xml, " \n"); + + g_string_append (xml, " \n"); + } + + g_free (s); + + next: + ++i; + } + + if (last_type != G_TYPE_INVALID) + g_string_append (xml, " \n"); + + g_free (specs); + + /* Close the XML, and send it to the requesting app */ + + g_string_append (xml, "\n"); + + ret = dbus_message_new_method_return (message); + if (ret == NULL) + g_error ("out of memory"); + + dbus_message_append_args (message, + DBUS_TYPE_STRING, xml->str, + DBUS_TYPE_INVALID); + + dbus_connection_send (connection, message, NULL); + dbus_message_unref (message); + + g_string_free (xml, TRUE); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusMessage* +set_object_property (DBusConnection *connection, + DBusMessage *message, + GObject *object, + GParamSpec *pspec) +{ + GValue value; + DBusMessageIter iter; + int type; + gboolean can_set; + DBusMessage *ret; + + dbus_message_iter_init (message, &iter); + type = dbus_message_get_type (message); + + can_set = TRUE; + switch (type) + { + case DBUS_TYPE_BYTE: + { + unsigned char b; + + b = dbus_message_iter_get_byte (&iter); + + g_value_init (&value, G_TYPE_UCHAR); + + g_value_set_uchar (&value, b); + } + break; + case DBUS_TYPE_BOOLEAN: + { + gboolean b; + + b = dbus_message_iter_get_boolean (&iter); + + g_value_init (&value, G_TYPE_BOOLEAN); + + g_value_set_boolean (&value, b); + } + break; + case DBUS_TYPE_INT32: + { + gint32 i; + + i = dbus_message_iter_get_int32 (&iter); + + g_value_init (&value, G_TYPE_INT); + + g_value_set_int (&value, i); + } + break; + case DBUS_TYPE_UINT32: + { + guint32 i; + + i = dbus_message_iter_get_uint32 (&iter); + + g_value_init (&value, G_TYPE_UINT); + + g_value_set_uint (&value, i); + } + break; + case DBUS_TYPE_INT64: + { + gint64 i; + + i = dbus_message_iter_get_int64 (&iter); + + g_value_init (&value, G_TYPE_INT64); + + g_value_set_int64 (&value, i); + } + break; + case DBUS_TYPE_UINT64: + { + guint64 i; + + i = dbus_message_iter_get_uint64 (&iter); + + g_value_init (&value, G_TYPE_UINT64); + + g_value_set_uint64 (&value, i); + } + break; + case DBUS_TYPE_DOUBLE: + { + double d; + + d = dbus_message_iter_get_double (&iter); + + g_value_init (&value, G_TYPE_DOUBLE); + + g_value_set_double (&value, d); + } + break; + case DBUS_TYPE_STRING: + { + char *s; + + /* FIXME use a const string accessor */ + + s = dbus_message_iter_get_string (&iter); + + g_value_init (&value, G_TYPE_STRING); + + g_value_set_string (&value, s); + + g_free (s); + } + break; + + /* FIXME array and other types, especially byte array + * converted to G_TYPE_STRING + */ + + default: + can_set = FALSE; + break; + } + + /* The g_object_set_property() will transform some types, e.g. it + * will let you use a uchar to set an int property etc. Note that + * any error in value range or value conversion will just + * g_warning(). These GObject skels are not for secure applications. + */ + + if (can_set) + { + g_object_set_property (object, + pspec->name, + &value); + + ret = dbus_message_new_method_return (message); + if (ret == NULL) + g_error ("out of memory"); + + g_value_unset (&value); + } + else + { + ret = dbus_message_new_error (message, + DBUS_ERROR_INVALID_ARGS, + "Argument's D-BUS type can't be converted to a GType"); + if (ret == NULL) + g_error ("out of memory"); + } + + return ret; +} + +static DBusMessage* +get_object_property (DBusConnection *connection, + DBusMessage *message, + GObject *object, + GParamSpec *pspec) +{ + GType value_type; + gboolean can_get; + DBusMessage *ret; + GValue value; + DBusMessageIter iter; + + value_type = G_PARAM_SPEC_VALUE_TYPE (pspec); + + ret = dbus_message_new_method_return (message); + if (ret == NULL) + g_error ("out of memory"); + + can_get = TRUE; + g_value_init (&value, value_type); + g_object_get_property (object, pspec->name, &value); + + value_type = G_VALUE_TYPE (&value); + + dbus_message_append_iter_init (message, &iter); + + switch (value_type) + { + case G_TYPE_CHAR: + dbus_message_iter_append_byte (&iter, + g_value_get_char (&value)); + break; + case G_TYPE_UCHAR: + dbus_message_iter_append_byte (&iter, + g_value_get_uchar (&value)); + break; + case G_TYPE_BOOLEAN: + dbus_message_iter_append_boolean (&iter, + g_value_get_boolean (&value)); + break; + case G_TYPE_INT: + dbus_message_iter_append_int32 (&iter, + g_value_get_int (&value)); + break; + case G_TYPE_UINT: + dbus_message_iter_append_uint32 (&iter, + g_value_get_uint (&value)); + break; + /* long gets cut to 32 bits so the remote API is consistent + * on all architectures + */ + case G_TYPE_LONG: + dbus_message_iter_append_int32 (&iter, + g_value_get_long (&value)); + break; + case G_TYPE_ULONG: + dbus_message_iter_append_uint32 (&iter, + g_value_get_ulong (&value)); + break; + case G_TYPE_INT64: + dbus_message_iter_append_int64 (&iter, + g_value_get_int64 (&value)); + break; + case G_TYPE_UINT64: + dbus_message_iter_append_uint64 (&iter, + g_value_get_uint64 (&value)); + break; + case G_TYPE_FLOAT: + dbus_message_iter_append_double (&iter, + g_value_get_float (&value)); + break; + case G_TYPE_DOUBLE: + dbus_message_iter_append_double (&iter, + g_value_get_double (&value)); + break; + case G_TYPE_STRING: + /* FIXME, the GValue string may not be valid UTF-8 */ + dbus_message_iter_append_string (&iter, + g_value_get_string (&value)); + break; + default: + can_get = FALSE; + break; + } + + g_value_unset (&value); + + if (!can_get) + { + dbus_message_unref (ret); + ret = dbus_message_new_error (message, + DBUS_ERROR_UNKNOWN_METHOD, + "Can't convert GType of object property to a D-BUS type"); + } + + return ret; +} + +static DBusHandlerResult +gobject_message_function (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + const DBusGObjectInfo *info; + GParamSpec *pspec; + GObject *object; + const char *member; + gboolean setter; + gboolean getter; + char *s; + + object = G_OBJECT (user_data); + + if (dbus_message_is_method_call (message, + DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE, + "Introspect")) + return handle_introspect (connection, message, object); + + member = dbus_message_get_member (message); + + /* Try the metainfo, which lets us invoke methods */ + + g_static_mutex_lock (&info_hash_mutex); + /* FIXME this needs to walk up the inheritance tree, not + * just look at the most-derived class + */ + info = g_hash_table_lookup (info_hash, + G_OBJECT_GET_CLASS (object)); + g_static_mutex_unlock (&info_hash_mutex); + + if (info != NULL) + { + + + + } + + /* If no metainfo, we can still do properties and signals + * via standard GLib introspection + */ + setter = (member[0] == 's' && member[1] == 'e' && member[2] == 't'); + getter = (member[0] == 'g' && member[1] == 'e' && member[2] == 't'); + + if (!(setter || getter)) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + s = javacaps_to_uscore (&member[3]); + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), + s); + + g_free (s); + + if (pspec != NULL) + { + DBusMessage *ret; + + if (setter) + { + ret = set_object_property (connection, message, + object, pspec); + } + else if (getter) + { + ret = get_object_property (connection, message, + object, pspec); + } + else + { + g_assert_not_reached (); + ret = NULL; + } + + g_assert (ret != NULL); + + dbus_connection_send (connection, ret, NULL); + dbus_message_unref (ret); + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusObjectPathVTable gobject_dbus_vtable = { + gobject_unregister_function, + gobject_message_function, + NULL +}; + +/** @} */ /* end of internals */ + +/** + * @addtogroup DBusGLib + * @{ + */ + +/** + * Install introspection information about the given object class + * sufficient to allow methods on the object to be invoked by name. + * The introspection information is normally generated by + * dbus-glib-tool, then this function is called in the + * class_init() for the object class. + * + * Once introspection information has been installed, instances of the + * object registered with dbus_connection_register_gobject() can have + * their methods invoked remotely. + * + * @param object_class class struct of the object + * @param info introspection data generated by dbus-glib-tool + */ +void +dbus_gobject_class_install_info (GObjectClass *object_class, + const DBusGObjectInfo *info) +{ + g_return_if_fail (G_IS_OBJECT_CLASS (object_class)); + + g_static_mutex_lock (&info_hash_mutex); + + if (info_hash == NULL) + { + info_hash = g_hash_table_new (NULL, NULL); /* direct hash */ + } + + g_hash_table_replace (info_hash, object_class, (void*) info); + + g_static_mutex_unlock (&info_hash_mutex); +} + +static char** +split_path (const char *path) +{ + int len; + char **split; + int n_components; + int i, j, comp; + + len = strlen (path); + + n_components = 0; + i = 0; + while (i < len) + { + if (path[i] == '/') + n_components += 1; + ++i; + } + + split = g_new0 (char*, n_components + 1); + + comp = 0; + i = 0; + while (i < len) + { + if (path[i] == '/') + ++i; + j = i; + + while (j < len && path[j] != '/') + ++j; + + /* Now [i, j) is the path component */ + g_assert (i < j); + g_assert (path[i] != '/'); + g_assert (j == len || path[j] == '/'); + + split[comp] = g_strndup (&path[i], j - i + 1); + + split[comp][j-i] = '\0'; + + ++comp; + i = j; + } + g_assert (i == len); + + return split; +} + +/** + * Registers a GObject at the given path. Properties, methods, and signals + * of the object can then be accessed remotely. Methods are only available + * if method introspection data has been added to the object's class + * with g_object_class_install_info(). + * + * The registration will be cancelled if either the DBusConnection or + * the GObject gets finalized. + * + * @param connection the D-BUS connection + * @param at_path the path where the object will live (the object's name) + * @param object the object + */ +void +dbus_connection_register_gobject (DBusConnection *connection, + const char *at_path, + GObject *object) +{ + char **split; + + g_return_if_fail (connection != NULL); + g_return_if_fail (at_path != NULL); + g_return_if_fail (G_IS_OBJECT (object)); + + split = split_path (at_path); + + if (!dbus_connection_register_object_path (connection, + (const char**) split, + &gobject_dbus_vtable, + object)) + g_error ("Failed to register GObject with DBusConnection"); + + g_strfreev (split); + + /* FIXME set up memory management (so we break the + * registration if object or connection vanishes) + */ +} + +/** @} */ /* end of public API */ + +#ifdef DBUS_BUILD_TESTS + +/** + * @ingroup DBusGLibInternals + * Unit test for GLib GObject integration ("skeletons") + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_gobject_test (const char *test_data_dir) +{ + static struct { const char *javacaps; const char *uscore; } name_pairs[] = { + { "setFoo", "set_foo" }, + { "foo", "foo" }, + { "getFooBar", "get_foo_bar" }, + { "Hello", "hello" }, + { "frobateUIHandler", "frobate_ui_handler" } + }; + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/glib/dbus-gparser.c b/glib/dbus-gparser.c index 6fea4abc..c2b54d31 100644 --- a/glib/dbus-gparser.c +++ b/glib/dbus-gparser.c @@ -22,11 +22,152 @@ */ #include "dbus-gparser.h" #include "dbus-gidl.h" +#include + +#include +#define _(x) gettext ((x)) +#define N_(x) x + +#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0) + +typedef struct +{ + const char *name; + const char **retloc; +} LocateAttr; + +static gboolean +locate_attributes (const char *element_name, + const char **attribute_names, + const char **attribute_values, + GError **error, + const char *first_attribute_name, + const char **first_attribute_retloc, + ...) +{ + va_list args; + const char *name; + const char **retloc; + int n_attrs; +#define MAX_ATTRS 24 + LocateAttr attrs[MAX_ATTRS]; + gboolean retval; + int i; + + g_return_val_if_fail (first_attribute_name != NULL, FALSE); + g_return_val_if_fail (first_attribute_retloc != NULL, FALSE); + + retval = TRUE; + + n_attrs = 1; + attrs[0].name = first_attribute_name; + attrs[0].retloc = first_attribute_retloc; + *first_attribute_retloc = NULL; + + va_start (args, first_attribute_retloc); + + name = va_arg (args, const char*); + retloc = va_arg (args, const char**); + + while (name != NULL) + { + g_return_val_if_fail (retloc != NULL, FALSE); + + g_assert (n_attrs < MAX_ATTRS); + + attrs[n_attrs].name = name; + attrs[n_attrs].retloc = retloc; + n_attrs += 1; + *retloc = NULL; + + name = va_arg (args, const char*); + retloc = va_arg (args, const char**); + } + + va_end (args); + + if (!retval) + return retval; + + i = 0; + while (attribute_names[i]) + { + int j; + gboolean found; + + found = FALSE; + j = 0; + while (j < n_attrs) + { + if (strcmp (attrs[j].name, attribute_names[i]) == 0) + { + retloc = attrs[j].retloc; + + if (*retloc != NULL) + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("Attribute \"%s\" repeated twice on the same <%s> element"), + attrs[j].name, element_name); + retval = FALSE; + goto out; + } + + *retloc = attribute_values[i]; + found = TRUE; + } + + ++j; + } + + if (!found) + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("Attribute \"%s\" is invalid on <%s> element in this context"), + attribute_names[i], element_name); + retval = FALSE; + goto out; + } + + ++i; + } + + out: + return retval; +} + +static gboolean +check_no_attributes (const char *element_name, + const char **attribute_names, + const char **attribute_values, + GError **error) +{ + if (attribute_names[0] != NULL) + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("Attribute \"%s\" is invalid on <%s> element in this context"), + attribute_names[0], element_name); + return FALSE; + } + + return TRUE; +} struct Parser { int refcount; + NodeInfo *result; /* Filled in when we pop the last node */ + GSList *node_stack; + InterfaceInfo *interface; + MethodInfo *method; + SignalInfo *signal; + ArgInfo *arg; }; Parser* @@ -53,7 +194,8 @@ parser_unref (Parser *parser) parser->refcount -= 1; if (parser->refcount == 0) { - + if (parser->result) + node_info_unref (parser->result); g_free (parser); } @@ -64,11 +206,12 @@ parser_check_doctype (Parser *parser, const char *doctype, GError **error) { - g_return_val_if_fail (error == NULL || *error == NULL); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (strcmp (doctype, "dbus_description") != 0) { g_set_error (error, + G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "D-BUS description file has the wrong document type %s, use dbus_description", doctype); @@ -78,6 +221,324 @@ parser_check_doctype (Parser *parser, return TRUE; } +static gboolean +parse_node (Parser *parser, + const char *element_name, + const char **attribute_names, + const char **attribute_values, + GError **error) +{ + const char *name; + NodeInfo *node; + + if (parser->interface || + parser->method || + parser->signal || + parser->arg) + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("Can't put a <%s> element here"), + element_name); + return FALSE; + } + + name = NULL; + if (!locate_attributes (element_name, attribute_names, + attribute_values, error, + "name", &name, + NULL)) + return FALSE; + + /* Only the root node can have no name */ + if (parser->node_stack != NULL && name == NULL) + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("\"%s\" attribute required on <%s> element "), + "name", element_name); + return FALSE; + } + + node = node_info_new (name); + parser->node_stack = g_slist_prepend (parser->node_stack, + node); + + return TRUE; +} + +static gboolean +parse_interface (Parser *parser, + const char *element_name, + const char **attribute_names, + const char **attribute_values, + GError **error) +{ + const char *name; + InterfaceInfo *iface; + NodeInfo *top; + + if (parser->interface || + parser->method || + parser->signal || + parser->arg || + (parser->node_stack == NULL)) + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("Can't put a <%s> element here"), + element_name); + return FALSE; + } + + name = NULL; + if (!locate_attributes (element_name, attribute_names, + attribute_values, error, + "name", &name, + NULL)) + return FALSE; + + if (name == NULL) + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("\"%s\" attribute required on <%s> element "), + "name", element_name); + return FALSE; + } + + top = parser->node_stack->data; + + iface = interface_info_new (name); + node_info_add_interface (top, iface); + interface_info_unref (iface); + + parser->interface = iface; + + return TRUE; +} + +static gboolean +parse_method (Parser *parser, + const char *element_name, + const char **attribute_names, + const char **attribute_values, + GError **error) +{ + const char *name; + MethodInfo *method; + NodeInfo *top; + + if (parser->interface == NULL || + parser->node_stack == NULL || + parser->method || + parser->signal || + parser->arg) + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("Can't put a <%s> element here"), + element_name); + return FALSE; + } + + name = NULL; + if (!locate_attributes (element_name, attribute_names, + attribute_values, error, + "name", &name, + NULL)) + return FALSE; + + if (name == NULL) + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("\"%s\" attribute required on <%s> element "), + "name", element_name); + return FALSE; + } + + top = parser->node_stack->data; + + method = method_info_new (name); + interface_info_add_method (parser->interface, method); + method_info_unref (method); + + parser->method = method; + + return TRUE; +} + +static gboolean +parse_signal (Parser *parser, + const char *element_name, + const char **attribute_names, + const char **attribute_values, + GError **error) +{ + const char *name; + SignalInfo *signal; + NodeInfo *top; + + if (parser->interface == NULL || + parser->node_stack == NULL || + parser->signal || + parser->signal || + parser->arg) + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("Can't put a <%s> element here"), + element_name); + return FALSE; + } + + name = NULL; + if (!locate_attributes (element_name, attribute_names, + attribute_values, error, + "name", &name, + NULL)) + return FALSE; + + if (name == NULL) + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("\"%s\" attribute required on <%s> element "), + "name", element_name); + return FALSE; + } + + top = parser->node_stack->data; + + signal = signal_info_new (name); + interface_info_add_signal (parser->interface, signal); + signal_info_unref (signal); + + parser->signal = signal; + + return TRUE; +} + +static int +basic_type_from_string (const char *str) +{ + if (strcmp (str, "string") == 0) + return DBUS_TYPE_STRING; + else if (strcmp (str, "int32") == 0) + return DBUS_TYPE_INT32; + else if (strcmp (str, "uint32") == 0) + return DBUS_TYPE_UINT32; + else if (strcmp (str, "int64") == 0) + return DBUS_TYPE_INT64; + else if (strcmp (str, "uint64") == 0) + return DBUS_TYPE_UINT64; + else if (strcmp (str, "double") == 0) + return DBUS_TYPE_DOUBLE; + else if (strcmp (str, "byte") == 0) + return DBUS_TYPE_BYTE; + else if (strcmp (str, "boolean") == 0) + return DBUS_TYPE_BOOLEAN; + else if (strcmp (str, "byte") == 0) + return DBUS_TYPE_BYTE; + else if (strcmp (str, "object") == 0) + return DBUS_TYPE_OBJECT_PATH; + else + return DBUS_TYPE_INVALID; +} + +static int +type_from_string (const char *str) +{ + return basic_type_from_string (str); +} + +static gboolean +parse_arg (Parser *parser, + const char *element_name, + const char **attribute_names, + const char **attribute_values, + GError **error) +{ + const char *name; + const char *type; + const char *direction; + ArgDirection dir; + int t; + ArgInfo *arg; + + if (!(parser->method || parser->signal) || + parser->node_stack == NULL || + parser->arg) + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("Can't put a <%s> element here"), + element_name); + return FALSE; + } + + name = NULL; + if (!locate_attributes (element_name, attribute_names, + attribute_values, error, + "name", &name, + "type", &type, + "direction", &direction, + NULL)) + return FALSE; + + /* name can be null for args */ + + if (type == NULL) + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("\"%s\" attribute required on <%s> element "), + "type", element_name); + return FALSE; + } + + if (direction == NULL) + { + /* methods default to in, signal to out */ + if (parser->method) + direction = "in"; + else if (parser->signal) + direction = "out"; + else + g_assert_not_reached (); + } + + if (strcmp (direction, "in") == 0) + dir = ARG_IN; + else if (strcmp (direction, "out") == 0) + dir = ARG_OUT; + else + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("\"%s\" attribute on <%s> has value \"in\" or \"out\""), + "direction", element_name); + return FALSE; + } + + t = type_from_string (type); + + arg = arg_info_new (name, dir, t); + if (parser->method) + method_info_add_arg (parser->method, arg); + else if (parser->signal) + signal_info_add_arg (parser->signal, arg); + else + g_assert_not_reached (); + + arg_info_unref (arg); + + parser->arg = arg; + + return TRUE; +} + gboolean parser_start_element (Parser *parser, const char *element_name, @@ -85,8 +546,46 @@ parser_start_element (Parser *parser, const char **attribute_values, GError **error) { - g_return_val_if_fail (error == NULL || *error == NULL); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + if (ELEMENT_IS ("node")) + { + if (!parse_node (parser, element_name, attribute_names, + attribute_values, error)) + return FALSE; + } + else if (ELEMENT_IS ("interface")) + { + if (!parse_interface (parser, element_name, attribute_names, + attribute_values, error)) + return FALSE; + } + else if (ELEMENT_IS ("method")) + { + if (!parse_method (parser, element_name, attribute_names, + attribute_values, error)) + return FALSE; + } + else if (ELEMENT_IS ("signal")) + { + if (!parse_signal (parser, element_name, attribute_names, + attribute_values, error)) + return FALSE; + } + else if (ELEMENT_IS ("arg")) + { + if (!parse_arg (parser, element_name, attribute_names, + attribute_values, error)) + return FALSE; + } + else + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("Element <%s> not recognized"), + element_name); + } + return TRUE; } @@ -95,8 +594,40 @@ parser_end_element (Parser *parser, const char *element_name, GError **error) { - g_return_val_if_fail (error == NULL || *error == NULL); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (ELEMENT_IS ("interface")) + { + parser->interface = NULL; + } + else if (ELEMENT_IS ("method")) + { + parser->method = NULL; + } + else if (ELEMENT_IS ("signal")) + { + parser->signal = NULL; + } + else if (ELEMENT_IS ("arg")) + { + parser->arg = NULL; + } + else if (ELEMENT_IS ("node")) + { + NodeInfo *top; + g_assert (parser->node_stack != NULL); + top = parser->node_stack->data; + + parser->node_stack = g_slist_remove (parser->node_stack, + top); + + if (parser->node_stack == NULL) + parser->result = top; /* We are done, store the result */ + } + else + g_assert_not_reached (); /* should have had an error on start_element */ + return TRUE; } @@ -106,7 +637,7 @@ parser_content (Parser *parser, int len, GError **error) { - g_return_val_if_fail (error == NULL || *error == NULL); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); return TRUE; } @@ -115,7 +646,13 @@ gboolean parser_finished (Parser *parser, GError **error) { - g_return_val_if_fail (error == NULL || *error == NULL); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); return TRUE; } + +NodeInfo* +parser_get_nodes (Parser *parser) +{ + return parser->result; +} diff --git a/glib/dbus-gparser.h b/glib/dbus-gparser.h index 01339fbf..3e87165b 100644 --- a/glib/dbus-gparser.h +++ b/glib/dbus-gparser.h @@ -25,10 +25,10 @@ #include #include +#include "dbus-gidl.h" G_BEGIN_DECLS - typedef struct Parser Parser; Parser* parser_new (void); @@ -52,7 +52,13 @@ gboolean parser_content (Parser *parser, gboolean parser_finished (Parser *parser, GError **error); +Parser* description_load_from_file (const char *filename, + GError **error); +Parser* description_load_from_string (const char *str, + int len, + GError **error); +NodeInfo* parser_get_nodes (Parser *parser); G_END_DECLS -- cgit v1.2.1