[gnome-builder/wip/chergert/debugger: 15/38] mi2: start on mi2 gdb wire protocol library
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/debugger: 15/38] mi2: start on mi2 gdb wire protocol library
- Date: Fri, 24 Mar 2017 11:43:10 +0000 (UTC)
commit 0a8f3f40b4fa9849823eeceb59ebb4d8e7698eed
Author: Christian Hergert <chergert redhat com>
Date: Thu Mar 23 14:02:18 2017 -0700
mi2: start on mi2 gdb wire protocol library
configure.ac | 2 +
contrib/Makefile.am | 1 +
contrib/mi2/Makefile.am | 118 ++++++++++++
contrib/mi2/mi2-client.c | 355 +++++++++++++++++++++++++++++++++++++
contrib/mi2/mi2-client.h | 69 +++++++
contrib/mi2/mi2-command-message.c | 140 +++++++++++++++
contrib/mi2/mi2-command-message.h | 37 ++++
contrib/mi2/mi2-console-message.c | 160 +++++++++++++++++
contrib/mi2/mi2-console-message.h | 37 ++++
contrib/mi2/mi2-event-message.c | 164 +++++++++++++++++
contrib/mi2/mi2-event-message.h | 37 ++++
contrib/mi2/mi2-glib.h | 35 ++++
contrib/mi2/mi2-info-message.c | 142 +++++++++++++++
contrib/mi2/mi2-info-message.h | 37 ++++
contrib/mi2/mi2-input-stream.c | 131 ++++++++++++++
contrib/mi2/mi2-input-stream.h | 57 ++++++
contrib/mi2/mi2-message.c | 157 ++++++++++++++++
contrib/mi2/mi2-message.h | 53 ++++++
contrib/mi2/mi2-output-stream.c | 97 ++++++++++
contrib/mi2/mi2-output-stream.h | 58 ++++++
contrib/mi2/mi2-util.c | 111 ++++++++++++
contrib/mi2/mi2-util.h | 33 ++++
contrib/mi2/test-client.c | 71 ++++++++
contrib/mi2/test-stream-1.txt | 55 ++++++
contrib/mi2/test-stream.c | 132 ++++++++++++++
25 files changed, 2289 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index fd6a2bc..27da1ff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -230,6 +230,7 @@ PKG_CHECK_MODULES(LIBIDE, [gio-2.0 >= glib_required_version
libpeas-1.0 >= peas_required_version
libxml-2.0 >= libxml_required_version
pangoft2 >= pangoft2_required_version])
+PKG_CHECK_MODULES(MI2, [gio-2.0 >= glib_required_version])
PKG_CHECK_MODULES(NAUTILUS, [glib-2.0 >= glib_required_version
gtk+-3.0 >= gtk_required_version])
PKG_CHECK_MODULES(PANEL_GTK,[gtk+-3.0 >= gtk_required_version])
@@ -514,6 +515,7 @@ AC_CONFIG_FILES([
contrib/jsonrpc-glib/Makefile
contrib/jsonrpc-glib/jsonrpc-version.h
contrib/libeditorconfig/Makefile
+ contrib/mi2/Makefile
contrib/nautilus/Makefile
contrib/pnl/Makefile
contrib/pnl/pnl-version.h
diff --git a/contrib/Makefile.am b/contrib/Makefile.am
index 201cc96..f3c697b 100644
--- a/contrib/Makefile.am
+++ b/contrib/Makefile.am
@@ -3,6 +3,7 @@ SUBDIRS = \
gd \
libeditorconfig \
jsonrpc-glib \
+ mi2 \
nautilus \
pnl \
rg \
diff --git a/contrib/mi2/Makefile.am b/contrib/mi2/Makefile.am
new file mode 100644
index 0000000..059878f
--- /dev/null
+++ b/contrib/mi2/Makefile.am
@@ -0,0 +1,118 @@
+CLEANFILES =
+DISTCLEANFILES =
+
+EXTRA_DIST = \
+ test-stream-1.txt \
+ $(NULL)
+
+pkglibdir = $(libdir)/gnome-builder
+pkglib_LTLIBRARIES = libmi2-glib.la
+
+libmi2_glib_la_public_sources = \
+ mi2-client.c \
+ mi2-client.h \
+ mi2-command-message.c \
+ mi2-command-message.h \
+ mi2-console-message.c \
+ mi2-console-message.h \
+ mi2-event-message.c \
+ mi2-event-message.h \
+ mi2-info-message.c \
+ mi2-info-message.h \
+ mi2-input-stream.c \
+ mi2-input-stream.h \
+ mi2-message.c \
+ mi2-message.h \
+ mi2-output-stream.c \
+ mi2-output-stream.h \
+ mi2-util.c \
+ mi2-util.h \
+ $(NULL)
+
+libmi2_glib_la_SOURCES = \
+ $(libmi2_glib_la_public_sources) \
+ mi2-glib.h \
+ $(NULL)
+
+libmi2_glib_la_CFLAGS = \
+ -DMI2_GLIB_COMPILATION \
+ $(DEBUG_CFLAGS) \
+ $(MI2_CFLAGS) \
+ $(OPTIMIZE_CFLAGS) \
+ $(NULL)
+
+libmi2_glib_la_LIBADD = $(MI2_LIBS)
+libmi2_glib_la_LDFLAGS = $(OPTIMIZE_LDFLAGS)
+
+if HAVE_INTROSPECTION
+-include $(INTROSPECTION_MAKEFILE)
+
+INTROSPECTION_GIRS =
+INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) --warn-all
+INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
+
+Mi2-1.0.gir: libmi2-glib.la
+Mi2_1_0_gir_INCLUDES = Gio-2.0
+Mi2_1_0_gir_CFLAGS = $(libmi2_glib_la_CFLAGS)
+Mi2_1_0_gir_LIBS = libmi2-glib.la
+Mi2_1_0_gir_FILES = $(libmi2_glib_la_public_sources)
+Mi2_1_0_gir_SCANNERFLAGS = \
+ --c-include="mi2-glib.h" \
+ -n Mi2 \
+ $(NULL)
+INTROSPECTION_GIRS += Mi2-1.0.gir
+
+girdir = $(datadir)/gnome-builder/gir-1.0
+dist_gir_DATA = $(INTROSPECTION_GIRS)
+
+typelibdir = $(pkglibdir)/girepository-1.0
+typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
+
+CLEANFILES += $(dist_gir_DATA) $(typelib_DATA)
+endif
+
+if ENABLE_VAPIGEN
+-include $(VAPIGEN_MAKEFILE)
+
+mi2-glib.vapi: Mi2-1.0.gir
+
+VAPIGEN_VAPIS = mi2-glib.vapi
+
+mi2_glib_vapi_DEPS = gio-2.0 json-glib-1.0
+mi2_glib_vapi_METADATADIRS = $(srcdir)
+mi2_glib_vapi_FILES = Mi2-1.0.gir
+
+mi2-glib.deps: Makefile
+ $(AM_V_GEN) echo $(libmi2_glib_vapi_DEPS) | tr ' ' '\n' > $@
+
+vapidir = $(datadir)/gnome-builder/vapi
+vapi_DATA = $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
+
+EXTRA_DIST += mi2-glib.deps
+
+DISTCLEANFILES += $(vapi_DATA)
+endif
+
+noinst_PROGRAMS =
+TESTS =
+
+tests_cflags = \
+ -DTEST_DATA_DIR="\"$(abs_srcdir)\"" \
+ $(MI2_CFLAGS) \
+ $(NULL)
+
+tests_libs = \
+ $(MI2_LIBS) \
+ libmi2-glib.la \
+ $(NULL)
+
+noinst_PROGRAMS += test-stream
+TESTS += test-stream
+test_stream_CFLAGS = $(tests_cflags)
+test_stream_LDADD = $(tests_libs)
+
+noinst_PROGRAMS += test-client
+test_client_CFLAGS = $(tests_cflags)
+test_client_LDADD = $(tests_libs)
+
+-include $(top_srcdir)/git.mk
diff --git a/contrib/mi2/mi2-client.c b/contrib/mi2/mi2-client.c
new file mode 100644
index 0000000..d89f9aa
--- /dev/null
+++ b/contrib/mi2/mi2-client.c
@@ -0,0 +1,355 @@
+/* mi2-client.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-client"
+
+#include "mi2-client.h"
+#include "mi2-command-message.h"
+#include "mi2-console-message.h"
+#include "mi2-event-message.h"
+#include "mi2-input-stream.h"
+#include "mi2-output-stream.h"
+
+typedef struct
+{
+ GIOStream *io_stream;
+ Mi2InputStream *input_stream;
+ Mi2OutputStream *output_stream;
+ GCancellable *read_loop_cancellable;
+
+ guint is_listening : 1;
+} Mi2ClientPrivate;
+
+enum {
+ PROP_0,
+ PROP_IO_STREAM,
+ N_PROPS
+};
+
+enum {
+ LOG,
+ N_SIGNALS
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (Mi2Client, mi2_client, G_TYPE_OBJECT)
+
+static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
+
+static gboolean
+mi2_client_check_ready (Mi2Client *self,
+ GError **error)
+{
+ Mi2ClientPrivate *priv = mi2_client_get_instance_private (self);
+
+ g_return_val_if_fail (MI2_IS_CLIENT (self), FALSE);
+
+ if (priv->input_stream == NULL || priv->output_stream == NULL)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_CONNECTED,
+ "Not connected to gdb");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+mi2_client_set_io_stream (Mi2Client *self,
+ GIOStream *io_stream)
+{
+ Mi2ClientPrivate *priv = mi2_client_get_instance_private (self);
+
+ g_assert (MI2_IS_CLIENT (self));
+ g_assert (!io_stream || G_IS_IO_STREAM (io_stream));
+
+ if (g_set_object (&priv->io_stream, io_stream))
+ {
+ priv->input_stream = mi2_input_stream_new (g_io_stream_get_input_stream (io_stream));
+ priv->output_stream = mi2_output_stream_new (g_io_stream_get_output_stream (io_stream));
+ }
+}
+
+static void
+mi2_client_finalize (GObject *object)
+{
+ Mi2Client *self = (Mi2Client *)object;
+ Mi2ClientPrivate *priv = mi2_client_get_instance_private (self);
+
+ g_clear_object (&priv->io_stream);
+ g_clear_object (&priv->input_stream);
+ g_clear_object (&priv->output_stream);
+ g_clear_object (&priv->read_loop_cancellable);
+
+ G_OBJECT_CLASS (mi2_client_parent_class)->finalize (object);
+}
+
+static void
+mi2_client_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ Mi2Client *self = MI2_CLIENT (object);
+ Mi2ClientPrivate *priv = mi2_client_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_IO_STREAM:
+ g_value_set_object (value, priv->io_stream);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+mi2_client_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ Mi2Client *self = MI2_CLIENT (object);
+
+ switch (prop_id)
+ {
+ case PROP_IO_STREAM:
+ mi2_client_set_io_stream (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+mi2_client_class_init (Mi2ClientClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = mi2_client_finalize;
+ object_class->get_property = mi2_client_get_property;
+ object_class->set_property = mi2_client_set_property;
+
+ properties [PROP_IO_STREAM] =
+ g_param_spec_object ("io-stream",
+ "IO Stream",
+ "The undelrying stream to communicate with",
+ G_TYPE_IO_STREAM,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ signals [LOG] =
+ g_signal_new ("log",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (Mi2ClientClass, log),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
+}
+
+static void
+mi2_client_init (Mi2Client *self)
+{
+}
+
+Mi2Client *
+mi2_client_new (GIOStream *io_stream)
+{
+ return g_object_new (MI2_TYPE_CLIENT,
+ "io-stream", io_stream,
+ NULL);
+}
+
+static void
+mi2_client_exec_write_message_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ Mi2OutputStream *stream = (Mi2OutputStream *)object;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (MI2_IS_OUTPUT_STREAM (stream));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ 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);
+}
+
+void
+mi2_client_exec_async (Mi2Client *self,
+ const gchar *command,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Mi2ClientPrivate *priv = mi2_client_get_instance_private (self);
+ g_autoptr(Mi2Message) message = NULL;
+ g_autoptr(GTask) task = NULL;
+ g_autoptr(GError) error = 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_exec_async);
+
+ if (!mi2_client_check_ready (self, &error))
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ message = g_object_new (MI2_TYPE_COMMAND_MESSAGE,
+ "command", command,
+ NULL);
+
+ mi2_output_stream_write_message_async (priv->output_stream,
+ message,
+ cancellable,
+ mi2_client_exec_write_message_cb,
+ g_steal_pointer (&task));
+}
+
+gboolean
+mi2_client_exec_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);
+}
+
+static void
+mi2_client_dispatch (Mi2Client *self,
+ Mi2Message *message)
+{
+ g_return_if_fail (MI2_IS_CLIENT (self));
+ g_return_if_fail (MI2_IS_MESSAGE (message));
+
+ if (MI2_IS_CONSOLE_MESSAGE (message))
+ {
+ const gchar *str;
+
+ str = mi2_console_message_get_message (MI2_CONSOLE_MESSAGE (message));
+ g_signal_emit (self, signals [LOG], 0, str);
+ }
+ else if (MI2_IS_EVENT_MESSAGE (message))
+ {
+ g_print ("Event: %s\n", mi2_event_message_get_name (MI2_EVENT_MESSAGE (message)));
+ }
+ else
+ g_print ("Got message of type %s\n", G_OBJECT_TYPE_NAME (message));
+}
+
+static void
+mi2_client_read_loop_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ Mi2InputStream *stream = (Mi2InputStream *)object;
+ g_autoptr(Mi2Client) self = user_data;
+ Mi2ClientPrivate *priv = mi2_client_get_instance_private (self);
+ g_autoptr(Mi2Message) message = NULL;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (MI2_IS_INPUT_STREAM (stream));
+ g_assert (MI2_IS_CLIENT (self));
+
+ message = mi2_input_stream_read_message_finish (stream, result, &error);
+
+ if (message == NULL)
+ {
+ priv->is_listening = FALSE;
+ g_clear_object (&priv->read_loop_cancellable);
+ if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("%s", error->message);
+ return;
+ }
+
+ mi2_client_dispatch (self, message);
+
+ if (priv->is_listening)
+ mi2_input_stream_read_message_async (priv->input_stream,
+ priv->read_loop_cancellable,
+ mi2_client_read_loop_cb,
+ g_steal_pointer (&self));
+}
+
+void
+mi2_client_start_listening (Mi2Client *self)
+{
+ Mi2ClientPrivate *priv = mi2_client_get_instance_private (self);
+
+ g_return_if_fail (MI2_IS_CLIENT (self));
+ g_return_if_fail (priv->is_listening == FALSE);
+ g_return_if_fail (priv->input_stream != NULL);
+
+ priv->is_listening = TRUE;
+ priv->read_loop_cancellable = g_cancellable_new ();
+
+ mi2_input_stream_read_message_async (priv->input_stream,
+ priv->read_loop_cancellable,
+ mi2_client_read_loop_cb,
+ g_object_ref (self));
+}
+
+void
+mi2_client_stop_listening (Mi2Client *self)
+{
+ Mi2ClientPrivate *priv = mi2_client_get_instance_private (self);
+
+ g_return_if_fail (MI2_IS_CLIENT (self));
+
+ if (priv->is_listening)
+ {
+ priv->is_listening = FALSE;
+ g_cancellable_cancel (priv->read_loop_cancellable);
+ }
+}
+
+#if 0
+void
+mi2_client_async (Mi2Client *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+}
+
+gboolean
+mi2_client_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);
+}
+#endif
diff --git a/contrib/mi2/mi2-client.h b/contrib/mi2/mi2-client.h
new file mode 100644
index 0000000..8231985
--- /dev/null
+++ b/contrib/mi2/mi2-client.h
@@ -0,0 +1,69 @@
+/* mi2-client.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_CLIENT_H
+#define MI2_CLIENT_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define MI2_TYPE_CLIENT (mi2_client_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (Mi2Client, mi2_client, MI2, CLIENT, GObject)
+
+struct _Mi2ClientClass
+{
+ GObjectClass parent_instance;
+
+ void (*log) (Mi2Client *self,
+ const gchar *log);
+
+ gpointer _reserved1;
+ gpointer _reserved2;
+ gpointer _reserved3;
+ gpointer _reserved4;
+ gpointer _reserved5;
+ gpointer _reserved6;
+ gpointer _reserved7;
+ gpointer _reserved8;
+ gpointer _reserved9;
+ gpointer _reserved10;
+ gpointer _reserved11;
+ gpointer _reserved12;
+ gpointer _reserved13;
+ gpointer _reserved14;
+ gpointer _reserved15;
+ 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,
+ GError **error);
+void mi2_client_start_listening (Mi2Client *self);
+void mi2_client_stop_listening (Mi2Client *self);
+
+G_END_DECLS
+
+#endif /* MI2_CLIENT_H */
diff --git a/contrib/mi2/mi2-command-message.c b/contrib/mi2/mi2-command-message.c
new file mode 100644
index 0000000..2f01ce9
--- /dev/null
+++ b/contrib/mi2/mi2-command-message.c
@@ -0,0 +1,140 @@
+/* mi2-command-message.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-command-message"
+
+#include <string.h>
+
+#include "mi2-command-message.h"
+#include "mi2-util.h"
+
+struct _Mi2CommandMessage
+{
+ Mi2Message parent;
+ gchar *command;
+};
+
+enum {
+ PROP_0,
+ PROP_COMMAND,
+ N_PROPS
+};
+
+G_DEFINE_TYPE (Mi2CommandMessage, mi2_command_message, MI2_TYPE_MESSAGE)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+mi2_command_message_finalize (GObject *object)
+{
+ Mi2CommandMessage *self = (Mi2CommandMessage *)object;
+
+ g_clear_pointer (&self->command, g_free);
+
+ G_OBJECT_CLASS (mi2_command_message_parent_class)->finalize (object);
+}
+
+static void
+mi2_command_message_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ Mi2CommandMessage *self = MI2_COMMAND_MESSAGE (object);
+
+ switch (prop_id)
+ {
+ case PROP_COMMAND:
+ g_value_set_string (value, mi2_command_message_get_command (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+mi2_command_message_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ Mi2CommandMessage *self = MI2_COMMAND_MESSAGE (object);
+
+ switch (prop_id)
+ {
+ case PROP_COMMAND:
+ mi2_command_message_set_command (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+mi2_command_message_class_init (Mi2CommandMessageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_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;
+
+ properties [PROP_COMMAND] =
+ g_param_spec_string ("command", NULL, NULL, 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_command_message_init (Mi2CommandMessage *self)
+{
+}
+
+const gchar *
+mi2_command_message_get_command (Mi2CommandMessage *self)
+{
+ g_return_val_if_fail (MI2_IS_COMMAND_MESSAGE (self), NULL);
+
+ return self->command;
+}
+
+void
+mi2_command_message_set_command (Mi2CommandMessage *self,
+ const gchar *command)
+{
+ if (command != self->command)
+ {
+ g_free (self->command);
+ self->command = g_strdup (command);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_COMMAND]);
+ }
+}
+
+Mi2Message *
+mi2_command_message_new_from_string (const gchar *line)
+{
+ Mi2CommandMessage *ret;
+
+ ret = g_object_new (MI2_TYPE_COMMAND_MESSAGE, NULL);
+ ret->command = g_strstrip (g_strdup (&line[1]));
+
+ return MI2_MESSAGE (ret);
+}
diff --git a/contrib/mi2/mi2-command-message.h b/contrib/mi2/mi2-command-message.h
new file mode 100644
index 0000000..ccb8c11
--- /dev/null
+++ b/contrib/mi2/mi2-command-message.h
@@ -0,0 +1,37 @@
+/* mi2-command-message.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_COMMAND_MESSAGE_H
+#define MI2_COMMAND_MESSAGE_H
+
+#include "mi2-message.h"
+
+G_BEGIN_DECLS
+
+#define MI2_TYPE_COMMAND_MESSAGE (mi2_command_message_get_type())
+
+G_DECLARE_FINAL_TYPE (Mi2CommandMessage, mi2_command_message, MI2, COMMAND_MESSAGE, Mi2Message)
+
+Mi2Message *mi2_command_message_new_from_string (const gchar *line);
+const gchar *mi2_command_message_get_command (Mi2CommandMessage *self);
+void mi2_command_message_set_command (Mi2CommandMessage *self,
+ const gchar *command);
+
+G_END_DECLS
+
+#endif /* MI2_COMMAND_MESSAGE_H */
diff --git a/contrib/mi2/mi2-console-message.c b/contrib/mi2/mi2-console-message.c
new file mode 100644
index 0000000..9e84fc7
--- /dev/null
+++ b/contrib/mi2/mi2-console-message.c
@@ -0,0 +1,160 @@
+/* mi2-console-message.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-console-message"
+
+#include <string.h>
+
+#include "mi2-console-message.h"
+#include "mi2-util.h"
+
+struct _Mi2ConsoleMessage
+{
+ Mi2Message parent;
+ gchar *message;
+};
+
+enum {
+ PROP_0,
+ PROP_MESSAGE,
+ N_PROPS
+};
+
+G_DEFINE_TYPE (Mi2ConsoleMessage, mi2_console_message, MI2_TYPE_MESSAGE)
+
+static GParamSpec *properties [N_PROPS];
+
+static GBytes *
+mi2_console_message_serialize (Mi2Message *message)
+{
+ Mi2ConsoleMessage *self = (Mi2ConsoleMessage *)message;
+ g_autofree gchar *escaped = NULL;
+ g_autofree gchar *str = NULL;
+
+ g_assert (MI2_IS_CONSOLE_MESSAGE (message));
+
+ escaped = g_strescape (self->message ? self->message : "", "");
+ str = g_strdup_printf ("~\"%s\"\n", escaped);
+
+ return g_bytes_new_take (g_steal_pointer (&str), strlen (str));
+}
+
+static void
+mi2_console_message_finalize (GObject *object)
+{
+ Mi2ConsoleMessage *self = (Mi2ConsoleMessage *)object;
+
+ g_clear_pointer (&self->message, g_free);
+
+ G_OBJECT_CLASS (mi2_console_message_parent_class)->finalize (object);
+}
+
+static void
+mi2_console_message_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ Mi2ConsoleMessage *self = MI2_CONSOLE_MESSAGE (object);
+
+ switch (prop_id)
+ {
+ case PROP_MESSAGE:
+ g_value_set_string (value, mi2_console_message_get_message (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+mi2_console_message_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ Mi2ConsoleMessage *self = MI2_CONSOLE_MESSAGE (object);
+
+ switch (prop_id)
+ {
+ case PROP_MESSAGE:
+ mi2_console_message_set_message (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+mi2_console_message_class_init (Mi2ConsoleMessageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ Mi2MessageClass *message_class = MI2_MESSAGE_CLASS (klass);
+
+ object_class->finalize = mi2_console_message_finalize;
+ object_class->get_property = mi2_console_message_get_property;
+ object_class->set_property = mi2_console_message_set_property;
+
+ message_class->serialize = mi2_console_message_serialize;
+
+ properties [PROP_MESSAGE] =
+ g_param_spec_string ("message", NULL, NULL, NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+mi2_console_message_init (Mi2ConsoleMessage *self)
+{
+}
+
+const gchar *
+mi2_console_message_get_message (Mi2ConsoleMessage *self)
+{
+ g_return_val_if_fail (MI2_IS_CONSOLE_MESSAGE (self), NULL);
+
+ return self->message;
+}
+
+void
+mi2_console_message_set_message (Mi2ConsoleMessage *self,
+ const gchar *message)
+{
+ if (message != self->message)
+ {
+ g_free (self->message);
+ self->message = g_strdup (message);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MESSAGE]);
+ }
+}
+
+Mi2Message *
+mi2_console_message_new_from_string (const gchar *line)
+{
+ Mi2ConsoleMessage *ret;
+
+ ret = g_object_new (MI2_TYPE_CONSOLE_MESSAGE, NULL);
+
+ if (line != NULL && line[0] == '~')
+ ret->message = mi2_util_parse_string (&line[1], NULL);
+
+ return MI2_MESSAGE (ret);
+}
diff --git a/contrib/mi2/mi2-console-message.h b/contrib/mi2/mi2-console-message.h
new file mode 100644
index 0000000..bf702b2
--- /dev/null
+++ b/contrib/mi2/mi2-console-message.h
@@ -0,0 +1,37 @@
+/* mi2-console-message.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_CONSOLE_MESSAGE_H
+#define MI2_CONSOLE_MESSAGE_H
+
+#include "mi2-message.h"
+
+G_BEGIN_DECLS
+
+#define MI2_TYPE_CONSOLE_MESSAGE (mi2_console_message_get_type())
+
+G_DECLARE_FINAL_TYPE (Mi2ConsoleMessage, mi2_console_message, MI2, CONSOLE_MESSAGE, Mi2Message)
+
+Mi2Message *mi2_console_message_new_from_string (const gchar *line);
+const gchar *mi2_console_message_get_message (Mi2ConsoleMessage *self);
+void mi2_console_message_set_message (Mi2ConsoleMessage *self,
+ const gchar *message);
+
+G_END_DECLS
+
+#endif /* MI2_CONSOLE_MESSAGE_H */
diff --git a/contrib/mi2/mi2-event-message.c b/contrib/mi2/mi2-event-message.c
new file mode 100644
index 0000000..e1ab1b9
--- /dev/null
+++ b/contrib/mi2/mi2-event-message.c
@@ -0,0 +1,164 @@
+/* mi2-event-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-event-message"
+
+#include "mi2-event-message.h"
+#include "mi2-util.h"
+
+struct _Mi2EventMessage
+{
+ Mi2Message parent_instance;
+
+ gchar *name;
+};
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ N_PROPS
+};
+
+G_DEFINE_TYPE (Mi2EventMessage, mi2_event_mesage, MI2_TYPE_MESSAGE)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+mi2_event_mesage_finalize (GObject *object)
+{
+ Mi2EventMessage *self = (Mi2EventMessage *)object;
+
+ g_clear_pointer (&self->name, g_free);
+
+ G_OBJECT_CLASS (mi2_event_mesage_parent_class)->finalize (object);
+}
+
+static void
+mi2_event_mesage_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ Mi2EventMessage *self = MI2_EVENT_MESSAGE (object);
+
+ switch (prop_id)
+ {
+ case PROP_NAME:
+ g_value_set_string (value, mi2_event_message_get_name (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+mi2_event_mesage_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ Mi2EventMessage *self = MI2_EVENT_MESSAGE (object);
+
+ switch (prop_id)
+ {
+ case PROP_NAME:
+ mi2_event_message_set_name (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+mi2_event_mesage_class_init (Mi2EventMessageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = mi2_event_mesage_finalize;
+ object_class->get_property = mi2_event_mesage_get_property;
+ object_class->set_property = mi2_event_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_event_mesage_init (Mi2EventMessage *self)
+{
+}
+
+/**
+ * mi2_event_message_new_from_string:
+ * @line: the string to be parsed
+ *
+ * Returns: (transfer full): An #Mi2Message
+ */
+Mi2Message *
+mi2_event_message_new_from_string (const gchar *line)
+{
+ Mi2EventMessage *ret;
+
+ ret = g_object_new (MI2_TYPE_EVENT_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;
+
+ g_print ("*%s* **%s**\n", key, value);
+ }
+ }
+
+ return MI2_MESSAGE (ret);
+}
+
+const gchar *
+mi2_event_message_get_name (Mi2EventMessage *self)
+{
+ g_return_val_if_fail (MI2_IS_EVENT_MESSAGE (self), NULL);
+
+ return self->name;
+}
+
+void
+mi2_event_message_set_name (Mi2EventMessage *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]);
+ }
+}
diff --git a/contrib/mi2/mi2-event-message.h b/contrib/mi2/mi2-event-message.h
new file mode 100644
index 0000000..6150fad
--- /dev/null
+++ b/contrib/mi2/mi2-event-message.h
@@ -0,0 +1,37 @@
+/* mi2-event-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_EVENT_MESAGE_H
+#define MI2_EVENT_MESAGE_H
+
+#include "mi2-message.h"
+
+G_BEGIN_DECLS
+
+#define MI2_TYPE_EVENT_MESSAGE (mi2_event_mesage_get_type())
+
+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);
+
+G_END_DECLS
+
+#endif /* MI2_EVENT_MESAGE_H */
diff --git a/contrib/mi2/mi2-glib.h b/contrib/mi2/mi2-glib.h
new file mode 100644
index 0000000..b097de1
--- /dev/null
+++ b/contrib/mi2/mi2-glib.h
@@ -0,0 +1,35 @@
+/* mi2-glib.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_GLIB_H
+#define MI2_GLIB_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#include "mi2-command-message.h"
+#include "mi2-console-message.h"
+#include "mi2-event-message.h"
+#include "mi2-info-message.h"
+#include "mi2-input-stream.h"
+#include "mi2-message.h"
+
+G_END_DECLS
+
+#endif /* MI2_GLIB_H */
diff --git a/contrib/mi2/mi2-info-message.c b/contrib/mi2/mi2-info-message.c
new file mode 100644
index 0000000..c077c20
--- /dev/null
+++ b/contrib/mi2/mi2-info-message.c
@@ -0,0 +1,142 @@
+/* mi2-info-message.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-info-message"
+
+#include <string.h>
+
+#include "mi2-info-message.h"
+#include "mi2-util.h"
+
+struct _Mi2InfoMessage
+{
+ Mi2Message parent;
+ gchar *message;
+};
+
+enum {
+ PROP_0,
+ PROP_MESSAGE,
+ N_PROPS
+};
+
+G_DEFINE_TYPE (Mi2InfoMessage, mi2_info_message, MI2_TYPE_MESSAGE)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+mi2_info_message_finalize (GObject *object)
+{
+ Mi2InfoMessage *self = (Mi2InfoMessage *)object;
+
+ g_clear_pointer (&self->message, g_free);
+
+ G_OBJECT_CLASS (mi2_info_message_parent_class)->finalize (object);
+}
+
+static void
+mi2_info_message_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ Mi2InfoMessage *self = MI2_INFO_MESSAGE (object);
+
+ switch (prop_id)
+ {
+ case PROP_MESSAGE:
+ g_value_set_string (value, mi2_info_message_get_message (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+mi2_info_message_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ Mi2InfoMessage *self = MI2_INFO_MESSAGE (object);
+
+ switch (prop_id)
+ {
+ case PROP_MESSAGE:
+ mi2_info_message_set_message (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+mi2_info_message_class_init (Mi2InfoMessageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = mi2_info_message_finalize;
+ object_class->get_property = mi2_info_message_get_property;
+ object_class->set_property = mi2_info_message_set_property;
+
+ properties [PROP_MESSAGE] =
+ g_param_spec_string ("message", NULL, NULL, NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+mi2_info_message_init (Mi2InfoMessage *self)
+{
+}
+
+const gchar *
+mi2_info_message_get_message (Mi2InfoMessage *self)
+{
+ g_return_val_if_fail (MI2_IS_INFO_MESSAGE (self), NULL);
+
+ return self->message;
+}
+
+void
+mi2_info_message_set_message (Mi2InfoMessage *self,
+ const gchar *message)
+{
+ if (message != self->message)
+ {
+ g_free (self->message);
+ self->message = g_strdup (message);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MESSAGE]);
+ }
+}
+
+Mi2Message *
+mi2_info_message_new_from_string (const gchar *line)
+{
+ Mi2InfoMessage *ret;
+
+ ret = g_object_new (MI2_TYPE_INFO_MESSAGE, NULL);
+
+ if (line != NULL && line[0] == '&')
+ ret->message = mi2_util_parse_string (&line[1], NULL);
+
+ return MI2_MESSAGE (ret);
+}
diff --git a/contrib/mi2/mi2-info-message.h b/contrib/mi2/mi2-info-message.h
new file mode 100644
index 0000000..59d2729
--- /dev/null
+++ b/contrib/mi2/mi2-info-message.h
@@ -0,0 +1,37 @@
+/* mi2-info-message.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_INFO_MESSAGE_H
+#define MI2_INFO_MESSAGE_H
+
+#include "mi2-message.h"
+
+G_BEGIN_DECLS
+
+#define MI2_TYPE_INFO_MESSAGE (mi2_info_message_get_type())
+
+G_DECLARE_FINAL_TYPE (Mi2InfoMessage, mi2_info_message, MI2, INFO_MESSAGE, Mi2Message)
+
+Mi2Message *mi2_info_message_new_from_string (const gchar *line);
+const gchar *mi2_info_message_get_message (Mi2InfoMessage *self);
+void mi2_info_message_set_message (Mi2InfoMessage *self,
+ const gchar *message);
+
+G_END_DECLS
+
+#endif /* MI2_INFO_MESSAGE_H */
diff --git a/contrib/mi2/mi2-input-stream.c b/contrib/mi2/mi2-input-stream.c
new file mode 100644
index 0000000..d513be0
--- /dev/null
+++ b/contrib/mi2/mi2-input-stream.c
@@ -0,0 +1,131 @@
+/* mi2-input-stream.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-input-stream"
+
+#include <string.h>
+
+#include "mi2-input-stream.h"
+
+G_DEFINE_TYPE (Mi2InputStream, mi2_input_stream, G_TYPE_DATA_INPUT_STREAM)
+
+static void
+mi2_input_stream_class_init (Mi2InputStreamClass *klass)
+{
+}
+
+static void
+mi2_input_stream_init (Mi2InputStream *self)
+{
+}
+
+Mi2InputStream *
+mi2_input_stream_new (GInputStream *base_stream)
+{
+ return g_object_new (MI2_TYPE_INPUT_STREAM,
+ "base-stream", base_stream,
+ NULL);
+}
+
+static void
+mi2_input_stream_read_message_read_line_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ Mi2InputStream *self = (Mi2InputStream *)object;
+ g_autoptr(Mi2Message) message = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GTask) task = user_data;
+ g_autofree gchar *line = NULL;
+ gsize len = 0;
+
+ g_assert (MI2_IS_INPUT_STREAM (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ line = g_data_input_stream_read_line_finish_utf8 (G_DATA_INPUT_STREAM (self), result, &len, &error);
+
+ /* Nothing to read, return NULL message */
+ if (line == NULL && error == NULL)
+ {
+ g_task_return_pointer (task, NULL, NULL);
+ return;
+ }
+
+ if (line != NULL && g_str_has_prefix (line, "(gdb)"))
+ {
+ /* Ignore this line, read again */
+ GCancellable *cancellable = g_task_get_cancellable (task);
+ g_data_input_stream_read_line_async (G_DATA_INPUT_STREAM (self),
+ G_PRIORITY_LOW,
+ cancellable,
+ mi2_input_stream_read_message_read_line_cb,
+ g_steal_pointer (&task));
+ return;
+ }
+
+ /* Let Mi2Message decode the protocol content */
+ if (NULL == (message = mi2_message_parse (line, len, &error)))
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ g_task_return_pointer (task, g_steal_pointer (&message), g_object_unref);
+}
+
+void
+mi2_input_stream_read_message_async (Mi2InputStream *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (MI2_IS_INPUT_STREAM (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_input_stream_read_message_async);
+
+ g_data_input_stream_read_line_async (G_DATA_INPUT_STREAM (self),
+ G_PRIORITY_LOW,
+ cancellable,
+ mi2_input_stream_read_message_read_line_cb,
+ g_steal_pointer (&task));
+}
+
+/**
+ * mi2_input_stream_read_message_finish:
+ *
+ * Finish an asynchronous call started by mi2_input_stream_read_message_async().
+ *
+ * Returns: (transfer full): An #Mi2Message if successful. If there is no data
+ * to read, this function returns %NULL and @error is not set. If an error
+ * occurred, %NULL is returned and @error is set.
+ */
+Mi2Message *
+mi2_input_stream_read_message_finish (Mi2InputStream *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (MI2_IS_INPUT_STREAM (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
diff --git a/contrib/mi2/mi2-input-stream.h b/contrib/mi2/mi2-input-stream.h
new file mode 100644
index 0000000..fd53843
--- /dev/null
+++ b/contrib/mi2/mi2-input-stream.h
@@ -0,0 +1,57 @@
+/* mi2-input-stream.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_INPUT_STREAM_H
+#define MI2_INPUT_STREAM_H
+
+#include <gio/gio.h>
+
+#include "mi2-message.h"
+
+G_BEGIN_DECLS
+
+#define MI2_TYPE_INPUT_STREAM (mi2_input_stream_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (Mi2InputStream, mi2_input_stream, MI2, INPUT_STREAM, GDataInputStream)
+
+struct _Mi2InputStreamClass
+{
+ GDataInputStreamClass parent_class;
+
+ gpointer _reserved1;
+ gpointer _reserved2;
+ gpointer _reserved3;
+ gpointer _reserved4;
+ gpointer _reserved5;
+ gpointer _reserved6;
+ gpointer _reserved7;
+ gpointer _reserved8;
+};
+
+Mi2InputStream *mi2_input_stream_new (GInputStream *base_stream);
+void mi2_input_stream_read_message_async (Mi2InputStream *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+Mi2Message *mi2_input_stream_read_message_finish (Mi2InputStream *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* MI2_INPUT_STREAM_H */
diff --git a/contrib/mi2/mi2-message.c b/contrib/mi2/mi2-message.c
new file mode 100644
index 0000000..3485674
--- /dev/null
+++ b/contrib/mi2/mi2-message.c
@@ -0,0 +1,157 @@
+/* mi2-message.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-message"
+
+#include "mi2-message.h"
+
+#include "mi2-command-message.h"
+#include "mi2-console-message.h"
+#include "mi2-event-message.h"
+#include "mi2-info-message.h"
+
+typedef struct
+{
+ gpointer dummy;
+} 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);
+
+ 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);
+ }
+}
+
+static void
+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
+mi2_message_init (Mi2Message *self)
+{
+}
+
+/**
+ * mi2_message_parse:
+ * @line: the line to parse
+ * @error: a location for a #GError or %NULL
+ *
+ * Parses @line into #Mi2Message.
+ *
+ * Returns: (transfer full): An #Mi2Message if successful; otherwise %NULL
+ * and @error is set.
+ */
+Mi2Message *
+mi2_message_parse (const gchar *line,
+ gsize len,
+ GError **error)
+{
+ Mi2Message *ret = NULL;
+
+ g_return_val_if_fail (line != NULL, NULL);
+ g_return_val_if_fail (len > 0, NULL);
+
+ switch (line[0])
+ {
+ case '~':
+ ret = mi2_console_message_new_from_string (line);
+ break;
+
+ case '&':
+ ret = mi2_info_message_new_from_string (line);
+ break;
+
+ case '=':
+ case '*':
+ case '^':
+ ret = mi2_event_message_new_from_string (line);
+ break;
+
+ case '-':
+ ret = mi2_command_message_new_from_string (line);
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * mi2_message_serialize:
+ * @self: An #Mi2Message
+ *
+ * Serialize the message to be sent to the peer.
+ *
+ * Returns: (transfer full): A #GBytes.
+ */
+GBytes *
+mi2_message_serialize (Mi2Message *self)
+{
+ g_return_val_if_fail (MI2_IS_MESSAGE (self), NULL);
+
+ return MI2_MESSAGE_GET_CLASS (self)->serialize (self);
+}
diff --git a/contrib/mi2/mi2-message.h b/contrib/mi2/mi2-message.h
new file mode 100644
index 0000000..68429eb
--- /dev/null
+++ b/contrib/mi2/mi2-message.h
@@ -0,0 +1,53 @@
+/* mi2-message.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_MESSAGE_H
+#define MI2_MESSAGE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define MI2_TYPE_MESSAGE (mi2_message_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (Mi2Message, mi2_message, MI2, MESSAGE, GObject)
+
+struct _Mi2MessageClass
+{
+ GObjectClass parent_class;
+
+ GBytes *(*serialize) (Mi2Message *self);
+
+ gpointer _reserved1;
+ gpointer _reserved2;
+ gpointer _reserved3;
+ gpointer _reserved4;
+ gpointer _reserved5;
+ gpointer _reserved6;
+ gpointer _reserved7;
+ gpointer _reserved8;
+};
+
+Mi2Message *mi2_message_parse (const gchar *line,
+ gsize len,
+ GError **error);
+GBytes *mi2_message_serialize (Mi2Message *self);
+
+G_END_DECLS
+
+#endif /* MI2_MESSAGE_H */
diff --git a/contrib/mi2/mi2-output-stream.c b/contrib/mi2/mi2-output-stream.c
new file mode 100644
index 0000000..8deb6b1
--- /dev/null
+++ b/contrib/mi2/mi2-output-stream.c
@@ -0,0 +1,97 @@
+/* mi2-output-stream.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_LOD_DOMAIN "mi2-output-stream"
+
+#include "mi2-output-stream.h"
+
+G_DEFINE_TYPE (Mi2OutputStream, mi2_output_stream, G_TYPE_DATA_OUTPUT_STREAM)
+
+static void
+mi2_output_stream_class_init (Mi2OutputStreamClass *klass)
+{
+}
+
+static void
+mi2_output_stream_init (Mi2OutputStream *self)
+{
+}
+
+Mi2OutputStream *
+mi2_output_stream_new (GOutputStream *base_stream)
+{
+ return g_object_new (MI2_TYPE_OUTPUT_STREAM,
+ "base-stream", base_stream,
+ NULL);
+}
+
+static void
+mi2_output_stream_write_message_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ Mi2OutputStream *self = (Mi2OutputStream *)object;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GTask) task = user_data;
+
+ g_assert (MI2_IS_OUTPUT_STREAM (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ if (!g_output_stream_write_bytes_finish (G_OUTPUT_STREAM (self), result, &error))
+ g_task_return_error (task, g_steal_pointer (&error));
+ else
+ g_task_return_boolean (task, TRUE);
+}
+
+void
+mi2_output_stream_write_message_async (Mi2OutputStream *self,
+ Mi2Message *message,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ g_autoptr(GBytes) bytes = NULL;
+
+ g_return_if_fail (MI2_IS_OUTPUT_STREAM (self));
+ g_return_if_fail (MI2_IS_MESSAGE (message));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, mi2_output_stream_write_message_async);
+
+ bytes = mi2_message_serialize (message);
+
+ g_output_stream_write_bytes_async (G_OUTPUT_STREAM (self),
+ bytes,
+ G_PRIORITY_LOW,
+ cancellable,
+ mi2_output_stream_write_message_cb,
+ g_steal_pointer (&task));
+}
+
+gboolean
+mi2_output_stream_write_message_finish (Mi2OutputStream *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (MI2_IS_OUTPUT_STREAM (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
diff --git a/contrib/mi2/mi2-output-stream.h b/contrib/mi2/mi2-output-stream.h
new file mode 100644
index 0000000..1597bda
--- /dev/null
+++ b/contrib/mi2/mi2-output-stream.h
@@ -0,0 +1,58 @@
+/* mi2-output-stream.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_OUTPUT_STREAM_H
+#define MI2_OUTPUT_STREAM_H
+
+#include <gio/gio.h>
+
+#include "mi2-message.h"
+
+G_BEGIN_DECLS
+
+#define MI2_TYPE_OUTPUT_STREAM (mi2_output_stream_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (Mi2OutputStream, mi2_output_stream, MI2, OUTPUT_STREAM, GDataOutputStream)
+
+struct _Mi2OutputStreamClass
+{
+ GDataOutputStreamClass parent_instance;
+
+ gpointer _reserved1;
+ gpointer _reserved2;
+ gpointer _reserved3;
+ gpointer _reserved4;
+ gpointer _reserved5;
+ gpointer _reserved6;
+ gpointer _reserved7;
+ gpointer _reserved8;
+};
+
+Mi2OutputStream *mi2_output_stream_new (GOutputStream *base_stream);
+void mi2_output_stream_write_message_async (Mi2OutputStream *stream,
+ Mi2Message *message,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mi2_output_stream_write_message_finish (Mi2OutputStream *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* MI2_OUTPUT_STREAM_H */
diff --git a/contrib/mi2/mi2-util.c b/contrib/mi2/mi2-util.c
new file mode 100644
index 0000000..a279784
--- /dev/null
+++ b/contrib/mi2/mi2-util.c
@@ -0,0 +1,111 @@
+/* mi2-util.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-util.h"
+
+gchar *
+mi2_util_parse_string (const gchar *line,
+ const gchar **endptr)
+{
+ g_autoptr(GString) str = NULL;
+
+ g_return_val_if_fail (line != NULL, NULL);
+
+ if (*line != '"')
+ goto failure;
+
+ str = g_string_new (NULL);
+
+ for (++line; *line; line = g_utf8_next_char (line))
+ {
+ gunichar ch = g_utf8_get_char (line);
+
+ if (ch == '"')
+ break;
+
+ /* Handle escape characters */
+ if (ch == '\\')
+ {
+ line = g_utf8_next_char (line);
+ if (!*line)
+ goto failure;
+ ch = g_utf8_get_char (line);
+
+ switch (ch)
+ {
+ case 'n':
+ g_string_append (str, "\n");
+ break;
+
+ case 't':
+ g_string_append (str, "\t");
+ break;
+
+ default:
+ g_string_append_unichar (str, ch);
+ break;
+ }
+
+ continue;
+ }
+
+ g_string_append_unichar (str, ch);
+ }
+
+ if (*line == '"')
+ line++;
+
+ if (endptr)
+ *endptr = line;
+
+ return g_string_free (g_steal_pointer (&str), FALSE);
+
+failure:
+ if (endptr)
+ *endptr = NULL;
+
+ return NULL;
+}
+
+gchar *
+mi2_util_parse_word (const gchar *line,
+ const gchar **endptr)
+{
+ const gchar *begin = line;
+ gchar *ret;
+
+ g_return_val_if_fail (line != NULL, NULL);
+
+ for (; *line; line = g_utf8_next_char (line))
+ {
+ gunichar ch = g_utf8_get_char (line);
+
+ if (ch == ',' || ch == '=' || g_unichar_isspace (ch))
+ break;
+ }
+
+ ret = g_strndup (begin, line - begin);
+
+ if (*line)
+ line++;
+
+ if (endptr)
+ *endptr = line;
+
+ return ret;
+}
diff --git a/contrib/mi2/mi2-util.h b/contrib/mi2/mi2-util.h
new file mode 100644
index 0000000..da32968
--- /dev/null
+++ b/contrib/mi2/mi2-util.h
@@ -0,0 +1,33 @@
+/* mi2-util.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_UTIL_H
+#define MI2_UTIL_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gchar *mi2_util_parse_string (const gchar *line,
+ const gchar **endptr);
+gchar *mi2_util_parse_word (const gchar *line,
+ const gchar **endptr);
+
+G_END_DECLS
+
+#endif /* MI2_UTIL_H */
diff --git a/contrib/mi2/test-client.c b/contrib/mi2/test-client.c
new file mode 100644
index 0000000..9c4d9e1
--- /dev/null
+++ b/contrib/mi2/test-client.c
@@ -0,0 +1,71 @@
+/* test-client.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-client.h"
+
+static GMainLoop *main_loop;
+
+static GIOStream *
+create_io_stream_to_gdb (void)
+{
+ g_autoptr(GSubprocess) subprocess = NULL;
+ g_autoptr(GIOStream) io_stream = NULL;
+ g_autoptr(GError) error = NULL;
+
+ subprocess = g_subprocess_new (G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE,
+ &error,
+ "gdb", "--interpreter", "mi2",
+ NULL);
+ g_assert_no_error (error);
+ g_assert (subprocess);
+
+ io_stream = g_simple_io_stream_new (g_subprocess_get_stdout_pipe (subprocess),
+ g_subprocess_get_stdin_pipe (subprocess));
+
+ g_subprocess_wait_async (subprocess, NULL, NULL, NULL);
+
+ return g_steal_pointer (&io_stream);
+}
+
+static void
+log_handler (Mi2Client *client,
+ const gchar *log)
+{
+ g_print ("%s", log);
+}
+
+gint
+main (gint argc,
+ gchar *argv[])
+{
+ g_autoptr(Mi2Client) client = NULL;
+ g_autoptr(GIOStream) io_stream = NULL;
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+ io_stream = create_io_stream_to_gdb ();
+ client = mi2_client_new (io_stream);
+
+ g_signal_connect (client, "log", G_CALLBACK (log_handler), NULL);
+
+ mi2_client_start_listening (client);
+
+ g_main_loop_run (main_loop);
+ g_main_loop_unref (main_loop);
+
+ return 0;
+}
diff --git a/contrib/mi2/test-stream-1.txt b/contrib/mi2/test-stream-1.txt
new file mode 100644
index 0000000..6c0cf32
--- /dev/null
+++ b/contrib/mi2/test-stream-1.txt
@@ -0,0 +1,55 @@
+=thread-group-added,id="i1"
+~"GNU gdb (GDB) Fedora 7.12.50.20170226-4.fc26\n"
+~"Copyright (C) 2017 Free Software Foundation, Inc.\n"
+~"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you
are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law. Type \"show
copying\"\nand \"show warranty\" for details.\n"
+~"This GDB was configured as \"x86_64-redhat-linux-gnu\".\nType \"show configuration\" for configuration
details."
+~"\nFor bug reporting instructions, please see:\n"
+~"<http://www.gnu.org/software/gdb/bugs/>.\n"
+~"Find the GDB manual and other documentation resources online
at:\n<http://www.gnu.org/software/gdb/documentation/>.\n"
+~"For help, type \"help\".\n"
+~"Type \"apropos word\" to search for commands related to \"word\"...\n"
+~"Reading symbols from ls..."
+~"Reading symbols from /home/christian/Projects/gnome-builder/ls..."
+~"(no debugging symbols found)...done.\n"
+~"(no debugging symbols found)...done.\n"
+(gdb)
+&"b main\n"
+~"Breakpoint 1 at 0x38b0\n"
+=breakpoint-created,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x00000000000038b0",at="<main>",thread-groups=["i1"],times="0",original-location="main"}
+^done
+(gdb)
+&"r\n"
+~"Starting program: /usr/bin/ls \n"
+=thread-group-started,id="i1",pid="4633"
+=thread-created,id="1",group-id="i1"
+=library-loaded,id="/lib64/ld-linux-x86-64.so.2",target-name="/lib64/ld-linux-x86-64.so.2",host-name="/lib64/ld-linux-x86-64.so.2",symbols-loaded="0",thread-group="i1"
+^running
+*running,thread-id="all"
+(gdb)
+=library-loaded,id="/lib64/libselinux.so.1",target-name="/lib64/libselinux.so.1",host-name="/lib64/libselinux.so.1",symbols-loaded="0",thread-group="i1"
+=library-loaded,id="/lib64/libcap.so.2",target-name="/lib64/libcap.so.2",host-name="/lib64/libcap.so.2",symbols-loaded="0",thread-group="i1"
+=library-loaded,id="/lib64/libc.so.6",target-name="/lib64/libc.so.6",host-name="/lib64/libc.so.6",symbols-loaded="0",thread-group="i1"
+=library-loaded,id="/lib64/libpcre.so.1",target-name="/lib64/libpcre.so.1",host-name="/lib64/libpcre.so.1",symbols-loaded="0",thread-group="i1"
+=library-loaded,id="/lib64/libdl.so.2",target-name="/lib64/libdl.so.2",host-name="/lib64/libdl.so.2",symbols-loaded="0",thread-group="i1"
+=library-loaded,id="/lib64/libpthread.so.0",target-name="/lib64/libpthread.so.0",host-name="/lib64/libpthread.so.0",symbols-loaded="0",thread-group="i1"
+~"[Thread debugging using libthread_db enabled]\n"
+~"Using host libthread_db library \"/lib64/libthread_db.so.1\".\n"
+=breakpoint-modified,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x00005555555578b0",at="<main>",thread-groups=["i1"],times="1",original-location="main"}
+~"\n"
+~"Breakpoint 1, 0x00005555555578b0 in main ()\n"
+*stopped,reason="breakpoint-hit",disp="keep",bkptno="1",frame={addr="0x00005555555578b0",func="main",args=[]},thread-id="1",stopped-threads="all",core="1"
+(gdb)
+&"c\n"
+~"Continuing.\n"
+^running
+*running,thread-id="all"
+(gdb)
+~"[Inferior 1 (process 4633) exited normally]\n"
+=thread-exited,id="1",group-id="i1"
+=thread-group-exited,id="i1",exit-code="0"
+*stopped,reason="exited-normally"
+(gdb)
+&"c\n"
+&"The program is not being run.\n"
+^error,msg="The program is not being run."
+(gdb)
diff --git a/contrib/mi2/test-stream.c b/contrib/mi2/test-stream.c
new file mode 100644
index 0000000..34f31ca
--- /dev/null
+++ b/contrib/mi2/test-stream.c
@@ -0,0 +1,132 @@
+/* test-stream.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-console-message.h"
+#include "mi2-input-stream.h"
+#include "mi2-output-stream.h"
+
+static GMainLoop *main_loop;
+
+static void
+read_message_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ Mi2InputStream *stream = (Mi2InputStream *)object;
+ g_autoptr(Mi2Message) message = NULL;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (MI2_IS_INPUT_STREAM (stream));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ message = mi2_input_stream_read_message_finish (stream, result, &error);
+ g_assert_no_error (error);
+
+ if (message != NULL)
+ mi2_input_stream_read_message_async (stream, NULL, read_message_cb, NULL);
+ else
+ g_main_loop_quit (main_loop);
+}
+
+static void
+test_basic_read (void)
+{
+ g_autoptr(Mi2InputStream) stream = NULL;
+ g_autoptr(GInputStream) base_stream = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GFile) file = NULL;
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+
+ file = g_file_new_for_path (TEST_DATA_DIR"/test-stream-1.txt");
+
+ base_stream = G_INPUT_STREAM (g_file_read (file, NULL, NULL));
+ g_assert_no_error (error);
+ g_assert (base_stream != NULL);
+
+ stream = mi2_input_stream_new (base_stream);
+ g_assert_no_error (error);
+ g_assert (stream != NULL);
+
+ mi2_input_stream_read_message_async (stream, NULL, read_message_cb, NULL);
+
+ g_main_loop_run (main_loop);
+ g_clear_pointer (&main_loop, g_main_loop_unref);
+}
+
+static void
+write_message_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ Mi2OutputStream *stream = (Mi2OutputStream *)object;
+ g_autoptr(GError) error = NULL;
+ gboolean r;
+
+ g_assert (MI2_IS_OUTPUT_STREAM (stream));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ r = mi2_output_stream_write_message_finish (stream, result, &error);
+ g_assert_no_error (error);
+ g_assert (r);
+
+ g_main_loop_quit (main_loop);
+}
+
+static void
+test_basic_write (void)
+{
+ g_autoptr(Mi2OutputStream) stream = NULL;
+ g_autoptr(GOutputStream) base_stream = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(Mi2Message) message = NULL;
+ const gchar *str;
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+
+ base_stream = g_memory_output_stream_new_resizable ();
+ g_assert_no_error (error);
+ g_assert (base_stream != NULL);
+
+ stream = mi2_output_stream_new (base_stream);
+ g_assert_no_error (error);
+ g_assert (stream != NULL);
+
+ message = g_object_new (MI2_TYPE_CONSOLE_MESSAGE,
+ "message", "this is a test message",
+ NULL);
+
+ mi2_output_stream_write_message_async (stream, message, NULL, write_message_cb, NULL);
+
+ g_main_loop_run (main_loop);
+
+ str = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (base_stream));
+ g_assert_cmpstr (str, ==, "~\"this is a test message\"\n");
+
+ g_clear_pointer (&main_loop, g_main_loop_unref);
+}
+
+gint
+main (gint argc,
+ gchar *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+ g_test_add_func ("/Mi2/InputStream/read_message_async", test_basic_read);
+ g_test_add_func ("/Mi2/InputStream/write_message_async", test_basic_write);
+ return g_test_run ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]