/* 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; }
/* 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; }