[gnome-builder/wip/chergert/debugger: 46/106] mi2: more client infrastructure



commit 0cae97568826f9736dd77275888bad57c8eebd59
Author: Christian Hergert <chergert redhat com>
Date:   Fri Mar 24 00:04:12 2017 -0700

    mi2: more client infrastructure

 contrib/mi2/Makefile.am         |   12 +++
 contrib/mi2/mi2-client.c        |  172 ++++++++++++++++++++++++++++++++++++---
 contrib/mi2/mi2-client.h        |   94 ++++++++++++++--------
 contrib/mi2/mi2-enums.c.in      |   39 +++++++++
 contrib/mi2/mi2-enums.h.in      |   24 ++++++
 contrib/mi2/mi2-error.h         |    1 +
 contrib/mi2/mi2-event-message.c |   13 +---
 contrib/mi2/mi2-message.c       |   34 ++++++++
 contrib/mi2/mi2-message.h       |    2 +
 contrib/mi2/mi2-util.c          |   81 ++++++++++---------
 contrib/mi2/test-client.c       |   45 +++++++++--
 11 files changed, 418 insertions(+), 99 deletions(-)
---
diff --git a/contrib/mi2/Makefile.am b/contrib/mi2/Makefile.am
index ea739c5..ab7d112 100644
--- a/contrib/mi2/Makefile.am
+++ b/contrib/mi2/Makefile.am
@@ -1,5 +1,6 @@
 CLEANFILES =
 DISTCLEANFILES =
+BUILT_SOURCES =
 
 EXTRA_DIST =              \
        test-stream-1.txt \
@@ -38,6 +39,8 @@ libmi2_glib_la_public_sources =          \
 libmi2_glib_la_SOURCES =                 \
        $(libmi2_glib_la_public_sources) \
        mi2-glib.h                       \
+       mi2-enums.c                      \
+       mi2-enums.h                      \
        $(NULL)
 
 libmi2_glib_la_CFLAGS =                     \
@@ -50,6 +53,15 @@ libmi2_glib_la_CFLAGS =                     \
 libmi2_glib_la_LIBADD = $(MI2_LIBS)
 libmi2_glib_la_LDFLAGS = $(OPTIMIZE_LDFLAGS)
 
+glib_enum_headers =  \
+       mi2-error.h \
+       $(NULL)
+
+glib_enum_h = mi2-enums.h
+glib_enum_c = mi2-enums.c
+
+include $(top_srcdir)/build/autotools/Makefile.am.enums
+
 if HAVE_INTROSPECTION
 -include $(INTROSPECTION_MAKEFILE)
 
diff --git a/contrib/mi2/mi2-client.c b/contrib/mi2/mi2-client.c
index 8807109..5783920 100644
--- a/contrib/mi2/mi2-client.c
+++ b/contrib/mi2/mi2-client.c
@@ -21,6 +21,7 @@
 #include "mi2-client.h"
 #include "mi2-command-message.h"
 #include "mi2-console-message.h"
+#include "mi2-enums.h"
 #include "mi2-error.h"
 #include "mi2-event-message.h"
 #include "mi2-input-stream.h"
@@ -49,6 +50,7 @@ enum {
   BREAKPOINT_REMOVED,
   EVENT,
   LOG,
+  STOPPED,
   N_SIGNALS
 };
 
@@ -94,6 +96,29 @@ mi2_client_set_io_stream (Mi2Client *self,
 }
 
 static void
+mi2_client_real_event (Mi2Client       *self,
+                       Mi2EventMessage *message)
+{
+  const gchar *name;
+
+  g_assert (MI2_IS_CLIENT (self));
+  g_assert (MI2_IS_EVENT_MESSAGE (message));
+
+  name = mi2_event_message_get_name (message);
+
+  if (g_strcmp0 (name, "stopped") == 0)
+    {
+      const gchar *reasonstr;
+      Mi2StopReason reason;
+
+      reasonstr = mi2_message_get_param_string (MI2_MESSAGE (message), "reason");
+      reason = mi2_stop_reason_parse (reasonstr);
+
+      g_signal_emit (self, signals [STOPPED], 0, reason, message);
+    }
+}
+
+static void
 mi2_client_finalize (GObject *object)
 {
   Mi2Client *self = (Mi2Client *)object;
@@ -155,6 +180,8 @@ mi2_client_class_init (Mi2ClientClass *klass)
   object_class->get_property = mi2_client_get_property;
   object_class->set_property = mi2_client_set_property;
 
+  klass->event = mi2_client_real_event;
+
   properties [PROP_IO_STREAM] =
     g_param_spec_object ("io-stream",
                          "IO Stream",
@@ -195,6 +222,14 @@ mi2_client_class_init (Mi2ClientClass *klass)
                   G_STRUCT_OFFSET (Mi2ClientClass, log),
                   NULL, NULL, NULL,
                   G_TYPE_NONE, 1, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+  signals [STOPPED] =
+    g_signal_new ("stopped",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (Mi2ClientClass, stopped),
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 2, MI2_TYPE_STOP_REASON, MI2_TYPE_MESSAGE);
 }
 
 static void
@@ -482,7 +517,6 @@ mi2_client_insert_breakpoint_async (Mi2Client           *self,
                                     GAsyncReadyCallback  callback,
                                     gpointer             user_data)
 {
-  g_autoptr(Mi2CommandMessage) command = NULL;
   g_autoptr(GTask) task = NULL;
   g_autoptr(GString) str = NULL;
   const gchar *address;
@@ -521,10 +555,6 @@ mi2_client_insert_breakpoint_async (Mi2Client           *self,
   if (address)
     g_string_append_printf (str, " %s", address);
 
-  command = g_object_new (MI2_TYPE_COMMAND_MESSAGE,
-                          "command", str,
-                          NULL);
-
   mi2_client_exec_async (self,
                          str->str,
                          cancellable,
@@ -588,7 +618,6 @@ mi2_client_remove_breakpoint_async (Mi2Client           *self,
                                     gpointer             user_data)
 {
   g_autoptr(GTask) task = NULL;
-  g_autoptr(Mi2CommandMessage) command = NULL;
   g_autofree gchar *str = NULL;
 
   g_return_if_fail (MI2_IS_CLIENT (self));
@@ -600,10 +629,6 @@ mi2_client_remove_breakpoint_async (Mi2Client           *self,
 
   str = g_strdup_printf ("-break-delete %d", breakpoint_id);
 
-  command = g_object_new (MI2_TYPE_COMMAND_MESSAGE,
-                          "command", str,
-                          NULL);
-
   mi2_client_exec_async (self,
                          str,
                          cancellable,
@@ -622,6 +647,89 @@ mi2_client_remove_breakpoint_finish (Mi2Client     *self,
   return g_task_propagate_boolean (G_TASK (result), error);
 }
 
+static void
+mi2_client_exec_cb (GObject      *object,
+                    GAsyncResult *result,
+                    gpointer      user_data)
+{
+  Mi2Client *self = (Mi2Client *)object;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GTask) task = user_data;
+
+  g_assert (MI2_IS_CLIENT (self));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  if (!mi2_client_exec_finish (self, result, NULL, &error))
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    g_task_return_boolean (task, TRUE);
+}
+
+void
+mi2_client_run_async (Mi2Client           *self,
+                      GCancellable        *cancellable,
+                      GAsyncReadyCallback  callback,
+                      gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+
+  g_return_if_fail (MI2_IS_CLIENT (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, mi2_client_run_async);
+
+  mi2_client_exec_async (self,
+                         "-exec-run --start",
+                         cancellable,
+                         mi2_client_exec_cb,
+                         g_steal_pointer (&task));
+}
+
+gboolean
+mi2_client_run_finish (Mi2Client     *self,
+                       GAsyncResult  *result,
+                       GError       **error)
+{
+  g_return_val_if_fail (MI2_IS_CLIENT (self), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+void
+mi2_client_continue_async (Mi2Client           *self,
+                           gboolean             reverse,
+                           GCancellable        *cancellable,
+                           GAsyncReadyCallback  callback,
+                           gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+
+  g_return_if_fail (MI2_IS_CLIENT (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, mi2_client_continue_async);
+
+  mi2_client_exec_async (self,
+                         reverse ? "-exec-continue --reverse" : "-exec-continue",
+                         cancellable,
+                         mi2_client_exec_cb,
+                         g_steal_pointer (&task));
+}
+
+gboolean
+mi2_client_continue_finish (Mi2Client     *self,
+                            GAsyncResult  *result,
+                            GError       **error)
+{
+  g_return_val_if_fail (MI2_IS_CLIENT (self), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
 #if 0
 void
 mi2_client_async (Mi2Client           *self,
@@ -629,6 +737,12 @@ mi2_client_async (Mi2Client           *self,
                   GAsyncReadyCallback  callback,
                   gpointer             user_data)
 {
+  g_autoptr(GTask) task = NULL;
+
+  g_return_if_fail (MI2_IS_CLIENT (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
 }
 
 gboolean
@@ -642,3 +756,41 @@ mi2_client_finish (Mi2Client     *self,
   return g_task_propagate_boolean (G_TASK (result), error);
 }
 #endif
+
+GType
+mi2_stop_reason_get_type (void)
+{
+  static GType type_id;
+
+  if (g_once_init_enter (&type_id))
+    {
+      GType _type_id;
+      static const GEnumValue values[] = {
+        { MI2_STOP_UNKNOWN, "MI2_STOP_UNKNOWN", "unknown" },
+        { MI2_STOP_EXITED_NORMALLY, "MI2_STOP_EXITED_NORMALLY", "exited-normally" },
+        { MI2_STOP_BREAKPOINT_HIT, "MI2_STOP_BREAKPOINT_HIT", "breakpoint-hit" },
+        { 0 }
+      };
+
+      _type_id = g_enum_register_static ("Mi2StopReason", values);
+      g_once_init_leave (&type_id, _type_id);
+    }
+
+  return type_id;
+}
+
+Mi2StopReason
+mi2_stop_reason_parse (const gchar *reason)
+{
+  GEnumClass *klass;
+  GEnumValue *value = NULL;
+
+  if (reason)
+    {
+      klass = g_type_class_ref (MI2_TYPE_STOP_REASON);
+      value = g_enum_get_value_by_nick (klass, reason);
+      g_type_class_unref (klass);
+    }
+
+  return value ? value->value : 0;
+}
diff --git a/contrib/mi2/mi2-client.h b/contrib/mi2/mi2-client.h
index 56de564..818b960 100644
--- a/contrib/mi2/mi2-client.h
+++ b/contrib/mi2/mi2-client.h
@@ -28,10 +28,18 @@ G_BEGIN_DECLS
 #include "mi2-event-message.h"
 #include "mi2-reply-message.h"
 
-#define MI2_TYPE_CLIENT (mi2_client_get_type())
+#define MI2_TYPE_CLIENT      (mi2_client_get_type())
+#define MI2_TYPE_STOP_REASON (mi2_stop_reason_get_type())
 
 G_DECLARE_DERIVABLE_TYPE (Mi2Client, mi2_client, MI2, CLIENT, GObject)
 
+typedef enum
+{
+  MI2_STOP_UNKNOWN,
+  MI2_STOP_EXITED_NORMALLY,
+  MI2_STOP_BREAKPOINT_HIT,
+} Mi2StopReason;
+
 struct _Mi2ClientClass
 {
   GObjectClass parent_instance;
@@ -40,10 +48,13 @@ struct _Mi2ClientClass
                                const gchar     *log);
   void (*event)               (Mi2Client       *self,
                                Mi2EventMessage *message);
-  void (*breakpoint_inserted) (Mi2Client     *client,
-                               Mi2Breakpoint *breakpoint);
-  void (*breakpoint_removed)  (Mi2Client     *client,
-                               gint           breakpoint_id);
+  void (*breakpoint_inserted) (Mi2Client       *client,
+                               Mi2Breakpoint   *breakpoint);
+  void (*breakpoint_removed)  (Mi2Client       *client,
+                               gint             breakpoint_id);
+  void (*stopped)             (Mi2Client       *self,
+                               Mi2StopReason    reason,
+                               Mi2Message      *message);
 
   gpointer _reserved1;
   gpointer _reserved2;
@@ -63,34 +74,51 @@ struct _Mi2ClientClass
   gpointer _reserved16;
 };
 
-Mi2Client *mi2_client_new                      (GIOStream            *stream);
-void       mi2_client_exec_async               (Mi2Client            *self,
-                                                const gchar          *command,
-                                                GCancellable         *cancellable,
-                                                GAsyncReadyCallback   callback,
-                                                gpointer              user_data);
-gboolean   mi2_client_exec_finish              (Mi2Client            *self,
-                                                GAsyncResult         *result,
-                                                Mi2ReplyMessage     **reply,
-                                                GError              **error);
-void       mi2_client_start_listening          (Mi2Client            *self);
-void       mi2_client_stop_listening           (Mi2Client            *self);
-void       mi2_client_insert_breakpoint_async  (Mi2Client            *self,
-                                                Mi2Breakpoint        *breakpoint,
-                                                GCancellable         *cancellable,
-                                                GAsyncReadyCallback   callback,
-                                                gpointer              user_data);
-gint       mi2_client_insert_breakpoint_finish (Mi2Client            *self,
-                                                GAsyncResult         *result,
-                                                GError              **error);
-void       mi2_client_remove_breakpoint_async  (Mi2Client            *self,
-                                                gint                  breakpoint_id,
-                                                GCancellable         *cancellable,
-                                                GAsyncReadyCallback   callback,
-                                                gpointer              user_data);
-gboolean   mi2_client_remove_breakpoint_finish (Mi2Client            *self,
-                                                GAsyncResult         *result,
-                                                GError              **error);
+GType          mi2_stop_reason_get_type                   (void);
+Mi2StopReason  mi2_stop_reason_parse                      (const gchar          *reason);
+Mi2Client     *mi2_client_new                             (GIOStream            *stream);
+void           mi2_client_exec_async                      (Mi2Client            *self,
+                                                           const gchar          *command,
+                                                           GCancellable         *cancellable,
+                                                           GAsyncReadyCallback   callback,
+                                                           gpointer              user_data);
+gboolean       mi2_client_exec_finish                     (Mi2Client            *self,
+                                                           GAsyncResult         *result,
+                                                           Mi2ReplyMessage     **reply,
+                                                           GError              **error);
+void           mi2_client_start_listening                 (Mi2Client            *self);
+void           mi2_client_stop_listening                  (Mi2Client            *self);
+void           mi2_client_continue_async                  (Mi2Client            *self,
+                                                           gboolean              reverse,
+                                                           GCancellable         *cancellable,
+                                                           GAsyncReadyCallback   callback,
+                                                           gpointer              user_data);
+gboolean       mi2_client_continue_finish                 (Mi2Client            *self,
+                                                           GAsyncResult         *result,
+                                                           GError              **error);
+void           mi2_client_run_async                       (Mi2Client            *self,
+                                                           GCancellable         *cancellable,
+                                                           GAsyncReadyCallback   callback,
+                                                           gpointer              user_data);
+gboolean       mi2_client_run_finish                      (Mi2Client            *self,
+                                                           GAsyncResult         *result,
+                                                           GError              **error);
+void           mi2_client_insert_breakpoint_async         (Mi2Client            *self,
+                                                           Mi2Breakpoint        *breakpoint,
+                                                           GCancellable         *cancellable,
+                                                           GAsyncReadyCallback   callback,
+                                                           gpointer              user_data);
+gint           mi2_client_insert_breakpoint_finish        (Mi2Client            *self,
+                                                           GAsyncResult         *result,
+                                                           GError              **error);
+void           mi2_client_remove_breakpoint_async         (Mi2Client            *self,
+                                                           gint                  breakpoint_id,
+                                                           GCancellable         *cancellable,
+                                                           GAsyncReadyCallback   callback,
+                                                           gpointer              user_data);
+gboolean       mi2_client_remove_breakpoint_finish        (Mi2Client            *self,
+                                                           GAsyncResult         *result,
+                                                           GError              **error);
 
 
 G_END_DECLS
diff --git a/contrib/mi2/mi2-enums.c.in b/contrib/mi2/mi2-enums.c.in
new file mode 100644
index 0000000..72d5d5c
--- /dev/null
+++ b/contrib/mi2/mi2-enums.c.in
@@ -0,0 +1,39 @@
+/*** BEGIN file-header ***/
+
+#include "config.h"
+
+#include "mi2-enums.h"
+#include "mi2-error.h"
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+@enum_name@_get_type (void)
+{
+    static GType etype = 0;
+    if (G_UNLIKELY(etype == 0)) {
+        static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+            { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+            { 0, NULL, NULL }
+        };
+        etype = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
+    }
+    return etype;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+
+/*** END file-tail ***/
diff --git a/contrib/mi2/mi2-enums.h.in b/contrib/mi2/mi2-enums.h.in
new file mode 100644
index 0000000..5b7a196
--- /dev/null
+++ b/contrib/mi2/mi2-enums.h.in
@@ -0,0 +1,24 @@
+/*** BEGIN file-header ***/
+#ifndef __MI2_ENUMS_H__
+#define __MI2_ENUMS_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void);
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* __MI2_ENUMS_H__ */
+/*** END file-tail ***/
diff --git a/contrib/mi2/mi2-error.h b/contrib/mi2/mi2-error.h
index 8975fd2..0bd46dc 100644
--- a/contrib/mi2/mi2-error.h
+++ b/contrib/mi2/mi2-error.h
@@ -29,6 +29,7 @@ typedef enum
 {
   MI2_ERROR_UNKNOWN_ERROR,
   MI2_ERROR_EXEC_PENDING,
+  MI2_ERROR_INVALID_DATA,
 } Mi2Error;
 
 GQuark mi2_error_quark (void);
diff --git a/contrib/mi2/mi2-event-message.c b/contrib/mi2/mi2-event-message.c
index e909a4a..079117e 100644
--- a/contrib/mi2/mi2-event-message.c
+++ b/contrib/mi2/mi2-event-message.c
@@ -125,18 +125,7 @@ mi2_event_message_new_from_string (const gchar *line)
   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);
-        }
+      mi2_message_parse_params (MI2_MESSAGE (ret), line);
     }
 
   return MI2_MESSAGE (ret);
diff --git a/contrib/mi2/mi2-message.c b/contrib/mi2/mi2-message.c
index 4c9c3b2..9fd873d 100644
--- a/contrib/mi2/mi2-message.c
+++ b/contrib/mi2/mi2-message.c
@@ -22,9 +22,11 @@
 
 #include "mi2-command-message.h"
 #include "mi2-console-message.h"
+#include "mi2-error.h"
 #include "mi2-event-message.h"
 #include "mi2-info-message.h"
 #include "mi2-reply-message.h"
+#include "mi2-util.h"
 
 typedef struct
 {
@@ -78,11 +80,14 @@ mi2_message_parse (const gchar  *line,
                    gsize         len,
                    GError      **error)
 {
+  const gchar *begin = line;
   Mi2Message *ret = NULL;
 
   g_return_val_if_fail (line != NULL, NULL);
   g_return_val_if_fail (len > 0, NULL);
 
+  g_print (">>> %s\n", line);
+
   switch (line[0])
     {
     case '~':
@@ -110,9 +115,38 @@ mi2_message_parse (const gchar  *line,
       break;
     }
 
+  if (ret == NULL)
+    g_set_error (error,
+                 MI2_ERROR,
+                 MI2_ERROR_INVALID_DATA,
+                 "Failed to parse: %s", begin);
+
   return ret;
 }
 
+void
+mi2_message_parse_params (Mi2Message  *self,
+                          const gchar *line)
+{
+  g_autoptr(GVariant) params = NULL;
+
+  g_return_if_fail (MI2_IS_MESSAGE (self));
+  g_return_if_fail (line != NULL);
+
+  params = mi2_util_parse_record (line, NULL);
+
+  if (params)
+    {
+      GVariantIter iter;
+      const gchar *key;
+      GVariant *value;
+
+      g_variant_iter_init (&iter, params);
+      while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
+        mi2_message_set_param (self, key, value);
+    }
+}
+
 /**
  * mi2_message_serialize:
  * @self: An #Mi2Message
diff --git a/contrib/mi2/mi2-message.h b/contrib/mi2/mi2-message.h
index dece0b7..58ffca0 100644
--- a/contrib/mi2/mi2-message.h
+++ b/contrib/mi2/mi2-message.h
@@ -48,6 +48,8 @@ Mi2Message   *mi2_message_parse            (const gchar  *line,
                                             GError      **error);
 GBytes       *mi2_message_serialize        (Mi2Message   *self);
 const gchar **mi2_message_get_params       (Mi2Message   *self);
+void          mi2_message_parse_params     (Mi2Message   *self,
+                                            const gchar  *line);
 GVariant     *mi2_message_get_param        (Mi2Message   *self,
                                             const gchar  *param);
 void          mi2_message_set_param        (Mi2Message   *self,
diff --git a/contrib/mi2/mi2-util.c b/contrib/mi2/mi2-util.c
index f5cefc6..299720e 100644
--- a/contrib/mi2/mi2-util.c
+++ b/contrib/mi2/mi2-util.c
@@ -25,9 +25,7 @@ mi2_util_parse_string (const gchar  *line,
   g_autoptr(GString) str = NULL;
 
   g_return_val_if_fail (line != NULL, NULL);
-
-  if (*line != '"')
-    goto failure;
+  g_return_val_if_fail (line[0] == '"', NULL);
 
   str = g_string_new (NULL);
 
@@ -76,6 +74,8 @@ mi2_util_parse_string (const gchar  *line,
   return g_string_free (g_steal_pointer (&str), FALSE);
 
 failure:
+  g_warning ("Failed to parse string");
+
   if (endptr)
     *endptr = NULL;
 
@@ -108,20 +108,20 @@ mi2_util_parse_word (const gchar  *line,
 }
 
 GVariant *
-mi2_util_parse_record (const gchar *line,
+mi2_util_parse_record (const gchar  *line,
                        const gchar **endptr)
 {
   GVariantDict dict;
 
   g_return_val_if_fail (line != NULL, NULL);
-  g_return_val_if_fail (*line == '{', NULL);
 
   g_variant_dict_init (&dict, NULL);
 
-  /* move past { */
-  line++;
+  /* move past { if we aren't starting from inside {} */
+  if (*line == '{')
+    line++;
 
-  while (*line != '}')
+  while (*line && *line != '}')
     {
       g_autofree gchar *key = NULL;
 
@@ -168,9 +168,8 @@ mi2_util_parse_record (const gchar *line,
         line++;
     }
 
-  g_assert (*line == '}');
-
-  line++;
+  if (*line == '}')
+    line++;
 
   if (endptr)
     *endptr = line;
@@ -178,6 +177,7 @@ mi2_util_parse_record (const gchar *line,
   return g_variant_ref_sink (g_variant_dict_end (&dict));
 
 failure:
+  g_warning ("Failed to parse record");
   g_variant_dict_clear (&dict);
   if (endptr)
     *endptr = NULL;
@@ -197,43 +197,49 @@ mi2_util_parse_list (const gchar  *line,
   line++;
 
   g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
-  g_variant_builder_open (&builder, G_VARIANT_TYPE ("v"));
 
-  while (*line != ']')
+  if (*line != ']')
     {
-      if (*line == '"')
+      g_variant_builder_open (&builder, G_VARIANT_TYPE ("v"));
+
+      while (*line != ']')
         {
-          g_autofree gchar *value = NULL;
+          if (*line == '"')
+            {
+              g_autofree gchar *value = NULL;
 
-          if (!(value = mi2_util_parse_string (line, &line)))
-            goto failure;
+              if (!(value = mi2_util_parse_string (line, &line)))
+                goto failure;
 
-          g_variant_builder_add (&builder, "s", value);
-        }
-      else if (*line == '{')
-        {
-          g_autoptr(GVariant) v = NULL;
+              g_variant_builder_add (&builder, "s", value);
+            }
+          else if (*line == '{')
+            {
+              g_autoptr(GVariant) v = NULL;
 
-          if (!(v = mi2_util_parse_record (line, &line)))
-            goto failure;
+              if (!(v = mi2_util_parse_record (line, &line)))
+                goto failure;
 
-          g_variant_builder_add_value (&builder, v);
-        }
-      else if (*line == '[')
-        {
-          g_autoptr(GVariant) ar = NULL;
+              g_variant_builder_add_value (&builder, v);
+            }
+          else if (*line == '[')
+            {
+              g_autoptr(GVariant) ar = NULL;
 
-          if (!(ar = mi2_util_parse_list (line, &line)))
+              if (!(ar = mi2_util_parse_list (line, &line)))
+                goto failure;
+
+              g_variant_builder_add_value (&builder, ar);
+            }
+          else
             goto failure;
 
-          g_variant_builder_add_value (&builder, ar);
-        }
-      else
-        goto failure;
 
+          if (*line == ',')
+            line++;
+        }
 
-      if (*line == ',')
-        line++;
+      g_variant_builder_close (&builder);
     }
 
   g_assert (*line == ']');
@@ -243,11 +249,10 @@ mi2_util_parse_list (const gchar  *line,
   if (endptr)
     *endptr = line;
 
-  g_variant_builder_close (&builder);
-
   return g_variant_ref_sink (g_variant_builder_end (&builder));
 
 failure:
+  g_warning ("Failed to parse list");
   g_variant_builder_clear (&builder);
   if (endptr)
     *endptr = NULL;
diff --git a/contrib/mi2/test-client.c b/contrib/mi2/test-client.c
index b5408de..8411c2f 100644
--- a/contrib/mi2/test-client.c
+++ b/contrib/mi2/test-client.c
@@ -20,6 +20,7 @@
 #include "mi2-error.h"
 
 static GMainLoop *main_loop;
+static gint g_breakpoint_id;
 
 static GIOStream *
 create_io_stream_to_gdb (void)
@@ -128,17 +129,48 @@ stack_info_frame_cb (GObject      *object,
 }
 
 static void
+on_stopped (Mi2Client     *client,
+            Mi2StopReason  reason,
+            Mi2Message    *message,
+            gpointer       user_data)
+{
+  g_assert (MI2_IS_CLIENT (client));
+  g_assert (MI2_IS_MESSAGE (message));
+
+
+  if (reason == MI2_STOP_BREAKPOINT_HIT)
+    mi2_client_continue_async (client, FALSE, NULL, NULL, NULL);
+  else
+    mi2_client_remove_breakpoint_async (client,
+                                        g_breakpoint_id,
+                                        NULL,
+                                        remove_breakpoint_cb,
+                                        NULL);
+}
+
+static void
+run_cb (GObject      *object,
+        GAsyncResult *result,
+        gpointer      user_data)
+{
+  Mi2Client *client = (Mi2Client *)object;
+  g_autoptr(GError) error = NULL;
+  gboolean r;
+
+  r = mi2_client_run_finish (client, result, &error);
+  g_assert_no_error (error);
+  g_assert_cmpint (r, ==, TRUE);
+}
+
+static void
 on_breakpoint_inserted (Mi2Client     *client,
                         Mi2Breakpoint *breakpoint,
                         gpointer       user_data)
 {
-  g_print ("breakpoint added: %d\n", mi2_breakpoint_get_id (breakpoint));
+  g_breakpoint_id = mi2_breakpoint_get_id (breakpoint);
+  g_print ("breakpoint added: %d\n", g_breakpoint_id);
 
-  mi2_client_remove_breakpoint_async (client,
-                                      mi2_breakpoint_get_id (breakpoint),
-                                      NULL,
-                                      remove_breakpoint_cb,
-                                      NULL);
+  mi2_client_run_async (client, NULL, run_cb, NULL);
 }
 
 static void
@@ -165,6 +197,7 @@ main (gint argc,
   g_signal_connect (client, "log", G_CALLBACK (log_handler), NULL);
   g_signal_connect (client, "event::thread-group-added", G_CALLBACK (thread_group_added), NULL);
   g_signal_connect (client, "event", G_CALLBACK (event), NULL);
+  g_signal_connect (client, "stopped", G_CALLBACK (on_stopped), NULL);
   g_signal_connect (client, "breakpoint-inserted", G_CALLBACK (on_breakpoint_inserted), NULL);
   g_signal_connect (client, "breakpoint-removed", G_CALLBACK (on_breakpoint_removed), NULL);
 


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