X-Git-Url: http://git.jankratochvil.net/?p=gdbmicli.git;a=blobdiff_plain;f=main.c;h=2ac00a719fe2d980582e3054d10cb1539a1d5109;hp=c43df326e92b4b06fc2995204bc0054a1a5282f5;hb=HEAD;hpb=f2ce960a8b335fdfd2a2defa26ffae990afa8049 diff --git a/main.c b/main.c index c43df32..2ac00a7 100644 --- a/main.c +++ b/main.c @@ -6,19 +6,25 @@ #include #include #include +#include #include "ansidecl.h" #include "mi_gdb.h" +#define fatal(fmt...) fatal_func (__FILE__, __LINE__, __PRETTY_FUNCTION__, fmt) #define gdb_assert assert +#define gdb_assert_not_reached(msg) fatal ("%s", (msg)) +#define _(x) x static ATTRIBUTE_NORETURN void -fatal (const char *fmt, ...) +fatal_func (const char *file, int line, const char *func, const char *fmt, ...) { va_list ap; + fprintf (stderr, "%s:%d:%s:", file, line, func); va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end (ap); + fputc ('\n', stderr); raise (SIGABRT); exit (EXIT_FAILURE); } @@ -34,6 +40,28 @@ xstrdup (const char *s) return retval; } +static void * +xmalloc (size_t size) +{ + void *p; + + gdb_assert (size != 0); + p = malloc (size); + gdb_assert (p != NULL); + return p; +} + +static void * +xrealloc (void *p, size_t size) +{ + if (p == NULL) + return xmalloc (size); + gdb_assert (size != 0); + p = realloc (p, size); + gdb_assert (p != NULL); + return p; +} + static void xfree (void *p) { @@ -55,10 +83,107 @@ xstrprintf (const char *fmt, ...) return retval; } +static jmp_buf gdb_readline_jmp_buf; + +static void +gdb_readline_sigint_handler (int signum) +{ + gdb_assert (signum == SIGINT); + longjmp (gdb_readline_jmp_buf, 1); +} + +static char * +gdb_readline (const char *prompt) +{ + char *retval; + sighandler_t saved_handler; + sigset_t mask; + int i; + + i = sigprocmask (SIG_SETMASK, NULL, &mask); + assert (i == 0); + saved_handler = signal (SIGINT, gdb_readline_sigint_handler); + if (setjmp (gdb_readline_jmp_buf) != 0) + { + rl_free_line_state (); + rl_cleanup_after_signal (); + + /* GDB prints this. */ + puts ("Quit"); + + i = sigprocmask (SIG_SETMASK, &mask, NULL); + assert (i == 0); + } + retval = readline (prompt); + saved_handler = signal (SIGINT, saved_handler); + gdb_assert (saved_handler == gdb_readline_sigint_handler); + return retval; +} + +static int console_cb_rows, console_cb_columns, console_cb_row, console_cb_column; +static bool console_cb_drop; + static void console_cb (const char *str, void *data) { - fputs (str, stdout); + const char *cs; + + if (console_cb_drop) + return; + if (console_cb_rows == 0) + { + fputs (str, stdout); + return; + } + + while (*str != '\0') + { + int columns; + size_t size; + char *answer; + + cs = strchr (str, '\n'); + if (cs == NULL) + cs = &str[strlen (str)]; + columns = min (cs - str, console_cb_columns - console_cb_column); + if (columns > 0) + { + size = fwrite (str, 1, columns, stdout); + gdb_assert (size == columns); + str += columns; + console_cb_column += columns; + continue; + } + if (*str == '\n') + str++; + else if (console_cb_column < console_cb_columns) + gdb_assert_not_reached ("we should not get here"); + putchar ('\n'); + console_cb_row++; + console_cb_column = 0; + if (console_cb_row < console_cb_rows - 1) + continue; + answer = gdb_readline (_("---Type to continue, " + "or q to quit---")); + for (cs = answer; isspace (*cs); cs++); + if (*cs == 'q') + { + xfree (answer); + puts ("Quit"); + console_cb_drop = true; + return; + } + xfree (answer); + console_cb_row = 0; + } +} + +static int +time_out_cb (void *data) +{ + fatal ("GDB response has timed out"); + /* NOTREACHED */ + return 0; } static ATTRIBUTE_UNUSED void @@ -90,16 +215,11 @@ history_save (int rc, void *arg) } 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; + gdb_assert (strchr (cmd, '\n') == 0); mi_send (h, "-interpreter-exec console \"%s\"\n", cmd); while (count > 0) @@ -122,6 +242,7 @@ default_command (mi_h *h, const char *cmd) count--; break; case MI_CL_ERROR: + count--; gdb_assert (rec->c->type == t_const); puts (rec->c->v.cstr); break; @@ -133,16 +254,139 @@ default_command (mi_h *h, const char *cmd) } } +static void +quit_command (mi_h *h, const char *cmd) +{ + exit (EXIT_SUCCESS); +} + +static char * +mi_escape (const char *cs) +{ + char *d, *retval = xmalloc (strlen (cs) * 2 + 1); + + d = retval; + while (*cs) + { + if (*cs == '"' || *cs == '\\') + *d++ = '\\'; + *d++ = *cs++; + } + *d = '\0'; + return retval; +} + +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 +commands_command_console_cb (const char *str, void *data) +{ + char **strptr = data; + + gdb_assert (*strptr == NULL); + *strptr = xstrdup (str); +} + +static void +commands_command (mi_h *h, const char *cmd_param) +{ + char *cmd = xstrdup (""); + size_t cmd_len = 0; + mi_output *res; + char *bpnum = NULL; + int nesting; + + if (*cmd_param == '\0') + { + mi_set_console_cb (h, commands_command_console_cb, &bpnum); + gdb_done (h, "output $bpnum"); + mi_set_console_cb (h, console_cb, NULL); + gdb_assert (bpnum != NULL); + cmd_param = bpnum; + } + + printf (_("Type commands for breakpoint(s) %s, one per line.\n" + "End with a line saying just \"end\".\n"), cmd_param); + + nesting = 0; + while (nesting >= 0) + { + char *prompt, *data, *data2, *start, *end; + size_t data2_len; + int do_nest = 0; + + prompt = xstrprintf ("%*s>", (int) nesting); + data = gdb_readline (prompt); + xfree (prompt); + if (data == NULL) + data = xstrdup ("end"); + + for (start = data; isspace (*start); start++); + for (end = data + strlen (data); end > start && isspace (end[-1]); end--); + data2 = xmalloc (end - start + 1); + memcpy (data2, start, end - start); + data2[end - start] = '\0'; + if (strcmp (data2, "python") == 0) + do_nest = 1; + if (strcmp (data2, "end") == 0) + do_nest = -1; + for (end = data2; *end && !isspace (*end); end++); + *end = '\0'; + + /* Here is a bug in GDB, it does not recognize command shortcuts. */ + if (strcmp (data2, "while") == 0 || strcmp (data2, "if") == 0 + || strcmp (data2, "commands") == 0 + || strcmp (data2, "while-stepping") == 0 + || strcmp (data2, "stepping") == 0 || strcmp (data2, "ws") == 0) + do_nest = 1; + xfree (data2); + + nesting += do_nest; + if (nesting < 0) + break; + + data2 = mi_escape (data); + xfree (data); + data2_len = strlen (data2); + cmd = xrealloc (cmd, cmd_len + 2 + data2_len + 2); + cmd[cmd_len] = ' '; + cmd[cmd_len + 1] = '"'; + memcpy (&cmd[cmd_len + 2], data2, data2_len); + cmd[cmd_len + 2 + data2_len] = '"'; + cmd[cmd_len + 2 + data2_len + 1] = '\0'; + cmd_len += 2 + data2_len + 1; + xfree (data2); + } + + mi_send (h, "-break-commands %s%s\n", cmd_param, cmd); + xfree (cmd); + xfree (bpnum); + + 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 const struct cmd { const char *name; + int min_shortcut; void (*func) (mi_h *h, const char *cmd); } cmds[] = { - { "q", quit_command }, - { "qu", quit_command }, - { "qui", quit_command }, - { "quit", quit_command }, + { "quit", 1, quit_command }, + { "commands", 4, commands_command }, }; static void @@ -158,28 +402,18 @@ executecommand (mi_h *h, const char *cmd) while (isalnum (*cs)) cs++; end = cs; + while (isspace (*cs)) + 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); + if (cmdp->min_shortcut <= end - start && end - start <= strlen (cmdp->name) + && strncmp (start, cmdp->name, end - start) == 0) + return cmdp->func (h, cs); 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); @@ -237,35 +471,132 @@ gdb_show_bool (mi_h *h, const char *setting) 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"); +} + +extern char **mi_gdb_argv; +extern void (*mi_gdb_start_hook) (mi_h *h); + +static void +start_hook (mi_h *h) +{ + on_exit (h_disconnect, h); + mi_set_console_cb (h, console_cb, NULL); + mi_set_time_out_cb (h, time_out_cb, NULL); + +// mi_set_to_gdb_cb (h, to_gdb_cb, NULL); +// mi_set_from_gdb_cb (h, from_gdb_cb, NULL); +} + int main (int argc, char **argv) { mi_h *h; - char *cmd; - const char *ex[argc]; - unsigned ex_count = 0, ex_used = 0; + mi_output *res; setbuf (stdout, NULL); - while (*++argv != NULL) - { - if (strcmp (*argv, "-ex") == 0 && argv[1] != NULL) - ex[ex_count++] = *++argv; - else - fatal ("Unknown parameter: %s", *argv); - } + mi_gdb_argv = xmalloc ((argc + 2) * sizeof (*mi_gdb_argv)); + memcpy (&mi_gdb_argv[2], &argv[1], argc * sizeof (*mi_gdb_argv)); + mi_gdb_argv[0] = "gdb"; + mi_gdb_argv[1] = "--interpreter=mi"; - mi_set_workaround (MI_PSYM_SEARCH, 0); + mi_gdb_start_hook = start_hook; h = mi_connect_local (); if (h == NULL) fatal ("Cannot connect to GDB"); - on_exit (h_disconnect, h); + /* First eat the prompt. Then run empty command so that additional results + from -ex or -x during mi_connect_local are flushed. */ + res = mi_get_response_blk (h); + gdb_assert (res == NULL); + default_command (h, "echo"); - mi_set_console_cb (h, console_cb, NULL); -// mi_set_to_gdb_cb (h, to_gdb_cb, NULL); -// mi_set_from_gdb_cb (h, from_gdb_cb, NULL); + completion_entry_function_h = h; + rl_completion_entry_function = completion_entry_function; + rl_readline_name = "gdb"; if (gdb_show_bool (h, "history save")) { @@ -281,19 +612,25 @@ main (int argc, char **argv) for (;;) { - if (ex_used < ex_count) - { - cmd = xstrdup (ex[ex_used++]); - printf ("(gdb) %s\n", cmd); - } + char *prompt, *cmd; + + prompt = gdb_show_string (h, "prompt"); + cmd = gdb_readline (prompt); + xfree (prompt); + + /* FIXME: -ex commands do not have pagination set. */ + if (!gdb_show_bool (h, "pagination")) + console_cb_rows = 0; else - { - cmd = readline ("(gdb) "); - if (cmd == NULL) - cmd = xstrdup ("quit"); - else - add_history (cmd); - } + rl_get_screen_size (&console_cb_rows, &console_cb_columns); + console_cb_drop = false; + console_cb_row = console_cb_column = 0; + + if (cmd == NULL) + cmd = xstrdup ("quit"); + else + add_history (cmd); + executecommand (h, cmd); xfree (cmd); }