Branch: refs/heads/master Author: Frank Lanitz frank@frank.uvena.de Committer: Frank Lanitz frank@frank.uvena.de Date: Sun, 15 Nov 2015 17:28:09 UTC Commit: 835bea07dc537efe2306c36e3654e67850b9b655 https://github.com/geany/geany-plugins/commit/835bea07dc537efe2306c36e3654e6...
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@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.h... + */ + +#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@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).
plugins-commits@lists.geany.org