summaryrefslogtreecommitdiffstats
path: root/glib
diff options
context:
space:
mode:
Diffstat (limited to 'glib')
-rw-r--r--glib/.cvsignore6
-rw-r--r--glib/Makefile.am70
-rw-r--r--glib/dbus-gidl.c524
-rw-r--r--glib/dbus-gidl.h120
-rw-r--r--glib/dbus-glib-tool.c77
-rw-r--r--glib/dbus-glib.h121
-rw-r--r--glib/dbus-gloader-expat.c262
-rw-r--r--glib/dbus-gmain.c100
-rw-r--r--glib/dbus-gobject.c790
-rw-r--r--glib/dbus-gparser.c670
-rw-r--r--glib/dbus-gparser.h65
-rw-r--r--glib/dbus-gproxy.c1249
-rw-r--r--glib/dbus-gtest-main.c46
-rw-r--r--glib/dbus-gtest.c77
-rw-r--r--glib/dbus-gtest.h35
-rw-r--r--glib/dbus-gthread.c2
-rw-r--r--glib/dbus-gtool-test.h31
-rw-r--r--glib/dbus-gutils.c96
-rw-r--r--glib/dbus-gutils.h40
19 files changed, 4345 insertions, 36 deletions
diff --git a/glib/.cvsignore b/glib/.cvsignore
index a6e3d2b5..881b4887 100644
--- a/glib/.cvsignore
+++ b/glib/.cvsignore
@@ -4,11 +4,9 @@ Makefile
Makefile.in
*.lo
*.la
-test-dbus-glib
+dbus-glib-test
+dbus-glib-tool
*.bb
*.bbg
*.da
*.gcov
-test-thread-client
-test-thread-server
-test-profile
diff --git a/glib/Makefile.am b/glib/Makefile.am
index ebdb932f..a45aa593 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -1,4 +1,4 @@
-INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS)
+INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_GLIB_TOOL_CFLAGS) -DDBUS_COMPILATION=1
dbusincludedir=$(includedir)/dbus-1.0/dbus
@@ -9,47 +9,61 @@ dbusinclude_HEADERS= \
libdbus_glib_1_la_SOURCES = \
dbus-gmain.c \
- dbus-gthread.c
+ dbus-gobject.c \
+ dbus-gproxy.c \
+ dbus-gtest.c \
+ dbus-gtest.h \
+ dbus-gthread.c \
+ dbus-gutils.c \
+ dbus-gutils.h
libdbus_glib_1_la_LIBADD= $(DBUS_GLIB_LIBS) $(top_builddir)/dbus/libdbus-1.la
+## don't export symbols that start with "_" (we use this
+## convention for internal symbols)
+libdbus_glib_1_la_LDFLAGS= -export-symbols-regex "^[^_].*"
-if DBUS_BUILD_TESTS
+# convenience lib used here and by dbus-viewer
+noinst_LTLIBRARIES=libdbus-gtool.la
-if HAVE_GLIB_THREADS
-THREAD_APPS=test-thread-server test-thread-client test-profile
+libdbus_gtool_la_SOURCES = \
+ dbus-gidl.c \
+ dbus-gidl.h \
+ dbus-gloader-expat.c \
+ dbus-gparser.c \
+ dbus-gparser.h \
+ dbus-gutils.c \
+ dbus-gutils.h
-test_thread_server_SOURCES= \
- test-thread-server.c \
- test-thread.h
+libdbus_gtool_la_LIBADD = libdbus-glib-1.la
-test_thread_server_LDADD= $(DBUS_GLIB_THREADS_LIBS) $(top_builddir)/glib/libdbus-glib-1.la
+bin_PROGRAMS=dbus-glib-tool
-test_thread_client_SOURCES= \
- test-thread-client.c \
- test-thread.h
+dbus_glib_tool_SOURCES = \
+ dbus-glib-tool.c \
+ dbus-gtool-test.h
-test_thread_client_LDADD= $(DBUS_GLIB_THREADS_LIBS) $(top_builddir)/glib/libdbus-glib-1.la
-endif
+dbus_glib_tool_LDADD= $(DBUS_GLIB_TOOL_LIBS) libdbus-gtool.la
+
+if DBUS_BUILD_TESTS
-noinst_PROGRAMS= test-dbus-glib $(THREAD_APPS)
+## we use noinst_PROGRAMS not check_PROGRAMS for TESTS so that we
+## build even when not doing "make check"
+noinst_PROGRAMS= $(TESTS)
-test_dbus_glib_SOURCES= \
- test-dbus-glib.c
+## note that TESTS has special meaning (stuff to use in make check)
+## so if adding tests not to be run in make check, don't add them to
+## TESTS
+TESTS_ENVIRONMENT=DBUS_TEST_DATA=$(top_builddir)/test/data DBUS_TEST_HOMEDIR=$(top_builddir)/dbus
+TESTS=dbus-glib-test
-test_dbus_glib_LDADD= $(top_builddir)/glib/libdbus-glib-1.la
+dbus_glib_test_SOURCES= \
+ dbus-gtest-main.c
+
+dbus_glib_test_LDADD= $(top_builddir)/glib/libdbus-glib-1.la
else
### not building tests
-
-if HAVE_GLIB_THREADS
-noinst_PROGRAMS=test-profile
-endif
+TESTS=
endif
-if HAVE_GLIB_THREADS
-test_profile_SOURCES= \
- test-profile.c
-
-test_profile_LDADD= $(DBUS_GLIB_THREADS_LIBS) $(top_builddir)/glib/libdbus-glib-1.la
-endif \ No newline at end of file
diff --git a/glib/dbus-gidl.c b/glib/dbus-gidl.c
new file mode 100644
index 00000000..596b43ca
--- /dev/null
+++ b/glib/dbus-gidl.c
@@ -0,0 +1,524 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gidl.c data structure describing an interface, to be generated from IDL
+ * or something
+ *
+ * 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-gidl.h"
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+struct BaseInfo
+{
+ unsigned int refcount : 28;
+ unsigned int type : 4;
+ char *name;
+};
+
+struct NodeInfo
+{
+ BaseInfo base;
+ GSList *interfaces;
+ GSList *nodes;
+};
+
+struct InterfaceInfo
+{
+ BaseInfo base;
+ /* Since we have BaseInfo now these could be one list */
+ GSList *methods;
+ GSList *signals;
+};
+
+struct MethodInfo
+{
+ BaseInfo base;
+ GSList *args;
+};
+
+struct SignalInfo
+{
+ BaseInfo base;
+ GSList *args;
+};
+
+struct ArgInfo
+{
+ BaseInfo base;
+ int type;
+ ArgDirection direction;
+};
+
+void
+base_info_ref (BaseInfo *info)
+{
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (info->refcount > 0);
+
+ info->refcount += 1;
+}
+
+static void
+base_info_free (void *ptr)
+{
+ BaseInfo *info;
+
+ info = ptr;
+
+ g_free (info->name);
+ g_free (info);
+}
+
+void
+base_info_unref (BaseInfo *info)
+{
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (info->refcount > 0);
+
+ /* This is sort of bizarre, BaseInfo was tacked on later */
+
+ switch (info->type)
+ {
+ case INFO_TYPE_NODE:
+ node_info_unref ((NodeInfo*) info);
+ break;
+ case INFO_TYPE_INTERFACE:
+ interface_info_unref ((InterfaceInfo*) info);
+ break;
+ case INFO_TYPE_SIGNAL:
+ signal_info_unref ((SignalInfo*) info);
+ break;
+ case INFO_TYPE_METHOD:
+ method_info_unref ((MethodInfo*) info);
+ break;
+ case INFO_TYPE_ARG:
+ arg_info_unref ((ArgInfo*) info);
+ break;
+ }
+}
+
+InfoType
+base_info_get_type (BaseInfo *info)
+{
+ return info->type;
+}
+
+const char*
+base_info_get_name (BaseInfo *info)
+{
+ return info->name;
+}
+
+void
+base_info_set_name (BaseInfo *info,
+ const char *name)
+{
+ char *old;
+
+ old = info->name;
+ info->name = g_strdup (name);
+ g_free (old);
+}
+
+GType
+base_info_get_gtype (void)
+{
+ static GType our_type = 0;
+
+ if (our_type == 0)
+ our_type = g_boxed_type_register_static ("BaseInfo",
+ (GBoxedCopyFunc) base_info_ref,
+ (GBoxedFreeFunc) base_info_unref);
+
+ return our_type;
+}
+
+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_node_list (GSList **nodes_p)
+{
+ GSList *tmp;
+ tmp = *nodes_p;
+ while (tmp != NULL)
+ {
+ node_info_unref (tmp->data);
+ tmp = tmp->next;
+ }
+ g_slist_free (*nodes_p);
+ *nodes_p = NULL;
+}
+
+static void
+free_method_list (GSList **methods_p)
+{
+ GSList *tmp;
+ tmp = *methods_p;
+ while (tmp != NULL)
+ {
+ method_info_unref (tmp->data);
+ tmp = tmp->next;
+ }
+ g_slist_free (*methods_p);
+ *methods_p = NULL;
+}
+
+static void
+free_signal_list (GSList **signals_p)
+{
+ GSList *tmp;
+ tmp = *signals_p;
+ while (tmp != NULL)
+ {
+ signal_info_unref (tmp->data);
+ tmp = tmp->next;
+ }
+ g_slist_free (*signals_p);
+ *signals_p = NULL;
+}
+
+NodeInfo*
+node_info_new (const char *name)
+{
+ NodeInfo *info;
+
+ /* name can be NULL */
+
+ info = g_new0 (NodeInfo, 1);
+ info->base.refcount = 1;
+ info->base.name = g_strdup (name);
+ info->base.type = INFO_TYPE_NODE;
+
+ return info;
+}
+
+void
+node_info_ref (NodeInfo *info)
+{
+ info->base.refcount += 1;
+}
+
+void
+node_info_unref (NodeInfo *info)
+{
+ info->base.refcount -= 1;
+ if (info->base.refcount == 0)
+ {
+ free_interface_list (&info->interfaces);
+ free_node_list (&info->nodes);
+ base_info_free (info);
+ }
+}
+
+const char*
+node_info_get_name (NodeInfo *info)
+{
+ return info->base.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);
+}
+
+GSList*
+node_info_get_nodes (NodeInfo *info)
+{
+ return info->nodes;
+}
+
+void
+node_info_add_node (NodeInfo *info,
+ NodeInfo *node)
+{
+ node_info_ref (node);
+ info->nodes = g_slist_append (info->nodes, node);
+}
+
+InterfaceInfo*
+interface_info_new (const char *name)
+{
+ InterfaceInfo *info;
+
+ info = g_new0 (InterfaceInfo, 1);
+ info->base.refcount = 1;
+ info->base.name = g_strdup (name);
+ info->base.type = INFO_TYPE_INTERFACE;
+
+ return info;
+}
+
+void
+interface_info_ref (InterfaceInfo *info)
+{
+ info->base.refcount += 1;
+}
+
+void
+interface_info_unref (InterfaceInfo *info)
+{
+ info->base.refcount -= 1;
+ if (info->base.refcount == 0)
+ {
+ free_method_list (&info->methods);
+ free_signal_list (&info->signals);
+ base_info_free (info);
+ }
+}
+
+const char*
+interface_info_get_name (InterfaceInfo *info)
+{
+ return info->base.name;
+}
+
+GSList*
+interface_info_get_methods (InterfaceInfo *info)
+{
+ return info->methods;
+}
+
+GSList*
+interface_info_get_signals (InterfaceInfo *info)
+{
+ return info->signals;
+}
+
+void
+interface_info_add_method (InterfaceInfo *info,
+ MethodInfo *method)
+{
+ method_info_ref (method);
+ info->methods = g_slist_append (info->methods, method);
+}
+
+void
+interface_info_add_signal (InterfaceInfo *info,
+ SignalInfo *signal)
+{
+ signal_info_ref (signal);
+ info->signals = g_slist_append (info->signals, signal);
+}
+
+static void
+free_arg_list (GSList **args_p)
+{
+ GSList *tmp;
+ tmp = *args_p;
+ while (tmp != NULL)
+ {
+ arg_info_unref (tmp->data);
+ tmp = tmp->next;
+ }
+ g_slist_free (*args_p);
+ *args_p = NULL;
+}
+
+MethodInfo*
+method_info_new (const char *name)
+{
+ MethodInfo *info;
+
+ info = g_new0 (MethodInfo, 1);
+ info->base.refcount = 1;
+ info->base.name = g_strdup (name);
+ info->base.type = INFO_TYPE_METHOD;
+
+ return info;
+}
+
+void
+method_info_ref (MethodInfo *info)
+{
+ info->base.refcount += 1;
+}
+
+void
+method_info_unref (MethodInfo *info)
+{
+ info->base.refcount -= 1;
+ if (info->base.refcount == 0)
+ {
+ free_arg_list (&info->args);
+ base_info_free (info);
+ }
+}
+
+const char*
+method_info_get_name (MethodInfo *info)
+{
+ return info->base.name;
+}
+
+GSList*
+method_info_get_args (MethodInfo *info)
+{
+ return info->args;
+}
+
+void
+method_info_add_arg (MethodInfo *info,
+ ArgInfo *arg)
+{
+ arg_info_ref (arg);
+ info->args = g_slist_append (info->args, arg);
+}
+
+SignalInfo*
+signal_info_new (const char *name)
+{
+ SignalInfo *info;
+
+ info = g_new0 (SignalInfo, 1);
+ info->base.refcount = 1;
+ info->base.name = g_strdup (name);
+ info->base.type = INFO_TYPE_SIGNAL;
+
+ return info;
+}
+
+void
+signal_info_ref (SignalInfo *info)
+{
+ info->base.refcount += 1;
+}
+
+void
+signal_info_unref (SignalInfo *info)
+{
+ info->base.refcount -= 1;
+ if (info->base.refcount == 0)
+ {
+ free_arg_list (&info->args);
+ base_info_free (info);
+ }
+}
+
+const char*
+signal_info_get_name (SignalInfo *info)
+{
+ return info->base.name;
+}
+
+GSList*
+signal_info_get_args (SignalInfo *info)
+{
+ return info->args;
+}
+
+void
+signal_info_add_arg (SignalInfo *info,
+ ArgInfo *arg)
+{
+ arg_info_ref (arg);
+ info->args = g_slist_append (info->args, arg);
+}
+
+ArgInfo*
+arg_info_new (const char *name,
+ ArgDirection direction,
+ int type)
+{
+ ArgInfo *info;
+
+ info = g_new0 (ArgInfo, 1);
+ info->base.refcount = 1;
+ info->base.type = INFO_TYPE_ARG;
+
+ /* name can be NULL */
+ info->base.name = g_strdup (name);
+ info->direction = direction;
+ info->type = type;
+
+ return info;
+}
+
+void
+arg_info_ref (ArgInfo *info)
+{
+ info->base.refcount += 1;
+}
+
+void
+arg_info_unref (ArgInfo *info)
+{
+ info->base.refcount -= 1;
+ if (info->base.refcount == 0)
+ {
+ base_info_free (info);
+ }
+}
+const char*
+arg_info_get_name (ArgInfo *info)
+{
+ return info->base.name;
+}
+
+int
+arg_info_get_type (ArgInfo *info)
+{
+ return info->type;
+}
+
+ArgDirection
+arg_info_get_direction (ArgInfo *info)
+{
+ return info->direction;
+}
+
+#ifdef DBUS_BUILD_TESTS
+
+/**
+ * @ingroup DBusGIDL
+ * Unit test for GLib IDL internals
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_gidl_test (void)
+{
+
+ return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
+
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
diff --git a/glib/dbus-gidl.h b/glib/dbus-gidl.h
new file mode 100644
index 00000000..f95abfbd
--- /dev/null
+++ b/glib/dbus-gidl.h
@@ -0,0 +1,120 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gidl.h data structure describing an interface, to be generated from IDL
+ * or something
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_GLIB_IDL_H
+#define DBUS_GLIB_IDL_H
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#include <dbus/dbus.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct BaseInfo BaseInfo;
+typedef struct NodeInfo NodeInfo;
+typedef struct InterfaceInfo InterfaceInfo;
+typedef struct MethodInfo MethodInfo;
+typedef struct SignalInfo SignalInfo;
+typedef struct ArgInfo ArgInfo;
+
+typedef enum
+{
+ ARG_IN,
+ ARG_OUT
+} ArgDirection;
+
+typedef enum
+{
+ INFO_TYPE_NODE,
+ INFO_TYPE_INTERFACE,
+ INFO_TYPE_METHOD,
+ INFO_TYPE_SIGNAL,
+ INFO_TYPE_ARG
+
+} InfoType;
+
+void base_info_ref (BaseInfo *info);
+void base_info_unref (BaseInfo *info);
+InfoType base_info_get_type (BaseInfo *info);
+const char* base_info_get_name (BaseInfo *info);
+void base_info_set_name (BaseInfo *info,
+ const char *name);
+GType base_info_get_gtype (void);
+#define BASE_INFO_TYPE (base_info_get_gtype ())
+
+
+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);
+GSList* node_info_get_nodes (NodeInfo *info);
+void node_info_add_interface (NodeInfo *info,
+ InterfaceInfo *interface);
+void node_info_add_node (NodeInfo *info,
+ NodeInfo *child);
+
+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,
+ MethodInfo *method);
+void interface_info_add_signal (InterfaceInfo *info,
+ SignalInfo *signal);
+
+MethodInfo* method_info_new (const char *name);
+void method_info_ref (MethodInfo *info);
+void method_info_unref (MethodInfo *info);
+
+const char* method_info_get_name (MethodInfo *info);
+GSList* method_info_get_args (MethodInfo *info);
+void method_info_add_arg (MethodInfo *info,
+ ArgInfo *arg);
+
+SignalInfo* signal_info_new (const char *name);
+void signal_info_ref (SignalInfo *info);
+void signal_info_unref (SignalInfo *info);
+
+const char* signal_info_get_name (SignalInfo *info);
+GSList* signal_info_get_args (SignalInfo *info);
+void signal_info_add_arg (SignalInfo *info,
+ ArgInfo *arg);
+
+ArgInfo* arg_info_new (const char *name,
+ ArgDirection direction,
+ int type);
+void arg_info_ref (ArgInfo *info);
+void arg_info_unref (ArgInfo *info);
+const char* arg_info_get_name (ArgInfo *info);
+int arg_info_get_type (ArgInfo *info);
+ArgDirection arg_info_get_direction (ArgInfo *info);
+
+G_END_DECLS
+
+#endif /* DBUS_GLIB_IDL_H */
+
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
diff --git a/glib/dbus-glib-tool.c b/glib/dbus-glib-tool.c
new file mode 100644
index 00000000..aaf133d4
--- /dev/null
+++ b/glib/dbus-glib-tool.c
@@ -0,0 +1,77 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-compiler-main.c main() for GLib stubs/skels generator
+ *
+ * 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-gidl.h"
+#include <locale.h>
+
+#ifdef DBUS_BUILD_TESTS
+static void run_all_tests (const char *test_data_dir);
+#endif
+
+int
+main (int argc, char **argv)
+{
+ setlocale(LC_ALL, "");
+
+ return 0;
+}
+
+#ifdef DBUS_BUILD_TESTS
+static void
+test_die (const char *failure)
+{
+ fprintf (stderr, "Unit test failed: %s\n", failure);
+ exit (1);
+}
+
+static void
+run_all_tests (const char *test_data_dir)
+{
+ if (test_data_dir == NULL)
+ test_data_dir = _dbus_getenv ("DBUS_TEST_DATA");
+
+ if (test_data_dir != NULL)
+ printf ("Test data in %s\n", test_data_dir);
+ else
+ printf ("No test data!\n");
+
+ printf ("%s: running gtool tests\n", "dbus-glib-tool");
+ if (!_dbus_gtool_test (test_data_dir))
+ test_die ("gtool");
+
+ printf ("%s: completed successfully\n", "dbus-glib-test");
+}
+
+/**
+ * @ingroup DBusGTool
+ * Unit test for GLib utility tool
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_gtool_test (const char *test_data_dir)
+{
+
+ return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/glib/dbus-glib.h b/glib/dbus-glib.h
index c6116c52..46f4555d 100644
--- a/glib/dbus-glib.h
+++ b/glib/dbus-glib.h
@@ -2,6 +2,7 @@
/* dbus-glib.h GLib integration
*
* Copyright (C) 2002, 2003 CodeFactory AB
+ * Copyright (C) 2003 Red Hat, Inc.
*
* Licensed under the Academic Free License version 1.2
*
@@ -24,16 +25,132 @@
#define DBUS_GLIB_H
#include <dbus/dbus.h>
-#include <glib.h>
+#include <glib-object.h>
G_BEGIN_DECLS
-void dbus_gthread_init (void);
+#define DBUS_INSIDE_DBUS_GLIB_H 1
+
+GQuark dbus_g_error_quark (void);
+#define DBUS_GERROR dbus_g_error_quark ()
+
+#define DBUS_TYPE_CONNECTION (dbus_connection_get_g_type ())
+#define DBUS_TYPE_MESSAGE (dbus_message_get_g_type ())
+GType dbus_connection_get_g_type (void) G_GNUC_CONST;
+GType dbus_message_get_g_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ /* FIXME map all the DBUS_ERROR to DBUS_GERROR, should
+ * probably be automated in some way, perhaps
+ * via lame perl script
+ */
+ DBUS_GERROR_FAILED
+} DBusGError;
+
+void dbus_set_g_error (GError **gerror,
+ DBusError *derror);
+
+void dbus_g_thread_init (void);
void dbus_connection_setup_with_g_main (DBusConnection *connection,
GMainContext *context);
void dbus_server_setup_with_g_main (DBusServer *server,
GMainContext *context);
+typedef struct DBusGObjectInfo DBusGObjectInfo;
+typedef struct DBusGMethodInfo DBusGMethodInfo;
+
+/**
+ * Object typically generated by dbus-glib-tool that
+ * stores a mapping from introspection data to a
+ * function pointer for a C method to be invoked.
+ */
+struct DBusGMethodInfo
+{
+ GCallback function; /**< C method to invoke */
+ DBusHandleMessageFunction marshaller; /**< Marshaller to go DBusMessage to C method */
+ int data_offset; /**< Offset into the introspection data */
+};
+
+/**
+ * Introspection data for a GObject, normally autogenerated by
+ * a tool such as dbus-glib-tool.
+ */
+struct DBusGObjectInfo
+{
+ const DBusGMethodInfo *infos; /**< Array of method pointers */
+ const unsigned char *data; /**< Introspection data */
+ void *dbus_internal_padding1; /**< Reserved for expansion */
+ void *dbus_internal_padding2; /**< Reserved for expansion */
+};
+
+void dbus_g_object_class_install_info (GObjectClass *object_class,
+ const DBusGObjectInfo *info);
+void dbus_connection_register_g_object (DBusConnection *connection,
+ const char *at_path,
+ GObject *object);
+
+
+typedef struct DBusGProxy DBusGProxy;
+typedef struct DBusGProxyClass DBusGProxyClass;
+
+typedef void (* DBusGProxySignalHandler) (DBusGProxy *proxy,
+ DBusMessage *signal,
+ void *user_data);
+
+#define DBUS_TYPE_GPROXY (dbus_gproxy_get_type ())
+#define DBUS_GPROXY(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), DBUS_TYPE_GPROXY, DBusGProxy))
+#define DBUS_GPROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUS_TYPE_GPROXY, DBusGProxyClass))
+#define DBUS_IS_GPROXY(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), DBUS_TYPE_GPROXY))
+#define DBUS_IS_GPROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUS_TYPE_GPROXY))
+#define DBUS_GPROXY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUS_TYPE_GPROXY, DBusGProxyClass))
+
+
+GType dbus_gproxy_get_type (void) G_GNUC_CONST;
+DBusGProxy* dbus_gproxy_new_for_service (DBusConnection *connection,
+ const char *service_name,
+ const char *path_name,
+ const char *interface_name);
+DBusGProxy* dbus_gproxy_new_for_service_owner (DBusConnection *connection,
+ const char *service_name,
+ const char *path_name,
+ const char *interface_name,
+ GError **error);
+DBusGProxy* dbus_gproxy_new_for_peer (DBusConnection *connection,
+ const char *path_name,
+ const char *interface_name);
+void dbus_gproxy_connect_signal (DBusGProxy *proxy,
+ const char *signal_name,
+ DBusGProxySignalHandler handler,
+ void *data,
+ GClosureNotify free_data_func);
+void dbus_gproxy_disconnect_signal (DBusGProxy *proxy,
+ const char *signal_name,
+ DBusGProxySignalHandler handler,
+ void *data);
+DBusPendingCall* dbus_gproxy_begin_call (DBusGProxy *proxy,
+ const char *method,
+ int first_arg_type,
+ ...);
+gboolean dbus_gproxy_end_call (DBusGProxy *proxy,
+ DBusPendingCall *pending,
+ GError **error,
+ int first_arg_type,
+ ...);
+void dbus_gproxy_oneway_call (DBusGProxy *proxy,
+ const char *method,
+ int first_arg_type,
+ ...);
+void dbus_gproxy_send (DBusGProxy *proxy,
+ DBusMessage *message,
+ dbus_uint32_t *client_serial);
+
+
+#undef DBUS_INSIDE_DBUS_GLIB_H
+
G_END_DECLS
#endif /* DBUS_GLIB_H */
+
+
+
diff --git a/glib/dbus-gloader-expat.c b/glib/dbus-gloader-expat.c
new file mode 100644
index 00000000..01587d21
--- /dev/null
+++ b/glib/dbus-gloader-expat.c
@@ -0,0 +1,262 @@
+/* -*- 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 <expat.h>
+
+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
+};
+
+/**
+ * Context for Expat parser for introspection data.
+ */
+typedef struct
+{
+ Parser *parser; /**< The parser for the introspection data */
+ const char *filename; /**< The filename being loaded */
+ GString *content; /**< The content of the current element */
+ GError **error; /**< Error return location */
+ gboolean failed; /**< True if parse has 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);
+}
+
+NodeInfo*
+description_load_from_file (const char *filename,
+ GError **error)
+{
+ char *contents;
+ gsize len;
+ NodeInfo *nodes;
+
+ contents = NULL;
+ if (!g_file_get_contents (filename, &contents, &len, error))
+ return NULL;
+
+ nodes = description_load_from_string (contents, len, error);
+ g_free (contents);
+
+ return nodes;
+}
+
+NodeInfo*
+description_load_from_string (const char *str,
+ int len,
+ GError **error)
+{
+ XML_Parser expat;
+ ExpatParseContext context;
+ NodeInfo *nodes;
+
+ 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);
+ nodes = parser_get_nodes (context.parser);
+ node_info_ref (nodes);
+ parser_unref (context.parser);
+ return nodes;
+
+ 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-gmain.c b/glib/dbus-gmain.c
index d948b431..68ba22d7 100644
--- a/glib/dbus-gmain.c
+++ b/glib/dbus-gmain.c
@@ -21,8 +21,13 @@
*
*/
+#include <config.h>
#include "dbus-glib.h"
-#include <glib.h>
+#include "dbus-gtest.h"
+
+#include <libintl.h>
+#define _(x) dgettext (GETTEXT_PACKAGE, x)
+#define N_(x) x
/**
* @defgroup DBusGLib GLib bindings
@@ -48,6 +53,9 @@
*/
typedef struct DBusGSource DBusGSource;
+/**
+ * A GSource subclass for a DBusConnection.
+ */
struct DBusGSource
{
GSource source; /**< the parent GSource */
@@ -530,4 +538,94 @@ dbus_server_setup_with_g_main (DBusServer *server,
g_error ("Not enough memory to set up DBusServer for use with GLib");
}
+/**
+ * The implementation of DBUS_GERROR error domain. See documentation
+ * for GError in GLib reference manual.
+ *
+ * @returns the error domain quark for use with GError
+ */
+GQuark
+dbus_g_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (quark == 0)
+ quark = g_quark_from_static_string ("g-exec-error-quark");
+ return quark;
+}
+
+
+/**
+ * Set a GError return location from a DBusError.
+ *
+ * @todo expand the DBUS_GERROR enum and take advantage of it here
+ *
+ * @param gerror location to store a GError, or #NULL
+ * @param derror the DBusError
+ */
+void
+dbus_set_g_error (GError **gerror,
+ DBusError *derror)
+{
+ g_return_if_fail (derror != NULL);
+ g_return_if_fail (dbus_error_is_set (derror));
+
+ g_set_error (gerror, DBUS_GERROR,
+ DBUS_GERROR_FAILED,
+ _("D-BUS error %s: %s"),
+ derror->name, derror->message);
+}
+
+/**
+ * Get the GLib type ID for a DBusConnection boxed type.
+ *
+ * @returns GLib type
+ */
+GType
+dbus_connection_get_g_type (void)
+{
+ static GType our_type = 0;
+
+ if (our_type == 0)
+ our_type = g_boxed_type_register_static ("DBusConnection",
+ (GBoxedCopyFunc) dbus_connection_ref,
+ (GBoxedFreeFunc) dbus_connection_unref);
+
+ return our_type;
+}
+
+/**
+ * Get the GLib type ID for a DBusMessage boxed type.
+ *
+ * @returns GLib type
+ */
+GType
+dbus_message_get_g_type (void)
+{
+ static GType our_type = 0;
+
+ if (our_type == 0)
+ our_type = g_boxed_type_register_static ("DBusMessage",
+ (GBoxedCopyFunc) dbus_message_ref,
+ (GBoxedFreeFunc) dbus_message_unref);
+
+ return our_type;
+}
+
+
/** @} */ /* end of public API */
+
+#ifdef DBUS_BUILD_TESTS
+
+/**
+ * @ingroup DBusGLibInternals
+ * Unit test for GLib main loop integration
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_gmain_test (const char *test_data_dir)
+{
+
+ return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/glib/dbus-gobject.c b/glib/dbus-gobject.c
new file mode 100644
index 00000000..6e65770f
--- /dev/null
+++ b/glib/dbus-gobject.c
@@ -0,0 +1,790 @@
+/* -*- 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 <config.h>
+#include "dbus-glib.h"
+#include "dbus-gtest.h"
+#include "dbus-gutils.h"
+#include <string.h>
+
+/**
+ * @addtogroup DBusGLibInternals
+ * @{
+ */
+
+static GStaticMutex info_hash_mutex = G_STATIC_MUTEX_INIT;
+static GHashTable *info_hash = NULL;
+
+static char*
+wincaps_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_wincaps (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);
+
+ /* FIXME */
+
+}
+
+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;
+ char **path;
+ char **children;
+
+ if (!dbus_message_get_path_decomposed (message, &path))
+ g_error ("Out of memory");
+
+ if (!dbus_connection_list_registered (connection, (const char**) path,
+ &children))
+ g_error ("Out of memory");
+
+ xml = g_string_new (NULL);
+
+ g_string_append (xml, "<node>\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, " </interface>\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, " <interface name=\"org.gtk.objects.");
+ g_string_append (xml, g_type_name (spec->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_wincaps (spec->name);
+
+ if (can_set)
+ {
+ g_string_append (xml, " <method name=\"set_");
+ g_string_append (xml, s);
+ g_string_append (xml, "\">\n");
+
+ g_string_append (xml, " <arg type=\"");
+ g_string_append (xml, dbus_type_to_string (dbus_type));
+ g_string_append (xml, "\"/>\n");
+ }
+
+ if (can_get)
+ {
+ g_string_append (xml, " <method name=\"get_");
+ g_string_append (xml, s);
+ g_string_append (xml, "\">\n");
+
+ g_string_append (xml, " <arg type=\"");
+ g_string_append (xml, dbus_type_to_string (dbus_type));
+ g_string_append (xml, "\" direction=\"out\"/>\n");
+ }
+
+ g_free (s);
+
+ next:
+ ++i;
+ }
+
+ if (last_type != G_TYPE_INVALID)
+ g_string_append (xml, " </interface>\n");
+
+ g_free (specs);
+
+ /* Append child nodes */
+
+ i = 0;
+ while (children[i])
+ {
+ g_string_append_printf (xml, " <node name=\"%s\"/>\n",
+ children[i]);
+ ++i;
+ }
+
+ /* Close the XML, and send it to the requesting app */
+
+ g_string_append (xml, "</node>\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);
+
+ dbus_free_string_array (path);
+ dbus_free_string_array (children);
+
+ 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' && member[3] == '_');
+ getter = (member[0] == 'g' && member[1] == 'e' && member[2] == 't' && member[3] == '_');
+
+ if (!(setter || getter))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ s = wincaps_to_uscore (&member[4]);
+
+ 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_g_object() 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_g_object_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);
+}
+
+/**
+ * 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_g_object (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 = _dbus_gutils_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
+#include <stdlib.h>
+
+/**
+ * @ingroup DBusGLibInternals
+ * Unit test for GLib GObject integration ("skeletons")
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_gobject_test (const char *test_data_dir)
+{
+ int i;
+ static struct { const char *wincaps; const char *uscore; } name_pairs[] = {
+ { "SetFoo", "set_foo" },
+ { "Foo", "foo" },
+ { "GetFooBar", "get_foo_bar" },
+ { "Hello", "hello" }
+
+ /* Impossible-to-handle cases */
+ /* { "FrobateUIHandler", "frobate_ui_handler" } */
+ };
+
+ i = 0;
+ while (i < (int) G_N_ELEMENTS (name_pairs))
+ {
+ char *uscore;
+ char *wincaps;
+
+ uscore = wincaps_to_uscore (name_pairs[i].wincaps);
+ wincaps = uscore_to_wincaps (name_pairs[i].uscore);
+
+ if (strcmp (uscore, name_pairs[i].uscore) != 0)
+ {
+ g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
+ name_pairs[i].wincaps, name_pairs[i].uscore,
+ uscore);
+ exit (1);
+ }
+
+ if (strcmp (wincaps, name_pairs[i].wincaps) != 0)
+ {
+ g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
+ name_pairs[i].uscore, name_pairs[i].wincaps,
+ wincaps);
+ exit (1);
+ }
+
+ g_free (uscore);
+ g_free (wincaps);
+
+ ++i;
+ }
+
+ return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/glib/dbus-gparser.c b/glib/dbus-gparser.c
new file mode 100644
index 00000000..16d17f3d
--- /dev/null
+++ b/glib/dbus-gparser.c
@@ -0,0 +1,670 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gparser.c parse DBus description files
+ *
+ * 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 "dbus-gidl.h"
+#include <string.h>
+
+#include <libintl.h>
+#define _(x) gettext ((x))
+#define N_(x) x
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#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*
+parser_new (void)
+{
+ Parser *parser;
+
+ parser = g_new0 (Parser, 1);
+
+ parser->refcount = 1;
+
+ return parser;
+}
+
+void
+parser_ref (Parser *parser)
+{
+ parser->refcount += 1;
+}
+
+void
+parser_unref (Parser *parser)
+{
+ parser->refcount -= 1;
+ if (parser->refcount == 0)
+ {
+ if (parser->result)
+ node_info_unref (parser->result);
+
+ g_free (parser);
+ }
+}
+
+gboolean
+parser_check_doctype (Parser *parser,
+ const char *doctype,
+ GError **error)
+{
+ 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);
+ return FALSE;
+ }
+ else
+ 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);
+
+ if (parser->node_stack != NULL)
+ {
+ node_info_add_node (parser->node_stack->data,
+ node);
+ }
+
+ 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,
+ const char **attribute_names,
+ const char **attribute_values,
+ GError **error)
+{
+ 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;
+}
+
+gboolean
+parser_end_element (Parser *parser,
+ const char *element_name,
+ GError **error)
+{
+ 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;
+}
+
+gboolean
+parser_content (Parser *parser,
+ const char *content,
+ int len,
+ GError **error)
+{
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ return TRUE;
+}
+
+gboolean
+parser_finished (Parser *parser,
+ GError **error)
+{
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ return TRUE;
+}
+
+NodeInfo*
+parser_get_nodes (Parser *parser)
+{
+ return parser->result;
+}
+
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
diff --git a/glib/dbus-gparser.h b/glib/dbus-gparser.h
new file mode 100644
index 00000000..cc58e5e0
--- /dev/null
+++ b/glib/dbus-gparser.h
@@ -0,0 +1,65 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gparser.h parse DBus description files
+ *
+ * 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
+ *
+ */
+#ifndef DBUS_GLIB_PARSER_H
+#define DBUS_GLIB_PARSER_H
+
+#include <dbus/dbus.h>
+#include <glib.h>
+#include "dbus-gidl.h"
+
+G_BEGIN_DECLS
+
+typedef struct Parser Parser;
+
+Parser* parser_new (void);
+void parser_ref (Parser *parser);
+void parser_unref (Parser *parser);
+gboolean parser_check_doctype (Parser *parser,
+ const char *doctype,
+ GError **error);
+gboolean parser_start_element (Parser *parser,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ GError **error);
+gboolean parser_end_element (Parser *parser,
+ const char *element_name,
+ GError **error);
+gboolean parser_content (Parser *parser,
+ const char *content,
+ int len,
+ GError **error);
+gboolean parser_finished (Parser *parser,
+ GError **error);
+
+NodeInfo* description_load_from_file (const char *filename,
+ GError **error);
+NodeInfo* description_load_from_string (const char *str,
+ int len,
+ GError **error);
+
+NodeInfo* parser_get_nodes (Parser *parser);
+
+G_END_DECLS
+
+#endif /* DBUS_GLIB_GPARSER_H */
diff --git a/glib/dbus-gproxy.c b/glib/dbus-gproxy.c
new file mode 100644
index 00000000..99900e4c
--- /dev/null
+++ b/glib/dbus-gproxy.c
@@ -0,0 +1,1249 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gcall.c convenience routines for calling methods, etc.
+ *
+ * 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-glib.h"
+#include <string.h>
+
+/**
+ * @addtogroup DBusGLibInternals
+ *
+ * @{
+ */
+
+typedef struct DBusGProxyManager DBusGProxyManager;
+
+/**
+ * Internals of DBusGProxy
+ */
+struct DBusGProxy
+{
+ GObject parent; /**< Parent instance */
+
+ DBusGProxyManager *manager; /**< Proxy manager */
+ char *service; /**< Service messages go to or NULL */
+ char *path; /**< Path messages go to or NULL */
+ char *interface; /**< Interface messages go to or NULL */
+};
+
+/**
+ * Class struct for DBusGProxy
+ */
+struct DBusGProxyClass
+{
+ GObjectClass parent_class;
+};
+
+static void dbus_gproxy_init (DBusGProxy *proxy);
+static void dbus_gproxy_class_init (DBusGProxyClass *klass);
+static void dbus_gproxy_finalize (GObject *object);
+static void dbus_gproxy_dispose (GObject *object);
+static void dbus_gproxy_destroy (DBusGProxy *proxy);
+static void dbus_gproxy_emit_received (DBusGProxy *proxy,
+ DBusMessage *message);
+
+
+/**
+ * A list of proxies with a given service+path+interface, used to route incoming
+ * signals.
+ */
+typedef struct
+{
+ GSList *proxies;
+
+ char name[4]; /**< service (empty string for none), nul byte,
+ * path, nul byte,
+ * interface, nul byte
+ */
+
+} DBusGProxyList;
+
+/**
+ * DBusGProxyManager's primary task is to route signals to the proxies
+ * those signals are emitted on. In order to do this it also has to
+ * track the owners of the services proxies are bound to.
+ */
+struct DBusGProxyManager
+{
+ GStaticMutex lock; /**< Thread lock */
+ int refcount; /**< Reference count */
+ DBusConnection *connection; /**< Connection we're associated with. */
+
+ GHashTable *proxy_lists; /**< Hash used to route incoming signals
+ * and iterate over proxies
+ */
+
+};
+
+static void dbus_gproxy_manager_ref (DBusGProxyManager *manager);
+static DBusHandlerResult dbus_gproxy_manager_filter (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data);
+
+/** Lock the DBusGProxyManager */
+#define LOCK_MANAGER(mgr) (g_static_mutex_lock (&(mgr)->lock))
+/** Unlock the DBusGProxyManager */
+#define UNLOCK_MANAGER(mgr) (g_static_mutex_unlock (&(mgr)->lock))
+
+static int gproxy_manager_slot = -1;
+
+/* Lock controlling get/set manager as data on each connection */
+static GStaticMutex connection_gproxy_lock = G_STATIC_MUTEX_INIT;
+
+static DBusGProxyManager*
+dbus_gproxy_manager_get (DBusConnection *connection)
+{
+ DBusGProxyManager *manager;
+
+ dbus_connection_allocate_data_slot (&gproxy_manager_slot);
+ if (gproxy_manager_slot < 0)
+ g_error ("out of memory");
+
+ g_static_mutex_lock (&connection_gproxy_lock);
+
+ manager = dbus_connection_get_data (connection, gproxy_manager_slot);
+ if (manager != NULL)
+ {
+ dbus_connection_free_data_slot (&gproxy_manager_slot);
+ dbus_gproxy_manager_ref (manager);
+ g_static_mutex_unlock (&connection_gproxy_lock);
+ return manager;
+ }
+
+ manager = g_new0 (DBusGProxyManager, 1);
+
+ manager->refcount = 1;
+ manager->connection = connection;
+
+ g_static_mutex_init (&manager->lock);
+
+ /* Proxy managers keep the connection alive, which means that
+ * DBusGProxy indirectly does. To free a connection you have to free
+ * all the proxies referring to it.
+ */
+ dbus_connection_ref (manager->connection);
+
+ dbus_connection_set_data (connection, gproxy_manager_slot,
+ manager, NULL);
+
+ dbus_connection_add_filter (connection, dbus_gproxy_manager_filter,
+ manager, NULL);
+
+ g_static_mutex_unlock (&connection_gproxy_lock);
+
+ return manager;
+}
+
+static void
+dbus_gproxy_manager_ref (DBusGProxyManager *manager)
+{
+ g_assert (manager != NULL);
+ g_assert (manager->refcount > 0);
+
+ LOCK_MANAGER (manager);
+
+ manager->refcount += 1;
+
+ UNLOCK_MANAGER (manager);
+}
+
+static void
+dbus_gproxy_manager_unref (DBusGProxyManager *manager)
+{
+ g_assert (manager != NULL);
+ g_assert (manager->refcount > 0);
+
+ LOCK_MANAGER (manager);
+ manager->refcount -= 1;
+ if (manager->refcount == 0)
+ {
+ UNLOCK_MANAGER (manager);
+
+ if (manager->proxy_lists)
+ {
+ /* can't have any proxies left since they hold
+ * a reference to the proxy manager.
+ */
+ g_assert (g_hash_table_size (manager->proxy_lists) == 0);
+
+ g_hash_table_destroy (manager->proxy_lists);
+ manager->proxy_lists = NULL;
+ }
+
+ g_static_mutex_free (&manager->lock);
+
+ g_static_mutex_lock (&connection_gproxy_lock);
+
+ dbus_connection_remove_filter (manager->connection, dbus_gproxy_manager_filter,
+ manager);
+
+ dbus_connection_set_data (manager->connection,
+ gproxy_manager_slot,
+ NULL, NULL);
+
+ g_static_mutex_unlock (&connection_gproxy_lock);
+
+ dbus_connection_unref (manager->connection);
+ g_free (manager);
+
+ dbus_connection_free_data_slot (&gproxy_manager_slot);
+ }
+ else
+ {
+ UNLOCK_MANAGER (manager);
+ }
+}
+
+static guint
+tristring_hash (gconstpointer key)
+{
+ const char *p = key;
+ guint h = *p;
+
+ if (h)
+ {
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + *p;
+ }
+
+ /* skip nul and do the next substring */
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + *p;
+
+ /* skip nul again and another substring */
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + *p;
+
+ return h;
+}
+
+static gboolean
+strequal_len (const char *a,
+ const char *b,
+ size_t *lenp)
+{
+ size_t a_len;
+ size_t b_len;
+
+ a_len = strlen (a);
+ b_len = strlen (b);
+
+ if (a_len != b_len)
+ return FALSE;
+
+ if (memcmp (a, b, a_len) != 0)
+ return FALSE;
+
+ *lenp = a_len;
+
+ return TRUE;
+}
+
+static gboolean
+tristring_equal (gconstpointer a,
+ gconstpointer b)
+{
+ const char *ap = a;
+ const char *bp = b;
+ size_t len;
+
+ if (!strequal_len (ap, bp, &len))
+ return FALSE;
+
+ ap += len + 1;
+ bp += len + 1;
+
+ if (!strequal_len (ap, bp, &len))
+ return FALSE;
+
+ ap += len + 1;
+ bp += len + 1;
+
+ if (strcmp (ap, bp) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static char*
+tristring_alloc_from_strings (size_t padding_before,
+ const char *service,
+ const char *path,
+ const char *interface)
+{
+ size_t service_len, iface_len, path_len, len;
+ char *tri;
+
+ if (service)
+ service_len = strlen (service);
+ else
+ service_len = 0;
+
+ path_len = strlen (path);
+
+ iface_len = strlen (interface);
+
+ tri = g_malloc (padding_before + service_len + path_len + iface_len + 3);
+
+ len = padding_before;
+
+ if (service)
+ memcpy (&tri[len], service, service_len);
+
+ len += service_len;
+ tri[len] = '\0';
+ len += 1;
+
+ g_assert (len == (padding_before + service_len + 1));
+
+ memcpy (&tri[len], path, path_len);
+ len += path_len;
+ tri[len] = '\0';
+ len += 1;
+
+ g_assert (len == (padding_before + service_len + path_len + 2));
+
+ memcpy (&tri[len], interface, iface_len);
+ len += iface_len;
+ tri[len] = '\0';
+ len += 1;
+
+ g_assert (len == (padding_before + service_len + path_len + iface_len + 3));
+
+ return tri;
+}
+
+static char*
+tristring_from_proxy (DBusGProxy *proxy)
+{
+ return tristring_alloc_from_strings (0,
+ proxy->service,
+ proxy->path,
+ proxy->interface);
+}
+
+static char*
+tristring_from_message (DBusMessage *message)
+{
+ return tristring_alloc_from_strings (0,
+ dbus_message_get_sender (message),
+ dbus_message_get_path (message),
+ dbus_message_get_interface (message));
+}
+
+static DBusGProxyList*
+gproxy_list_new (DBusGProxy *first_proxy)
+{
+ DBusGProxyList *list;
+
+ list = (void*) tristring_alloc_from_strings (G_STRUCT_OFFSET (DBusGProxyList, name),
+ first_proxy->service,
+ first_proxy->path,
+ first_proxy->interface);
+ list->proxies = NULL;
+
+ return list;
+}
+
+static void
+gproxy_list_free (DBusGProxyList *list)
+{
+ /* we don't hold a reference to the proxies in the list,
+ * as they ref the GProxyManager
+ */
+ g_slist_free (list->proxies);
+
+ g_free (list);
+}
+
+static char*
+gproxy_get_match_rule (DBusGProxy *proxy)
+{
+ /* FIXME Some sort of escaping is required here I think */
+
+ if (proxy->service)
+ return g_strdup_printf ("type='signal',service='%s',path='%s',interface='%s'",
+ proxy->service, proxy->path, proxy->interface);
+ else
+ return g_strdup_printf ("type='signal',path='%s',interface='%s'",
+ proxy->path, proxy->interface);
+}
+
+static void
+dbus_gproxy_manager_register (DBusGProxyManager *manager,
+ DBusGProxy *proxy)
+{
+ DBusGProxyList *list;
+
+ LOCK_MANAGER (manager);
+
+ if (manager->proxy_lists == NULL)
+ {
+ list = NULL;
+ manager->proxy_lists = g_hash_table_new_full (tristring_hash,
+ tristring_equal,
+ NULL,
+ (GFreeFunc) gproxy_list_free);
+ }
+ else
+ {
+ char *tri;
+
+ tri = tristring_from_proxy (proxy);
+
+ list = g_hash_table_lookup (manager->proxy_lists, tri);
+
+ g_free (tri);
+ }
+
+ if (list == NULL)
+ {
+ list = gproxy_list_new (proxy);
+
+ g_hash_table_replace (manager->proxy_lists,
+ list->name, list);
+ }
+
+ if (list->proxies == NULL)
+ {
+ /* We have to add the match rule to the server,
+ * but FIXME only if the server is a message bus,
+ * not if it's a peer.
+ */
+ char *rule;
+
+ rule = gproxy_get_match_rule (proxy);
+
+ /* We don't check for errors; it's not like anyone would handle them,
+ * and we don't want a round trip here.
+ */
+ dbus_bus_add_match (manager->connection,
+ rule, NULL);
+
+ g_free (rule);
+ }
+
+ g_assert (g_slist_find (list->proxies, proxy) == NULL);
+
+ list->proxies = g_slist_prepend (list->proxies, proxy);
+
+ UNLOCK_MANAGER (manager);
+}
+
+static void
+dbus_gproxy_manager_unregister (DBusGProxyManager *manager,
+ DBusGProxy *proxy)
+{
+ DBusGProxyList *list;
+ char *tri;
+
+ LOCK_MANAGER (manager);
+
+#ifndef G_DISABLE_CHECKS
+ if (manager->proxy_lists == NULL)
+ {
+ g_warning ("Trying to disconnect a signal on a proxy but none are connected\n");
+ return;
+ }
+#endif
+
+ tri = tristring_from_proxy (proxy);
+
+ list = g_hash_table_lookup (manager->proxy_lists, tri);
+
+ g_free (tri);
+
+#ifndef G_DISABLE_CHECKS
+ if (list == NULL)
+ {
+ g_warning ("Trying to disconnect a signal on a proxy but none are connected\n");
+ return;
+ }
+#endif
+
+ g_assert (g_slist_find (list->proxies, proxy) != NULL);
+
+ list->proxies = g_slist_remove (list->proxies, proxy);
+
+ g_assert (g_slist_find (list->proxies, proxy) == NULL);
+
+ if (g_hash_table_size (manager->proxy_lists) == 0)
+ {
+ g_hash_table_destroy (manager->proxy_lists);
+ manager->proxy_lists = NULL;
+ }
+
+ UNLOCK_MANAGER (manager);
+}
+
+static void
+list_proxies_foreach (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ DBusGProxyList *list;
+ GSList **ret;
+ GSList *tmp;
+
+ list = value;
+ ret = user_data;
+
+ tmp = list->proxies;
+ while (tmp != NULL)
+ {
+ DBusGProxy *proxy = DBUS_GPROXY (tmp->data);
+
+ g_object_ref (proxy);
+ *ret = g_slist_prepend (*ret, proxy);
+
+ tmp = tmp->next;
+ }
+}
+
+static GSList*
+dbus_gproxy_manager_list_all (DBusGProxyManager *manager)
+{
+ GSList *ret;
+
+ ret = NULL;
+
+ if (manager->proxy_lists)
+ {
+ g_hash_table_foreach (manager->proxy_lists,
+ list_proxies_foreach,
+ &ret);
+ }
+
+ return ret;
+}
+
+static DBusHandlerResult
+dbus_gproxy_manager_filter (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ DBusGProxyManager *manager;
+
+ if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ manager = user_data;
+
+ dbus_gproxy_manager_ref (manager);
+
+ LOCK_MANAGER (manager);
+
+ if (dbus_message_is_signal (message,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL,
+ "Disconnected"))
+ {
+ /* Destroy all the proxies, quite possibly resulting in unreferencing
+ * the proxy manager and the connection as well.
+ */
+ GSList *all;
+ GSList *tmp;
+
+ all = dbus_gproxy_manager_list_all (manager);
+
+ tmp = all;
+ while (tmp != NULL)
+ {
+ DBusGProxy *proxy;
+
+ proxy = DBUS_GPROXY (tmp->data);
+
+ UNLOCK_MANAGER (manager);
+ dbus_gproxy_destroy (proxy);
+ g_object_unref (G_OBJECT (proxy));
+ LOCK_MANAGER (manager);
+
+ tmp = tmp->next;
+ }
+
+ g_slist_free (all);
+
+#ifndef G_DISABLE_CHECKS
+ if (manager->proxy_lists != NULL)
+ g_warning ("Disconnection emitted \"destroy\" on all DBusGProxy, but somehow new proxies were created in response to one of those destroy signals. This will cause a memory leak.");
+#endif
+ }
+ else
+ {
+ char *tri;
+ DBusGProxyList *list;
+
+ tri = tristring_from_message (message);
+
+ if (manager->proxy_lists)
+ list = g_hash_table_lookup (manager->proxy_lists, tri);
+ else
+ list = NULL;
+
+ g_free (tri);
+
+ /* Emit the signal */
+
+ if (list != NULL)
+ {
+ GSList *tmp;
+ GSList *copy;
+
+ copy = g_slist_copy (list->proxies);
+ g_slist_foreach (copy, (GFunc) g_object_ref, NULL);
+
+ tmp = copy;
+ while (tmp != NULL)
+ {
+ DBusGProxy *proxy;
+
+ proxy = DBUS_GPROXY (tmp->data);
+
+ UNLOCK_MANAGER (manager);
+ dbus_gproxy_emit_received (proxy, message);
+ g_object_unref (G_OBJECT (proxy));
+ LOCK_MANAGER (manager);
+
+ tmp = tmp->next;
+ }
+
+ g_slist_free (copy);
+ }
+ }
+
+ UNLOCK_MANAGER (manager);
+ dbus_gproxy_manager_unref (manager);
+
+ /* "Handling" signals doesn't make sense, they are for everyone
+ * who cares
+ */
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+
+/* ---------- DBusGProxy -------------- */
+
+
+
+enum
+{
+ DESTROY,
+ RECEIVED,
+ LAST_SIGNAL
+};
+
+static void *parent_class;
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+dbus_gproxy_init (DBusGProxy *proxy)
+{
+ /* Nothing */
+}
+
+static void
+dbus_gproxy_class_init (DBusGProxyClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->finalize = dbus_gproxy_finalize;
+ object_class->dispose = dbus_gproxy_dispose;
+
+ signals[DESTROY] =
+ g_signal_new ("destroy",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[RECEIVED] =
+ g_signal_new ("received",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1,
+ DBUS_TYPE_MESSAGE);
+}
+
+
+static void
+dbus_gproxy_dispose (GObject *object)
+{
+ DBusGProxy *proxy;
+
+ proxy = DBUS_GPROXY (object);
+
+ g_signal_emit (object, signals[DESTROY], 0);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+dbus_gproxy_finalize (GObject *object)
+{
+ DBusGProxy *proxy;
+
+ proxy = DBUS_GPROXY (object);
+
+ if (proxy->manager)
+ {
+ dbus_gproxy_manager_unregister (proxy->manager, proxy);
+ dbus_gproxy_manager_unref (proxy->manager);
+ }
+
+ g_free (proxy->service);
+ g_free (proxy->path);
+ g_free (proxy->interface);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+dbus_gproxy_destroy (DBusGProxy *proxy)
+{
+ /* FIXME do we need the GTK_IN_DESTRUCTION style flag
+ * from GtkObject?
+ */
+ g_object_run_dispose (G_OBJECT (proxy));
+}
+
+static char*
+create_signal_detail (const char *interface,
+ const char *signal)
+{
+ GString *str;
+
+ str = g_string_new (interface);
+
+ g_string_append (str, ".");
+
+ g_string_append (str, signal);
+
+ return g_string_free (str, FALSE);
+}
+
+static void
+dbus_gproxy_emit_received (DBusGProxy *proxy,
+ DBusMessage *message)
+{
+ const char *interface;
+ const char *signal;
+ char *detail;
+ GQuark q;
+
+ interface = dbus_message_get_interface (message);
+ signal = dbus_message_get_member (message);
+
+ g_assert (interface != NULL);
+ g_assert (signal != NULL);
+
+ detail = create_signal_detail (interface, signal);
+
+ /* If the quark isn't preexisting, there's no way there
+ * are any handlers connected. We don't want to create
+ * extra quarks for every possible signal.
+ */
+ q = g_quark_try_string (detail);
+
+ if (q != 0)
+ g_signal_emit (G_OBJECT (proxy),
+ signals[RECEIVED],
+ q,
+ message);
+
+ g_free (detail);
+}
+
+/** @} End of DBusGLibInternals */
+
+/** @addtogroup DBusGLib
+ * @{
+ */
+
+/**
+ * Standard GObject get_type() function for DBusGProxy.
+ *
+ * @returns type ID for DBusGProxy class
+ */
+GType
+dbus_gproxy_get_type (void)
+{
+ static GType object_type = 0;
+
+ if (!object_type)
+ {
+ static const GTypeInfo object_info =
+ {
+ sizeof (DBusGProxyClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) dbus_gproxy_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (DBusGProxy),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) dbus_gproxy_init,
+ };
+
+ object_type = g_type_register_static (G_TYPE_OBJECT,
+ "DBusGProxy",
+ &object_info, 0);
+ }
+
+ return object_type;
+}
+
+static DBusGProxy*
+dbus_gproxy_new (DBusConnection *connection,
+ const char *service_name,
+ const char *path_name,
+ const char *interface_name)
+{
+ DBusGProxy *proxy;
+
+ g_assert (connection != NULL);
+
+ proxy = g_object_new (DBUS_TYPE_GPROXY, NULL);
+
+ /* These should all be construct-only mandatory properties,
+ * for now we just don't let people use g_object_new().
+ */
+
+ proxy->manager = dbus_gproxy_manager_get (connection);
+
+ proxy->service = g_strdup (service_name);
+ proxy->path = g_strdup (path_name);
+ proxy->interface = g_strdup (interface_name);
+
+ dbus_gproxy_manager_register (proxy->manager, proxy);
+
+ return proxy;
+}
+
+/**
+ * Creates a new proxy for a remote interface exported by a service on
+ * a message bus. Method calls and signal connections over this proxy
+ * will go to the service owner; the service owner is expected to
+ * support the given interface name. THE SERVICE OWNER MAY CHANGE OVER
+ * TIME, for example between two different method calls. If you need a
+ * fixed owner, you need to request the current owner and bind a proxy
+ * to that rather than to the generic service name; see
+ * dbus_gproxy_new_for_service_owner().
+ *
+ * A service-associated proxy only makes sense with a message bus,
+ * not for app-to-app direct dbus connections.
+ *
+ * This proxy will only emit the "destroy" signal if the #DBusConnection
+ * is disconnected or the proxy is has no remaining references.
+ *
+ * @param connection the connection to the remote bus
+ * @param service_name name of the service on the message bus
+ * @param path_name name of the object inside the service to call methods on
+ * @param interface_name name of the interface to call methods on
+ * @returns new proxy object
+ */
+DBusGProxy*
+dbus_gproxy_new_for_service (DBusConnection *connection,
+ const char *service_name,
+ const char *path_name,
+ const char *interface_name)
+{
+ DBusGProxy *proxy;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (service_name != NULL, NULL);
+ g_return_val_if_fail (path_name != NULL, NULL);
+ g_return_val_if_fail (interface_name != NULL, NULL);
+
+ proxy = dbus_gproxy_new (connection, service_name,
+ path_name, interface_name);
+
+ return proxy;
+}
+
+/**
+ * Similar to dbus_gproxy_new_for_service(), but makes a round-trip
+ * request to the message bus to get the current service owner, then
+ * binds the proxy specifically to the current owner. As a result, the
+ * service owner will not change over time, and the proxy will emit
+ * the "destroy" signal when the owner disappears from the message
+ * bus.
+ *
+ * An example of the difference between dbus_gproxy_new_for_service()
+ * and dbus_gproxy_new_for_service_owner(): if you pass the service name
+ * "org.freedesktop.Database" dbus_gproxy_new_for_service() remains bound
+ * to that name as it changes owner. dbus_gproxy_new_for_service_owner()
+ * will fail if the service has no owner. If the service has an owner,
+ * dbus_gproxy_new_for_service_owner() will bind to the unique name
+ * of that owner rather than the generic service name.
+ *
+ * @param connection the connection to the remote bus
+ * @param service_name name of the service on the message bus
+ * @param path_name name of the object inside the service to call methods on
+ * @param interface_name name of the interface to call methods on
+ * @param error return location for an error
+ * @returns new proxy object, or #NULL on error
+ */
+DBusGProxy*
+dbus_gproxy_new_for_service_owner (DBusConnection *connection,
+ const char *service_name,
+ const char *path_name,
+ const char *interface_name,
+ GError **error)
+{
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (service_name != NULL, NULL);
+ g_return_val_if_fail (path_name != NULL, NULL);
+ g_return_val_if_fail (interface_name != NULL, NULL);
+
+
+}
+
+/**
+ * Creates a proxy for an object in peer application (one
+ * we're directly connected to). That is, this function is
+ * intended for use when there's no message bus involved,
+ * we're doing a simple 1-to-1 communication between two
+ * applications.
+ *
+ *
+ * @param connection the connection to the peer
+ * @param path_name name of the object inside the peer to call methods on
+ * @param interface_name name of the interface to call methods on
+ * @returns new proxy object
+ *
+ */
+DBusGProxy*
+dbus_gproxy_new_for_peer (DBusConnection *connection,
+ const char *path_name,
+ const char *interface_name)
+{
+ DBusGProxy *proxy;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path_name != NULL, NULL);
+ g_return_val_if_fail (interface_name != NULL, NULL);
+
+ proxy = dbus_gproxy_new (connection, NULL,
+ path_name, interface_name);
+
+ return proxy;
+}
+
+/**
+ * Invokes a method on a remote interface. This function does not
+ * block; instead it returns an opaque #DBusPendingCall object that
+ * tracks the pending call. The method call will not be sent over the
+ * wire until the application returns to the main loop, or blocks in
+ * dbus_connection_flush() to write out pending data. The call will
+ * be completed after a timeout, or when a reply is received.
+ * To collect the results of the call (which may be an error,
+ * or a reply), use dbus_gproxy_end_call().
+ *
+ * @todo this particular function shouldn't die on out of memory,
+ * since you should be able to do a call with large arguments.
+ *
+ * @param proxy a proxy for a remote interface
+ * @param method the name of the method to invoke
+ * @param first_arg_type type of the first argument
+ *
+ * @returns opaque pending call object
+ * */
+DBusPendingCall*
+dbus_gproxy_begin_call (DBusGProxy *proxy,
+ const char *method,
+ int first_arg_type,
+ ...)
+{
+ DBusPendingCall *pending;
+ DBusMessage *message;
+ va_list args;
+
+ g_return_val_if_fail (DBUS_IS_GPROXY (proxy), NULL);
+
+ message = dbus_message_new_method_call (proxy->service,
+ proxy->path,
+ proxy->interface,
+ method);
+ if (message == NULL)
+ goto oom;
+
+ va_start (args, first_arg_type);
+ if (!dbus_message_append_args_valist (message, first_arg_type,
+ args))
+ goto oom;
+ va_end (args);
+
+ if (!dbus_connection_send_with_reply (proxy->manager->connection,
+ message,
+ &pending,
+ -1))
+ goto oom;
+
+ return pending;
+
+ oom:
+ /* FIXME we should create a pending call that's
+ * immediately completed with an error status without
+ * ever going on the wire.
+ */
+
+ g_error ("Out of memory");
+ return NULL;
+}
+
+/**
+ * Collects the results of a method call. The method call was normally
+ * initiated with dbus_gproxy_end_call(). This function will block if
+ * the results haven't yet been received; use
+ * dbus_pending_call_set_notify() to be notified asynchronously that a
+ * pending call has been completed. Use
+ * dbus_pending_call_get_completed() to check whether a call has been
+ * completed. If it's completed, it will not block.
+ *
+ * If the call results in an error, the error is set as normal for
+ * GError and the function returns #FALSE.
+ *
+ * Otherwise, the "out" parameters and return value of the
+ * method are stored in the provided varargs list.
+ * The list should be terminated with DBUS_TYPE_INVALID.
+ *
+ * This function doesn't affect the reference count of the
+ * #DBusPendingCall, the caller of dbus_gproxy_begin_call() still owns
+ * a reference.
+ *
+ * @param proxy a proxy for a remote interface
+ * @param pending the pending call from dbus_gproxy_begin_call()
+ * @param error return location for an error
+ * @param first_arg_type type of first "out" argument
+ * @returns #FALSE if an error is set */
+gboolean
+dbus_gproxy_end_call (DBusGProxy *proxy,
+ DBusPendingCall *pending,
+ GError **error,
+ int first_arg_type,
+ ...)
+{
+ DBusMessage *message;
+ va_list args;
+ DBusError derror;
+
+ g_return_val_if_fail (DBUS_IS_GPROXY (proxy), FALSE);
+ g_return_val_if_fail (pending != NULL, FALSE);
+
+ dbus_pending_call_block (pending);
+ message = dbus_pending_call_get_reply (pending);
+
+ g_assert (message != NULL);
+
+ dbus_error_init (&derror);
+ va_start (args, first_arg_type);
+ if (!dbus_message_get_args_valist (message, &derror, first_arg_type, args))
+ {
+ va_end (args);
+ goto error;
+ }
+ va_end (args);
+
+ return TRUE;
+
+ error:
+ dbus_set_g_error (error, &derror);
+ dbus_error_free (&derror);
+ return FALSE;
+}
+
+/**
+ * Sends a method call message as with dbus_gproxy_begin_call(), but
+ * does not ask for a reply or allow you to receive one.
+ *
+ * @todo this particular function shouldn't die on out of memory,
+ * since you should be able to do a call with large arguments.
+ *
+ * @param proxy a proxy for a remote interface
+ * @param method the name of the method to invoke
+ * @param first_arg_type type of the first argument
+ */
+void
+dbus_gproxy_oneway_call (DBusGProxy *proxy,
+ const char *method,
+ int first_arg_type,
+ ...)
+{
+ DBusMessage *message;
+ va_list args;
+
+ g_return_if_fail (DBUS_IS_GPROXY (proxy));
+
+ message = dbus_message_new_method_call (proxy->service,
+ proxy->path,
+ proxy->interface,
+ method);
+ if (message == NULL)
+ goto oom;
+
+ dbus_message_set_no_reply (message, TRUE);
+
+ va_start (args, first_arg_type);
+ if (!dbus_message_append_args_valist (message, first_arg_type,
+ args))
+ goto oom;
+ va_end (args);
+
+ if (!dbus_connection_send (proxy->manager->connection,
+ message,
+ NULL))
+ goto oom;
+
+ oom:
+ g_error ("Out of memory");
+}
+
+/**
+ * Sends a message to the interface we're proxying for. Does not
+ * block or wait for a reply. The message is only actually written out
+ * when you return to the main loop or block in
+ * dbus_connection_flush().
+ *
+ * The message is modified to be addressed to the target interface.
+ * That is, a destination service field or whatever is needed will be
+ * added to the message. The basic point of this function is to add
+ * the necessary header fields, otherwise it's equivalent to
+ * dbus_connection_send().
+ *
+ * This function adds a reference to the message, so the caller
+ * still owns its original reference.
+ *
+ * @param proxy a proxy for a remote interface
+ * @param message the message to address and send
+ * @param client_serial return location for message's serial, or #NULL */
+void
+dbus_gproxy_send (DBusGProxy *proxy,
+ DBusMessage *message,
+ dbus_uint32_t *client_serial)
+{
+ g_return_if_fail (DBUS_IS_GPROXY (proxy));
+
+ if (proxy->service)
+ {
+ if (!dbus_message_set_destination (message, proxy->service))
+ g_error ("Out of memory");
+ }
+ if (proxy->path)
+ {
+ if (!dbus_message_set_path (message, proxy->path))
+ g_error ("Out of memory");
+ }
+ if (proxy->interface)
+ {
+ if (!dbus_message_set_interface (message, proxy->interface))
+ g_error ("Out of memory");
+ }
+
+ if (!dbus_connection_send (proxy->manager->connection, message, client_serial))
+ g_error ("Out of memory\n");
+}
+
+void
+dbus_gproxy_connect_signal (DBusGProxy *proxy,
+ const char *signal_name,
+ DBusGProxySignalHandler handler,
+ void *data,
+ GClosureNotify free_data_func)
+{
+ GClosure *closure;
+ char *detail;
+
+ g_return_if_fail (DBUS_IS_GPROXY (proxy));
+ g_return_if_fail (signal_name != NULL);
+ g_return_if_fail (handler != NULL);
+
+ detail = create_signal_detail (proxy->interface, signal_name);
+
+ closure = g_cclosure_new (G_CALLBACK (handler), data, free_data_func);
+ g_signal_connect_closure_by_id (G_OBJECT (proxy),
+ signals[RECEIVED],
+ g_quark_from_string (detail),
+ closure, FALSE);
+
+ g_free (detail);
+}
+
+void
+dbus_gproxy_disconnect_signal (DBusGProxy *proxy,
+ const char *signal_name,
+ DBusGProxySignalHandler handler,
+ void *data)
+{
+ char *detail;
+ GQuark q;
+
+ g_return_if_fail (DBUS_IS_GPROXY (proxy));
+ g_return_if_fail (signal_name != NULL);
+ g_return_if_fail (handler != NULL);
+
+ detail = create_signal_detail (proxy->interface, signal_name);
+ q = g_quark_try_string (detail);
+ g_free (detail);
+
+#ifndef G_DISABLE_CHECKS
+ if (q == 0)
+ {
+ g_warning ("%s: No signal handlers for %s found on this DBusGProxy",
+ G_GNUC_FUNCTION, signal_name);
+ return;
+ }
+#endif
+
+ g_signal_handlers_disconnect_matched (G_OBJECT (proxy),
+ G_SIGNAL_MATCH_DETAIL |
+ G_SIGNAL_MATCH_FUNC |
+ G_SIGNAL_MATCH_DATA,
+ signals[RECEIVED],
+ q,
+ NULL,
+ G_CALLBACK (handler), data);
+}
+
+/** @} End of DBusGLib public */
+
+#ifdef DBUS_BUILD_TESTS
+
+/**
+ * @ingroup DBusGLibInternals
+ * Unit test for GLib proxy functions
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_gproxy_test (void)
+{
+
+
+ return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/glib/dbus-gtest-main.c b/glib/dbus-gtest-main.c
new file mode 100644
index 00000000..5cc6cb78
--- /dev/null
+++ b/glib/dbus-gtest-main.c
@@ -0,0 +1,46 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gtest-main.c Program to run all libdbus-glib tests
+ *
+ * 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-gtest.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+
+int
+main (int argc,
+ char **argv)
+{
+ const char *test_data_dir;
+
+ setlocale(LC_ALL, "");
+
+
+ if (argc > 1)
+ test_data_dir = argv[1];
+ else
+ test_data_dir = NULL;
+
+ dbus_glib_internal_do_not_use_run_tests (test_data_dir);
+
+ return 0;
+}
diff --git a/glib/dbus-gtest.c b/glib/dbus-gtest.c
new file mode 100644
index 00000000..48cd13f0
--- /dev/null
+++ b/glib/dbus-gtest.c
@@ -0,0 +1,77 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-test.c Program to run all tests
+ *
+ * Copyright (C) 2002, 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 <config.h>
+#include "dbus-gtest.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef DBUS_BUILD_TESTS
+static void
+die (const char *failure)
+{
+ fprintf (stderr, "Unit test failed: %s\n", failure);
+ exit (1);
+}
+#endif /* DBUS_BUILD_TESTS */
+
+/**
+ * An exported symbol to be run in order to execute
+ * unit tests. Should not be used by
+ * any app other than our test app, this symbol
+ * won't exist in some builds of the library.
+ * (with --enable-tests=no)
+ *
+ * @param test_data_dir the directory with test data (test/data normally)
+ */
+void
+dbus_glib_internal_do_not_use_run_tests (const char *test_data_dir)
+{
+#ifdef DBUS_BUILD_TESTS
+ if (test_data_dir == NULL)
+ test_data_dir = g_getenv ("DBUS_TEST_DATA");
+
+ if (test_data_dir != NULL)
+ printf ("Test data in %s\n", test_data_dir);
+ else
+ printf ("No test data!\n");
+
+ printf ("%s: running utils tests\n", "dbus-glib-test");
+ if (!_dbus_gutils_test (test_data_dir))
+ die ("gutils");
+
+ printf ("%s: running mainloop integration tests\n", "dbus-glib-test");
+ if (!_dbus_gmain_test (test_data_dir))
+ die ("gmain");
+
+ printf ("%s: running GObject tests\n", "dbus-glib-test");
+ if (!_dbus_gobject_test (test_data_dir))
+ die ("gobject");
+
+ printf ("%s: completed successfully\n", "dbus-glib-test");
+#else
+ printf ("Not compiled with unit tests, not running any\n");
+#endif
+}
+
+
diff --git a/glib/dbus-gtest.h b/glib/dbus-gtest.h
new file mode 100644
index 00000000..1174eb0a
--- /dev/null
+++ b/glib/dbus-gtest.h
@@ -0,0 +1,35 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gtest.h Declarations of test functions.
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_GLIB_TEST_H
+#define DBUS_GLIB_TEST_H
+
+#include "dbus-glib.h"
+
+dbus_bool_t _dbus_gmain_test (const char *test_data_dir);
+dbus_bool_t _dbus_gobject_test (const char *test_data_dir);
+dbus_bool_t _dbus_gutils_test (const char *test_data_dir);
+
+void dbus_glib_internal_do_not_use_run_tests (const char *test_data_dir);
+
+#endif /* DBUS_GLIB_TEST_H */
diff --git a/glib/dbus-gthread.c b/glib/dbus-gthread.c
index 71a3c1f5..eb3e5572 100644
--- a/glib/dbus-gthread.c
+++ b/glib/dbus-gthread.c
@@ -165,7 +165,7 @@ dbus_gcondvar_wake_all (DBusCondVar *cond)
* other function in the D-BUS API.
*/
void
-dbus_gthread_init (void)
+dbus_g_thread_init (void)
{
if (!g_thread_supported ())
g_error ("g_thread_init() must be called before dbus_threads_init()");
diff --git a/glib/dbus-gtool-test.h b/glib/dbus-gtool-test.h
new file mode 100644
index 00000000..74a7649f
--- /dev/null
+++ b/glib/dbus-gtool-test.h
@@ -0,0 +1,31 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gtool-test.h Declarations of test functions for dbus-glib-tool.
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_GLIB_TOOL_TEST_H
+#define DBUS_GLIB_TOOL_TEST_H
+
+#include "dbus-glib.h"
+
+dbus_bool_t _dbus_gtool_test (const char *test_data_dir);
+
+#endif /* DBUS_GLIB_TEST_H */
diff --git a/glib/dbus-gutils.c b/glib/dbus-gutils.c
new file mode 100644
index 00000000..e99286f8
--- /dev/null
+++ b/glib/dbus-gutils.c
@@ -0,0 +1,96 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gutils.c Utils shared between convenience lib and installed lib
+ *
+ * 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 <config.h>
+#include "dbus-gutils.h"
+#include "dbus-gtest.h"
+#include <string.h>
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+char**
+_dbus_gutils_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;
+}
+
+#ifdef DBUS_BUILD_TESTS
+
+/**
+ * @ingroup DBusGLibInternals
+ * Unit test for GLib utils internals
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_gutils_test (const char *test_data_dir)
+{
+
+ return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
+
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
diff --git a/glib/dbus-gutils.h b/glib/dbus-gutils.h
new file mode 100644
index 00000000..af7cee45
--- /dev/null
+++ b/glib/dbus-gutils.h
@@ -0,0 +1,40 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gutils.h Utils shared between convenience lib and installed lib
+ *
+ * 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
+ *
+ */
+
+#ifndef DBUS_GLIB_UTILS_H
+#define DBUS_GLIB_UTILS_H
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#include <dbus/dbus.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+char** _dbus_gutils_split_path (const char *path);
+
+G_END_DECLS
+
+#endif /* DBUS_GLIB_UTILS_H */
+
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */