[kdbus] Use new API insted of direct call to org.freedesktop.DBus
[platform/upstream/glib.git] / gio / gdbus-tool.c
index b3233cf..09ee399 100644 (file)
@@ -1,6 +1,6 @@
 /* GDBus - GLib D-Bus Library
  *
- * Copyright (C) 2008-2009 Red Hat, Inc.
+ * Copyright (C) 2008-2010 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * Author: David Zeuthen <davidz@redhat.com>
  */
 
 #include "config.h"
 
-#include <glib/gi18n.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <locale.h>
 
 #include <gio/gio.h>
-#include <stdio.h>
-#include <stdlib.h>
+
+#include <gi18n.h>
+
+#ifdef G_OS_WIN32
+#include "glib/glib-private.h"
+#endif
 
 /* ---------------------------------------------------------------------------------------------------- */
 
@@ -90,7 +95,9 @@ usage (gint *argc, gchar **argv[], gboolean use_stdout)
   s = g_strdup_printf (_("Commands:\n"
                          "  help         Shows this information\n"
                          "  introspect   Introspect a remote object\n"
+                         "  monitor      Monitor a remote object\n"
                          "  call         Invoke a method on a remote object\n"
+                         "  emit         Emit a signal\n"
                          "\n"
                          "Use \"%s COMMAND --help\" to get help on each command.\n"),
                        program_name);
@@ -141,30 +148,24 @@ print_methods (GDBusConnection *c,
   guint m;
 
   error = NULL;
-  result = g_dbus_connection_invoke_method_sync (c,
-                                                 name,
-                                                 path,
-                                                 "org.freedesktop.DBus.Introspectable",
-                                                 "Introspect",
-                                                 NULL,
-                                                 G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-                                                 3000, /* 3 secs */
-                                                 NULL,
-                                                 &error);
+  result = g_dbus_connection_call_sync (c,
+                                        name,
+                                        path,
+                                        "org.freedesktop.DBus.Introspectable",
+                                        "Introspect",
+                                        NULL,
+                                        G_VARIANT_TYPE ("(s)"),
+                                        G_DBUS_CALL_FLAGS_NONE,
+                                        3000, /* 3 secs */
+                                        NULL,
+                                        &error);
   if (result == NULL)
     {
       g_printerr (_("Error: %s\n"), error->message);
       g_error_free (error);
       goto out;
     }
-  if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)")))
-    {
-      g_printerr (_("Error: Result is type `%s', expected `(s)'\n"),
-                  g_variant_get_type_string (result));
-      g_variant_unref (result);
-      goto out;
-    }
-  g_variant_get (result, "(s)", &xml_data);
+  g_variant_get (result, "(&s)", &xml_data);
 
   error = NULL;
   node = g_dbus_node_info_new_for_xml (xml_data, &error);
@@ -203,30 +204,26 @@ print_paths (GDBusConnection *c,
   guint n;
 
   error = NULL;
-  result = g_dbus_connection_invoke_method_sync (c,
-                                                 name,
-                                                 path,
-                                                 "org.freedesktop.DBus.Introspectable",
-                                                 "Introspect",
-                                                 NULL,
-                                                 G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-                                                 3000, /* 3 secs */
-                                                 NULL,
-                                                 &error);
+  result = g_dbus_connection_call_sync (c,
+                                        name,
+                                        path,
+                                        "org.freedesktop.DBus.Introspectable",
+                                        "Introspect",
+                                        NULL,
+                                        G_VARIANT_TYPE ("(s)"),
+                                        G_DBUS_CALL_FLAGS_NONE,
+                                        3000, /* 3 secs */
+                                        NULL,
+                                        &error);
   if (result == NULL)
     {
       g_printerr (_("Error: %s\n"), error->message);
       g_error_free (error);
       goto out;
     }
-  if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)")))
-    {
-      g_printerr (_("Error: Result is type `%s', expected `(s)'\n"),
-                  g_variant_get_type_string (result));
-      g_variant_unref (result);
-      goto out;
-    }
-  g_variant_get (result, "(s)", &xml_data);
+  g_variant_get (result, "(&s)", &xml_data);
+
+  //g_printerr ("xml='%s'", xml_data);
 
   error = NULL;
   node = g_dbus_node_info_new_for_xml (xml_data, &error);
@@ -238,9 +235,7 @@ print_paths (GDBusConnection *c,
       goto out;
     }
 
-  //g_printerr ("xml=`%s'", xml_data);
-
-  //g_printerr ("bar `%s'\n", path);
+  //g_printerr ("bar '%s'\n", path);
 
   if (node->interfaces != NULL)
     g_print ("%s \n", path);
@@ -249,7 +244,7 @@ print_paths (GDBusConnection *c,
     {
       gchar *s;
 
-      //g_printerr ("foo `%s'\n", node->nodes[n].path);
+      //g_printerr ("foo '%s'\n", node->nodes[n].path);
 
       if (g_strcmp0 (path, "/") == 0)
         s = g_strdup_printf ("/%s", node->nodes[n]->path);
@@ -270,10 +265,9 @@ static void
 print_names (GDBusConnection *c,
              gboolean         include_unique_names)
 {
-  GVariant *result;
+  gchar **list_names;
   GError *error;
-  GVariantIter *iter;
-  gchar *str;
+  guint cnt;
   GHashTable *name_set;
   GList *keys;
   GList *l;
@@ -281,62 +275,32 @@ print_names (GDBusConnection *c,
   name_set = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
   error = NULL;
-  result = g_dbus_connection_invoke_method_sync (c,
-                                                 "org.freedesktop.DBus",
-                                                 "/org/freedesktop/DBus",
-                                                 "org.freedesktop.DBus",
-                                                 "ListNames",
-                                                 NULL,
-                                                 G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-                                                 3000, /* 3 secs */
-                                                 NULL,
-                                                 &error);
-  if (result == NULL)
+  cnt = 0;
+  list_names = g_dbus_get_list_names (c, &error);
+  if (list_names == NULL)
     {
       g_printerr (_("Error: %s\n"), error->message);
       g_error_free (error);
       goto out;
     }
-  if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(as)")))
-    {
-      g_printerr (_("Error: Result is type `%s', expected `(as)'\n"), g_variant_get_type_string (result));
-      g_variant_unref (result);
-      goto out;
-    }
-  g_variant_get (result, "(as)", &iter);
-  while (g_variant_iter_loop (iter, "s", &str))
-    g_hash_table_insert (name_set, g_strdup (str), NULL);
-  g_variant_iter_free (iter);
-  g_variant_unref (result);
+
+  while (list_names[cnt])
+    g_hash_table_insert (name_set, g_strdup (list_names[cnt++]), NULL);
+  g_strfreev(list_names);
 
   error = NULL;
-  result = g_dbus_connection_invoke_method_sync (c,
-                                                 "org.freedesktop.DBus",
-                                                 "/org/freedesktop/DBus",
-                                                 "org.freedesktop.DBus",
-                                                 "ListActivatableNames",
-                                                 NULL,
-                                                 G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-                                                 3000, /* 3 secs */
-                                                 NULL,
-                                                 &error);
-  if (result == NULL)
+  cnt = 0;
+  list_names = g_dbus_get_list_activatable_names (c, &error);
+  if (list_names == NULL)
     {
       g_printerr (_("Error: %s\n"), error->message);
       g_error_free (error);
       goto out;
     }
-  if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(as)")))
-    {
-      g_printerr (_("Error: Result is type `%s', expected `(as)'\n"), g_variant_get_type_string (result));
-      g_variant_unref (result);
-      goto out;
-    }
-  g_variant_get (result, "(as)", &iter);
-  while (g_variant_iter_loop (iter, "s", &str))
-    g_hash_table_insert (name_set, g_strdup (str), NULL);
-  g_variant_iter_free (iter);
-  g_variant_unref (result);
+
+  while (list_names[cnt])
+    g_hash_table_insert (name_set, g_strdup (list_names[cnt++]), NULL);
+  g_strfreev(list_names);
 
   keys = g_hash_table_get_keys (name_set);
   keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
@@ -358,12 +322,16 @@ print_names (GDBusConnection *c,
 
 static gboolean  opt_connection_system  = FALSE;
 static gboolean  opt_connection_session = FALSE;
+static gboolean  opt_connection_user    = FALSE;
+static gboolean  opt_connection_machine = FALSE;
 static gchar    *opt_connection_address = NULL;
 
 static const GOptionEntry connection_entries[] =
 {
   { "system", 'y', 0, G_OPTION_ARG_NONE, &opt_connection_system, N_("Connect to the system bus"), NULL},
   { "session", 'e', 0, G_OPTION_ARG_NONE, &opt_connection_session, N_("Connect to the session bus"), NULL},
+  { "machine", 'm', 0, G_OPTION_ARG_NONE, &opt_connection_machine, N_("Connect to the machine bus"), NULL},
+  { "user", 'u', 0, G_OPTION_ARG_NONE, &opt_connection_user, N_("Connect to the user bus"), NULL},
   { "address", 'a', 0, G_OPTION_ARG_STRING, &opt_connection_address, N_("Connect to given D-Bus address"), NULL},
   { NULL }
 };
@@ -378,7 +346,9 @@ connection_get_group (void)
                           N_("Options specifying the connection endpoint"),
                           NULL,
                           NULL);
+  g_option_group_set_translation_domain (g, GETTEXT_PACKAGE);
   g_option_group_add_entries (g, connection_entries);
+
   return g;
 }
 
@@ -386,11 +356,18 @@ static GDBusConnection *
 connection_get_dbus_connection (GError **error)
 {
   GDBusConnection *c;
+  guint count;
 
   c = NULL;
 
+  count = !!opt_connection_system +
+          !!opt_connection_session +
+          !!opt_connection_machine +
+          !!opt_connection_user +
+          !!opt_connection_address;
+
   /* First, ensure we have exactly one connect */
-  if (!opt_connection_system && !opt_connection_session && opt_connection_address == NULL)
+  if (count == 0)
     {
       g_set_error (error,
                    G_IO_ERROR,
@@ -398,9 +375,7 @@ connection_get_dbus_connection (GError **error)
                    _("No connection endpoint specified"));
       goto out;
     }
-  else if ((opt_connection_system && (opt_connection_session || opt_connection_address != NULL)) ||
-           (opt_connection_session && (opt_connection_system || opt_connection_address != NULL)) ||
-           (opt_connection_address != NULL && (opt_connection_system || opt_connection_session)))
+  else if (count > 1)
     {
       g_set_error (error,
                    G_IO_ERROR,
@@ -417,10 +392,19 @@ connection_get_dbus_connection (GError **error)
     {
       c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
     }
+  else if (opt_connection_machine)
+    {
+      c = g_bus_get_sync (G_BUS_TYPE_MACHINE, NULL, error);
+    }
+  else if (opt_connection_user)
+    {
+      c = g_bus_get_sync (G_BUS_TYPE_USER, NULL, error);
+    }
   else if (opt_connection_address != NULL)
     {
       c = g_dbus_connection_new_for_address_sync (opt_connection_address,
                                                   G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+                                                  NULL, /* GDBusAuthObserver */
                                                   NULL, /* GCancellable */
                                                   error);
     }
@@ -443,36 +427,29 @@ call_helper_get_method_in_signature (GDBusConnection  *c,
   GVariant *result;
   GDBusNodeInfo *node_info;
   const gchar *xml_data;
-  const GDBusInterfaceInfo *interface_info;
-  const GDBusMethodInfo *method_info;
+  GDBusInterfaceInfo *interface_info;
+  GDBusMethodInfo *method_info;
   guint n;
 
   ret = NULL;
   result = NULL;
   node_info = NULL;
 
-  result = g_dbus_connection_invoke_method_sync (c,
-                                                 dest,
-                                                 path,
-                                                 "org.freedesktop.DBus.Introspectable",
-                                                 "Introspect",
-                                                 NULL,
-                                                 G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-                                                 3000, /* 3 secs */
-                                                 NULL,
-                                                 error);
+  result = g_dbus_connection_call_sync (c,
+                                        dest,
+                                        path,
+                                        "org.freedesktop.DBus.Introspectable",
+                                        "Introspect",
+                                        NULL,
+                                        G_VARIANT_TYPE ("(s)"),
+                                        G_DBUS_CALL_FLAGS_NONE,
+                                        3000, /* 3 secs */
+                                        NULL,
+                                        error);
   if (result == NULL)
     goto out;
 
-  if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)")))
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   _("Error: Result is type `%s', expected `(s)'\n"),
-                   g_variant_get_type_string (result));
-      goto out;
-    }
-
-  g_variant_get (result, "(s)", &xml_data);
+  g_variant_get (result, "(&s)", &xml_data);
   node_info = g_dbus_node_info_new_for_xml (xml_data, error);
   if (node_info == NULL)
       goto out;
@@ -481,7 +458,7 @@ call_helper_get_method_in_signature (GDBusConnection  *c,
   if (interface_info == NULL)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   _("Warning: According to introspection data, interface `%s' does not exist\n"),
+                   _("Warning: According to introspection data, interface '%s' does not exist\n"),
                    interface_name);
       goto out;
     }
@@ -490,7 +467,7 @@ call_helper_get_method_in_signature (GDBusConnection  *c,
   if (method_info == NULL)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   _("Warning: According to introspection data, method `%s' does not exist on interface `%s'\n"),
+                   _("Warning: According to introspection data, method '%s' does not exist on interface '%s'\n"),
                    method_name,
                    interface_name);
       goto out;
@@ -546,15 +523,219 @@ _g_variant_parse_me_harder (GVariantType   *type,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static gchar *opt_emit_dest = NULL;
+static gchar *opt_emit_object_path = NULL;
+static gchar *opt_emit_signal = NULL;
+
+static const GOptionEntry emit_entries[] =
+{
+  { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_emit_dest, N_("Optional destination for signal (unique name)"), NULL},
+  { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_emit_object_path, N_("Object path to emit signal on"), NULL},
+  { "signal", 's', 0, G_OPTION_ARG_STRING, &opt_emit_signal, N_("Signal and interface name"), NULL},
+  { NULL }
+};
+
+static gboolean
+handle_emit (gint        *argc,
+             gchar      **argv[],
+             gboolean     request_completion,
+             const gchar *completion_cur,
+             const gchar *completion_prev)
+{
+  gint ret;
+  GOptionContext *o;
+  gchar *s;
+  GError *error;
+  GDBusConnection *c;
+  GVariant *parameters;
+  gchar *interface_name;
+  gchar *signal_name;
+  GVariantBuilder builder;
+  guint n;
+
+  ret = FALSE;
+  c = NULL;
+  parameters = NULL;
+  interface_name = NULL;
+  signal_name = NULL;
+
+  modify_argv0_for_command (argc, argv, "emit");
+
+  o = g_option_context_new (NULL);
+  g_option_context_set_help_enabled (o, FALSE);
+  g_option_context_set_summary (o, _("Emit a signal."));
+  g_option_context_add_main_entries (o, emit_entries, GETTEXT_PACKAGE);
+  g_option_context_add_group (o, connection_get_group ());
+
+  if (!g_option_context_parse (o, argc, argv, NULL))
+    {
+      if (!request_completion)
+        {
+          s = g_option_context_get_help (o, FALSE, NULL);
+          g_printerr ("%s", s);
+          g_free (s);
+          goto out;
+        }
+    }
+
+  error = NULL;
+  c = connection_get_dbus_connection (&error);
+  if (c == NULL)
+    {
+      if (request_completion)
+        {
+          if (g_strcmp0 (completion_prev, "--address") == 0)
+            {
+              g_print ("unix:\n"
+                       "tcp:\n"
+                       "nonce-tcp:\n");
+            }
+          else
+            {
+              g_print ("--system \n--session \n--machine \n--user \n--address \n");
+            }
+        }
+      else
+        {
+          g_printerr (_("Error connecting: %s\n"), error->message);
+          g_error_free (error);
+        }
+      goto out;
+    }
+
+  /* All done with completion now */
+  if (request_completion)
+    goto out;
+
+  if (opt_emit_object_path == NULL)
+    {
+      g_printerr (_("Error: object path not specified.\n"));
+      goto out;
+    }
+  if (!g_variant_is_object_path (opt_emit_object_path))
+    {
+      g_printerr (_("Error: %s is not a valid object path\n"), opt_emit_object_path);
+      goto out;
+    }
+
+  if (opt_emit_signal == NULL)
+    {
+      g_printerr (_("Error: signal not specified.\n"));
+      goto out;
+    }
+
+  s = strrchr (opt_emit_signal, '.');
+  if (s == NULL)
+    {
+      g_printerr (_("Error: signal must be the fully-qualified name.\n"));
+      goto out;
+    }
+  signal_name = g_strdup (s + 1);
+  interface_name = g_strndup (opt_emit_signal, s - opt_emit_signal);
+
+  if (!g_dbus_is_interface_name (interface_name))
+    {
+      g_printerr (_("Error: %s is not a valid interface name\n"), interface_name);
+      goto out;
+    }
+
+  if (!g_dbus_is_member_name (signal_name))
+    {
+      g_printerr (_("Error: %s is not a valid member name\n"), signal_name);
+      goto out;
+    }
+
+  if (opt_emit_dest != NULL && !g_dbus_is_unique_name (opt_emit_dest))
+    {
+      g_printerr (_("Error: %s is not a valid unique bus name.\n"), opt_emit_dest);
+      goto out;
+    }
+
+  /* Read parameters */
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+  for (n = 1; n < (guint) *argc; n++)
+    {
+      GVariant *value;
+
+      error = NULL;
+      value = g_variant_parse (NULL,
+                               (*argv)[n],
+                               NULL,
+                               NULL,
+                               &error);
+      if (value == NULL)
+        {
+          gchar *context;
+
+          context = g_variant_parse_error_print_context (error, (*argv)[n]);
+          g_error_free (error);
+          error = NULL;
+          value = _g_variant_parse_me_harder (NULL, (*argv)[n], &error);
+          if (value == NULL)
+            {
+              /* Use the original non-"parse-me-harder" error */
+              g_printerr (_("Error parsing parameter %d: %s\n"),
+                          n,
+                          context);
+              g_error_free (error);
+              g_free (context);
+              g_variant_builder_clear (&builder);
+              goto out;
+            }
+          g_free (context);
+        }
+      g_variant_builder_add_value (&builder, value);
+    }
+  parameters = g_variant_builder_end (&builder);
+
+  if (parameters != NULL)
+    parameters = g_variant_ref_sink (parameters);
+  if (!g_dbus_connection_emit_signal (c,
+                                      opt_emit_dest,
+                                      opt_emit_object_path,
+                                      interface_name,
+                                      signal_name,
+                                      parameters,
+                                      &error))
+    {
+      g_printerr (_("Error: %s\n"), error->message);
+      g_error_free (error);
+      goto out;
+    }
+
+  if (!g_dbus_connection_flush_sync (c, NULL, &error))
+    {
+      g_printerr (_("Error flushing connection: %s\n"), error->message);
+      g_error_free (error);
+      goto out;
+    }
+
+  ret = TRUE;
+
+ out:
+  if (c != NULL)
+    g_object_unref (c);
+  if (parameters != NULL)
+    g_variant_unref (parameters);
+  g_free (interface_name);
+  g_free (signal_name);
+  g_option_context_free (o);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static gchar *opt_call_dest = NULL;
 static gchar *opt_call_object_path = NULL;
 static gchar *opt_call_method = NULL;
+static gint opt_call_timeout = -1;
 
 static const GOptionEntry call_entries[] =
 {
   { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_call_dest, N_("Destination name to invoke method on"), NULL},
   { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_call_object_path, N_("Object path to invoke method on"), NULL},
   { "method", 'm', 0, G_OPTION_ARG_STRING, &opt_call_method, N_("Method and interface name"), NULL},
+  { "timeout", 't', 0, G_OPTION_ARG_INT, &opt_call_timeout, N_("Timeout in seconds"), NULL},
   { NULL }
 };
 
@@ -594,7 +775,7 @@ handle_call (gint        *argc,
   o = g_option_context_new (NULL);
   g_option_context_set_help_enabled (o, FALSE);
   g_option_context_set_summary (o, _("Invoke a method on a remote object."));
-  g_option_context_add_main_entries (o, call_entries, NULL /* GETTEXT_PACKAGE*/);
+  g_option_context_add_main_entries (o, call_entries, GETTEXT_PACKAGE);
   g_option_context_add_group (o, connection_get_group ());
 
   complete_names = FALSE;
@@ -643,7 +824,7 @@ handle_call (gint        *argc,
             }
           else
             {
-              g_print ("--system \n--session \n--address \n");
+              g_print ("--system \n--session \n--machine \n--user \n--address \n");
             }
         }
       else
@@ -735,7 +916,7 @@ handle_call (gint        *argc,
   s = strrchr (opt_call_method, '.');
   if (!request_completion && s == NULL)
     {
-      g_printerr (_("Error: Method name `%s' is invalid\n"), opt_call_method);
+      g_printerr (_("Error: Method name '%s' is invalid\n"), opt_call_method);
       goto out;
     }
   method_name = g_strdup (s + 1);
@@ -792,6 +973,9 @@ handle_call (gint        *argc,
                                &error);
       if (value == NULL)
         {
+          gchar *context;
+
+          context = g_variant_parse_error_print_context (error, (*argv)[n]);
           g_error_free (error);
           error = NULL;
           value = _g_variant_parse_me_harder (type, (*argv)[n], &error);
@@ -800,22 +984,24 @@ handle_call (gint        *argc,
               if (type != NULL)
                 {
                   s = g_variant_type_dup_string (type);
-                  g_printerr (_("Error parsing parameter %d of type `%s': %s\n"),
+                  g_printerr (_("Error parsing parameter %d of type '%s': %s\n"),
                               n,
                               s,
-                              error->message);
+                              context);
                   g_free (s);
                 }
               else
                 {
                   g_printerr (_("Error parsing parameter %d: %s\n"),
                               n,
-                              error->message);
+                              context);
                 }
               g_error_free (error);
               g_variant_builder_clear (&builder);
+              g_free (context);
               goto out;
             }
+          g_free (context);
         }
       g_variant_builder_add_value (&builder, value);
     }
@@ -823,20 +1009,24 @@ handle_call (gint        *argc,
 
   if (parameters != NULL)
     parameters = g_variant_ref_sink (parameters);
-  result = g_dbus_connection_invoke_method_sync (c,
-                                                 opt_call_dest,
-                                                 opt_call_object_path,
-                                                 interface_name,
-                                                 method_name,
-                                                 parameters,
-                                                 G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-                                                 -1,
-                                                 NULL,
-                                                 &error);
+  result = g_dbus_connection_call_sync (c,
+                                        opt_call_dest,
+                                        opt_call_object_path,
+                                        interface_name,
+                                        method_name,
+                                        parameters,
+                                        NULL,
+                                        G_DBUS_CALL_FLAGS_NONE,
+                                        opt_call_timeout > 0 ? opt_call_timeout * 1000 : opt_call_timeout,
+                                        NULL,
+                                        &error);
   if (result == NULL)
     {
-      g_printerr (_("Error: %s\n"), error->message);
-      g_error_free (error);
+      if (error)
+        {
+          g_printerr (_("Error: %s\n"), error->message);
+          g_error_free (error);
+        }
       if (in_signature_types != NULL)
         {
           GString *s;
@@ -848,7 +1038,7 @@ handle_call (gint        *argc,
                                    g_variant_type_peek_string (type),
                                    g_variant_type_get_string_length (type));
             }
-          g_printerr ("(According to introspection data, you need to pass `%s')\n", s->str);
+          g_printerr ("(According to introspection data, you need to pass '%s')\n", s->str);
           g_string_free (s, TRUE);
         }
       goto out;
@@ -877,7 +1067,25 @@ handle_call (gint        *argc,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
-/* TODO: dump annotations */
+static gchar *opt_introspect_dest = NULL;
+static gchar *opt_introspect_object_path = NULL;
+static gboolean opt_introspect_xml = FALSE;
+static gboolean opt_introspect_recurse = FALSE;
+static gboolean opt_introspect_only_properties = FALSE;
+
+static void
+dump_annotation (const GDBusAnnotationInfo *o,
+                 guint indent,
+                 gboolean ignore_indent)
+{
+  guint n;
+  g_print ("%*s@%s(\"%s\")\n",
+           ignore_indent ? 0 : indent, "",
+           o->key,
+           o->value);
+  for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
+    dump_annotation (o->annotations[n], indent + 2, FALSE);
+}
 
 static void
 dump_arg (const GDBusArgInfo *o,
@@ -886,6 +1094,14 @@ dump_arg (const GDBusArgInfo *o,
           gboolean ignore_indent,
           gboolean include_newline)
 {
+  guint n;
+
+  for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
+    {
+      dump_annotation (o->annotations[n], indent, ignore_indent);
+      ignore_indent = FALSE;
+    }
+
   g_print ("%*s%s%s %s%s",
            ignore_indent ? 0 : indent, "",
            direction,
@@ -915,6 +1131,10 @@ dump_method (const GDBusMethodInfo *o,
   guint m;
   guint name_len;
   guint total_num_args;
+
+  for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
+    dump_annotation (o->annotations[n], indent, FALSE);
+
   g_print ("%*s%s(", indent, "", o->name);
   name_len = strlen (o->name);
   total_num_args = count_args (o->in_args) + count_args (o->out_args);
@@ -922,6 +1142,7 @@ dump_method (const GDBusMethodInfo *o,
     {
       gboolean ignore_indent = (m == 0);
       gboolean include_newline = (m != total_num_args - 1);
+
       dump_arg (o->in_args[n],
                 indent + name_len + 1,
                 "in  ",
@@ -948,6 +1169,10 @@ dump_signal (const GDBusSignalInfo *o,
   guint n;
   guint name_len;
   guint total_num_args;
+
+  for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
+    dump_annotation (o->annotations[n], indent, FALSE);
+
   g_print ("%*s%s(", indent, "", o->name);
   name_len = strlen (o->name);
   total_num_args = count_args (o->args);
@@ -970,6 +1195,8 @@ dump_property (const GDBusPropertyInfo *o,
                GVariant                *value)
 {
   const gchar *access;
+  guint n;
+
   if (o->flags == G_DBUS_PROPERTY_INFO_FLAGS_READABLE)
     access = "readonly";
   else if (o->flags == G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)
@@ -978,6 +1205,10 @@ dump_property (const GDBusPropertyInfo *o,
     access = "readwrite";
   else
     g_assert_not_reached ();
+
+  for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
+    dump_annotation (o->annotations[n], indent, FALSE);
+
   if (value != NULL)
     {
       gchar *s = g_variant_print (value, FALSE);
@@ -1006,19 +1237,20 @@ dump_interface (GDBusConnection          *c,
                                       (GDestroyNotify) g_variant_unref);
 
   /* Try to get properties */
-  if (c != NULL && name != NULL && object_path != NULL)
+  if (c != NULL && name != NULL && object_path != NULL && o->properties != NULL)
     {
       GVariant *result;
-      result = g_dbus_connection_invoke_method_sync (c,
-                                                     name,
-                                                     object_path,
-                                                     "org.freedesktop.DBus.Properties",
-                                                     "GetAll",
-                                                     g_variant_new ("(s)", o->name),
-                                                     G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-                                                     3000,
-                                                     NULL,
-                                                     NULL);
+      result = g_dbus_connection_call_sync (c,
+                                            name,
+                                            object_path,
+                                            "org.freedesktop.DBus.Properties",
+                                            "GetAll",
+                                            g_variant_new ("(s)", o->name),
+                                            NULL,
+                                            G_DBUS_CALL_FLAGS_NONE,
+                                            3000,
+                                            NULL,
+                                            NULL);
       if (result != NULL)
         {
           if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(a{sv})")))
@@ -1030,28 +1262,60 @@ dump_interface (GDBusConnection          *c,
                              &iter);
               while ((item = g_variant_iter_next_value (iter)))
                 {
-                  const gchar *key;
+                  gchar *key;
                   GVariant *value;
                   g_variant_get (item,
                                  "{sv}",
                                  &key,
                                  &value);
 
-                  g_hash_table_insert (properties, g_strdup (key), g_variant_ref (value));
+                  g_hash_table_insert (properties, key, g_variant_ref (value));
                 }
             }
           g_variant_unref (result);
         }
+      else
+        {
+          guint n;
+          for (n = 0; o->properties != NULL && o->properties[n] != NULL; n++)
+            {
+              result = g_dbus_connection_call_sync (c,
+                                                    name,
+                                                    object_path,
+                                                    "org.freedesktop.DBus.Properties",
+                                                    "Get",
+                                                    g_variant_new ("(ss)", o->name, o->properties[n]->name),
+                                                    G_VARIANT_TYPE ("(v)"),
+                                                    G_DBUS_CALL_FLAGS_NONE,
+                                                    3000,
+                                                    NULL,
+                                                    NULL);
+              if (result != NULL)
+                {
+                  GVariant *property_value;
+                  g_variant_get (result,
+                                 "(v)",
+                                 &property_value);
+                  g_hash_table_insert (properties,
+                                       g_strdup (o->properties[n]->name),
+                                       g_variant_ref (property_value));
+                  g_variant_unref (result);
+                }
+            }
+        }
     }
 
+  for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
+    dump_annotation (o->annotations[n], indent, FALSE);
+
   g_print ("%*sinterface %s {\n", indent, "", o->name);
-  if (o->methods != NULL)
+  if (o->methods != NULL && !opt_introspect_only_properties)
     {
       g_print ("%*s  methods:\n", indent, "");
       for (n = 0; o->methods[n] != NULL; n++)
         dump_method (o->methods[n], indent + 4);
     }
-  if (o->signals != NULL)
+  if (o->signals != NULL && !opt_introspect_only_properties)
     {
       g_print ("%*s  signals:\n", indent, "");
       for (n = 0; o->signals[n] != NULL; n++)
@@ -1073,12 +1337,18 @@ dump_interface (GDBusConnection          *c,
   g_hash_table_unref (properties);
 }
 
+static gboolean
+introspect_do (GDBusConnection *c,
+               const gchar     *object_path,
+               guint            indent);
+
 static void
 dump_node (GDBusConnection      *c,
            const gchar          *name,
            const GDBusNodeInfo  *o,
            guint                 indent,
-           const gchar          *object_path)
+           const gchar          *object_path,
+           gboolean              recurse)
 {
   guint n;
   const gchar *object_path_to_print;
@@ -1087,14 +1357,59 @@ dump_node (GDBusConnection      *c,
   if (o->path != NULL)
     object_path_to_print = o->path;
 
+  for (n = 0; o->annotations != NULL && o->annotations[n] != NULL; n++)
+    dump_annotation (o->annotations[n], indent, FALSE);
+
   g_print ("%*snode %s", indent, "", object_path_to_print != NULL ? object_path_to_print : "(not set)");
   if (o->interfaces != NULL || o->nodes != NULL)
     {
       g_print (" {\n");
       for (n = 0; o->interfaces != NULL && o->interfaces[n] != NULL; n++)
-        dump_interface (c, name, o->interfaces[n], indent + 2, object_path);
+        {
+          if (opt_introspect_only_properties)
+            {
+              if (o->interfaces[n]->properties != NULL && o->interfaces[n]->properties[0] != NULL)
+                dump_interface (c, name, o->interfaces[n], indent + 2, object_path);
+            }
+          else
+            {
+              dump_interface (c, name, o->interfaces[n], indent + 2, object_path);
+            }
+        }
       for (n = 0; o->nodes != NULL && o->nodes[n] != NULL; n++)
-        dump_node (NULL, NULL, o->nodes[n], indent + 2, NULL);
+        {
+          if (recurse)
+            {
+              gchar *child_path;
+              if (g_variant_is_object_path (o->nodes[n]->path))
+                {
+                  child_path = g_strdup (o->nodes[n]->path);
+                  /* avoid infinite loops */
+                  if (g_str_has_prefix (child_path, object_path))
+                    {
+                      introspect_do (c, child_path, indent + 2);
+                    }
+                  else
+                    {
+                      g_print ("Skipping path %s that is not enclosed by parent %s\n",
+                               child_path, object_path);
+                    }
+                }
+              else
+                {
+                  if (g_strcmp0 (object_path, "/") == 0)
+                    child_path = g_strdup_printf ("/%s", o->nodes[n]->path);
+                  else
+                    child_path = g_strdup_printf ("%s/%s", object_path, o->nodes[n]->path);
+                  introspect_do (c, child_path, indent + 2);
+                }
+              g_free (child_path);
+            }
+          else
+            {
+              dump_node (NULL, NULL, o->nodes[n], indent + 2, NULL, recurse);
+            }
+        }
       g_print ("%*s};\n",
                indent, "");
     }
@@ -1104,17 +1419,80 @@ dump_node (GDBusConnection      *c,
     }
 }
 
-static gchar *opt_introspect_dest = NULL;
-static gchar *opt_introspect_object_path = NULL;
-
 static const GOptionEntry introspect_entries[] =
 {
   { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_introspect_dest, N_("Destination name to introspect"), NULL},
   { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_introspect_object_path, N_("Object path to introspect"), NULL},
+  { "xml", 'x', 0, G_OPTION_ARG_NONE, &opt_introspect_xml, N_("Print XML"), NULL},
+  { "recurse", 'r', 0, G_OPTION_ARG_NONE, &opt_introspect_recurse, N_("Introspect children"), NULL},
+  { "only-properties", 'p', 0, G_OPTION_ARG_NONE, &opt_introspect_only_properties, N_("Only print properties"), NULL},
   { NULL }
 };
 
 static gboolean
+introspect_do (GDBusConnection *c,
+               const gchar     *object_path,
+               guint            indent)
+{
+  GError *error;
+  GVariant *result;
+  GDBusNodeInfo *node;
+  gboolean ret;
+  const gchar *xml_data;
+
+  ret = FALSE;
+  node = NULL;
+  result = NULL;
+
+  error = NULL;
+  result = g_dbus_connection_call_sync (c,
+                                        opt_introspect_dest,
+                                        object_path,
+                                        "org.freedesktop.DBus.Introspectable",
+                                        "Introspect",
+                                        NULL,
+                                        G_VARIANT_TYPE ("(s)"),
+                                        G_DBUS_CALL_FLAGS_NONE,
+                                        3000, /* 3 sec */
+                                        NULL,
+                                        &error);
+  if (result == NULL)
+    {
+      g_printerr (_("Error: %s\n"), error->message);
+      g_error_free (error);
+      goto out;
+    }
+  g_variant_get (result, "(&s)", &xml_data);
+
+  if (opt_introspect_xml)
+    {
+      g_print ("%s", xml_data);
+    }
+  else
+    {
+      error = NULL;
+      node = g_dbus_node_info_new_for_xml (xml_data, &error);
+      if (node == NULL)
+        {
+          g_printerr (_("Error parsing introspection XML: %s\n"), error->message);
+          g_error_free (error);
+          goto out;
+        }
+
+      dump_node (c, opt_introspect_dest, node, indent, object_path, opt_introspect_recurse);
+    }
+
+  ret = TRUE;
+
+ out:
+  if (node != NULL)
+    g_dbus_node_info_unref (node);
+  if (result != NULL)
+    g_variant_unref (result);
+  return ret;
+}
+
+static gboolean
 handle_introspect (gint        *argc,
                    gchar      **argv[],
                    gboolean     request_completion,
@@ -1126,16 +1504,11 @@ handle_introspect (gint        *argc,
   gchar *s;
   GError *error;
   GDBusConnection *c;
-  GVariant *result;
-  const gchar *xml_data;
-  GDBusNodeInfo *node;
   gboolean complete_names;
   gboolean complete_paths;
 
   ret = FALSE;
   c = NULL;
-  node = NULL;
-  result = NULL;
 
   modify_argv0_for_command (argc, argv, "introspect");
 
@@ -1144,7 +1517,7 @@ handle_introspect (gint        *argc,
     g_option_context_set_ignore_unknown_options (o, TRUE);
   g_option_context_set_help_enabled (o, FALSE);
   g_option_context_set_summary (o, _("Introspect a remote object."));
-  g_option_context_add_main_entries (o, introspect_entries, NULL /* GETTEXT_PACKAGE*/);
+  g_option_context_add_main_entries (o, introspect_entries, GETTEXT_PACKAGE);
   g_option_context_add_group (o, connection_get_group ());
 
   complete_names = FALSE;
@@ -1186,7 +1559,7 @@ handle_introspect (gint        *argc,
             }
           else
             {
-              g_print ("--system \n--session \n--address \n");
+              g_print ("--system \n--session \n--machine \n--user \n--address \n");
             }
         }
       else
@@ -1253,52 +1626,259 @@ handle_introspect (gint        *argc,
       goto out;
     }
 
+  if (request_completion && opt_introspect_object_path != NULL && !opt_introspect_recurse)
+    {
+      g_print ("--recurse \n");
+    }
+
+  if (request_completion && opt_introspect_object_path != NULL && !opt_introspect_only_properties)
+    {
+      g_print ("--only-properties \n");
+    }
+
   /* All done with completion now */
   if (request_completion)
     goto out;
 
-  result = g_dbus_connection_invoke_method_sync (c,
-                                                 opt_introspect_dest,
-                                                 opt_introspect_object_path,
-                                                 "org.freedesktop.DBus.Introspectable",
-                                                 "Introspect",
-                                                 NULL,
-                                                 G_DBUS_INVOKE_METHOD_FLAGS_NONE,
-                                                 3000, /* 3 sec */
-                                                 NULL,
-                                                 &error);
-  if (result == NULL)
+  if (!introspect_do (c, opt_introspect_object_path, 0))
+    goto out;
+
+  ret = TRUE;
+
+ out:
+  if (c != NULL)
+    g_object_unref (c);
+  g_option_context_free (o);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gchar *opt_monitor_dest = NULL;
+static gchar *opt_monitor_object_path = NULL;
+
+static guint monitor_filter_id = 0;
+
+static void
+monitor_signal_cb (GDBusConnection *connection,
+                   const gchar     *sender_name,
+                   const gchar     *object_path,
+                   const gchar     *interface_name,
+                   const gchar     *signal_name,
+                   GVariant        *parameters,
+                   gpointer         user_data)
+{
+  gchar *s;
+  s = g_variant_print (parameters, TRUE);
+  g_print ("%s: %s.%s %s\n",
+           object_path,
+           interface_name,
+           signal_name,
+           s);
+  g_free (s);
+}
+
+static void
+monitor_on_name_appeared (GDBusConnection *connection,
+                          const gchar *name,
+                          const gchar *name_owner,
+                          gpointer user_data)
+{
+  g_print ("The name %s is owned by %s\n", name, name_owner);
+  g_assert (monitor_filter_id == 0);
+  monitor_filter_id = g_dbus_connection_signal_subscribe (connection,
+                                                          name_owner,
+                                                          NULL,  /* any interface */
+                                                          NULL,  /* any member */
+                                                          opt_monitor_object_path,
+                                                          NULL,  /* arg0 */
+                                                          G_DBUS_SIGNAL_FLAGS_NONE,
+                                                          monitor_signal_cb,
+                                                          NULL,  /* user_data */
+                                                          NULL); /* user_data destroy notify */
+}
+
+static void
+monitor_on_name_vanished (GDBusConnection *connection,
+                          const gchar *name,
+                          gpointer user_data)
+{
+  g_print ("The name %s does not have an owner\n", name);
+
+  if (monitor_filter_id != 0)
     {
-      g_printerr (_("Error: %s\n"), error->message);
-      g_error_free (error);
-      goto out;
+      g_dbus_connection_signal_unsubscribe (connection, monitor_filter_id);
+      monitor_filter_id = 0;
     }
-  if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)")))
+}
+
+static const GOptionEntry monitor_entries[] =
+{
+  { "dest", 'd', 0, G_OPTION_ARG_STRING, &opt_monitor_dest, N_("Destination name to monitor"), NULL},
+  { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_monitor_object_path, N_("Object path to monitor"), NULL},
+  { NULL }
+};
+
+static gboolean
+handle_monitor (gint        *argc,
+                gchar      **argv[],
+                gboolean     request_completion,
+                const gchar *completion_cur,
+                const gchar *completion_prev)
+{
+  gint ret;
+  GOptionContext *o;
+  gchar *s;
+  GError *error;
+  GDBusConnection *c;
+  gboolean complete_names;
+  gboolean complete_paths;
+  GMainLoop *loop;
+
+  ret = FALSE;
+  c = NULL;
+
+  modify_argv0_for_command (argc, argv, "monitor");
+
+  o = g_option_context_new (NULL);
+  if (request_completion)
+    g_option_context_set_ignore_unknown_options (o, TRUE);
+  g_option_context_set_help_enabled (o, FALSE);
+  g_option_context_set_summary (o, _("Monitor a remote object."));
+  g_option_context_add_main_entries (o, monitor_entries, GETTEXT_PACKAGE);
+  g_option_context_add_group (o, connection_get_group ());
+
+  complete_names = FALSE;
+  if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--dest") == 0)
     {
-      g_printerr (_("Error: Result is type `%s', expected `(s)'\n"),
-                  g_variant_get_type_string (result));
-      goto out;
+      complete_names = TRUE;
+      remove_arg ((*argc) - 1, argc, argv);
+    }
+
+  complete_paths = FALSE;
+  if (request_completion && *argc > 1 && g_strcmp0 ((*argv)[(*argc)-1], "--object-path") == 0)
+    {
+      complete_paths = TRUE;
+      remove_arg ((*argc) - 1, argc, argv);
+    }
+
+  if (!g_option_context_parse (o, argc, argv, NULL))
+    {
+      if (!request_completion)
+        {
+          s = g_option_context_get_help (o, FALSE, NULL);
+          g_printerr ("%s", s);
+          g_free (s);
+          goto out;
+        }
     }
-  g_variant_get (result, "(s)", &xml_data);
 
   error = NULL;
-  node = g_dbus_node_info_new_for_xml (xml_data, &error);
-  if (node == NULL)
+  c = connection_get_dbus_connection (&error);
+  if (c == NULL)
     {
-      g_printerr (_("Error parsing introspection XML: %s\n"), error->message);
-      g_error_free (error);
+      if (request_completion)
+        {
+          if (g_strcmp0 (completion_prev, "--address") == 0)
+            {
+              g_print ("unix:\n"
+                       "tcp:\n"
+                       "nonce-tcp:\n");
+            }
+          else
+            {
+              g_print ("--system \n--session \n--machine \n--user \n--address \n");
+            }
+        }
+      else
+        {
+          g_printerr (_("Error connecting: %s\n"), error->message);
+          g_error_free (error);
+        }
       goto out;
     }
 
-  dump_node (c, opt_introspect_dest, node, 0, opt_introspect_object_path);
+  if (g_dbus_connection_get_unique_name (c) != NULL)
+    {
+      if (complete_names)
+        {
+          print_names (c, FALSE);
+          goto out;
+        }
+      /* this only makes sense on message bus connections */
+      if (opt_monitor_dest == NULL)
+        {
+          if (request_completion)
+            g_print ("--dest \n");
+          else
+            g_printerr (_("Error: Destination is not specified\n"));
+          goto out;
+        }
+      if (request_completion && g_strcmp0 ("--dest", completion_prev) == 0)
+        {
+          print_names (c, g_str_has_prefix (opt_monitor_dest, ":"));
+          goto out;
+        }
+    }
+  if (complete_paths)
+    {
+      print_paths (c, opt_monitor_dest, "/");
+      goto out;
+    }
+  if (opt_monitor_object_path == NULL)
+    {
+      if (request_completion)
+        {
+          g_print ("--object-path \n");
+          goto out;
+        }
+      /* it's fine to not have an object path */
+    }
+  if (request_completion && g_strcmp0 ("--object-path", completion_prev) == 0)
+    {
+      gchar *p;
+      s = g_strdup (opt_monitor_object_path);
+      p = strrchr (s, '/');
+      if (p != NULL)
+        {
+          if (p == s)
+            p++;
+          *p = '\0';
+        }
+      print_paths (c, opt_monitor_dest, s);
+      g_free (s);
+      goto out;
+    }
+  if (!request_completion && (opt_monitor_object_path != NULL && !g_variant_is_object_path (opt_monitor_object_path)))
+    {
+      g_printerr (_("Error: %s is not a valid object path\n"), opt_monitor_object_path);
+      goto out;
+    }
+
+  /* All done with completion now */
+  if (request_completion)
+    goto out;
+
+  if (opt_monitor_object_path != NULL)
+    g_print ("Monitoring signals on object %s owned by %s\n", opt_monitor_object_path, opt_monitor_dest);
+  else
+    g_print ("Monitoring signals from all objects owned by %s\n", opt_monitor_dest);
+
+  loop = g_main_loop_new (NULL, FALSE);
+  g_bus_watch_name_on_connection (c,
+                                  opt_monitor_dest,
+                                  G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
+                                  monitor_on_name_appeared,
+                                  monitor_on_name_vanished,
+                                  NULL,
+                                  NULL);
+
+  g_main_loop_run (loop);
+  g_main_loop_unref (loop);
 
   ret = TRUE;
 
  out:
-  if (node != NULL)
-    g_dbus_node_info_unref (node);
-  if (result != NULL)
-    g_variant_unref (result);
   if (c != NULL)
     g_object_unref (c);
   g_option_context_free (o);
@@ -1351,13 +1931,29 @@ main (gint argc, gchar *argv[])
   gboolean request_completion;
   gchar *completion_cur;
   gchar *completion_prev;
+#ifdef G_OS_WIN32
+  gchar *tmp;
+#endif
+
+  setlocale (LC_ALL, "");
+  textdomain (GETTEXT_PACKAGE);
+
+#ifdef G_OS_WIN32
+  tmp = _glib_get_locale_dir ();
+  bindtextdomain (GETTEXT_PACKAGE, tmp);
+  g_free (tmp);
+#else
+  bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
+#endif
+
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
 
   ret = 1;
   completion_cur = NULL;
   completion_prev = NULL;
 
-  g_type_init ();
-
   if (argc < 2)
     {
       usage (&argc, &argv, FALSE);
@@ -1383,6 +1979,16 @@ main (gint argc, gchar *argv[])
         }
       goto out;
     }
+  else if (g_strcmp0 (command, "emit") == 0)
+    {
+      if (handle_emit (&argc,
+                       &argv,
+                       request_completion,
+                       completion_cur,
+                       completion_prev))
+        ret = 0;
+      goto out;
+    }
   else if (g_strcmp0 (command, "call") == 0)
     {
       if (handle_call (&argc,
@@ -1403,6 +2009,16 @@ main (gint argc, gchar *argv[])
         ret = 0;
       goto out;
     }
+  else if (g_strcmp0 (command, "monitor") == 0)
+    {
+      if (handle_monitor (&argc,
+                          &argv,
+                          request_completion,
+                          completion_cur,
+                          completion_prev))
+        ret = 0;
+      goto out;
+    }
   else if (g_strcmp0 (command, "complete") == 0 && argc == 4 && !request_completion)
     {
       const gchar *completion_line;
@@ -1423,7 +2039,7 @@ main (gint argc, gchar *argv[])
       completion_debug ("completion_point=%d", completion_point);
       completion_debug ("----");
       completion_debug (" 0123456789012345678901234567890123456789012345678901234567890123456789");
-      completion_debug ("`%s'", completion_line);
+      completion_debug ("'%s'", completion_line);
       completion_debug (" %*s^",
                          completion_point, "");
       completion_debug ("----");
@@ -1457,8 +2073,8 @@ main (gint argc, gchar *argv[])
             }
         }
 #if 0
-      completion_debug (" cur=`%s'", completion_cur);
-      completion_debug ("prev=`%s'", completion_prev);
+      completion_debug (" cur='%s'", completion_cur);
+      completion_debug ("prev='%s'", completion_prev);
 #endif
 
       argc = completion_argc;
@@ -1472,13 +2088,13 @@ main (gint argc, gchar *argv[])
     {
       if (request_completion)
         {
-          g_print ("help \ncall \nintrospect \n");
+          g_print ("help \nemit \ncall \nintrospect \nmonitor \n");
           ret = 0;
           goto out;
         }
       else
         {
-          g_printerr ("Unknown command `%s'\n", command);
+          g_printerr ("Unknown command '%s'\n", command);
           usage (&argc, &argv, FALSE);
           goto out;
         }