+static ATTRIBUTE_UNUSED void
+to_gdb_cb (const char *str, void *data)
+{
+ printf ("to_gdb: %s\n", str);
+}
+
+static ATTRIBUTE_UNUSED void
+from_gdb_cb (const char *str, void *data)
+{
+ printf ("from_gdb: %s\n", str);
+}
+
+static void
+h_disconnect (int rc, void *arg)
+{
+ mi_h *h = arg;
+
+ mi_disconnect (h);
+}
+
+static void
+history_save (int rc, void *arg)
+{
+ const char *history_filename = arg;
+
+ write_history (history_filename);
+}
+
+static void
+quit_command (mi_h *h, const char *cmd)
+{
+ exit (EXIT_SUCCESS);
+}
+
+static void
+default_command (mi_h *h, const char *cmd)
+{
+ int count = 1;
+
+ mi_send (h, "-interpreter-exec console \"%s\"\n", cmd);
+
+ while (count > 0)
+ {
+ mi_output *rec, *res;
+
+ res = mi_get_response_blk (h);
+ gdb_assert (res != NULL);
+
+ for (rec = res; rec != NULL; rec = rec->next)
+ switch (rec->tclass)
+ {
+ case MI_CL_DONE:
+ count--;
+ break;
+ case MI_CL_RUNNING:
+ /* We do not get MI_CL_DONE here, wait for MI_CL_STOPPED. */
+ break;
+ case MI_CL_STOPPED:
+ count--;
+ break;
+ case MI_CL_ERROR:
+ count--;
+ gdb_assert (rec->c->type == t_const);
+ puts (rec->c->v.cstr);
+ break;
+ default:
+ fatal ("mi_get_rrecord == MI_CL_??? (%d)", rec->tclass);
+ }
+
+ mi_free_output (res);
+ }
+}
+
+static const struct cmd
+{
+ const char *name;
+ void (*func) (mi_h *h, const char *cmd);
+} cmds[] =
+{
+ { "q", quit_command },
+ { "qu", quit_command },
+ { "qui", quit_command },
+ { "quit", quit_command },
+};
+
+static void
+executecommand (mi_h *h, const char *cmd)
+{
+ const char *start, *end, *cs;
+ const struct cmd *cmdp;
+
+ cs = cmd;
+ while (isspace (*cs))
+ cs++;
+ start = cs;
+ while (isalnum (*cs))
+ cs++;
+ end = cs;
+
+ for (cmdp = cmds; cmdp < &cmds[LENGTH (cmds)]; cmdp++)
+ if (strlen (cmdp->name) == end - start
+ && strncmp (cmd, cmdp->name, end - start) == 0)
+ return cmdp->func (h, cmd);
+
+ return default_command (h, cmd);
+}
+
+static void
+gdb_done (mi_h *h, const char *command)
+{
+ mi_output *res;
+
+ mi_send (h, "%s\n", command);
+ res = mi_get_response_blk (h);
+ gdb_assert (res != NULL && res->next == NULL && res->tclass == MI_CL_DONE
+ && res->c == NULL);
+ mi_free_output (res);
+}
+
+static void
+gdb_set_string (mi_h *h, const char *setting, const char *value)
+{
+ char *cmd = xstrprintf ("-gdb-set %s %s", setting, value);
+
+ gdb_done (h, cmd);
+ xfree (cmd);
+}
+
+static void
+gdb_set_bool (mi_h *h, const char *setting, bool value)
+{
+ gdb_set_string (h, setting, value ? "on" : "off");
+}
+
+static char *
+gdb_show_string (mi_h *h, const char *setting)
+{
+ mi_output *res;
+ char *retval;
+
+ mi_send (h, "-gdb-show %s\n", setting);
+ res = mi_get_response_blk (h);
+ gdb_assert (res != NULL && res->next == NULL && res->tclass == MI_CL_DONE
+ && res->c != NULL && res->c->next == NULL
+ && res->c->type == t_const && strcmp (res->c->var, "value") == 0);
+ retval = xstrdup (res->c->v.cstr);
+ mi_free_output (res);
+ return retval;
+}
+
+static int
+gdb_show_int (mi_h *h, const char *setting)
+{
+ char *string = gdb_show_string (h, setting);
+ long l;
+ int retval;
+ char *end;
+
+ errno = 0;
+ retval = l = strtol (string, &end, 10);
+ gdb_assert (errno == 0 && (end == NULL || *end == '\0') && retval == l);
+ xfree (string);
+ return retval;
+}
+
+static bool
+gdb_show_bool (mi_h *h, const char *setting)
+{
+ char *string = gdb_show_string (h, setting);
+ bool retval;
+
+ retval = strcmp (string, "on") == 0;
+ gdb_assert (retval || strcmp (string, "off") == 0);
+ xfree (string);
+ return retval;
+}
+
+static mi_h *completion_entry_function_h;
+
+static char **completion_entry_function_data;
+static size_t completion_entry_function_data_used;
+static size_t completion_entry_function_data_allocated;
+
+static void
+completion_entry_function_console_cb (const char *str, void *data)
+{
+ int line_start = (intptr_t) data;
+ const char *cs;
+ char *s;
+
+ if (completion_entry_function_data_used
+ == completion_entry_function_data_allocated)
+ {
+ if (completion_entry_function_data_allocated > 0)
+ completion_entry_function_data_allocated *= 2;
+ else
+ completion_entry_function_data_allocated = 0x100;
+ completion_entry_function_data = xrealloc (completion_entry_function_data,
+ (sizeof (*completion_entry_function_data)
+ * completion_entry_function_data_allocated));
+ }
+
+ cs = strchr (str, '\n');
+ if (cs == NULL || cs[1] != '\0')
+ fatal ("Invalid GDB data: %s", str);
+
+ if (strncmp (rl_line_buffer, str, rl_point) != 0)
+ fatal ("Completion GDB data do not match, have \"%.*s\", got \"%.*s\".",
+ (int) rl_point, rl_line_buffer, (int) (cs - str), str);
+
+ s = xmalloc (cs - str - line_start + 1);
+ memcpy (s, &str[line_start], cs - str - line_start);
+ s[cs - str - line_start] = '\0';
+ completion_entry_function_data[completion_entry_function_data_used++] = s;
+}
+
+static char *
+completion_entry_function (const char *text, int matches)
+{
+ mi_h *h = completion_entry_function_h;
+
+ gdb_assert (matches >= 0);
+ if (matches == 0)
+ {
+ mi_output *res;
+ int line_start;
+
+ while (completion_entry_function_data_used)
+ xfree (completion_entry_function_data
+ [--completion_entry_function_data_used]);
+ xfree (completion_entry_function_data);
+ completion_entry_function_data = NULL;
+ completion_entry_function_data_used = 0;
+ completion_entry_function_data_allocated = 0;
+
+ gdb_assert (rl_point >= 0);
+ gdb_assert (strlen (rl_line_buffer) >= rl_point);
+ gdb_assert (strlen (text) <= rl_point);
+ line_start = rl_point - strlen (text);
+ gdb_assert (strncmp (text, &rl_line_buffer[line_start],
+ strlen (text)) == 0);
+ mi_send (h, "-interpreter-exec console \"complete %.*s\"\n",
+ (int) rl_point, rl_line_buffer);
+
+ mi_set_console_cb (h, completion_entry_function_console_cb,
+ (void *) (intptr_t) line_start);
+ res = mi_get_response_blk (h);
+ gdb_assert (res != NULL && res->next == NULL && res->tclass == MI_CL_DONE
+ && res->c == NULL);
+ mi_free_output (res);
+ mi_set_console_cb (h, console_cb, NULL);
+ }
+
+ if (matches < completion_entry_function_data_used)
+ return xstrdup (completion_entry_function_data[matches]);
+ else if (matches == completion_entry_function_data_used)
+ return NULL;
+ else
+ gdb_assert_not_reached ("too many matches");
+}
+