[gnome-builder/wip/chergert/debugger: 15/46] mi2: implement command and replies



commit 5c765ecbb0440bd263da71704f3856c775edff25
Author: Christian Hergert <chergert redhat com>
Date:   Thu Mar 23 18:08:23 2017 -0700

    mi2: implement command and replies

 contrib/mi2/Makefile.am           |    4 +
 contrib/mi2/mi2-client.c          |   41 ++++++++-
 contrib/mi2/mi2-command-message.c |   28 ++++++
 contrib/mi2/mi2-error.c           |   25 +++++
 contrib/mi2/mi2-error.h           |   38 ++++++++
 contrib/mi2/mi2-event-message.c   |   44 +---------
 contrib/mi2/mi2-event-message.h   |   14 +--
 contrib/mi2/mi2-message.c         |  101 ++++++++++++--------
 contrib/mi2/mi2-message.h         |   14 ++-
 contrib/mi2/mi2-reply-message.c   |  188 +++++++++++++++++++++++++++++++++++++
 contrib/mi2/mi2-reply-message.h   |   39 ++++++++
 contrib/mi2/test-client.c         |   29 ++++++-
 12 files changed, 463 insertions(+), 102 deletions(-)
---
diff --git a/contrib/mi2/Makefile.am b/contrib/mi2/Makefile.am
index 059878f..c7e3b3b 100644
--- a/contrib/mi2/Makefile.am
+++ b/contrib/mi2/Makefile.am
@@ -17,12 +17,16 @@ libmi2_glib_la_public_sources =          \
        mi2-console-message.h            \
        mi2-event-message.c              \
        mi2-event-message.h              \
+       mi2-error.c                      \
+       mi2-error.h                      \
        mi2-info-message.c               \
        mi2-info-message.h               \
        mi2-input-stream.c               \
        mi2-input-stream.h               \
        mi2-message.c                    \
        mi2-message.h                    \
+       mi2-reply-message.c              \
+       mi2-reply-message.h              \
        mi2-output-stream.c              \
        mi2-output-stream.h              \
        mi2-util.c                       \
diff --git a/contrib/mi2/mi2-client.c b/contrib/mi2/mi2-client.c
index b6a69c5..184fe6a 100644
--- a/contrib/mi2/mi2-client.c
+++ b/contrib/mi2/mi2-client.c
@@ -21,9 +21,11 @@
 #include "mi2-client.h"
 #include "mi2-command-message.h"
 #include "mi2-console-message.h"
+#include "mi2-error.h"
 #include "mi2-event-message.h"
 #include "mi2-input-stream.h"
 #include "mi2-output-stream.h"
+#include "mi2-reply-message.h"
 
 typedef struct
 {
@@ -31,8 +33,9 @@ typedef struct
   Mi2InputStream  *input_stream;
   Mi2OutputStream *output_stream;
   GCancellable    *read_loop_cancellable;
+  GTask           *current_exec;
 
-  guint is_listening : 1;
+  guint            is_listening : 1;
 } Mi2ClientPrivate;
 
 enum {
@@ -204,8 +207,13 @@ mi2_client_exec_write_message_cb (GObject      *object,
 
   if (!mi2_output_stream_write_message_finish (stream, result, &error))
     g_task_return_error (task, g_steal_pointer (&error));
-  else
-    g_task_return_boolean (task, TRUE);
+
+  /*
+   * Do not successfully complete request here.
+   *
+   * Successful completion of the task must come from a reply
+   * sent to us by the peer looking something like ^running.
+   */
 }
 
 void
@@ -232,10 +240,21 @@ mi2_client_exec_async (Mi2Client           *self,
       return;
     }
 
+  if (priv->current_exec != NULL)
+    {
+      g_task_return_new_error (task,
+                               MI2_ERROR,
+                               MI2_ERROR_EXEC_PENDING,
+                               "An operation is already pending");
+      return;
+    }
+
   message = g_object_new (MI2_TYPE_COMMAND_MESSAGE,
                           "command", command,
                           NULL);
 
+  priv->current_exec = g_object_ref (task);
+
   mi2_output_stream_write_message_async (priv->output_stream,
                                          message,
                                          cancellable,
@@ -258,6 +277,8 @@ static void
 mi2_client_dispatch (Mi2Client  *self,
                      Mi2Message *message)
 {
+  Mi2ClientPrivate *priv = mi2_client_get_instance_private (self);
+
   g_return_if_fail (MI2_IS_CLIENT (self));
   g_return_if_fail (MI2_IS_MESSAGE (message));
 
@@ -275,6 +296,20 @@ mi2_client_dispatch (Mi2Client  *self,
 
       g_signal_emit (self, signals [EVENT], detail, message);
     }
+  else if (MI2_IS_REPLY_MESSAGE (message))
+    {
+      g_autoptr(GTask) task = g_steal_pointer (&priv->current_exec);
+
+      if (task != NULL)
+        {
+          g_autoptr(GError) error = NULL;
+
+          if (mi2_reply_message_check_error (MI2_REPLY_MESSAGE (message), &error))
+            g_task_return_error (task, g_steal_pointer (&error));
+          else
+            g_task_return_boolean (task, TRUE);
+        }
+    }
   else
     g_print ("Got message of type %s\n", G_OBJECT_TYPE_NAME (message));
 }
diff --git a/contrib/mi2/mi2-command-message.c b/contrib/mi2/mi2-command-message.c
index 2f01ce9..25b73cd 100644
--- a/contrib/mi2/mi2-command-message.c
+++ b/contrib/mi2/mi2-command-message.c
@@ -39,6 +39,31 @@ G_DEFINE_TYPE (Mi2CommandMessage, mi2_command_message, MI2_TYPE_MESSAGE)
 
 static GParamSpec *properties [N_PROPS];
 
+static GBytes *
+mi2_command_message_serialize (Mi2Message *message)
+{
+  Mi2CommandMessage *self = (Mi2CommandMessage *)message;
+  GString *str;
+
+  g_assert (MI2_IS_COMMAND_MESSAGE (self));
+
+  if (!self->command || !*self->command)
+    return NULL;
+
+  str = g_string_new (NULL);
+
+  if (*self->command == '-')
+    g_string_append (str, self->command);
+  else
+    g_string_append_printf (str, "-%s", self->command);
+
+  /* TODO: Params? */
+
+  g_string_append_c (str, '\n');
+
+  return g_string_free_to_bytes (str);
+}
+
 static void
 mi2_command_message_finalize (GObject *object)
 {
@@ -91,11 +116,14 @@ static void
 mi2_command_message_class_init (Mi2CommandMessageClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  Mi2MessageClass *message_class = MI2_MESSAGE_CLASS (klass);
 
   object_class->finalize = mi2_command_message_finalize;
   object_class->get_property = mi2_command_message_get_property;
   object_class->set_property = mi2_command_message_set_property;
 
+  message_class->serialize = mi2_command_message_serialize;
+
   properties [PROP_COMMAND] =
     g_param_spec_string ("command", NULL, NULL, NULL,
                          G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
diff --git a/contrib/mi2/mi2-error.c b/contrib/mi2/mi2-error.c
new file mode 100644
index 0000000..9e81263
--- /dev/null
+++ b/contrib/mi2/mi2-error.c
@@ -0,0 +1,25 @@
+/* mi2-error.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "mi2-error.h"
+
+GQuark
+mi2_error_quark (void)
+{
+  return g_quark_from_static_string ("mi2-error-domain");
+}
diff --git a/contrib/mi2/mi2-error.h b/contrib/mi2/mi2-error.h
new file mode 100644
index 0000000..8975fd2
--- /dev/null
+++ b/contrib/mi2/mi2-error.h
@@ -0,0 +1,38 @@
+/* mi2-error.h
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MI2_ERROR_H
+#define MI2_ERROR_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define MI2_ERROR (mi2_error_quark())
+
+typedef enum
+{
+  MI2_ERROR_UNKNOWN_ERROR,
+  MI2_ERROR_EXEC_PENDING,
+} Mi2Error;
+
+GQuark mi2_error_quark (void);
+
+G_END_DECLS
+
+#endif /* MI2_ERROR_H */
diff --git a/contrib/mi2/mi2-event-message.c b/contrib/mi2/mi2-event-message.c
index a074df1..e909a4a 100644
--- a/contrib/mi2/mi2-event-message.c
+++ b/contrib/mi2/mi2-event-message.c
@@ -24,9 +24,7 @@
 struct _Mi2EventMessage
 {
   Mi2Message  parent_instance;
-
   gchar      *name;
-  GHashTable *params;
 };
 
 enum {
@@ -45,7 +43,6 @@ mi2_event_mesage_finalize (GObject *object)
   Mi2EventMessage *self = (Mi2EventMessage *)object;
 
   g_clear_pointer (&self->name, g_free);
-  g_clear_pointer (&self->params, g_hash_table_unref);
 
   G_OBJECT_CLASS (mi2_event_mesage_parent_class)->finalize (object);
 }
@@ -110,7 +107,6 @@ mi2_event_mesage_class_init (Mi2EventMessageClass *klass)
 static void
 mi2_event_mesage_init (Mi2EventMessage *self)
 {
-  self->params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 }
 
 /**
@@ -139,7 +135,7 @@ mi2_event_message_new_from_string (const gchar *line)
               !(value = mi2_util_parse_string (line, &line)))
             break;
 
-          g_hash_table_insert (ret->params, g_steal_pointer (&key), g_steal_pointer (&value));
+          mi2_message_set_param_string (MI2_MESSAGE (ret), key, value);
         }
     }
 
@@ -165,41 +161,3 @@ mi2_event_message_set_name (Mi2EventMessage *self,
       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_NAME]);
     }
 }
-
-const gchar *
-mi2_event_message_get_param_string (Mi2EventMessage *self,
-                                    const gchar     *name)
-{
-  g_return_val_if_fail (MI2_IS_EVENT_MESSAGE (self), NULL);
-
-  return g_hash_table_lookup (self->params, name);
-}
-
-void
-mi2_event_message_set_param_string (Mi2EventMessage *self,
-                                    const gchar     *name,
-                                    const gchar     *value)
-{
-  g_return_if_fail (MI2_IS_EVENT_MESSAGE (self));
-  g_return_if_fail (name != NULL);
-
-  g_hash_table_insert (self->params, g_strdup (name), g_strdup (value));
-}
-
-/**
- * mi2_event_message_get_params:
- * @self: An #Mi2EventMessage
- *
- * Gets the keys for params that are stored in the message, free the
- * result with g_free() as ownership of the fields is owned by the
- * #Mi2EventMessage.
- *
- * Returns: (transfer container): A %NULL-terminated array of param names.
- */
-const gchar **
-mi2_event_message_get_params (Mi2EventMessage *self)
-{
-  g_return_val_if_fail (MI2_IS_EVENT_MESSAGE (self), NULL);
-
-  return (const gchar **)g_hash_table_get_keys_as_array (self->params, NULL);
-}
diff --git a/contrib/mi2/mi2-event-message.h b/contrib/mi2/mi2-event-message.h
index 8edd7e6..a9b62f5 100644
--- a/contrib/mi2/mi2-event-message.h
+++ b/contrib/mi2/mi2-event-message.h
@@ -27,16 +27,10 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (Mi2EventMessage, mi2_event_mesage, MI2, EVENT_MESSAGE, Mi2Message)
 
-Mi2Message   *mi2_event_message_new_from_string  (const gchar     *line);
-const gchar  *mi2_event_message_get_name         (Mi2EventMessage *self);
-void          mi2_event_message_set_name         (Mi2EventMessage *self,
-                                                  const gchar     *name);
-const gchar **mi2_event_message_get_params       (Mi2EventMessage *self);
-const gchar  *mi2_event_message_get_param_string (Mi2EventMessage *self,
-                                                  const gchar     *name);
-void          mi2_event_message_set_param_string (Mi2EventMessage *self,
-                                                  const gchar     *name,
-                                                  const gchar     *value);
+Mi2Message   *mi2_event_message_new_from_string (const gchar     *line);
+const gchar  *mi2_event_message_get_name        (Mi2EventMessage *self);
+void          mi2_event_message_set_name        (Mi2EventMessage *self,
+                                                 const gchar     *name);
 
 G_END_DECLS
 
diff --git a/contrib/mi2/mi2-message.c b/contrib/mi2/mi2-message.c
index 3485674..5d3ac0c 100644
--- a/contrib/mi2/mi2-message.c
+++ b/contrib/mi2/mi2-message.c
@@ -24,58 +24,24 @@
 #include "mi2-console-message.h"
 #include "mi2-event-message.h"
 #include "mi2-info-message.h"
+#include "mi2-reply-message.h"
 
 typedef struct
 {
-  gpointer dummy;
+  GHashTable *params;
 } Mi2MessagePrivate;
 
-enum {
-  PROP_0,
-  N_PROPS
-};
-
 G_DEFINE_TYPE_WITH_PRIVATE (Mi2Message, mi2_message, G_TYPE_OBJECT)
 
-static GParamSpec *properties [N_PROPS];
-
 static void
 mi2_message_finalize (GObject *object)
 {
   Mi2Message *self = (Mi2Message *)object;
   Mi2MessagePrivate *priv = mi2_message_get_instance_private (self);
 
-  G_OBJECT_CLASS (mi2_message_parent_class)->finalize (object);
-}
-
-static void
-mi2_message_get_property (GObject    *object,
-                          guint       prop_id,
-                          GValue     *value,
-                          GParamSpec *pspec)
-{
-  Mi2Message *self = MI2_MESSAGE (object);
+  g_clear_pointer (&priv->params, g_hash_table_unref);
 
-  switch (prop_id)
-    {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
-
-static void
-mi2_message_set_property (GObject      *object,
-                          guint         prop_id,
-                          const GValue *value,
-                          GParamSpec   *pspec)
-{
-  Mi2Message *self = MI2_MESSAGE (object);
-
-  switch (prop_id)
-    {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
+  G_OBJECT_CLASS (mi2_message_parent_class)->finalize (object);
 }
 
 static void
@@ -84,8 +50,6 @@ mi2_message_class_init (Mi2MessageClass *klass)
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
   object_class->finalize = mi2_message_finalize;
-  object_class->get_property = mi2_message_get_property;
-  object_class->set_property = mi2_message_set_property;
 }
 
 static void
@@ -123,9 +87,12 @@ mi2_message_parse (const gchar  *line,
       ret = mi2_info_message_new_from_string (line);
       break;
 
+    case '^':
+      ret = mi2_reply_message_new_from_string (line);
+      break;
+
     case '=':
     case '*':
-    case '^':
       ret = mi2_event_message_new_from_string (line);
       break;
 
@@ -155,3 +122,55 @@ mi2_message_serialize (Mi2Message *self)
 
   return MI2_MESSAGE_GET_CLASS (self)->serialize (self);
 }
+
+const gchar *
+mi2_message_get_param_string (Mi2Message  *self,
+                              const gchar *name)
+{
+  Mi2MessagePrivate *priv = mi2_message_get_instance_private (self);
+
+  g_return_val_if_fail (MI2_IS_MESSAGE (self), NULL);
+
+  if (priv->params != NULL)
+    return g_hash_table_lookup (priv->params, name);
+
+  return NULL;
+}
+
+void
+mi2_message_set_param_string (Mi2Message  *self,
+                              const gchar *name,
+                              const gchar *value)
+{
+  Mi2MessagePrivate *priv = mi2_message_get_instance_private (self);
+
+  g_return_if_fail (MI2_IS_MESSAGE (self));
+  g_return_if_fail (name != NULL);
+
+  if (priv->params == NULL)
+    priv->params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+  g_hash_table_insert (priv->params, g_strdup (name), g_strdup (value));
+}
+
+/**
+ * mi2_message_get_params:
+ * @self: An #Mi2Message
+ *
+ * Gets the keys for params that are stored in the message, free the
+ * result with g_free() as ownership of the fields is owned by the
+ * #Mi2Message.
+ *
+ * Returns: (transfer container): A %NULL-terminated array of param names.
+ */
+const gchar **
+mi2_message_get_params (Mi2Message *self)
+{
+  Mi2MessagePrivate *priv = mi2_message_get_instance_private (self);
+
+  g_return_val_if_fail (MI2_IS_MESSAGE (self), NULL);
+
+  if (priv->params != NULL)
+    return (const gchar **)g_hash_table_get_keys_as_array (priv->params, NULL);
+
+  return (const gchar **)g_new0 (gchar *, 1);
+}
diff --git a/contrib/mi2/mi2-message.h b/contrib/mi2/mi2-message.h
index 68429eb..261e866 100644
--- a/contrib/mi2/mi2-message.h
+++ b/contrib/mi2/mi2-message.h
@@ -43,10 +43,16 @@ struct _Mi2MessageClass
   gpointer _reserved8;
 };
 
-Mi2Message *mi2_message_parse     (const gchar  *line,
-                                   gsize         len,
-                                   GError      **error);
-GBytes     *mi2_message_serialize (Mi2Message   *self);
+Mi2Message   *mi2_message_parse            (const gchar  *line,
+                                            gsize         len,
+                                            GError      **error);
+GBytes       *mi2_message_serialize        (Mi2Message   *self);
+const gchar **mi2_message_get_params       (Mi2Message   *self);
+const gchar  *mi2_message_get_param_string (Mi2Message   *self,
+                                            const gchar  *name);
+void          mi2_message_set_param_string (Mi2Message   *self,
+                                            const gchar  *name,
+                                            const gchar  *value);
 
 G_END_DECLS
 
diff --git a/contrib/mi2/mi2-reply-message.c b/contrib/mi2/mi2-reply-message.c
new file mode 100644
index 0000000..d941bdd
--- /dev/null
+++ b/contrib/mi2/mi2-reply-message.c
@@ -0,0 +1,188 @@
+/* mi2-reply-mesage.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "mi2-reply-message"
+
+#include "mi2-error.h"
+#include "mi2-reply-message.h"
+#include "mi2-util.h"
+
+struct _Mi2ReplyMessage
+{
+  Mi2Message  parent_instance;
+  gchar      *name;
+};
+
+enum {
+  PROP_0,
+  PROP_NAME,
+  N_PROPS
+};
+
+G_DEFINE_TYPE (Mi2ReplyMessage, mi2_reply_mesage, MI2_TYPE_MESSAGE)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+mi2_reply_mesage_finalize (GObject *object)
+{
+  Mi2ReplyMessage *self = (Mi2ReplyMessage *)object;
+
+  g_clear_pointer (&self->name, g_free);
+
+  G_OBJECT_CLASS (mi2_reply_mesage_parent_class)->finalize (object);
+}
+
+static void
+mi2_reply_mesage_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  Mi2ReplyMessage *self = MI2_REPLY_MESSAGE (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_value_set_string (value, mi2_reply_message_get_name (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+mi2_reply_mesage_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  Mi2ReplyMessage *self = MI2_REPLY_MESSAGE (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      mi2_reply_message_set_name (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+mi2_reply_mesage_class_init (Mi2ReplyMessageClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = mi2_reply_mesage_finalize;
+  object_class->get_property = mi2_reply_mesage_get_property;
+  object_class->set_property = mi2_reply_mesage_set_property;
+
+  properties [PROP_NAME] =
+    g_param_spec_string ("name",
+                         "Name",
+                         "Name",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+mi2_reply_mesage_init (Mi2ReplyMessage *self)
+{
+}
+
+/**
+ * mi2_reply_message_new_from_string:
+ * @line: the string to be parsed
+ *
+ * Returns: (transfer full): An #Mi2Message
+ */
+Mi2Message *
+mi2_reply_message_new_from_string (const gchar *line)
+{
+  Mi2ReplyMessage *ret;
+
+  ret = g_object_new (MI2_TYPE_REPLY_MESSAGE, NULL);
+
+  if (line && *line)
+    {
+      ret->name = mi2_util_parse_word (&line[1], &line);
+
+      while (line != NULL && *line != '\0')
+        {
+          g_autofree gchar *key = NULL;
+          g_autofree gchar *value = NULL;
+
+          if (!(key = mi2_util_parse_word (line, &line)) ||
+              !(value = mi2_util_parse_string (line, &line)))
+            break;
+
+          mi2_message_set_param_string (MI2_MESSAGE (ret), key, value);
+        }
+    }
+
+  return MI2_MESSAGE (ret);
+}
+
+const gchar *
+mi2_reply_message_get_name (Mi2ReplyMessage *self)
+{
+  g_return_val_if_fail (MI2_IS_REPLY_MESSAGE (self), NULL);
+
+  return self->name;
+}
+
+void
+mi2_reply_message_set_name (Mi2ReplyMessage *self,
+                            const gchar     *name)
+{
+  if (name != self->name)
+    {
+      g_free (self->name);
+      self->name = g_strdup (name);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_NAME]);
+    }
+}
+
+gboolean
+mi2_reply_message_check_error (Mi2ReplyMessage  *self,
+                               GError          **error)
+{
+  g_return_val_if_fail (MI2_IS_REPLY_MESSAGE (self), FALSE);
+
+  if (g_strcmp0 (self->name, "error") == 0)
+    {
+      const gchar *msg = mi2_message_get_param_string (MI2_MESSAGE (self), "msg");
+
+      if (msg == NULL || *msg == '\0')
+        msg = "An unknown error occrred";
+
+      g_set_error_literal (error,
+                           MI2_ERROR,
+                           MI2_ERROR_UNKNOWN_ERROR,
+                           msg);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
diff --git a/contrib/mi2/mi2-reply-message.h b/contrib/mi2/mi2-reply-message.h
new file mode 100644
index 0000000..d5c80e0
--- /dev/null
+++ b/contrib/mi2/mi2-reply-message.h
@@ -0,0 +1,39 @@
+/* mi2-reply-mesage.h
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MI2_REPLY_MESAGE_H
+#define MI2_REPLY_MESAGE_H
+
+#include "mi2-message.h"
+
+G_BEGIN_DECLS
+
+#define MI2_TYPE_REPLY_MESSAGE (mi2_reply_mesage_get_type())
+
+G_DECLARE_FINAL_TYPE (Mi2ReplyMessage, mi2_reply_mesage, MI2, REPLY_MESSAGE, Mi2Message)
+
+Mi2Message   *mi2_reply_message_new_from_string (const gchar      *line);
+const gchar  *mi2_reply_message_get_name        (Mi2ReplyMessage  *self);
+void          mi2_reply_message_set_name        (Mi2ReplyMessage  *self,
+                                                 const gchar      *name);
+gboolean      mi2_reply_message_check_error     (Mi2ReplyMessage  *self,
+                                                 GError          **error);
+
+G_END_DECLS
+
+#endif /* MI2_REPLY_MESAGE_H */
diff --git a/contrib/mi2/test-client.c b/contrib/mi2/test-client.c
index 04c933d..045e578 100644
--- a/contrib/mi2/test-client.c
+++ b/contrib/mi2/test-client.c
@@ -17,6 +17,7 @@
  */
 
 #include "mi2-client.h"
+#include "mi2-error.h"
 
 static GMainLoop *main_loop;
 
@@ -46,7 +47,7 @@ static void
 log_handler (Mi2Client   *client,
              const gchar *log)
 {
-  //g_print ("%s", log);
+  g_print ("%s", log);
 }
 
 static void
@@ -64,6 +65,26 @@ event (Mi2Client       *client,
   g_print ("EVENT: %s\n", mi2_event_message_get_name (message));
 }
 
+static void
+stack_info_frame_cb (GObject      *object,
+                     GAsyncResult *result,
+                     gpointer      user_data)
+{
+  Mi2Client *client = (Mi2Client *)object;
+  g_autoptr(GError) error = NULL;
+  gboolean r;
+
+  g_assert (MI2_IS_CLIENT (client));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  r = mi2_client_exec_finish (client, result, &error);
+  g_assert_error (error, MI2_ERROR, MI2_ERROR_UNKNOWN_ERROR);
+  g_assert_cmpstr (error->message, ==, "No registers.");
+  g_assert_cmpint (r, ==, FALSE);
+
+  g_main_loop_quit (main_loop);
+}
+
 gint
 main (gint argc,
       gchar *argv[])
@@ -81,6 +102,12 @@ main (gint argc,
 
   mi2_client_start_listening (client);
 
+  mi2_client_exec_async (client,
+                         "stack-info-frame",
+                         NULL,
+                         stack_info_frame_cb,
+                         NULL);
+
   g_main_loop_run (main_loop);
   g_main_loop_unref (main_loop);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]