David Zeuthen 30f12c7
From 91f6977a2dc01207072b5718b89c57f439f9339a Mon Sep 17 00:00:00 2001
David Zeuthen 30f12c7
From: David Zeuthen <davidz@redhat.com>
David Zeuthen 30f12c7
Date: Thu, 31 Jul 2008 12:28:07 -0400
David Zeuthen 30f12c7
Subject: [PATCH] add bash completion for dbus-send(1)
David Zeuthen 30f12c7
David Zeuthen 30f12c7
For now, it's in dbus-glib since dbus doesn't have an introspection
David Zeuthen 30f12c7
XML parser (yet).
David Zeuthen 30f12c7
---
David Zeuthen 30f12c7
 configure.ac                       |   13 +
David Zeuthen 30f12c7
 dbus/Makefile.am                   |   17 ++-
David Zeuthen 30f12c7
 dbus/dbus-bash-completion-helper.c |  513 ++++++++++++++++++++++++++++++++++++
David Zeuthen 30f12c7
 dbus/dbus-bash-completion.sh.in    |   21 ++
David Zeuthen 30f12c7
 4 files changed, 563 insertions(+), 1 deletions(-)
David Zeuthen 30f12c7
 create mode 100644 dbus/dbus-bash-completion-helper.c
David Zeuthen 30f12c7
 create mode 100644 dbus/dbus-bash-completion.sh.in
David Zeuthen 30f12c7
David Zeuthen 30f12c7
diff --git a/configure.ac b/configure.ac
David Zeuthen 30f12c7
index 839e0e1..a163935 100644
David Zeuthen 30f12c7
--- a/configure.ac
David Zeuthen 30f12c7
+++ b/configure.ac
David Zeuthen 30f12c7
@@ -54,11 +54,23 @@ AC_ARG_ENABLE(asserts, AS_HELP_STRING([--enable-asserts],[include assertion chec
David Zeuthen 30f12c7
 AC_ARG_ENABLE(checks, AS_HELP_STRING([--enable-checks],[include sanity checks on public API]),enable_checks=$enableval,enable_checks=yes)
David Zeuthen 30f12c7
 AC_ARG_ENABLE(doxygen-docs, AS_HELP_STRING([--enable-doxygen-docs],[build DOXYGEN documentation (requires Doxygen)]),enable_doxygen_docs=$enableval,enable_doxygen_docs=auto)
David Zeuthen 30f12c7
 AC_ARG_ENABLE(gcov, AS_HELP_STRING([--enable-gcov],[compile with coverage profiling instrumentation (gcc only)]),enable_gcov=$enableval,enable_gcov=no)
David Zeuthen 30f12c7
+AC_ARG_ENABLE(bash-completion, AS_HELP_STRING([--enable-bash-completion],[install bash completion scripts]),enable_bash_completion=$enableval,enable_bash_completion=yes)
David Zeuthen 30f12c7
 AC_ARG_WITH(test-socket-dir, AS_HELP_STRING([--with-test-socket-dir=[dirname]],[Where to put sockets for make check]))
David Zeuthen 30f12c7
 
David Zeuthen 30f12c7
 
David Zeuthen 30f12c7
 AC_ARG_WITH(introspect-xml, AS_HELP_STRING([--with-introspect-xml=[filename]],[Pass in a pregenerated dbus daemon introspection xml file (as generated by 'dbus-daemon --introspect') to use instead of querying the installed dbus daemon]))
David Zeuthen 30f12c7
 
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+AM_CONDITIONAL(DBUS_BASH_COMPLETION, test x$enable_bash_completion = xyes)
David Zeuthen 30f12c7
+if test x$enable_bash_completion = xyes; then
David Zeuthen 30f12c7
+  AC_DEFINE(DBUS_BASH_COMPLETION,1,[Enable bash completion])
David Zeuthen 30f12c7
+fi
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+if test x$enable_verbose_mode = xyes; then
David Zeuthen 30f12c7
+    AC_DEFINE(DBUS_ENABLE_VERBOSE_MODE,1,[Support a verbose mode])
David Zeuthen 30f12c7
+fi
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
 dnl DBUS_BUILD_TESTS controls unit tests built in to .c files 
David Zeuthen 30f12c7
 dnl and also some stuff in the test/ subdir
David Zeuthen 30f12c7
 AM_CONDITIONAL(DBUS_BUILD_TESTS, test x$enable_tests = xyes)
David Zeuthen 30f12c7
@@ -867,6 +879,7 @@ echo "
David Zeuthen 30f12c7
         Building checks:          ${enable_checks}
David Zeuthen 30f12c7
         Building Doxygen docs:    ${enable_doxygen_docs}
David Zeuthen 30f12c7
         Building Gtk-doc docs:    ${enable_gtk_doc}
David Zeuthen 30f12c7
+        Bash Completion:          ${enable_bash_completion}
David Zeuthen 30f12c7
         Gettext libs (empty OK):  ${INTLLIBS}
David Zeuthen 30f12c7
         Using XML parser:         ${with_xml}
David Zeuthen 30f12c7
         'make check' socket dir:  ${TEST_SOCKET_DIR}
David Zeuthen 30f12c7
diff --git a/dbus/Makefile.am b/dbus/Makefile.am
David Zeuthen 30f12c7
index 2650e8b..bd01d7f 100644
David Zeuthen 30f12c7
--- a/dbus/Makefile.am
David Zeuthen 30f12c7
+++ b/dbus/Makefile.am
David Zeuthen 30f12c7
@@ -91,7 +91,22 @@ regenerate-built-sources:
David Zeuthen 30f12c7
 	echo '#include "dbus-gmarshal.h"' > dbus-gmarshal.c &&					    \
David Zeuthen 30f12c7
         @GLIB_GENMARSHAL@ --prefix=_dbus_g_marshal dbus-gmarshal.list --body >> dbus-gmarshal.c
David Zeuthen 30f12c7
 
David Zeuthen 30f12c7
-EXTRA_DIST=dbus-gmarshal.list make-dbus-glib-error-switch.sh make-dbus-glib-error-enum.sh
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+profiledir = $(sysconfdir)/profile.d
David Zeuthen 30f12c7
+if DBUS_BASH_COMPLETION
David Zeuthen 30f12c7
+libexec_PROGRAMS=dbus-bash-completion-helper
David Zeuthen 30f12c7
+profile_SCRIPTS=dbus-bash-completion.sh
David Zeuthen 30f12c7
+endif
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+dbus-bash-completion.sh : dbus-bash-completion.sh.in
David Zeuthen 30f12c7
+	@sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+dbus_bash_completion_helper_SOURCES =                 	\
David Zeuthen 30f12c7
+	dbus-bash-completion-helper.c
David Zeuthen 30f12c7
+dbus_bash_completion_helper_LDADD=$(DBUS_LIBS) $(DBUS_GLIB_LIBS) -lexpat libdbus-gtool.la libdbus-glib-1.la
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+EXTRA_DIST=dbus-gmarshal.list make-dbus-glib-error-switch.sh make-dbus-glib-error-enum.sh dbus-bash-completion.sh.in
David Zeuthen 30f12c7
 
David Zeuthen 30f12c7
 if DBUS_BUILD_TESTS
David Zeuthen 30f12c7
 
David Zeuthen 30f12c7
diff --git a/dbus/dbus-bash-completion-helper.c b/dbus/dbus-bash-completion-helper.c
David Zeuthen 30f12c7
new file mode 100644
David Zeuthen 30f12c7
index 0000000..022c722
David Zeuthen 30f12c7
--- /dev/null
David Zeuthen 30f12c7
+++ b/dbus/dbus-bash-completion-helper.c
David Zeuthen 30f12c7
@@ -0,0 +1,513 @@
David Zeuthen 30f12c7
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
David Zeuthen 30f12c7
+/* dbus-bash-completion-helper.c  Bash Completion helper routines
David Zeuthen 30f12c7
+ *
David Zeuthen 30f12c7
+ * Copyright (C) 2008 David Zeuthen <davidz@redhat.com>
David Zeuthen 30f12c7
+ *
David Zeuthen 30f12c7
+ * This program is free software; you can redistribute it and/or modify
David Zeuthen 30f12c7
+ * it under the terms of the GNU General Public License as published by
David Zeuthen 30f12c7
+ * the Free Software Foundation; either version 2 of the License, or
David Zeuthen 30f12c7
+ * (at your option) any later version.
David Zeuthen 30f12c7
+ *
David Zeuthen 30f12c7
+ * This program is distributed in the hope that it will be useful,
David Zeuthen 30f12c7
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
David Zeuthen 30f12c7
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
David Zeuthen 30f12c7
+ * GNU General Public License for more details.
David Zeuthen 30f12c7
+ *
David Zeuthen 30f12c7
+ * You should have received a copy of the GNU General Public License
David Zeuthen 30f12c7
+ * along with this program; if not, write to the Free Software
David Zeuthen 30f12c7
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
David Zeuthen 30f12c7
+ *
David Zeuthen 30f12c7
+ */
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+#include <config.h>
David Zeuthen 30f12c7
+#include <ctype.h>
David Zeuthen 30f12c7
+#include <stdio.h>
David Zeuthen 30f12c7
+#include <stdlib.h>
David Zeuthen 30f12c7
+#include <string.h>
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+#include <dbus/dbus.h>
David Zeuthen 30f12c7
+#include <glib.h>
David Zeuthen 30f12c7
+#include "dbus-gparser.h"
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+static void
David Zeuthen 30f12c7
+print_services (DBusConnection *connection)
David Zeuthen 30f12c7
+{
David Zeuthen 30f12c7
+  DBusMessage *message;
David Zeuthen 30f12c7
+  DBusMessage *reply;
David Zeuthen 30f12c7
+  DBusError error;
David Zeuthen 30f12c7
+  DBusMessageIter iter;
David Zeuthen 30f12c7
+  DBusMessageIter iter_array;
David Zeuthen 30f12c7
+  const char *name;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  /* list both active and activatable names (the shell will sort and
David Zeuthen 30f12c7
+   * uniquify them) - also avoid names that are not well-known
David Zeuthen 30f12c7
+   * (e.g. :1.42).
David Zeuthen 30f12c7
+   */
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
David Zeuthen 30f12c7
+                                          DBUS_PATH_DBUS,
David Zeuthen 30f12c7
+                                          DBUS_INTERFACE_DBUS,
David Zeuthen 30f12c7
+                                          "ListNames");
David Zeuthen 30f12c7
+  dbus_error_init (&error);
David Zeuthen 30f12c7
+  reply = dbus_connection_send_with_reply_and_block (connection,
David Zeuthen 30f12c7
+                                                     message,
David Zeuthen 30f12c7
+                                                     -1,
David Zeuthen 30f12c7
+                                                     &error);
David Zeuthen 30f12c7
+  dbus_message_unref (message);
David Zeuthen 30f12c7
+  dbus_message_iter_init (reply, &iter);
David Zeuthen 30f12c7
+  dbus_message_iter_recurse (&iter, &iter_array);
David Zeuthen 30f12c7
+  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      dbus_message_iter_get_basic (&iter_array, &name);
David Zeuthen 30f12c7
+      if (name[0] != ':')
David Zeuthen 30f12c7
+        printf ("%s \n", name);
David Zeuthen 30f12c7
+      dbus_message_iter_next (&iter_array);
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+  dbus_message_unref (reply);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
David Zeuthen 30f12c7
+                                          DBUS_PATH_DBUS,
David Zeuthen 30f12c7
+                                          DBUS_INTERFACE_DBUS,
David Zeuthen 30f12c7
+                                          "ListActivatableNames");
David Zeuthen 30f12c7
+  dbus_error_init (&error);
David Zeuthen 30f12c7
+  reply = dbus_connection_send_with_reply_and_block (connection,
David Zeuthen 30f12c7
+                                                     message,
David Zeuthen 30f12c7
+                                                     -1,
David Zeuthen 30f12c7
+                                                     &error);
David Zeuthen 30f12c7
+  dbus_message_unref (message);
David Zeuthen 30f12c7
+  dbus_message_iter_init (reply, &iter);
David Zeuthen 30f12c7
+  dbus_message_iter_recurse (&iter, &iter_array);
David Zeuthen 30f12c7
+  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      dbus_message_iter_get_basic (&iter_array, &name);
David Zeuthen 30f12c7
+      printf ("%s \n", name);
David Zeuthen 30f12c7
+      dbus_message_iter_next (&iter_array);
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+  dbus_message_unref (reply);
David Zeuthen 30f12c7
+}
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+static gboolean
David Zeuthen 30f12c7
+have_option (char **tokens, const char *option)
David Zeuthen 30f12c7
+{
David Zeuthen 30f12c7
+  int n;
David Zeuthen 30f12c7
+  for (n = 0; tokens[n] != NULL; n++)
David Zeuthen 30f12c7
+    if (strcmp (tokens[n], option) == 0)
David Zeuthen 30f12c7
+      return TRUE;
David Zeuthen 30f12c7
+  return FALSE;
David Zeuthen 30f12c7
+}
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+static gboolean
David Zeuthen 30f12c7
+have_option_with_value (char **tokens, const char *option, const char **value)
David Zeuthen 30f12c7
+{
David Zeuthen 30f12c7
+  int n;
David Zeuthen 30f12c7
+  for (n = 0; tokens[n] != NULL; n++)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      if (g_str_has_prefix (tokens[n], option))
David Zeuthen 30f12c7
+        {
David Zeuthen 30f12c7
+          if (strlen (tokens[n]) > strlen (option))
David Zeuthen 30f12c7
+            *value = tokens[n] + strlen (option);
David Zeuthen 30f12c7
+          return TRUE;
David Zeuthen 30f12c7
+        }
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+  return FALSE;
David Zeuthen 30f12c7
+}
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+static void
David Zeuthen 30f12c7
+print_objects (DBusConnection *connection, const char *service_name, const char *cur)
David Zeuthen 30f12c7
+{
David Zeuthen 30f12c7
+  DBusMessage *message;
David Zeuthen 30f12c7
+  DBusMessage *reply;
David Zeuthen 30f12c7
+  DBusError error;
David Zeuthen 30f12c7
+  DBusMessageIter iter;
David Zeuthen 30f12c7
+  const char *introspection_xml;
David Zeuthen 30f12c7
+  NodeInfo *root;
David Zeuthen 30f12c7
+  GSList *nodes;
David Zeuthen 30f12c7
+  GSList *l;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  if (cur == NULL)
David Zeuthen 30f12c7
+    cur = "/";
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  message = dbus_message_new_method_call (service_name,
David Zeuthen 30f12c7
+                                          cur,
David Zeuthen 30f12c7
+                                          DBUS_INTERFACE_INTROSPECTABLE,
David Zeuthen 30f12c7
+                                          "Introspect");
David Zeuthen 30f12c7
+  dbus_error_init (&error);
David Zeuthen 30f12c7
+  reply = dbus_connection_send_with_reply_and_block (connection,
David Zeuthen 30f12c7
+                                                     message,
David Zeuthen 30f12c7
+                                                     -1,
David Zeuthen 30f12c7
+                                                     &error);
David Zeuthen 30f12c7
+  dbus_message_unref (message);
David Zeuthen 30f12c7
+  dbus_message_iter_init (reply, &iter);
David Zeuthen 30f12c7
+  dbus_message_iter_get_basic (&iter, &introspection_xml);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL);
David Zeuthen 30f12c7
+  nodes = node_info_get_nodes (root);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  if (g_slist_length (node_info_get_interfaces (root)) > 0)
David Zeuthen 30f12c7
+    printf ("%s \n", cur);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  for (l = nodes; l != NULL; l = l->next)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      NodeInfo *node = (NodeInfo *) l->data;
David Zeuthen 30f12c7
+      const char *name;
David Zeuthen 30f12c7
+      char *new_path;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+      name = node_info_get_name (node);
David Zeuthen 30f12c7
+      if (strcmp (cur, "/") == 0)
David Zeuthen 30f12c7
+        new_path = g_strdup_printf ("/%s", name);
David Zeuthen 30f12c7
+      else
David Zeuthen 30f12c7
+        new_path = g_strdup_printf ("%s/%s", cur, name);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+      print_objects (connection, service_name, new_path);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+      g_free (new_path);
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+  node_info_unref (root);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  dbus_message_unref (reply);
David Zeuthen 30f12c7
+}
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+static gboolean
David Zeuthen 30f12c7
+is_object_path_with_interfaces (DBusConnection *connection, const char *service_name, const char *object_path)
David Zeuthen 30f12c7
+{
David Zeuthen 30f12c7
+  DBusMessage *message;
David Zeuthen 30f12c7
+  DBusMessage *reply;
David Zeuthen 30f12c7
+  DBusError error;
David Zeuthen 30f12c7
+  DBusMessageIter iter;
David Zeuthen 30f12c7
+  const char *introspection_xml;
David Zeuthen 30f12c7
+  NodeInfo *root;
David Zeuthen 30f12c7
+  gboolean ret;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  ret = FALSE;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  message = dbus_message_new_method_call (service_name,
David Zeuthen 30f12c7
+                                          object_path,
David Zeuthen 30f12c7
+                                          DBUS_INTERFACE_INTROSPECTABLE,
David Zeuthen 30f12c7
+                                          "Introspect");
David Zeuthen 30f12c7
+  dbus_error_init (&error);
David Zeuthen 30f12c7
+  reply = dbus_connection_send_with_reply_and_block (connection,
David Zeuthen 30f12c7
+                                                     message,
David Zeuthen 30f12c7
+                                                     -1,
David Zeuthen 30f12c7
+                                                     &error);
David Zeuthen 30f12c7
+  dbus_message_unref (message);
David Zeuthen 30f12c7
+  dbus_message_iter_init (reply, &iter);
David Zeuthen 30f12c7
+  dbus_message_iter_get_basic (&iter, &introspection_xml);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  if (g_slist_length (node_info_get_interfaces (root)) > 0)
David Zeuthen 30f12c7
+    ret = TRUE;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  node_info_unref (root);
David Zeuthen 30f12c7
+  dbus_message_unref (reply);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  return ret;
David Zeuthen 30f12c7
+}
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+static void
David Zeuthen 30f12c7
+print_methods (DBusConnection *connection, const char *service_name, const char *object_path)
David Zeuthen 30f12c7
+{
David Zeuthen 30f12c7
+  DBusMessage *message;
David Zeuthen 30f12c7
+  DBusMessage *reply;
David Zeuthen 30f12c7
+  DBusError error;
David Zeuthen 30f12c7
+  DBusMessageIter iter;
David Zeuthen 30f12c7
+  const char *introspection_xml;
David Zeuthen 30f12c7
+  NodeInfo *root;
David Zeuthen 30f12c7
+  GSList *interfaces;
David Zeuthen 30f12c7
+  GSList *l;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  message = dbus_message_new_method_call (service_name,
David Zeuthen 30f12c7
+                                          object_path,
David Zeuthen 30f12c7
+                                          DBUS_INTERFACE_INTROSPECTABLE,
David Zeuthen 30f12c7
+                                          "Introspect");
David Zeuthen 30f12c7
+  dbus_error_init (&error);
David Zeuthen 30f12c7
+  reply = dbus_connection_send_with_reply_and_block (connection,
David Zeuthen 30f12c7
+                                                     message,
David Zeuthen 30f12c7
+                                                     -1,
David Zeuthen 30f12c7
+                                                     &error);
David Zeuthen 30f12c7
+  dbus_message_unref (message);
David Zeuthen 30f12c7
+  dbus_message_iter_init (reply, &iter);
David Zeuthen 30f12c7
+  dbus_message_iter_get_basic (&iter, &introspection_xml);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL);
David Zeuthen 30f12c7
+  interfaces = node_info_get_interfaces (root);
David Zeuthen 30f12c7
+  for (l = interfaces; l != NULL; l = l->next)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      InterfaceInfo *interface = (InterfaceInfo *) l->data;
David Zeuthen 30f12c7
+      GSList *methods;
David Zeuthen 30f12c7
+      GSList *ll;
David Zeuthen 30f12c7
+      methods = interface_info_get_methods (interface);
David Zeuthen 30f12c7
+      for (ll = methods; ll != NULL; ll = ll->next)
David Zeuthen 30f12c7
+        {
David Zeuthen 30f12c7
+          MethodInfo *method = (MethodInfo *) ll->data;
David Zeuthen 30f12c7
+          printf ("%s.%s \n", interface_info_get_name (interface), method_info_get_name (method));
David Zeuthen 30f12c7
+        }
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+  node_info_unref (root);
David Zeuthen 30f12c7
+  dbus_message_unref (reply);
David Zeuthen 30f12c7
+}
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+static void
David Zeuthen 30f12c7
+print_signature (DBusConnection *connection, const char *service_name, const char *object_path, const char *method)
David Zeuthen 30f12c7
+{
David Zeuthen 30f12c7
+  DBusMessage *message;
David Zeuthen 30f12c7
+  DBusMessage *reply;
David Zeuthen 30f12c7
+  DBusError error;
David Zeuthen 30f12c7
+  DBusMessageIter iter;
David Zeuthen 30f12c7
+  const char *introspection_xml;
David Zeuthen 30f12c7
+  NodeInfo *root;
David Zeuthen 30f12c7
+  GSList *interfaces;
David Zeuthen 30f12c7
+  GSList *l;
David Zeuthen 30f12c7
+  char *s;
David Zeuthen 30f12c7
+  char *method_name;
David Zeuthen 30f12c7
+  char *interface_name;
David Zeuthen 30f12c7
+  int n;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  method_name = NULL;
David Zeuthen 30f12c7
+  interface_name = NULL;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  s = strrchr (method, '.');
David Zeuthen 30f12c7
+  if (s == NULL || strlen (s) < 2 || s - method < 1)
David Zeuthen 30f12c7
+    goto fail;
David Zeuthen 30f12c7
+  method_name = g_strdup (s + 1);
David Zeuthen 30f12c7
+  interface_name = g_strndup (method, s - method);
David Zeuthen 30f12c7
+  printf (" \n");
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  message = dbus_message_new_method_call (service_name,
David Zeuthen 30f12c7
+                                          object_path,
David Zeuthen 30f12c7
+                                          DBUS_INTERFACE_INTROSPECTABLE,
David Zeuthen 30f12c7
+                                          "Introspect");
David Zeuthen 30f12c7
+  dbus_error_init (&error);
David Zeuthen 30f12c7
+  reply = dbus_connection_send_with_reply_and_block (connection,
David Zeuthen 30f12c7
+                                                     message,
David Zeuthen 30f12c7
+                                                     -1,
David Zeuthen 30f12c7
+                                                     &error);
David Zeuthen 30f12c7
+  dbus_message_unref (message);
David Zeuthen 30f12c7
+  dbus_message_iter_init (reply, &iter);
David Zeuthen 30f12c7
+  dbus_message_iter_get_basic (&iter, &introspection_xml);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  root = description_load_from_string (introspection_xml, strlen (introspection_xml), NULL);
David Zeuthen 30f12c7
+  interfaces = node_info_get_interfaces (root);
David Zeuthen 30f12c7
+  for (l = interfaces; l != NULL; l = l->next)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      InterfaceInfo *interface = (InterfaceInfo *) l->data;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+      if (strcmp (interface_name, interface_info_get_name (interface)) == 0)
David Zeuthen 30f12c7
+        {
David Zeuthen 30f12c7
+          GSList *methods;
David Zeuthen 30f12c7
+          GSList *ll;
David Zeuthen 30f12c7
+          methods = interface_info_get_methods (interface);
David Zeuthen 30f12c7
+          for (ll = methods; ll != NULL; ll = ll->next)
David Zeuthen 30f12c7
+            {
David Zeuthen 30f12c7
+              MethodInfo *method = (MethodInfo *) ll->data;
David Zeuthen 30f12c7
+              if (strcmp (method_name, method_info_get_name (method)) == 0)
David Zeuthen 30f12c7
+                {
David Zeuthen 30f12c7
+                  GSList *args;
David Zeuthen 30f12c7
+                  GSList *lll;
David Zeuthen 30f12c7
+                  args = method_info_get_args (method);
David Zeuthen 30f12c7
+                  for (lll = args, n = 0; lll != NULL; lll = lll->next, n++)
David Zeuthen 30f12c7
+                    {
David Zeuthen 30f12c7
+                      ArgInfo *arg = (ArgInfo *) lll->data;
David Zeuthen 30f12c7
+                      printf ("#    %s: arg %d: %s (%s)         \n",
David Zeuthen 30f12c7
+                              arg_info_get_direction (arg) == ARG_IN ? " IN" : "OUT",
David Zeuthen 30f12c7
+                              n,
David Zeuthen 30f12c7
+                              arg_info_get_name (arg),
David Zeuthen 30f12c7
+                              arg_info_get_type (arg));
David Zeuthen 30f12c7
+                    }
David Zeuthen 30f12c7
+                  break;
David Zeuthen 30f12c7
+                }
David Zeuthen 30f12c7
+            }
David Zeuthen 30f12c7
+        }
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+  node_info_unref (root);
David Zeuthen 30f12c7
+  dbus_message_unref (reply);
David Zeuthen 30f12c7
+ fail:
David Zeuthen 30f12c7
+  g_free (method_name);
David Zeuthen 30f12c7
+  g_free (interface_name);
David Zeuthen 30f12c7
+}
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+static int
David Zeuthen 30f12c7
+complete_dbus_send (char *str)
David Zeuthen 30f12c7
+{
David Zeuthen 30f12c7
+  int ret;
David Zeuthen 30f12c7
+  char **tokens;
David Zeuthen 30f12c7
+  int num_tokens;
David Zeuthen 30f12c7
+  const char *cur;
David Zeuthen 30f12c7
+  const char *prev;
David Zeuthen 30f12c7
+  gboolean have_system;
David Zeuthen 30f12c7
+  gboolean have_session;
David Zeuthen 30f12c7
+  gboolean have_print_reply;
David Zeuthen 30f12c7
+  gboolean have_dest;
David Zeuthen 30f12c7
+  DBusConnection *connection;
David Zeuthen 30f12c7
+  DBusBusType bus_type;
David Zeuthen 30f12c7
+  DBusError error;
David Zeuthen 30f12c7
+  const char *target_service;
David Zeuthen 30f12c7
+  const char *object_path;
David Zeuthen 30f12c7
+  const char *method;
David Zeuthen 30f12c7
+  int n;
David Zeuthen 30f12c7
+  int object_path_index;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  ret = 1;
David Zeuthen 30f12c7
+  connection = NULL;
David Zeuthen 30f12c7
+  target_service = NULL;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  tokens = g_strsplit (str, " ", 0);
David Zeuthen 30f12c7
+  num_tokens = g_strv_length (tokens);
David Zeuthen 30f12c7
+  if (num_tokens >= 2) {
David Zeuthen 30f12c7
+    cur = tokens[num_tokens - 1];
David Zeuthen 30f12c7
+    prev = tokens[num_tokens - 2];
David Zeuthen 30f12c7
+  } else if (num_tokens == 1) {
David Zeuthen 30f12c7
+    cur = tokens[num_tokens - 1];
David Zeuthen 30f12c7
+    prev = "";
David Zeuthen 30f12c7
+  } else {
David Zeuthen 30f12c7
+    cur = "";
David Zeuthen 30f12c7
+    prev = "";
David Zeuthen 30f12c7
+  }
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  have_system = have_option (tokens, "--system");
David Zeuthen 30f12c7
+  have_session = have_option (tokens, "--session");
David Zeuthen 30f12c7
+  have_print_reply = have_option (tokens, "--print-reply");
David Zeuthen 30f12c7
+  have_dest = have_option_with_value (tokens, "--dest=", &target_service);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  if (!have_print_reply)
David Zeuthen 30f12c7
+    printf ("--print-reply \n");
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  if (!have_system && !have_session)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      printf ("--system \n");
David Zeuthen 30f12c7
+      printf ("--session \n");
David Zeuthen 30f12c7
+      goto done;
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  if (!have_dest && !g_str_has_prefix (cur, "--dest="))
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      printf ("--dest=\n");
David Zeuthen 30f12c7
+      goto done;
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  if (have_system || have_session)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      bus_type = have_system ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+      dbus_error_init (&error);
David Zeuthen 30f12c7
+      connection = dbus_bus_get (bus_type, &error);
David Zeuthen 30f12c7
+      if (connection == NULL)
David Zeuthen 30f12c7
+        {
David Zeuthen 30f12c7
+          fprintf (stderr, "Failed to open connection to %s message bus: %s: %s\n",
David Zeuthen 30f12c7
+                   (bus_type == DBUS_BUS_SYSTEM) ? "system" : "session",
David Zeuthen 30f12c7
+                   error.name, error.message);
David Zeuthen 30f12c7
+          dbus_error_free (&error);
David Zeuthen 30f12c7
+          goto fail;
David Zeuthen 30f12c7
+        }
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  if (connection != NULL && g_str_has_prefix (cur, "--dest="))
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      print_services (connection);
David Zeuthen 30f12c7
+      goto done;
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  /* see if we have an object path */
David Zeuthen 30f12c7
+  object_path = NULL;
David Zeuthen 30f12c7
+  object_path_index = 0;
David Zeuthen 30f12c7
+  if (connection != NULL && target_service != NULL)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      for (n = 0; tokens[n] != NULL; n++)
David Zeuthen 30f12c7
+        {
David Zeuthen 30f12c7
+          if (tokens[n] == cur)
David Zeuthen 30f12c7
+            continue;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+          if (*(tokens[n]) == '/')
David Zeuthen 30f12c7
+            {
David Zeuthen 30f12c7
+              if (is_object_path_with_interfaces (connection, target_service, tokens[n]))
David Zeuthen 30f12c7
+                {
David Zeuthen 30f12c7
+                  object_path = tokens[n];
David Zeuthen 30f12c7
+                  object_path_index = n;
David Zeuthen 30f12c7
+                }
David Zeuthen 30f12c7
+            }
David Zeuthen 30f12c7
+        }
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  /* if we have a connection and a destination but no object path, go ahead and list the object paths */
David Zeuthen 30f12c7
+  if (connection != NULL && target_service != NULL && object_path == NULL)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      print_objects (connection, target_service, NULL);
David Zeuthen 30f12c7
+      goto done;
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  /* see if we have a method; it's directly after the object_path */
David Zeuthen 30f12c7
+  method = NULL;
David Zeuthen 30f12c7
+  if (connection != NULL && target_service != NULL && object_path != NULL)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      if ((object_path_index + 1 < num_tokens - 1) &&
David Zeuthen 30f12c7
+          (strlen (tokens[object_path_index + 1]) > 0) &&
David Zeuthen 30f12c7
+          !(strcmp (cur, tokens[object_path_index + 1]) == 0))
David Zeuthen 30f12c7
+        method = tokens[object_path_index + 1];
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  /* if we have connection, destination and object path but no method yet, list the methods */
David Zeuthen 30f12c7
+  if (connection != NULL && target_service != NULL && object_path != NULL && method == NULL)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      print_methods (connection, target_service, object_path);
David Zeuthen 30f12c7
+      goto done;
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  /* print signature as comment */
David Zeuthen 30f12c7
+  if (connection != NULL && target_service != NULL && object_path != NULL && method != NULL)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      print_signature (connection, target_service, object_path, method);
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+ done:
David Zeuthen 30f12c7
+  ret = 0;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+ fail:
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  g_strfreev (tokens);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  if (connection != NULL)
David Zeuthen 30f12c7
+    dbus_connection_unref (connection);
David Zeuthen 30f12c7
+  return ret;
David Zeuthen 30f12c7
+}
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+int
David Zeuthen 30f12c7
+main (int argc, char *argv[])
David Zeuthen 30f12c7
+{
David Zeuthen 30f12c7
+  int ret;
David Zeuthen 30f12c7
+  char *cur;
David Zeuthen 30f12c7
+  gboolean dbus_send;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  ret = 1;
David Zeuthen 30f12c7
+  dbus_send = FALSE;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  if (argc != 3)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      fprintf (stderr, "invalid use\n");
David Zeuthen 30f12c7
+      goto out;
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  if (strcmp (argv[1], "dbus-send") == 0)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      dbus_send = TRUE;
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+  else
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      fprintf (stderr, "unknown program '%s'\n", argv[1]);
David Zeuthen 30f12c7
+      goto out;
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  if (strlen (argv[2]) < strlen (argv[1]) + 1)
David Zeuthen 30f12c7
+    {
David Zeuthen 30f12c7
+      fprintf (stderr, "error");
David Zeuthen 30f12c7
+      goto out;
David Zeuthen 30f12c7
+    }
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  cur = argv[2] + strlen (argv[1]) + 1;
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+  if (dbus_send)
David Zeuthen 30f12c7
+    ret = complete_dbus_send (cur);
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+ out:
David Zeuthen 30f12c7
+  return ret;
David Zeuthen 30f12c7
+}
David Zeuthen 30f12c7
diff --git a/dbus/dbus-bash-completion.sh.in b/dbus/dbus-bash-completion.sh.in
David Zeuthen 30f12c7
new file mode 100644
David Zeuthen 30f12c7
index 0000000..a7751da
David Zeuthen 30f12c7
--- /dev/null
David Zeuthen 30f12c7
+++ b/dbus/dbus-bash-completion.sh.in
David Zeuthen 30f12c7
@@ -0,0 +1,21 @@
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+# Check for bash
David Zeuthen 30f12c7
+[ -z "$BASH_VERSION" ] && return
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+################################################################################
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+__dbus_send() {
David Zeuthen 30f12c7
+    local IFS=$'\n'
David Zeuthen 30f12c7
+    local cur="${COMP_WORDS[COMP_CWORD]}"
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+    # --name=value style option
David Zeuthen 30f12c7
+    if [[ "$cur" == *=* ]] ; then
David Zeuthen 30f12c7
+        cur=${cur/*=/}
David Zeuthen 30f12c7
+    fi
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+    COMPREPLY=($(compgen -W "$(@libexecdir@/dbus-bash-completion-helper dbus-send ${COMP_WORDS[@]:0})" -- $cur))
David Zeuthen 30f12c7
+}
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+################################################################################
David Zeuthen 30f12c7
+
David Zeuthen 30f12c7
+complete -o nospace -F __dbus_send dbus-send
David Zeuthen 30f12c7
-- 
David Zeuthen 30f12c7
1.5.6.4
David Zeuthen 30f12c7