[geany/geany-plugins] 835bea: Merge pull request #202 from b4n/debugger/safer-gdb-mi-parsing
Frank Lanitz
git-noreply at xxxxx
Sun Nov 15 17:28:09 UTC 2015
Branch: refs/heads/master
Author: Frank Lanitz <frank at frank.uvena.de>
Committer: Frank Lanitz <frank at frank.uvena.de>
Date: Sun, 15 Nov 2015 17:28:09 UTC
Commit: 835bea07dc537efe2306c36e3654e67850b9b655
https://github.com/geany/geany-plugins/commit/835bea07dc537efe2306c36e3654e67850b9b655
Log Message:
-----------
Merge pull request #202 from b4n/debugger/safer-gdb-mi-parsing
debugger: Safer GDB/MI parsing and other fixes
Modified Paths:
--------------
debugger/src/Makefile.am
debugger/src/breakpoint.c
debugger/src/breakpoints.c
debugger/src/dbm_gdb.c
debugger/src/debug.c
debugger/src/debug_module.c
debugger/src/debug_module.h
debugger/src/dpaned.c
debugger/src/gdb_mi.c
debugger/src/gdb_mi.h
debugger/src/markers.c
debugger/src/plugin.c
debugger/src/tests/gdb_mi_test.expected
debugger/src/tests/gdb_mi_test.input
debugger/src/tests/gdb_mi_test.sh
debugger/src/tpage.c
Modified: debugger/src/Makefile.am
12 lines changed, 12 insertions(+), 0 deletions(-)
===================================================================
@@ -39,6 +39,8 @@ debugger_la_SOURCES = \
envtree.h \
gui.h \
gui.c \
+ gdb_mi.h \
+ gdb_mi.c \
keys.c \
keys.h \
atree.c \
@@ -72,4 +74,14 @@ debugger_la_SOURCES = \
debugger_la_LIBADD = $(COMMONLIBS) $(VTE_LIBS) -lutil
debugger_la_CFLAGS = $(AM_CFLAGS) $(VTE_CFLAGS) -DDBGPLUG_DATA_DIR=\"$(plugindatadir)\" -DPLUGIN_NAME=\"$(plugin)\"
+check_PROGRAMS = gdb_mi_test
+dist_check_SCRIPTS = tests/gdb_mi_test.sh
+dist_check_DATA = tests/gdb_mi_test.input tests/gdb_mi_test.expected
+TESTS = $(dist_check_SCRIPTS)
+
+gdb_mi_test_SOURCES = gdb_mi.c gdb_mi.h
+gdb_mi_test_CFLAGS = $(AM_CFLAGS) -DTEST
+gdb_mi_test_LDFLAGS =
+gdb_mi_test_LDADD = $(COMMONLIBS)
+
include $(top_srcdir)/build/cppcheck.mk
Modified: debugger/src/breakpoint.c
4 lines changed, 2 insertions(+), 2 deletions(-)
===================================================================
@@ -50,10 +50,10 @@ breakpoint* break_new(void)
breakpoint* break_new_full(const char* file, int line, const char* condition, int enabled, int hitscount)
{
breakpoint* bp = break_new();
- strcpy(bp->file, file);
+ strncpy(bp->file, file, G_N_ELEMENTS(bp->file) - 1);
bp->line = line;
if (condition)
- strcpy(bp->condition, condition);
+ strncpy(bp->condition, condition, G_N_ELEMENTS(bp->condition) - 1);
bp->enabled = enabled;
bp->hitscount = hitscount;
Modified: debugger/src/breakpoints.c
4 lines changed, 2 insertions(+), 2 deletions(-)
===================================================================
@@ -227,7 +227,7 @@ static void breaks_set_condition_debug(breakpoint* bp)
{
/* revert to old condition (taken from tree) */
gchar* oldcondition = bptree_get_condition(bp);
- strcpy(bp->condition, oldcondition);
+ strncpy(bp->condition, oldcondition, G_N_ELEMENTS(bp->condition) - 1);
g_free(oldcondition);
/* show error message */
dialogs_show_msgbox(GTK_MESSAGE_ERROR, "%s", debug_error_message());
@@ -596,7 +596,7 @@ void breaks_set_condition(const char* file, int line, const char* condition)
return;
/* change condition */
- strcpy(bp->condition, condition);
+ strncpy(bp->condition, condition, G_N_ELEMENTS(bp->condition) - 1);
/* handle setting condition instantly if debugger is idle or stopped
and request debug module interruption overwise */
Modified: debugger/src/dbm_gdb.c
950 lines changed, 370 insertions(+), 580 deletions(-)
===================================================================
@@ -38,6 +38,7 @@ extern GeanyData *geany_data;
#include "breakpoint.h"
#include "debug_module.h"
+#include "gdb_mi.h"
/* module features */
#define MODULE_FEATURES MF_ASYNC_BREAKS
@@ -155,9 +156,8 @@ static void shutdown_channel(GIOChannel ** ch)
{
if (*ch)
{
- GError *err = NULL;
gint fd = g_io_channel_unix_get_fd(*ch);
- g_io_channel_shutdown(*ch, TRUE, &err);
+ g_io_channel_shutdown(*ch, TRUE, NULL);
g_io_channel_unref(*ch);
*ch = NULL;
if (fd >= 0)
@@ -193,6 +193,7 @@ static void on_gdb_exit(GPid pid, gint status, gpointer data)
files = NULL;
g_source_remove(gdb_src_id);
+ gdb_src_id = 0;
dbg_cbs->set_exited(0);
}
@@ -226,19 +227,20 @@ static void gdb_input_write_line(const gchar *line)
GIOStatus st;
GError *err = NULL;
gsize count;
-
+ const char *p;
char command[1000];
- sprintf(command, "%s\n", line);
+ g_snprintf(command, sizeof command, "%s\n", line);
- while (strlen(command))
+ for (p = command; *p; p += count)
{
- st = g_io_channel_write_chars(gdb_ch_in, command, strlen(command), &count, &err);
- strcpy(command, command + count);
+ st = g_io_channel_write_chars(gdb_ch_in, p, strlen(p), &count, &err);
if (err || (st == G_IO_STATUS_ERROR) || (st == G_IO_STATUS_EOF))
{
#ifdef DEBUG_OUTPUT
dbg_cbs->send_message(err->message, "red");
#endif
+ if (err)
+ g_clear_error(&err);
break;
}
}
@@ -249,6 +251,8 @@ static void gdb_input_write_line(const gchar *line)
#ifdef DEBUG_OUTPUT
dbg_cbs->send_message(err->message, "red");
#endif
+ if (err)
+ g_clear_error(&err);
}
}
@@ -257,9 +261,11 @@ static void gdb_input_write_line(const gchar *line)
*/
static void free_queue_item(queue_item *item)
{
- g_string_free(item->message, TRUE);
+ if (item->message)
+ g_string_free(item->message, TRUE);
g_string_free(item->command, TRUE);
- g_string_free(item->error_message, TRUE);
+ if (item->error_message)
+ g_string_free(item->error_message, TRUE);
g_free(item);
}
@@ -307,36 +313,31 @@ static gboolean on_read_async_output(GIOChannel * src, GIOCondition cond, gpoint
{
gchar *line;
gsize length;
+ struct gdb_mi_record *record;
if (G_IO_STATUS_NORMAL != g_io_channel_read_line(src, &line, NULL, &length, NULL))
return TRUE;
- *(line + length) = '\0';
+ record = gdb_mi_record_parse(line);
- if ('^' == line[0])
+ if (record && record->type == '^')
{
/* got some result */
GList *lines;
GList *commands = (GList*)data;
- gchar *coma;
- g_source_remove(gdb_id_out);
+ if (gdb_id_out)
+ {
+ g_source_remove(gdb_id_out);
+ gdb_id_out = 0;
+ }
lines = read_until_prompt();
g_list_foreach(lines, (GFunc)g_free, NULL);
g_list_free (lines);
- coma = strchr(line, ',');
- if (coma)
- {
- *coma = '\0';
- coma++;
- }
- else
- coma = line + strlen(line);
-
- if (!strcmp(line, "^done"))
+ if (!strcmp(record->klass, "done"))
{
/* command completed succesfully - run next command if exists */
if (commands->next)
@@ -363,7 +364,11 @@ static gboolean on_read_async_output(GIOChannel * src, GIOCondition cond, gpoint
free_commands_queue(commands);
/* removing read callback */
- g_source_remove(gdb_id_out);
+ if (gdb_id_out)
+ {
+ g_source_remove(gdb_id_out);
+ gdb_id_out = 0;
+ }
/* update source files list */
update_files();
@@ -379,13 +384,12 @@ static gboolean on_read_async_output(GIOChannel * src, GIOCondition cond, gpoint
{
if (item->format_error_message)
{
- gchar* gdb_msg = g_strcompress(strstr(coma, "msg=\"") + strlen("msg=\""));
+ const gchar* gdb_msg = gdb_mi_result_var(record->first, "msg", GDB_MI_VAL_STRING);
GString *msg = g_string_new("");
g_string_printf(msg, item->error_message->str, gdb_msg);
dbg_cbs->report_error(msg->str);
- g_free(gdb_msg);
g_string_free(msg, FALSE);
}
else
@@ -401,6 +405,7 @@ static gboolean on_read_async_output(GIOChannel * src, GIOCondition cond, gpoint
}
}
+ gdb_mi_record_free(record);
g_free(line);
return TRUE;
@@ -415,18 +420,18 @@ static gboolean on_read_from_gdb(GIOChannel * src, GIOCondition cond, gpointer d
{
gchar *line;
gsize length;
- gboolean prompt;
+ const gchar *id;
+ struct gdb_mi_record *record;
if (G_IO_STATUS_NORMAL != g_io_channel_read_line(src, &line, NULL, &length, NULL))
return TRUE;
- prompt = !strcmp(line, GDB_PROMPT);
-
- *(line + length) = '\0';
+ record = gdb_mi_record_parse(line);
- if (!prompt)
+ if (! record || record->type != GDB_MI_TYPE_PROMPT)
{
- if ('~' == line[0])
+ line[length] = '\0';
+ if ((record && '~' == record->type) || '~' == line[0])
{
colorize_message(line);
}
@@ -437,161 +442,138 @@ static gboolean on_read_from_gdb(GIOChannel * src, GIOCondition cond, gpointer d
g_free(compressed);
}
}
-
- if (!target_pid && g_str_has_prefix(line, "=thread-group-created"))
+
+ if (! record)
{
- *(strrchr(line, '\"')) = '\0';
- target_pid = atoi(line + strlen("=thread-group-created,id=\""));
+ g_free(line);
+ return TRUE;
}
- else if (!target_pid && g_str_has_prefix(line, "=thread-group-started"))
+
+ if (!target_pid &&
+ /* FIXME: =thread-group-created doesn't seem to exist, at least not in latest GDB docs */
+ (gdb_mi_record_matches(record, '=', "thread-group-created", "id", &id, NULL) ||
+ gdb_mi_record_matches(record, '=', "thread-group-started", "pid", &id, NULL)))
{
- *(strrchr(line, '\"')) = '\0';
- target_pid = atoi(strrchr(line, '\"') + 1);
+ target_pid = atoi(id);
}
- else if (g_str_has_prefix(line, "=thread-created"))
+ else if (gdb_mi_record_matches(record, '=', "thread-created", "id", &id, NULL))
{
- int thread_id;
-
- *(strrchr(line, ',') - 1) = '\0';
- thread_id = atoi(line + strlen("=thread-created,id=\""));
- dbg_cbs->add_thread(thread_id);
+ dbg_cbs->add_thread(atoi(id));
}
- else if (g_str_has_prefix(line, "=thread-exited"))
+ else if (gdb_mi_record_matches(record, '=', "thread-exited", "id", &id, NULL))
{
- int thread_id;
-
- *(strrchr(line, ',') - 1) = '\0';
- thread_id = atoi(line + strlen("=thread-exited,id=\""));
- dbg_cbs->remove_thread(thread_id);
+ dbg_cbs->remove_thread(atoi(id));
}
- else if (g_str_has_prefix(line, "=library-loaded") || g_str_has_prefix(line, "=library-unloaded"))
+ else if (gdb_mi_record_matches(record, '=', "library-loaded", NULL) ||
+ gdb_mi_record_matches(record, '=', "library-unloaded", NULL))
{
file_refresh_needed = TRUE;
}
- else if (*line == '*')
+ else if (gdb_mi_record_matches(record, '*', "running", NULL))
+ dbg_cbs->set_run();
+ else if (gdb_mi_record_matches(record, '*', "stopped", NULL))
{
- /* asyncronous record found */
- char *record = NULL;
- if ( (record = strchr(line, ',')) )
+ const gchar *reason;
+
+ /* removing read callback (will pulling all output left manually) */
+ if (gdb_id_out)
{
- *record = '\0';
- record++;
+ g_source_remove(gdb_id_out);
+ gdb_id_out = 0;
+ }
+
+ /* looking for a reason to stop */
+ if ((reason = gdb_mi_result_var(record->first, "reason", GDB_MI_VAL_STRING)) != NULL)
+ {
+ if (!strcmp(reason, "breakpoint-hit"))
+ stop_reason = SR_BREAKPOINT_HIT;
+ else if (!strcmp(reason, "end-stepping-range"))
+ stop_reason = SR_END_STEPPING_RANGE;
+ else if (!strcmp(reason, "signal-received"))
+ stop_reason = SR_SIGNAL_RECIEVED;
+ else if (!strcmp(reason, "exited-normally"))
+ stop_reason = SR_EXITED_NORMALLY;
+ else if (!strcmp(reason, "exited-signalled"))
+ stop_reason = SR_EXITED_SIGNALLED;
+ else if (!strcmp(reason, "exited"))
+ stop_reason = SR_EXITED_WITH_CODE;
+ /* FIXME: handle "location-reached" */
}
else
- record = line + strlen(line);
+ {
+ /* somehow, sometimes there can be no stop reason */
+ stop_reason = SR_EXITED_NORMALLY;
+ }
- if (!strcmp(line, "*running"))
- dbg_cbs->set_run();
- else if (!strcmp(line, "*stopped"))
+ if (SR_BREAKPOINT_HIT == stop_reason || SR_END_STEPPING_RANGE == stop_reason || SR_SIGNAL_RECIEVED == stop_reason)
{
- char *reason;
+ const gchar *thread_id = gdb_mi_result_var(record->first, "thread-id", GDB_MI_VAL_STRING);
- /* removing read callback (will pulling all output left manually) */
- g_source_remove(gdb_id_out);
+ active_frame = 0;
- /* looking for a reason to stop */
- reason = strstr(record, "reason=\"");
- if (reason)
+ if (SR_BREAKPOINT_HIT == stop_reason || SR_END_STEPPING_RANGE == stop_reason)
{
- char *next;
-
- reason += strlen("reason=\"");
- next = strstr(reason, "\"") + 1;
- *(next - 1) = '\0';
- if (!strcmp(reason, "breakpoint-hit"))
- stop_reason = SR_BREAKPOINT_HIT;
- else if (!strcmp(reason, "end-stepping-range"))
- stop_reason = SR_END_STEPPING_RANGE;
- else if (!strcmp(reason, "signal-received"))
- stop_reason = SR_SIGNAL_RECIEVED;
- else if (!strcmp(reason, "exited-normally"))
- stop_reason = SR_EXITED_NORMALLY;
- else if (!strcmp(reason, "exited-signalled"))
- stop_reason = SR_EXITED_SIGNALLED;
- else if (!strcmp(reason, "exited"))
- stop_reason = SR_EXITED_WITH_CODE;
- }
- else
- {
- /* somehow, sometimes there can be no stop reason */
- stop_reason = SR_EXITED_NORMALLY;
- }
-
- if (SR_BREAKPOINT_HIT == stop_reason || SR_END_STEPPING_RANGE == stop_reason || SR_SIGNAL_RECIEVED == stop_reason)
- {
- gchar *thread_id = strstr(reason + strlen(reason) + 1,"thread-id=\"") + strlen("thread-id=\"");
- *(strchr(thread_id, '\"')) = '\0';
-
- active_frame = 0;
+ /* update autos */
+ update_autos();
- if (SR_BREAKPOINT_HIT == stop_reason || SR_END_STEPPING_RANGE == stop_reason)
- {
- /* update autos */
- update_autos();
-
- /* update watches */
- update_watches();
-
- /* update files */
- if (file_refresh_needed)
- {
- update_files();
- file_refresh_needed = FALSE;
- }
-
- dbg_cbs->set_stopped(atoi(thread_id));
- }
- else
+ /* update watches */
+ update_watches();
+
+ /* update files */
+ if (file_refresh_needed)
{
- if (!requested_interrupt)
- dbg_cbs->report_error(_("Program received a signal"));
- else
- requested_interrupt = FALSE;
-
- dbg_cbs->set_stopped(atoi(thread_id));
+ update_files();
+ file_refresh_needed = FALSE;
}
}
- else if (stop_reason == SR_EXITED_NORMALLY || stop_reason == SR_EXITED_SIGNALLED || stop_reason == SR_EXITED_WITH_CODE)
+ else
{
- if (stop_reason == SR_EXITED_WITH_CODE)
- {
- gchar *code;
- gchar *message;
+ if (!requested_interrupt)
+ dbg_cbs->report_error(_("Program received a signal"));
+ else
+ requested_interrupt = FALSE;
+ }
- code = strstr(reason + strlen(reason) + 1,"exit-code=\"") + strlen("exit-code=\"");
- *(strchr(code, '\"')) = '\0';
- message = g_strdup_printf(_("Program exited with code \"%i\""), (int)(char)strtol(code, NULL, 8));
- dbg_cbs->report_error(message);
+ dbg_cbs->set_stopped(thread_id ? atoi(thread_id) : 0);
+ }
+ else if (stop_reason == SR_EXITED_NORMALLY || stop_reason == SR_EXITED_SIGNALLED || stop_reason == SR_EXITED_WITH_CODE)
+ {
+ if (stop_reason == SR_EXITED_WITH_CODE)
+ {
+ const gchar *exit_code = gdb_mi_result_var(record->first, "exit-code", GDB_MI_VAL_STRING);
+ long int code = exit_code ? strtol(exit_code, NULL, 8) : 0;
+ gchar *message;
- g_free(message);
- }
+ message = g_strdup_printf(_("Program exited with code \"%i\""), (int)(char)code);
+ dbg_cbs->report_error(message);
- stop();
+ g_free(message);
}
+
+ stop();
}
}
- else if (g_str_has_prefix (line, "^error"))
+ else if (gdb_mi_record_matches(record, '^', "error", NULL))
{
GList *lines, *iter;
- char *msg;
+ const gchar *msg = gdb_mi_result_var(record->first, "msg", GDB_MI_VAL_STRING);
/* removing read callback (will pulling all output left manually) */
- g_source_remove(gdb_id_out);
+ if (gdb_id_out)
+ {
+ g_source_remove(gdb_id_out);
+ gdb_id_out = 0;
+ }
/* set debugger stopped if is running */
if (DBS_STOPPED != debug_get_state())
{
- gchar *thread_id = strstr(line + strlen(line) + 1,"thread-id=\"");
- *(strchr(thread_id, '\"')) = '\0';
+ /* FIXME: does GDB/MI ever return a thread-id with an error?? */
+ const gchar *thread_id = gdb_mi_result_var(record->first, "thread-id", GDB_MI_VAL_STRING);
- dbg_cbs->set_stopped(atoi(thread_id));
+ dbg_cbs->set_stopped(thread_id ? atoi(thread_id) : 0);
}
- /* get message */
- msg = strstr(line, "msg=\"") + strlen("msg=\"");
- *strrchr(msg, '\"') = '\0';
- msg = g_strcompress(msg);
-
/* reading until prompt */
lines = read_until_prompt();
for (iter = lines; iter; iter = iter->next)
@@ -605,11 +587,10 @@ static gboolean on_read_from_gdb(GIOChannel * src, GIOCondition cond, gpointer d
/* send error message */
dbg_cbs->report_error(msg);
-
- g_free(msg);
}
g_free(line);
+ gdb_mi_record_free(record);
return TRUE;
}
@@ -637,7 +618,7 @@ static void exec_async_command(const gchar* command)
* i.e. reading output right
* after execution
*/
-static result_class exec_sync_command(const gchar* command, gboolean wait4prompt, gchar** command_record)
+static result_class exec_sync_command(const gchar* command, gboolean wait4prompt, struct gdb_mi_record ** command_record)
{
GList *lines, *iter;
result_class rc;
@@ -652,6 +633,9 @@ static result_class exec_sync_command(const gchar* command, gboolean wait4prompt
if (!wait4prompt)
return RC_DONE;
+ if (command_record)
+ *command_record = NULL;
+
lines = read_until_prompt();
#ifdef DEBUG_OUTPUT
@@ -666,42 +650,34 @@ static result_class exec_sync_command(const gchar* command, gboolean wait4prompt
for (iter = lines; iter; iter = iter->next)
{
gchar *line = (gchar*)iter->data;
+ struct gdb_mi_record *record = gdb_mi_record_parse(line);
- if ('^' == line[0])
+ if (record && '^' == record->type)
{
- gchar* coma = strchr(line, ',');
- if (coma)
- {
- *coma = '\0';
- coma++;
- }
- else
- coma = line + strlen(line);
-
- if (command_record)
- {
- *command_record = (gchar*)g_malloc(strlen(coma) + 1);
- strcpy(*command_record, coma);
- }
-
- if (!strcmp(line, "^done"))
+ if (gdb_mi_record_matches(record, '^', "done", NULL))
rc = RC_DONE;
- else if (!strcmp(line, "^error"))
+ else if (gdb_mi_record_matches(record, '^', "error", NULL))
{
/* save error message */
- gchar* msg = g_strcompress(strstr(coma, "msg=\"") + strlen("msg=\""));
- strcpy(err_message, msg);
- g_free(msg);
+ const gchar *msg = gdb_mi_result_var(record->first, "msg", GDB_MI_VAL_STRING);
+ strncpy(err_message, msg ? msg : "", G_N_ELEMENTS(err_message) - 1);
rc = RC_ERROR;
}
- else if (!strcmp(line, "^exit"))
+ else if (gdb_mi_record_matches(record, '^', "exit", NULL))
rc = RC_EXIT;
+
+ if (command_record)
+ {
+ *command_record = record;
+ record = NULL;
+ }
}
- else if ('&' != line[0])
+ else if (! record || '&' != record->type)
{
colorize_message (line);
}
+ gdb_mi_record_free(record);
}
g_list_foreach(lines, (GFunc)g_free, NULL);
@@ -715,7 +691,6 @@ static result_class exec_sync_command(const gchar* command, gboolean wait4prompt
*/
static gboolean run(const gchar* file, const gchar* commandline, GList* env, GList *witer, GList *biter, const gchar* terminal_device, dbg_callbacks* callbacks)
{
- GError *err = NULL;
const gchar *exclude[] = { "LANG", NULL };
gchar **gdb_env = utils_copy_environment(exclude, "LANG", "C", NULL);
gchar *working_directory = g_path_get_dirname(file);
@@ -730,7 +705,7 @@ static gboolean run(const gchar* file, const gchar* commandline, GList* env, GLi
/* spawn GDB */
if (!g_spawn_async_with_pipes(working_directory, (gchar**)gdb_args, gdb_env,
GDB_SPAWN_FLAGS, NULL,
- NULL, &gdb_pid, &gdb_in, &gdb_out, NULL, &err))
+ NULL, &gdb_pid, &gdb_in, &gdb_out, NULL, NULL))
{
dbg_cbs->report_error(_("Failed to spawn gdb process"));
g_free(working_directory);
@@ -944,7 +919,7 @@ static void step_out(void)
static void execute_until(const gchar *file, int line)
{
gchar command[1000];
- sprintf(command, "-exec-until %s:%i", file, line);
+ g_snprintf(command, sizeof command, "-exec-until %s:%i", file, line);
exec_async_command(command);
}
@@ -953,43 +928,41 @@ static void execute_until(const gchar *file, int line)
*/
static int get_break_number(char* file, int line)
{
- gchar *record, *bstart;
+ struct gdb_mi_record *record;
+ const struct gdb_mi_result *table, *body, *bkpt;
exec_sync_command("-break-list", TRUE, &record);
- bstart = record;
+ if (! record)
+ return -1;
- while ( (bstart = strstr(bstart, "bkpt=")) )
+ table = gdb_mi_result_var(record->first, "BreakpointTable", GDB_MI_VAL_LIST);
+ body = gdb_mi_result_var(table, "body", GDB_MI_VAL_LIST);
+ gdb_mi_result_foreach_matched (bkpt, body, "bkpt", GDB_MI_VAL_LIST)
{
- gchar *fname, *file_quoted;
- int num, bline;
- gboolean break_found;
+ const gchar *number = gdb_mi_result_var(bkpt->val->list, "number", GDB_MI_VAL_STRING);
+ const gchar *location = gdb_mi_result_var(bkpt->val->list, "original-location", GDB_MI_VAL_STRING);
+ const gchar *colon;
+ gboolean break_found = FALSE;
- bstart += strlen("bkpt={number=\"");
- *strchr(bstart, '\"') = '\0';
- num = atoi(bstart);
-
- bstart += strlen(bstart) + 1;
- bstart = strstr(bstart, "original-location=\"") + strlen("original-location=\"");
- *strchr(bstart, ':') = '\0';
- fname = bstart;
-
- bstart += strlen(bstart) + 1;
- *strchr(bstart, '\"') = '\0';
- bline = atoi(bstart);
+ if (! number || ! location)
+ continue;
- file_quoted = g_strdup_printf("\\\"%s\\\"", file);
- break_found = !strcmp(fname, file_quoted) && bline == line;
- g_free(file_quoted);
-
+ colon = strrchr(location, ':');
+ if (colon && atoi(colon + 1) == line)
+ {
+ gchar *fname = g_strndup(location, colon - location);
+ /* FIXME: the check used to be made against \"file\" (e.g. file surrounded
+ * by backslash-quote), but that's not at least how GDB 7.7 does it */
+ break_found = strcmp(fname, file) == 0;
+ g_free(fname);
+ }
if (break_found)
{
- return num;
+ return atoi(number);
}
-
- bstart += strlen(bstart) + 1;
- }
+ }
- free(record);
+ gdb_mi_record_free(record);
return -1;
}
@@ -1003,45 +976,46 @@ static gboolean set_break(breakpoint* bp, break_set_activity bsa)
if (BSA_NEW_BREAK == bsa)
{
/* new breakpoint */
-
- char *pos;
- int number;
- gchar *record = NULL;
+ struct gdb_mi_record *record;
+ const struct gdb_mi_result *bkpt;
+ const gchar *number;
+ int num = 0;
/* 1. insert breakpoint */
- sprintf (command, "-break-insert \"\\\"%s\\\":%i\"", bp->file, bp->line);
- if (RC_DONE != exec_sync_command(command, TRUE, &record))
+ g_snprintf(command, sizeof command, "-break-insert \"\\\"%s\\\":%i\"", bp->file, bp->line);
+ if (RC_DONE != exec_sync_command(command, TRUE, &record) || !record)
{
- g_free(record);
- sprintf (command, "-break-insert -f \"\\\"%s\\\":%i\"", bp->file, bp->line);
- if (RC_DONE != exec_sync_command(command, TRUE, &record))
+ gdb_mi_record_free(record);
+ record = NULL;
+ g_snprintf(command, sizeof command, "-break-insert -f \"\\\"%s\\\":%i\"", bp->file, bp->line);
+ if (RC_DONE != exec_sync_command(command, TRUE, &record) || !record)
{
- g_free(record);
+ gdb_mi_record_free(record);
return FALSE;
}
}
/* lookup break-number */
- pos = strstr(record, "number=\"") + strlen("number=\"");
- *strchr(pos, '\"') = '\0';
- number = atoi(pos);
- g_free(record);
+ bkpt = gdb_mi_result_var(record->first, "bkpt", GDB_MI_VAL_LIST);
+ if ((number = gdb_mi_result_var(bkpt, "number", GDB_MI_VAL_STRING)))
+ num = atoi(number);
+ gdb_mi_record_free(record);
/* 2. set hits count if differs from 0 */
if (bp->hitscount)
{
- sprintf (command, "-break-after %i %i", number, bp->hitscount);
+ g_snprintf(command, sizeof command, "-break-after %i %i", num, bp->hitscount);
exec_sync_command(command, TRUE, NULL);
}
/* 3. set condition if exists */
if (strlen(bp->condition))
{
- sprintf (command, "-break-condition %i %s", number, bp->condition);
+ g_snprintf(command, sizeof command, "-break-condition %i %s", num, bp->condition);
if (RC_DONE != exec_sync_command(command, TRUE, NULL))
return FALSE;
}
/* 4. disable if disabled */
if (!bp->enabled)
{
- sprintf (command, "-break-disable %i", number);
+ g_snprintf(command, sizeof command, "-break-disable %i", num);
exec_sync_command(command, TRUE, NULL);
}
@@ -1055,11 +1029,11 @@ static gboolean set_break(breakpoint* bp, break_set_activity bsa)
return FALSE;
if (BSA_UPDATE_ENABLE == bsa)
- sprintf (command, bp->enabled ? "-break-enable %i" : "-break-disable %i", bnumber);
+ g_snprintf(command, sizeof command, bp->enabled ? "-break-enable %i" : "-break-disable %i", bnumber);
else if (BSA_UPDATE_HITS_COUNT == bsa)
- sprintf (command, "-break-after %i %i", bnumber, bp->hitscount);
+ g_snprintf(command, sizeof command, "-break-after %i %i", bnumber, bp->hitscount);
else if (BSA_UPDATE_CONDITION == bsa)
- sprintf (command, "-break-condition %i %s", bnumber, bp->condition);
+ g_snprintf(command, sizeof command, "-break-condition %i %s", bnumber, bp->condition);
return RC_DONE == exec_sync_command(command, TRUE, NULL);
}
@@ -1079,7 +1053,7 @@ static gboolean remove_break(breakpoint* bp)
result_class rc;
gchar command[100];
- sprintf(command, "-break-delete %i", number);
+ g_snprintf(command, sizeof command, "-break-delete %i", number);
rc = exec_sync_command(command, TRUE, NULL);
return RC_DONE == rc;
@@ -1115,63 +1089,34 @@ static void set_active_frame(int frame_number)
*/
static GList* get_stack(void)
{
- gchar* record = NULL;
+ struct gdb_mi_record *record = NULL;
+ const struct gdb_mi_result *stack_node, *frame_node;
GList *stack = NULL;
- gchar **frames, **next;
- result_class rc;
- rc = exec_sync_command("-stack-list-frames", TRUE, &record);
- if (RC_DONE != rc)
+ if (RC_DONE != exec_sync_command("-stack-list-frames", TRUE, &record) || ! record)
+ {
+ gdb_mi_record_free(record);
return NULL;
+ }
- frames = g_strsplit(record, "frame=", 0);
- next = frames + 1;
- while (*next)
+ stack_node = gdb_mi_result_var(record->first, "stack", GDB_MI_VAL_LIST);
+ gdb_mi_result_foreach_matched (frame_node, stack_node, "frame", GDB_MI_VAL_LIST)
{
+ const gchar *addr = gdb_mi_result_var(frame_node->val->list, "addr", GDB_MI_VAL_STRING);
+ const gchar *func = gdb_mi_result_var(frame_node->val->list, "func", GDB_MI_VAL_STRING);
+ const gchar *line = gdb_mi_result_var(frame_node->val->list, "line", GDB_MI_VAL_STRING);
+ const gchar *file, *fullname;
frame *f = frame_new();
- int line;
- gchar *pos, *fullname, *file, *from;
-
- /* adresss */
- pos = strstr(*next, "addr=\"") + strlen("addr=\"");
- *strchr(pos, '\"') = '\0';
- f->address = g_strdup(pos);
- pos += strlen(pos) + 1;
-
- /* function */
- pos = strstr(pos, "func=\"") + strlen("func=\"");
- *strchr(pos, '\"') = '\0';
- f->function = g_strdup(pos);
- pos += strlen(pos) + 1;
+
+ f->address = g_strdup(addr);
+ f->function = g_strdup(func);
/* file: fullname | file | from */
- fullname = strstr(pos, "fullname=\"");
- file = strstr(pos, "file=\"");
- from = strstr(pos, "from=\"");
-
- if (fullname)
- {
- fullname += strlen("fullname=\"");
- pos = fullname;
- *strchr(pos, '\"') = '\0';
- f->file = g_strdup(pos);
- pos += strlen(pos) + 1;
- }
- else if (file)
+ if ((fullname = file = gdb_mi_result_var(frame_node->val->list, "fullname", GDB_MI_VAL_STRING)) ||
+ (file = gdb_mi_result_var(frame_node->val->list, "file", GDB_MI_VAL_STRING)) ||
+ (file = gdb_mi_result_var(frame_node->val->list, "from", GDB_MI_VAL_STRING)))
{
- file += strlen("file=\"");
- pos = file;
- *strchr(pos, '\"') = '\0';
- f->file = g_strdup(pos);
- pos += strlen(pos) + 1;
- }
- else if (from)
- {
- from += strlen("from=\"");
- pos = from;
- *strchr(pos, '\"') = '\0';
- f->file = g_strdup(pos);
- pos += strlen(pos) + 1;
+ f->file = g_strdup(file);
}
else
{
@@ -1182,168 +1127,16 @@ static GList* get_stack(void)
f->have_source = fullname ? TRUE : FALSE;
/* line */
- line = 0;
- pos = strstr(pos, "line=\"");
- if (pos)
- {
- pos += strlen("line=\"");
- *strchr(pos, '\"') = '\0';
- line = atoi(pos);
- pos += strlen(pos) + 1;
- }
- f->line = line;
+ f->line = line ? atoi(line) : 0;
stack = g_list_append(stack, f);
-
- next++;
}
- g_strfreev(frames);
-
- free(record);
+ gdb_mi_record_free(record);
return stack;
}
/*
- * unescapes hex values (\0xXXX) to readable chars
- * converting it from wide character value to char
- */
-static gchar* unescape_hex_values(gchar *src)
-{
- GString *dest = g_string_new("");
-
- gchar *slash;
- while ( (slash = strstr(src, "\\x")) )
- {
- char hex[4] = { 0, 0, 0, '\0' };
- wchar_t wc;
-
- /* append what has been missed
- unescaping it in advance */
- if (slash - src)
- {
- gchar *missed = g_strndup(src, slash - src);
- gchar *unescaped = g_strcompress(missed);
- g_string_append(dest, unescaped);
- g_free(missed);
- g_free(unescaped);
- }
-
- strncpy(hex, slash + 2, 3);
- wc = (wchar_t)strtol(hex, NULL, 16);
-
- if (iswalpha(wc))
- {
- gchar mb[5];
- int len = wctomb(mb, wc);
- mb[len] = '\0';
- g_string_append(dest, mb);
- }
- else
- g_string_append_len(dest, slash, 5);
-
- src = slash + 5;
- }
-
- if (strlen(src))
- {
- gchar *unescaped = g_strcompress(src);
- g_string_append(dest, unescaped);
- g_free(unescaped);
- }
-
- return g_string_free(dest, FALSE);
-}
-
-/*
- * checks if pc pointer points to the
- * valid printable charater
- */
-static gboolean isvalidcharacter(gchar *pc, gboolean utf8)
-{
- if (utf8)
- return -1 != g_utf8_get_char_validated(pc, -1);
- else
- return isprint(*pc);
-}
-
-/*
- * unescapes string, handles octal characters representations
- */
-static gchar* unescape_octal_values(gchar *text)
-{
- GString *value = g_string_new("");
-
- gboolean utf8 = g_str_has_suffix(getenv("LANG"), "UTF-8");
-
- gchar *tmp = g_strdup(text);
- gchar *unescaped = g_strcompress(tmp);
-
- gchar *pos = unescaped;
- while (*pos)
- {
- if (isvalidcharacter(pos, utf8))
- {
- if (utf8)
- {
- /* valid utf8 character, copy to output
- and move to the next character */
- gchar *next = g_utf8_next_char(pos);
- g_string_append_len(value, pos, next - pos);
- pos = next;
- }
- else
- {
- g_string_append_len(value, pos++, 1);
- }
- }
- else
- {
- /* not a valid character, convert it to its octal representation
- and append to the result string */
- gchar *invalid = g_strndup(pos, 1);
- gchar *escaped = g_strescape(invalid, NULL);
-
- g_string_append(value, escaped);
-
- g_free(escaped);
- g_free(invalid);
-
- pos += 1;
- }
- }
-
- g_free(tmp);
-
- return g_string_free (value, FALSE);
-}
-
-/*
- * unescapes value string, handles hexidecimal and octal characters representations
- */
-static gchar *unescape(gchar *text)
-{
- gchar *retval = NULL;
-
- /* create string copy */
- gchar *value = g_strdup(text);
-
- /* make first unescaping */
- gchar *tmp = g_strcompress(value);
-
- /* make first unescaping */
- if (strstr(tmp, "\\x"))
- retval = unescape_hex_values(tmp);
- else
- retval = unescape_octal_values(tmp);
-
- g_free(tmp);
- g_free(value);
-
- return retval;
-}
-
-/*
* updates variables from vars list
*/
static void get_variables (GList *vars)
@@ -1355,56 +1148,51 @@ static void get_variables (GList *vars)
variable *var = (variable*)vars->data;
gchar *varname = var->internal->str;
- gchar *record = NULL;
- gchar *pos;
- gchar *expression;
- int numchild;
- gchar *value;
+ struct gdb_mi_record *record = NULL;
+ const gchar *expression = NULL;
+ const gchar *numchild = NULL;
+ const gchar *value = NULL;
+ const gchar *type = NULL;
/* path expression */
- sprintf(command, "-var-info-path-expression \"%s\"", varname);
+ g_snprintf(command, sizeof command, "-var-info-path-expression \"%s\"", varname);
exec_sync_command(command, TRUE, &record);
- pos = strstr(record, "path_expr=\"") + strlen("path_expr=\"");
- *(strrchr(pos, '\"')) = '\0';
- expression = unescape(pos);
- g_string_assign(var->expression, expression);
- g_free(expression);
- g_free(record);
+ if (record)
+ expression = gdb_mi_result_var(record->first, "path_expr", GDB_MI_VAL_STRING);
+ g_string_assign(var->expression, expression ? expression : "");
+ gdb_mi_record_free(record);
/* children number */
- sprintf(command, "-var-info-num-children \"%s\"", varname);
+ g_snprintf(command, sizeof command, "-var-info-num-children \"%s\"", varname);
exec_sync_command(command, TRUE, &record);
- pos = strstr(record, "numchild=\"") + strlen("numchild=\"");
- *(strchr(pos, '\"')) = '\0';
- numchild = atoi(pos);
- var->has_children = numchild > 0;
- g_free(record);
+ if (record)
+ numchild = gdb_mi_result_var(record->first, "numchild", GDB_MI_VAL_STRING);
+ var->has_children = numchild && atoi(numchild) > 0;
+ gdb_mi_record_free(record);
/* value */
- sprintf(command, "-data-evaluate-expression \"%s\"", var->expression->str);
+ g_snprintf(command, sizeof command, "-data-evaluate-expression \"%s\"", var->expression->str);
exec_sync_command(command, TRUE, &record);
- pos = strstr(record, "value=\"");
- if (!pos)
+ if (record)
+ value = gdb_mi_result_var(record->first, "value", GDB_MI_VAL_STRING);
+ if (!value)
{
- g_free(record);
- sprintf(command, "-var-evaluate-expression \"%s\"", varname);
+ gdb_mi_record_free(record);
+ g_snprintf(command, sizeof command, "-var-evaluate-expression \"%s\"", varname);
exec_sync_command(command, TRUE, &record);
- pos = strstr(record, "value=\"");
+ if (record)
+ value = gdb_mi_result_var(record->first, "value", GDB_MI_VAL_STRING);
}
- pos += + strlen("value=\"");
- *(strrchr(pos, '\"')) = '\0';
- value = unescape(pos);
- g_string_assign(var->value, value);
- g_free(value);
- g_free(record);
+ g_string_assign(var->value, value ? value : "");
+ gdb_mi_record_free(record);
/* type */
- sprintf(command, "-var-info-type \"%s\"", varname);
+ g_snprintf(command, sizeof command, "-var-info-type \"%s\"", varname);
exec_sync_command(command, TRUE, &record);
- pos = strstr(record, "type=\"") + strlen("type=\"");
- *(strchr(pos, '\"')) = '\0';
- g_string_assign(var->type, pos);
- g_free(record);
+ if (record)
+ type = gdb_mi_result_var(record->first, "type", GDB_MI_VAL_STRING);
+ g_string_assign(var->type, type ? type : "");
+ gdb_mi_record_free(record);
vars = vars->next;
}
@@ -1415,9 +1203,9 @@ static void get_variables (GList *vars)
*/
static void update_files(void)
{
- GHashTable *ht = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
- gchar *record = NULL;
- gchar *pos;
+ GHashTable *ht;
+ struct gdb_mi_record *record = NULL;
+ const struct gdb_mi_result *files_node;
if (files)
{
@@ -1428,22 +1216,25 @@ static void update_files(void)
}
exec_sync_command("-file-list-exec-source-files", TRUE, &record);
- pos = record;
- while ( (pos = strstr(pos, "fullname=\"")) )
+ if (! record)
+ return;
+
+ ht = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
+
+ files_node = gdb_mi_result_var(record->first, "files", GDB_MI_VAL_LIST);
+ gdb_mi_result_foreach_matched (files_node, files_node, NULL, GDB_MI_VAL_LIST)
{
- pos += strlen("fullname=\"");
- *(strchr(pos, '\"')) = '\0';
- if (!g_hash_table_lookup(ht, pos))
+ const gchar *fullname = gdb_mi_result_var(files_node->val->list, "fullname", GDB_MI_VAL_STRING);
+
+ if (fullname && !g_hash_table_lookup(ht, fullname))
{
- g_hash_table_insert(ht, (gpointer)pos, (gpointer)1);
- files = g_list_append(files, g_strdup(pos));
+ g_hash_table_insert(ht, (gpointer)fullname, (gpointer)1);
+ files = g_list_append(files, g_strdup(fullname));
}
-
- pos += strlen(pos) + 1;
}
g_hash_table_destroy(ht);
- g_free(record);
+ gdb_mi_record_free(record);
}
/*
@@ -1462,7 +1253,7 @@ static void update_watches(void)
if (var->internal->len)
{
- sprintf(command, "-var-delete %s", var->internal->str);
+ g_snprintf(command, sizeof command, "-var-delete %s", var->internal->str);
exec_sync_command(command, TRUE, NULL);
}
@@ -1475,32 +1266,31 @@ static void update_watches(void)
for (iter = watches; iter; iter = iter->next)
{
variable *var = (variable*)iter->data;
- gchar *record = NULL;
- gchar *pos;
+ struct gdb_mi_record *record = NULL;
+ const gchar *name;
gchar *escaped;
/* try to create variable */
escaped = g_strescape(var->name->str, NULL);
- sprintf(command, "-var-create - * \"%s\"", escaped);
+ g_snprintf(command, sizeof command, "-var-create - * \"%s\"", escaped);
g_free(escaped);
- if (RC_DONE != exec_sync_command(command, TRUE, &record))
+ if (RC_DONE != exec_sync_command(command, TRUE, &record) || !record)
{
/* do not include to updating list, move to next watch */
var->evaluated = FALSE;
g_string_assign(var->internal, "");
- g_free(record);
+ gdb_mi_record_free(record);
continue;
}
/* find and assign internal name */
- pos = strstr(record, "name=\"") + strlen("name=\"");
- *strchr(pos, '\"') = '\0';
- g_string_assign(var->internal, pos);
- g_free(record);
+ name = gdb_mi_result_var(record->first, "name", GDB_MI_VAL_STRING);
+ g_string_assign(var->internal, name ? name : "");
+ gdb_mi_record_free(record);
- var->evaluated = TRUE;
+ var->evaluated = name != NULL;
/* add to updating list */
updating = g_list_append(updating, var);
@@ -1519,16 +1309,14 @@ static void update_watches(void)
static void update_autos(void)
{
gchar command[1000];
- GList *unevaluated = NULL, *iter;
- const char *gdb_commands[2];
- int i;
+ GList *unevaluated = NULL, *vars = NULL, *iter;
/* remove all previous GDB variables for autos */
for (iter = autos; iter; iter = iter->next)
{
variable *var = (variable*)iter->data;
- sprintf(command, "-var-delete %s", var->internal->str);
+ g_snprintf(command, sizeof command, "-var-delete %s", var->internal->str);
exec_sync_command(command, TRUE, NULL);
}
@@ -1538,55 +1326,67 @@ static void update_autos(void)
/* add current autos to the list */
- gdb_commands[0] = g_strdup_printf("-stack-list-arguments 0 %i %i", active_frame, active_frame);
- gdb_commands[1] = "-stack-list-locals 0";
- for (i = 0; i < sizeof (gdb_commands) / sizeof(*gdb_commands); i++)
- {
- gchar *record = NULL, *pos;
+ struct gdb_mi_record *record = NULL;
- result_class rc = exec_sync_command(gdb_commands[i], TRUE, &record);
- if (RC_DONE != rc)
- break;
+ g_snprintf(command, sizeof command, "-stack-list-arguments 0 %i %i", active_frame, active_frame);
+ if (RC_DONE == exec_sync_command(command, TRUE, &record) && record)
+ {
+ const struct gdb_mi_result *stack_args = gdb_mi_result_var(record->first, "stack-args", GDB_MI_VAL_LIST);
- pos = record;
- while ((pos = strstr(pos, "name=\"")))
+ gdb_mi_result_foreach_matched (stack_args, stack_args, "frame", GDB_MI_VAL_LIST)
{
- variable *var;
- gchar *create_record = NULL, *escaped;
+ const struct gdb_mi_result *args = gdb_mi_result_var(stack_args->val->list, "args", GDB_MI_VAL_LIST);
- pos += strlen("name=\"");
- *(strchr(pos, '\"')) = '\0';
+ gdb_mi_result_foreach_matched (args, args, "name", GDB_MI_VAL_STRING)
+ {
+ variable *var = variable_new(args->val->string, VT_ARGUMENT);
+ vars = g_list_append(vars, var);
+ }
+ }
+ }
+ gdb_mi_record_free(record);
- var = variable_new(pos, i ? VT_LOCAL : VT_ARGUMENT);
+ if (RC_DONE == exec_sync_command("-stack-list-locals 0", TRUE, &record) && record)
+ {
+ const struct gdb_mi_result *locals = gdb_mi_result_var(record->first, "locals", GDB_MI_VAL_LIST);
- /* create new gdb variable */
- escaped = g_strescape(pos, NULL);
- sprintf(command, "-var-create - * \"%s\"", escaped);
- g_free(escaped);
+ gdb_mi_result_foreach_matched (locals, locals, "name", GDB_MI_VAL_STRING)
+ {
+ variable *var = variable_new(locals->val->string, VT_LOCAL);
+ vars = g_list_append(vars, var);
+ }
+ }
+ gdb_mi_record_free(record);
- /* form new variable */
- if (RC_DONE == exec_sync_command(command, TRUE, &create_record))
- {
- gchar *intname = strstr(create_record, "name=\"") + strlen ("name=\"");
- *strchr(intname, '\"') = '\0';
- var->evaluated = TRUE;
- g_string_assign(var->internal, intname);
- autos = g_list_append(autos, var);
+ for (iter = vars; iter; iter = iter->next)
+ {
+ variable *var = iter->data;
+ struct gdb_mi_record *create_record = NULL;
+ gchar *escaped;
+ const gchar *intname;
- g_free(create_record);
- }
- else
- {
- var->evaluated = FALSE;
- g_string_assign(var->internal, "");
- unevaluated = g_list_append(unevaluated, var);
- }
-
- pos += strlen(pos) + 1;
+ /* create new gdb variable */
+ escaped = g_strescape(var->name->str, NULL);
+ g_snprintf(command, sizeof command, "-var-create - * \"%s\"", escaped);
+ g_free(escaped);
+
+ /* form new variable */
+ if (RC_DONE == exec_sync_command(command, TRUE, &create_record) && create_record &&
+ (intname = gdb_mi_result_var(create_record->first, "name", GDB_MI_VAL_STRING)))
+ {
+ var->evaluated = TRUE;
+ g_string_assign(var->internal, intname);
+ autos = g_list_append(autos, var);
}
- g_free(record);
+ else
+ {
+ var->evaluated = FALSE;
+ g_string_assign(var->internal, "");
+ unevaluated = g_list_append(unevaluated, var);
+ }
+ gdb_mi_record_free(create_record);
}
- g_free((void*)gdb_commands[0]);
+ g_list_free(vars);
/* get values for the autos (without incorrect variables) */
get_variables(autos);
@@ -1628,56 +1428,47 @@ static GList* get_children (gchar* path)
gchar command[1000];
result_class rc;
- gchar *record = NULL;
- gchar *pos = NULL;
- int numchild;
+ struct gdb_mi_record *record = NULL;
+ const gchar *numchild;
+ int n;
/* children number */
- sprintf(command, "-var-info-num-children \"%s\"", path);
+ g_snprintf(command, sizeof command, "-var-info-num-children \"%s\"", path);
rc = exec_sync_command(command, TRUE, &record);
- if (RC_DONE != rc)
+ if (RC_DONE != rc || ! record)
+ {
+ gdb_mi_record_free(record);
return NULL;
- pos = strstr(record, "numchild=\"") + strlen("numchild=\"");
- *(strchr(pos, '\"')) = '\0';
- numchild = atoi(pos);
- g_free(record);
- if (!numchild)
+ }
+ numchild = gdb_mi_result_var(record->first, "numchild", GDB_MI_VAL_STRING);
+ n = numchild ? atoi(numchild) : 0;
+ gdb_mi_record_free(record);
+ if (!n)
return NULL;
/* recursive get children and put into list */
- sprintf(command, "-var-list-children \"%s\"", path);
+ g_snprintf(command, sizeof command, "-var-list-children \"%s\"", path);
rc = exec_sync_command(command, TRUE, &record);
- if (RC_DONE == rc)
+ if (RC_DONE == rc && record)
{
- pos = record;
- while ( (pos = strstr(pos, "child={")) )
+ const struct gdb_mi_result *child_node = gdb_mi_result_var(record->first, "children", GDB_MI_VAL_LIST);
+
+ gdb_mi_result_foreach_matched (child_node, child_node, "child", GDB_MI_VAL_LIST)
{
- gchar *name, *internal;
+ const gchar *internal = gdb_mi_result_var(child_node->val->list, "name", GDB_MI_VAL_STRING);
+ const gchar *name = gdb_mi_result_var(child_node->val->list, "exp", GDB_MI_VAL_STRING);
variable *var;
-
- /* name */
- pos = strstr(pos, "name=\"") + strlen("name=\"");
- *(strstr(pos, "\",exp=\"")) = '\0';
- internal = pos;
- pos += strlen(pos) + 1;
-
- /* exp */
- pos = strstr(pos, "exp=\"") + strlen("exp=\"");
- *(strstr(pos, "\",numchild=\"")) = '\0';
-
- name = g_strcompress(pos);
-
+
+ if (! name || ! internal)
+ continue;
+
var = variable_new2(name, internal, VT_CHILD);
var->evaluated = TRUE;
-
- pos += strlen(pos) + 1;
children = g_list_append(children, var);
-
- g_free(name);
}
}
- g_free(record);
+ gdb_mi_record_free(record);
get_variables(children);
@@ -1690,7 +1481,9 @@ static GList* get_children (gchar* path)
static variable* add_watch(gchar* expression)
{
gchar command[1000];
- gchar *record = NULL, *escaped, *pos;
+ gchar *escaped;
+ struct gdb_mi_record *record = NULL;
+ const gchar *name;
GList *vars = NULL;
variable *var = variable_new(expression, VT_WATCH);
@@ -1698,24 +1491,23 @@ static variable* add_watch(gchar* expression)
/* try to create a variable */
escaped = g_strescape(expression, NULL);
- sprintf(command, "-var-create - * \"%s\"", escaped);
+ g_snprintf(command, sizeof command, "-var-create - * \"%s\"", escaped);
g_free(escaped);
- if (RC_DONE != exec_sync_command(command, TRUE, &record))
+ if (RC_DONE != exec_sync_command(command, TRUE, &record) || !record)
{
- g_free(record);
+ gdb_mi_record_free(record);
return var;
}
- pos = strstr(record, "name=\"") + strlen("name=\"");
- *strchr(pos, '\"') = '\0';
- g_string_assign(var->internal, pos);
- var->evaluated = TRUE;
+ name = gdb_mi_result_var(record->first, "name", GDB_MI_VAL_STRING);
+ g_string_assign(var->internal, name ? name : "");
+ var->evaluated = name != NULL;
vars = g_list_append(NULL, var);
get_variables(vars);
- g_free(record);
+ gdb_mi_record_free(record);
g_list_free(vars);
return var;
@@ -1733,7 +1525,7 @@ static void remove_watch(gchar* internal)
if (!strcmp(var->internal->str, internal))
{
gchar command[1000];
- sprintf(command, "-var-delete %s", internal);
+ g_snprintf(command, sizeof command, "-var-delete %s", internal);
exec_sync_command(command, TRUE, NULL);
variable_free(var);
watches = g_list_delete_link(watches, iter);
@@ -1747,23 +1539,21 @@ static void remove_watch(gchar* internal)
*/
static gchar *evaluate_expression(gchar *expression)
{
- gchar *record = NULL, *pos;
+ struct gdb_mi_record *record = NULL;
+ gchar *value;
char command[1000];
- result_class rc;
- sprintf (command, "-data-evaluate-expression \"%s\"", expression);
- rc = exec_sync_command(command, TRUE, &record);
-
- if (RC_DONE != rc)
+ g_snprintf(command, sizeof command, "-data-evaluate-expression \"%s\"", expression);
+ if (RC_DONE != exec_sync_command(command, TRUE, &record) || ! record)
{
- g_free(record);
+ gdb_mi_record_free(record);
return NULL;
}
- pos = strstr(record, "value=\"") + strlen("value=\"");
- *(strrchr(pos, '\"')) = '\0';
+ value = g_strdup(gdb_mi_result_var(record->first, "value", GDB_MI_VAL_STRING));
+ gdb_mi_record_free(record);
- return unescape(pos);
+ return value;
}
/*
@@ -1773,7 +1563,7 @@ static gboolean request_interrupt(void)
{
#ifdef DEBUG_OUTPUT
char msg[1000];
- sprintf(msg, "interrupting pid=%i", target_pid);
+ g_snprintf(msg, sizeof msg, "interrupting pid=%i", target_pid);
dbg_cbs->send_message(msg, "red");
#endif
Modified: debugger/src/debug.c
53 lines changed, 30 insertions(+), 23 deletions(-)
===================================================================
@@ -672,7 +672,8 @@ static void on_debugger_stopped (int thread_id)
}
/* clear calltips cache */
- g_hash_table_remove_all(calltips);
+ if (calltips)
+ g_hash_table_remove_all(calltips);
/* if a stop was requested for asyncronous exiting -
* stop debug module and exit */
@@ -841,8 +842,11 @@ static void on_debugger_exited (int code)
read_only_pages = NULL;
/* clear and destroy calltips cache */
- g_hash_table_destroy(calltips);
- calltips = NULL;
+ if (calltips)
+ {
+ g_hash_table_destroy(calltips);
+ calltips = NULL;
+ }
/* enable widgets */
enable_sensitive_widgets(TRUE);
@@ -937,7 +941,8 @@ static void on_select_frame(int frame_number)
active_module->set_active_frame(frame_number);
/* clear calltips cache */
- g_hash_table_remove_all(calltips);
+ if (calltips)
+ g_hash_table_remove_all(calltips);
/* autos */
autos = active_module->get_autos();
@@ -1336,33 +1341,35 @@ gchar* debug_get_calltip_for_expression(gchar* expression)
if (var)
{
calltip_str = get_calltip_line(var, TRUE);
- if (var->has_children)
+ if (calltip_str)
{
- int lines_left = MAX_CALLTIP_HEIGHT - 1;
- GList* children = active_module->get_children(var->internal->str);
- GList* child = children;
- while(child && lines_left)
+ if (var->has_children)
{
- variable *varchild = (variable*)child->data;
- GString *child_string = get_calltip_line(varchild, FALSE);
- g_string_append_printf(calltip_str, "\n%s", child_string->str);
- g_string_free(child_string, TRUE);
+ int lines_left = MAX_CALLTIP_HEIGHT - 1;
+ GList* children = active_module->get_children(var->internal->str);
+ GList* child = children;
+ while(child && lines_left)
+ {
+ variable *varchild = (variable*)child->data;
+ GString *child_string = get_calltip_line(varchild, FALSE);
+ g_string_append_printf(calltip_str, "\n%s", child_string->str);
+ g_string_free(child_string, TRUE);
- child = child->next;
- lines_left--;
- }
- if (!lines_left && child)
- {
- g_string_append(calltip_str, "\n\t\t........");
+ child = child->next;
+ lines_left--;
+ }
+ if (!lines_left && child)
+ {
+ g_string_append(calltip_str, "\n\t\t........");
+ }
+ g_list_foreach(children, (GFunc)variable_free, NULL);
+ g_list_free(children);
}
- g_list_foreach(children, (GFunc)variable_free, NULL);
- g_list_free(children);
+ calltip = g_string_free(calltip_str, FALSE);
}
active_module->remove_watch(var->internal->str);
- calltip = g_string_free(calltip_str, FALSE);
-
if (!calltips)
{
calltips = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)g_free);
Modified: debugger/src/debug_module.c
4 lines changed, 2 insertions(+), 2 deletions(-)
===================================================================
@@ -32,7 +32,7 @@
#include "debug_module.h"
/* creates new variable */
-variable *variable_new(gchar *name, variable_type vt)
+variable *variable_new(const gchar *name, variable_type vt)
{
variable *var = g_malloc(sizeof(variable));
var->name = g_string_new(name);
@@ -47,7 +47,7 @@ variable *variable_new(gchar *name, variable_type vt)
}
/* creates new variable with internal name */
-variable *variable_new2(gchar *name, gchar *internal, variable_type vt)
+variable *variable_new2(const gchar *name, const gchar *internal, variable_type vt)
{
variable *var = variable_new(name, vt);
g_string_assign(var->internal, internal);
Modified: debugger/src/debug_module.h
4 lines changed, 2 insertions(+), 2 deletions(-)
===================================================================
@@ -166,8 +166,8 @@ typedef struct _dbg_module {
MODULE_FEATURES }
void variable_free(variable *var);
-variable* variable_new(gchar *name, variable_type vt);
-variable* variable_new2(gchar *name, gchar *internal, variable_type vt);
+variable* variable_new(const gchar *name, variable_type vt);
+variable* variable_new2(const gchar *name, const gchar *internal, variable_type vt);
void variable_reset(variable *var);
frame* frame_new(void);
Modified: debugger/src/dpaned.c
7 lines changed, 5 insertions(+), 2 deletions(-)
===================================================================
@@ -154,10 +154,10 @@ static void on_page_reordered(GtkNotebook *notebook, GtkWidget *child, guint pag
gboolean is_tabbed = config_get_tabbed();
int *tabs = NULL;
gsize length;
- int prev_index;
+ gsize prev_index, min, max;
GtkWidget *page;
tab_id id;
- int i, min, max;
+ int i;
int config_part_tabs;
int config_part_selected_index;
int *array;
@@ -351,6 +351,7 @@ void dpaned_init(void)
gtk_notebook_set_tab_detachable(GTK_NOTEBOOK(debug_notebook_left), tab, TRUE);
gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(debug_notebook_left), tab, TRUE);
}
+ g_free(tab_ids);
gtk_widget_show_all(hpaned);
gtk_notebook_set_current_page(GTK_NOTEBOOK(debug_notebook_left), config_get_selected_tab_index());
@@ -406,6 +407,7 @@ void dpaned_set_tabbed(gboolean tabbed)
gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(debug_notebook_left), tab, TRUE);
}
}
+ g_free(tab_ids);
gtk_notebook_set_current_page(GTK_NOTEBOOK(debug_notebook_left), config_get_selected_tab_index());
@@ -431,6 +433,7 @@ void dpaned_set_tabbed(gboolean tabbed)
gtk_notebook_set_tab_detachable(GTK_NOTEBOOK(debug_notebook_right), tab, TRUE);
gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(debug_notebook_right), tab, TRUE);
}
+ g_free(tab_ids);
gtk_notebook_set_current_page(GTK_NOTEBOOK(debug_notebook_left), config_get_left_selected_tab_index());
gtk_notebook_set_current_page(GTK_NOTEBOOK(debug_notebook_right), config_get_right_selected_tab_index());
Modified: debugger/src/gdb_mi.c
496 lines changed, 496 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,496 @@
+/*
+ * gdb_mi.c
+ *
+ * Copyright 2014 Colomban Wendling <colomban at geany.org>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+/*
+ * Parses GDB/MI records
+ * https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI-Output-Syntax.html
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "gdb_mi.h"
+
+
+#define ascii_isodigit(c) (((guchar) (c)) >= '0' && ((guchar) (c)) <= '7')
+
+
+static struct gdb_mi_value *parse_value(const gchar **p);
+
+
+void gdb_mi_value_free(struct gdb_mi_value *val)
+{
+ if (! val)
+ return;
+ switch (val->type)
+ {
+ case GDB_MI_VAL_STRING:
+ g_free(val->string);
+ g_warn_if_fail(val->list == NULL);
+ break;
+
+ case GDB_MI_VAL_LIST:
+ gdb_mi_result_free(val->list, TRUE);
+ g_warn_if_fail(val->string == NULL);
+ break;
+ }
+ g_free(val);
+}
+
+void gdb_mi_result_free(struct gdb_mi_result *res, gboolean next)
+{
+ if (! res)
+ return;
+ g_free(res->var);
+ gdb_mi_value_free(res->val);
+ if (next)
+ gdb_mi_result_free(res->next, next);
+ g_free(res);
+}
+
+void gdb_mi_record_free(struct gdb_mi_record *record)
+{
+ if (! record)
+ return;
+ g_free(record->token);
+ g_free(record->klass);
+ gdb_mi_result_free(record->first, TRUE);
+ g_free(record);
+}
+
+/* parses: cstring
+ *
+ * cstring is defined as:
+ *
+ * c-string ==>
+ * """ seven-bit-iso-c-string-content """
+ *
+ * FIXME: what exactly does "seven-bit-iso-c-string-content" mean?
+ * reading between the lines suggests it's US-ASCII with values >= 0x80
+ * encoded as \NNN (most likely octal), but that's not really clear --
+ * although it parses everything I encountered
+ * FIXME: this does NOT convert to UTF-8. should it? */
+static gchar *parse_cstring(const gchar **p)
+{
+ GString *str = g_string_new(NULL);
+
+ if (**p == '"')
+ {
+ (*p)++;
+ while (**p != '"')
+ {
+ gchar c = **p;
+ /* TODO: check expansions here */
+ if (c == '\\')
+ {
+ (*p)++;
+ c = **p;
+ switch (g_ascii_tolower(c))
+ {
+ case '\\':
+ case '"': break;
+ case 'a': c = '\a'; break;
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'v': c = '\v'; break;
+ default:
+ /* hex escape, 1-2 digits (\xN or \xNN)
+ *
+ * FIXME: is this useful? Is this right?
+ * the original dbm_gdb.c:unescape_hex_values() used to
+ * read escapes of the form \xNNN and treat them as wide
+ * characters numbers, but this looks weird for a C-like
+ * escape.
+ * Also, note that this doesn't seem to be referenced anywhere
+ * in GDB/MI syntax. Only reference in GDB manual is about
+ * keybindings, which use the syntax implemented here */
+ if (g_ascii_tolower(**p) == 'x' && g_ascii_isxdigit((*p)[1]))
+ {
+ c = (gchar) g_ascii_xdigit_value(*++(*p));
+ if (g_ascii_isxdigit((*p)[1]))
+ c = (gchar) ((c * 16) + g_ascii_xdigit_value(*++(*p)));
+ }
+ /* octal escape, 1-3 digits (\N, \NN or \NNN) */
+ else if (ascii_isodigit(**p))
+ {
+ int i, v;
+ v = g_ascii_digit_value(**p);
+ for (i = 0; ascii_isodigit((*p)[1]) && i < 2; i++)
+ v = (v * 8) + g_ascii_digit_value(*++(*p));
+ if (v <= 0xff)
+ c = (gchar) v;
+ else
+ {
+ *p = *p - 3; /* put the whole sequence back */
+ c = **p;
+ g_warning("Octal escape sequence out of range: %.4s", *p);
+ }
+ }
+ else
+ {
+ g_warning("Unkown escape \"\\%c\"", **p);
+ (*p)--; /* put the \ back */
+ c = **p;
+ }
+ break;
+ }
+ }
+ if (**p == '\0')
+ break;
+ g_string_append_c(str, c);
+ (*p)++;
+ }
+ if (**p == '"')
+ (*p)++;
+ }
+ return g_string_free(str, FALSE);
+}
+
+/* parses: string
+ * FIXME: what really is a string? here it uses [a-zA-Z_-.][a-zA-Z0-9_-.]* but
+ * the docs aren't clear on this */
+static gchar *parse_string(const gchar **p)
+{
+ GString *str = g_string_new(NULL);
+
+ if (g_ascii_isalpha(**p) || strchr("-_.", **p))
+ {
+ g_string_append_c(str, **p);
+ for ((*p)++; g_ascii_isalnum(**p) || strchr("-_.", **p); (*p)++)
+ g_string_append_c(str, **p);
+ }
+ return g_string_free(str, FALSE);
+}
+
+/* parses: string "=" value */
+static gboolean parse_result(struct gdb_mi_result *result, const gchar **p)
+{
+ result->var = parse_string(p);
+ while (g_ascii_isspace(**p)) (*p)++;
+ if (**p == '=')
+ {
+ (*p)++;
+ while (g_ascii_isspace(**p)) (*p)++;
+ result->val = parse_value(p);
+ }
+ return result->var && result->val;
+}
+
+/* parses: cstring | list | tuple
+ * Actually, this is more permissive and allows mixed tuples/lists */
+static struct gdb_mi_value *parse_value(const gchar **p)
+{
+ struct gdb_mi_value *val = g_malloc0(sizeof *val);
+ if (**p == '"')
+ {
+ val->type = GDB_MI_VAL_STRING;
+ val->string = parse_cstring(p);
+ }
+ else if (**p == '{' || **p == '[')
+ {
+ struct gdb_mi_result *prev = NULL;
+ val->type = GDB_MI_VAL_LIST;
+ gchar end = **p == '{' ? '}' : ']';
+ (*p)++;
+ while (**p && **p != end)
+ {
+ struct gdb_mi_result *item = g_malloc0(sizeof *item);
+ while (g_ascii_isspace(**p)) (*p)++;
+ if ((item->val = parse_value(p)) ||
+ parse_result(item, p))
+ {
+ if (prev)
+ prev->next = item;
+ else
+ val->list = item;
+ prev = item;
+ }
+ else
+ {
+ gdb_mi_result_free(item, TRUE);
+ break;
+ }
+ while (g_ascii_isspace(**p)) (*p)++;
+ if (**p != ',') break;
+ (*p)++;
+ }
+ if (**p == end)
+ (*p)++;
+ }
+ else
+ {
+ gdb_mi_value_free(val);
+ val = NULL;
+ }
+ return val;
+}
+
+static gboolean is_prompt(const gchar *p)
+{
+ if (strncmp("(gdb)", p, 5) == 0)
+ {
+ p += 5;
+ while (g_ascii_isspace(*p)) p++;
+ }
+ return *p == 0;
+}
+
+/* parses: async-record | stream-record | result-record
+ * note: post-value data is ignored.
+ *
+ * FIXME: that's NOT exactly what the GDB docs call an output, and that's not
+ * exactly what a line could be. The GDB docs state that a line can
+ * contain more than one stream-record, as it's not terminated by a
+ * newline, and as it defines:
+ *
+ * output ==>
+ * ( out-of-band-record )* [ result-record ] "(gdb)" nl
+ * out-of-band-record ==>
+ * async-record | stream-record
+ * stream-record ==>
+ * console-stream-output | target-stream-output | log-stream-output
+ * console-stream-output ==>
+ * "~" c-string
+ * target-stream-output ==>
+ * "@" c-string
+ * log-stream-output ==>
+ * "&" c-string
+ *
+ * so as none of the stream-outputs are terminated by a newline, and the
+ * parser here only extracts the first record it will fail with combined
+ * records in one line.
+ */
+struct gdb_mi_record *gdb_mi_record_parse(const gchar *line)
+{
+ struct gdb_mi_record *record = g_malloc0(sizeof *record);
+
+ /* FIXME: prompt detection should not really be useful, especially not as a
+ * special case, as the prompt should always follow an (optional) record */
+ if (is_prompt(line))
+ record->type = GDB_MI_TYPE_PROMPT;
+ else
+ {
+ /* extract token */
+ const gchar *token_end = line;
+ for (token_end = line; g_ascii_isdigit(*token_end); token_end++)
+ ;
+ if (token_end > line)
+ {
+ record->token = g_strndup(line, (gsize)(token_end - line));
+ line = token_end;
+ while (g_ascii_isspace(*line)) line++;
+ }
+
+ /* extract record */
+ record->type = *line;
+ if (*line) ++line;
+ while (g_ascii_isspace(*line)) line++;
+ switch (record->type)
+ {
+ case '~':
+ case '@':
+ case '&':
+ /* FIXME: although the syntax description in the docs are clear,
+ * the "GDB/MI Stream Records" section does not agree with it,
+ * widening the input to:
+ *
+ * > [string-output] is either raw text (with an implicit new
+ * > line) or a quoted C string (which does not contain an
+ * > implicit newline).
+ *
+ * This adds "raw text" to "c-string"... so? */
+ record->klass = parse_cstring(&line);
+ break;
+ case '^':
+ case '*':
+ case '+':
+ case '=':
+ {
+ struct gdb_mi_result *prev = NULL;
+ record->klass = parse_string(&line);
+ while (*line)
+ {
+ while (g_ascii_isspace(*line)) line++;
+ if (*line != ',')
+ break;
+ else
+ {
+ struct gdb_mi_result *res = g_malloc0(sizeof *res);
+ line++;
+ while (g_ascii_isspace(*line)) line++;
+ if (!parse_result(res, &line))
+ {
+ g_warning("failed to parse result");
+ gdb_mi_result_free(res, TRUE);
+ break;
+ }
+ if (prev)
+ prev->next = res;
+ else
+ record->first = res;
+ prev = res;
+ }
+ }
+ break;
+ }
+ default:
+ /* FIXME: what to do with invalid prefix? */
+ record->type = GDB_MI_TYPE_PROMPT;
+ }
+ }
+
+ return record;
+}
+
+/* Extracts a variable value from a result
+ * @res may be NULL */
+static const struct gdb_mi_value *gdb_mi_result_var_value(const struct gdb_mi_result *result, const gchar *name)
+{
+ g_return_val_if_fail(name != NULL, NULL);
+
+ for (; result; result = result->next)
+ {
+ if (result->var && strcmp(result->var, name) == 0)
+ return result->val;
+ }
+ return NULL;
+}
+
+/* Extracts a variable value from a record
+ * @param res a first result, or NULL
+ * @param name the variable name
+ * @param type the expected type of the value
+ * @returns the value of @p name variable (type depending on @p type), or NULL
+ */
+const void *gdb_mi_result_var(const struct gdb_mi_result *result, const gchar *name, enum gdb_mi_value_type type)
+{
+ const struct gdb_mi_value *val = gdb_mi_result_var_value(result, name);
+ if (! val || val->type != type)
+ return NULL;
+ else if (val->type == GDB_MI_VAL_STRING)
+ return val->string;
+ else if (val->type == GDB_MI_VAL_LIST)
+ return val->list;
+ return NULL;
+}
+
+/* checks whether a record matches, possibly including some string values
+ * @param record a record
+ * @param type the expected type of the record
+ * @param klass the expected class of the record
+ * @param ... a NULL-terminated name/return location pairs for string results
+ * @returns TRUE if record matched, FALSE otherwise
+ *
+ * Usage example
+ * @{
+ * const gchar *id;
+ * if (gdb_mi_record_matches(record, '=', 'thread-created', "id", &id, NULL))
+ * // here record matched and `id` is present and a string
+ * @}
+ */
+gboolean gdb_mi_record_matches(const struct gdb_mi_record *record, enum gdb_mi_record_type type, const gchar *klass, ...)
+{
+ va_list ap;
+ const gchar *name;
+ gboolean success = TRUE;
+
+ g_return_val_if_fail(record != NULL, FALSE);
+
+ if (record->type != type || strcmp(record->klass, klass) != 0)
+ return FALSE;
+
+ va_start(ap, klass);
+ while ((name = va_arg(ap, const gchar *)) != NULL && success)
+ {
+ const gchar **out = va_arg(ap, const gchar **);
+
+ g_return_val_if_fail(out != NULL, FALSE);
+
+ *out = gdb_mi_result_var(record->first, name, GDB_MI_VAL_STRING);
+ success = *out != NULL;
+ }
+ va_end(ap);
+ return success;
+}
+
+
+#ifdef TEST
+
+static void gdb_mi_result_dump(const struct gdb_mi_result *r, gboolean next, gint indent);
+
+static void gdb_mi_value_dump(const struct gdb_mi_value *v, gint indent)
+{
+ fprintf(stderr, "%*stype = %d\n", indent * 2, "", v->type);
+ switch (v->type)
+ {
+ case GDB_MI_VAL_STRING:
+ fprintf(stderr, "%*sstring = %s\n", indent * 2, "", v->string);
+ break;
+ case GDB_MI_VAL_LIST:
+ fprintf(stderr, "%*slist =>\n", indent * 2, "");
+ if (v->list)
+ gdb_mi_result_dump(v->list, TRUE, indent + 1);
+ break;
+ }
+}
+
+static void gdb_mi_result_dump(const struct gdb_mi_result *r, gboolean next, gint indent)
+{
+ fprintf(stderr, "%*svar = %s\n", indent * 2, "", r->var);
+ fprintf(stderr, "%*sval =>\n", indent * 2, "");
+ gdb_mi_value_dump(r->val, indent + 1);
+ if (next && r->next)
+ gdb_mi_result_dump(r->next, next, indent);
+}
+
+static void gdb_mi_record_dump(const struct gdb_mi_record *record)
+{
+ fprintf(stderr, "record =>\n");
+ fprintf(stderr, " type = '%c' (%d)\n", record->type ? record->type : '0', record->type);
+ fprintf(stderr, " token = %s\n", record->token);
+ fprintf(stderr, " class = %s\n", record->klass);
+ fprintf(stderr, " results =>\n");
+ if (record->first)
+ gdb_mi_result_dump(record->first, TRUE, 2);
+}
+
+int main(void)
+{
+ char buf[1024] = {0};
+
+ while (fgets(buf, sizeof buf, stdin))
+ {
+ struct gdb_mi_record *record = gdb_mi_record_parse(buf);
+
+ gdb_mi_record_dump(record);
+ gdb_mi_record_free(record);
+ }
+ return 0;
+}
+
+#endif
Modified: debugger/src/gdb_mi.h
86 lines changed, 86 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,86 @@
+/*
+ * gdb_mi.h
+ *
+ * Copyright 2014 Colomban Wendling <colomban at geany.org>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#ifndef GDB_MI_H
+#define GDB_MI_H
+
+#include <glib.h>
+
+enum gdb_mi_value_type
+{
+ GDB_MI_VAL_STRING,
+ GDB_MI_VAL_LIST
+};
+
+struct gdb_mi_result;
+struct gdb_mi_value
+{
+ enum gdb_mi_value_type type;
+ gchar *string;
+ struct gdb_mi_result *list;
+};
+
+struct gdb_mi_result
+{
+ gchar *var;
+ struct gdb_mi_value *val;
+ struct gdb_mi_result *next;
+};
+
+enum gdb_mi_record_type
+{
+ GDB_MI_TYPE_PROMPT = 0,
+ GDB_MI_TYPE_RESULT = '^',
+ GDB_MI_TYPE_EXEC_ASYNC = '*',
+ GDB_MI_TYPE_STATUS_ASYNC = '+',
+ GDB_MI_TYPE_NOTIFY_ASYNC = '=',
+ GDB_MI_TYPE_CONSOLE_STREAM = '~',
+ GDB_MI_TYPE_TARGET_STREAM = '@',
+ GDB_MI_TYPE_LOG_STREAM = '&'
+};
+
+struct gdb_mi_record
+{
+ enum gdb_mi_record_type type;
+ gchar *token;
+ gchar *klass; /*< contains the async record class or the stream output */
+ struct gdb_mi_result *first; /*< pointer to the first result (if any) */
+};
+
+
+void gdb_mi_value_free(struct gdb_mi_value *val);
+void gdb_mi_result_free(struct gdb_mi_result *res, gboolean next);
+void gdb_mi_record_free(struct gdb_mi_record *record);
+struct gdb_mi_record *gdb_mi_record_parse(const gchar *line);
+const void *gdb_mi_result_var(const struct gdb_mi_result *result, const gchar *name, enum gdb_mi_value_type type);
+gboolean gdb_mi_record_matches(const struct gdb_mi_record *record, enum gdb_mi_record_type type, const gchar *klass, ...) G_GNUC_NULL_TERMINATED;
+
+#define gdb_mi_result_foreach(node_, result_) \
+ for ((node_) = (result_); (node_); (node_) = (node_)->next)
+
+#define gdb_mi_result_foreach_matched(node_, result_, name_, type_) \
+ gdb_mi_result_foreach ((node_), (result_)) \
+ if (((name_) != NULL && (! (node_)->var || strcmp((node_)->var, (name_) ? (name_) : "") != 0)) || \
+ ((type_) >= 0 && (node_)->val->type != (type_))) \
+ continue; \
+ else
+
+#endif /* guard */
Modified: debugger/src/markers.c
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -98,7 +98,7 @@ void markers_set_for_document(ScintillaObject *sci)
void markers_init(void)
{
/* set markers in all currently opened documents */
- int i;
+ guint i;
foreach_document(i)
markers_set_for_document(document_index(i)->editor->sci);
}
Modified: debugger/src/plugin.c
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -89,7 +89,7 @@ static void on_paned_mode_changed(GtkToggleButton *button, gpointer user_data)
void plugin_init(GeanyData *data)
{
GtkWidget* vbox;
- int i;
+ guint i;
plugin_module_make_resident(geany_plugin);
Modified: debugger/src/tests/gdb_mi_test.expected
338 lines changed, 338 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,338 @@
+# (gdb)
+record =>
+ type = '0' (0)
+ token = (null)
+ class = (null)
+ results =>
+# (gdb)
+record =>
+ type = '0' (0)
+ token = (null)
+ class = (null)
+ results =>
+# (gdb)
+record =>
+ type = '0' (0)
+ token = (null)
+ class = (null)
+ results =>
+# ^error,msg="failed"
+record =>
+ type = '^' (94)
+ token = (null)
+ class = error
+ results =>
+ var = msg
+ val =>
+ type = 0
+ string = failed
+# *stopped,reason="signal-received",signal-name="SIGINT",signal-meaning="Interrupt",frame={addr="0x00000000deadbeef",func="somefunc",args=[],file="somefile.c",fullname="/some/path/somefile.c",line="123"},thread-id="1",stopped-threads="all",core="1"
+record =>
+ type = '*' (42)
+ token = (null)
+ class = stopped
+ results =>
+ var = reason
+ val =>
+ type = 0
+ string = signal-received
+ var = signal-name
+ val =>
+ type = 0
+ string = SIGINT
+ var = signal-meaning
+ val =>
+ type = 0
+ string = Interrupt
+ var = frame
+ val =>
+ type = 1
+ list =>
+ var = addr
+ val =>
+ type = 0
+ string = 0x00000000deadbeef
+ var = func
+ val =>
+ type = 0
+ string = somefunc
+ var = args
+ val =>
+ type = 1
+ list =>
+ var = file
+ val =>
+ type = 0
+ string = somefile.c
+ var = fullname
+ val =>
+ type = 0
+ string = /some/path/somefile.c
+ var = line
+ val =>
+ type = 0
+ string = 123
+ var = thread-id
+ val =>
+ type = 0
+ string = 1
+ var = stopped-threads
+ val =>
+ type = 0
+ string = all
+ var = core
+ val =>
+ type = 0
+ string = 1
+# *stopped,reason="breakpoint-hit",disp="keep",bkptno="1",frame={addr="0x0000000000400c55",func="gdb_mi_record_dump",args=[{name="record",value="0x603010"}],file="gdb_mi.c",fullname="/geany-plugins/debugger/src/gdb_mi.c",line="469"},thread-id="1",stopped-threads="all",core="1"
+record =>
+ type = '*' (42)
+ token = (null)
+ class = stopped
+ results =>
+ var = reason
+ val =>
+ type = 0
+ string = breakpoint-hit
+ var = disp
+ val =>
+ type = 0
+ string = keep
+ var = bkptno
+ val =>
+ type = 0
+ string = 1
+ var = frame
+ val =>
+ type = 1
+ list =>
+ var = addr
+ val =>
+ type = 0
+ string = 0x0000000000400c55
+ var = func
+ val =>
+ type = 0
+ string = gdb_mi_record_dump
+ var = args
+ val =>
+ type = 1
+ list =>
+ var = (null)
+ val =>
+ type = 1
+ list =>
+ var = name
+ val =>
+ type = 0
+ string = record
+ var = value
+ val =>
+ type = 0
+ string = 0x603010
+ var = file
+ val =>
+ type = 0
+ string = gdb_mi.c
+ var = fullname
+ val =>
+ type = 0
+ string = /geany-plugins/debugger/src/gdb_mi.c
+ var = line
+ val =>
+ type = 0
+ string = 469
+ var = thread-id
+ val =>
+ type = 0
+ string = 1
+ var = stopped-threads
+ val =>
+ type = 0
+ string = all
+ var = core
+ val =>
+ type = 0
+ string = 1
+# =breakpoint-modified,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x0000000000400bc0",func="main",file="gdb_mi.c",fullname="/geany-plugins/debugger/src/gdb_mi.c",line="469",thread-groups=["i1"],times="1",original-location="/geany-plugins/debugger/src/gdb_mi.c:469"}
+record =>
+ type = '=' (61)
+ token = (null)
+ class = breakpoint-modified
+ results =>
+ var = bkpt
+ val =>
+ type = 1
+ list =>
+ var = number
+ val =>
+ type = 0
+ string = 1
+ var = type
+ val =>
+ type = 0
+ string = breakpoint
+ var = disp
+ val =>
+ type = 0
+ string = keep
+ var = enabled
+ val =>
+ type = 0
+ string = y
+ var = addr
+ val =>
+ type = 0
+ string = 0x0000000000400bc0
+ var = func
+ val =>
+ type = 0
+ string = main
+ var = file
+ val =>
+ type = 0
+ string = gdb_mi.c
+ var = fullname
+ val =>
+ type = 0
+ string = /geany-plugins/debugger/src/gdb_mi.c
+ var = line
+ val =>
+ type = 0
+ string = 469
+ var = thread-groups
+ val =>
+ type = 1
+ list =>
+ var = (null)
+ val =>
+ type = 0
+ string = i1
+ var = times
+ val =>
+ type = 0
+ string = 1
+ var = original-location
+ val =>
+ type = 0
+ string = /geany-plugins/debugger/src/gdb_mi.c:469
+# =breakpoint-modified,bkpt={number="7",type="breakpoint",disp="keep",enabled="y",addr="0x0000000000400414",func="main",file="/tmp/\303\271\303\261\303\256\303\247\304\201\305\225\305\241.c",fullname="/tmp/\303\271\303\261\303\256\303\247\304\201\305\225\305\241.c",line="5",thread-groups=["i1"],times="1",original-location="/tmp/\303\271\303\261\303\256\303\247\304\201\305\225\305\241.c:5"}
+record =>
+ type = '=' (61)
+ token = (null)
+ class = breakpoint-modified
+ results =>
+ var = bkpt
+ val =>
+ type = 1
+ list =>
+ var = number
+ val =>
+ type = 0
+ string = 7
+ var = type
+ val =>
+ type = 0
+ string = breakpoint
+ var = disp
+ val =>
+ type = 0
+ string = keep
+ var = enabled
+ val =>
+ type = 0
+ string = y
+ var = addr
+ val =>
+ type = 0
+ string = 0x0000000000400414
+ var = func
+ val =>
+ type = 0
+ string = main
+ var = file
+ val =>
+ type = 0
+ string = /tmp/ùñîçāŕš.c
+ var = fullname
+ val =>
+ type = 0
+ string = /tmp/ùñîçāŕš.c
+ var = line
+ val =>
+ type = 0
+ string = 5
+ var = thread-groups
+ val =>
+ type = 1
+ list =>
+ var = (null)
+ val =>
+ type = 0
+ string = i1
+ var = times
+ val =>
+ type = 0
+ string = 1
+ var = original-location
+ val =>
+ type = 0
+ string = /tmp/ùñîçāŕš.c:5
+# *stopped,reason="breakpoint-hit",disp="keep",bkptno="7",frame={addr="0x0000000000400414",func="main",args=[],file="/tmp/ùñîçāŕš.c",fullname="/tmp/ùñîçāŕš.c",line="5"},thread-id="1",stopped-threads="all",core="1"
+record =>
+ type = '*' (42)
+ token = (null)
+ class = stopped
+ results =>
+ var = reason
+ val =>
+ type = 0
+ string = breakpoint-hit
+ var = disp
+ val =>
+ type = 0
+ string = keep
+ var = bkptno
+ val =>
+ type = 0
+ string = 7
+ var = frame
+ val =>
+ type = 1
+ list =>
+ var = addr
+ val =>
+ type = 0
+ string = 0x0000000000400414
+ var = func
+ val =>
+ type = 0
+ string = main
+ var = args
+ val =>
+ type = 1
+ list =>
+ var = file
+ val =>
+ type = 0
+ string = /tmp/ùñîçāŕš.c
+ var = fullname
+ val =>
+ type = 0
+ string = /tmp/ùñîçāŕš.c
+ var = line
+ val =>
+ type = 0
+ string = 5
+ var = thread-id
+ val =>
+ type = 0
+ string = 1
+ var = stopped-threads
+ val =>
+ type = 0
+ string = all
+ var = core
+ val =>
+ type = 0
+ string = 1
Modified: debugger/src/tests/gdb_mi_test.input
14 lines changed, 14 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,14 @@
+# empty prompts
+(gdb)
+(gdb)
+(gdb)
+# simple result
+^error,msg="failed"
+# a bit more subtle records
+*stopped,reason="signal-received",signal-name="SIGINT",signal-meaning="Interrupt",frame={addr="0x00000000deadbeef",func="somefunc",args=[],file="somefile.c",fullname="/some/path/somefile.c",line="123"},thread-id="1",stopped-threads="all",core="1"
+*stopped,reason="breakpoint-hit",disp="keep",bkptno="1",frame={addr="0x0000000000400c55",func="gdb_mi_record_dump",args=[{name="record",value="0x603010"}],file="gdb_mi.c",fullname="/geany-plugins/debugger/src/gdb_mi.c",line="469"},thread-id="1",stopped-threads="all",core="1"
+=breakpoint-modified,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x0000000000400bc0",func="main",file="gdb_mi.c",fullname="/geany-plugins/debugger/src/gdb_mi.c",line="469",thread-groups=["i1"],times="1",original-location="/geany-plugins/debugger/src/gdb_mi.c:469"}
+# escapes for weird filenames
+=breakpoint-modified,bkpt={number="7",type="breakpoint",disp="keep",enabled="y",addr="0x0000000000400414",func="main",file="/tmp/\303\271\303\261\303\256\303\247\304\201\305\225\305\241.c",fullname="/tmp/\303\271\303\261\303\256\303\247\304\201\305\225\305\241.c",line="5",thread-groups=["i1"],times="1",original-location="/tmp/\303\271\303\261\303\256\303\247\304\201\305\225\305\241.c:5"}
+# for some reason in this output there are unescaped bytes (consistency, anyone?), but it works too
+*stopped,reason="breakpoint-hit",disp="keep",bkptno="7",frame={addr="0x0000000000400414",func="main",args=[],file="/tmp/ùñîçāŕš.c",fullname="/tmp/ùñîçāŕš.c",line="5"},thread-id="1",stopped-threads="all",core="1"
Modified: debugger/src/tests/gdb_mi_test.sh
19 lines changed, 19 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+set -e
+
+srcdir=${srcdir:-.}
+
+strip_comments()
+{
+ ${SED:-sed} -e '/^#/d'
+}
+
+TMPOUT=tests/gdb_mi_test.output.tmp
+TMPEXCPT=tests/gdb_mi_test.expected.tmp
+
+trap 'rm -f "$TMPOUT" "$TMPEXCPT"' EXIT QUIT TERM INT
+
+strip_comments < "$srcdir/tests/gdb_mi_test.input" | ./gdb_mi_test 2> "$TMPOUT"
+strip_comments < "$srcdir/tests/gdb_mi_test.expected" > "$TMPEXCPT"
+diff -u "$TMPEXCPT" "$TMPOUT"
Modified: debugger/src/tpage.c
17 lines changed, 9 insertions(+), 8 deletions(-)
===================================================================
@@ -97,10 +97,10 @@ static void on_arguments_changed(GtkTextBuffer *textbuffer, gpointer user_data)
*/
static void on_target_browse_clicked(GtkButton *button, gpointer user_data)
{
- gchar path[FILENAME_MAX];
+ gchar *path;
const gchar *prevfile;
- gchar *prevdir;
GtkWidget *dialog;
+ GeanyDocument *doc;
dialog = gtk_file_chooser_dialog_new (_("Choose target file"),
NULL,
@@ -110,14 +110,15 @@ static void on_target_browse_clicked(GtkButton *button, gpointer user_data)
NULL);
prevfile = gtk_entry_get_text(GTK_ENTRY(target_name));
- prevdir = g_path_get_dirname(prevfile);
- if (strcmp(".", prevdir))
- strcpy(path, prevdir);
- else
- strcpy(path, g_path_get_dirname(DOC_FILENAME(document_get_current())));
- g_free(prevdir);
+ path = g_path_get_dirname(prevfile);
+ if (strcmp(".", path) == 0 && (doc = document_get_current()) != NULL)
+ {
+ g_free(path);
+ path = g_path_get_dirname(DOC_FILENAME(doc));
+ }
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER (dialog), path);
+ g_free(path);
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
{
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
More information about the Plugins-Commits
mailing list