mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-01-06 12:09:26 +08:00
951dbdfeec
Remove the static mi_parse::make functions, and instead use the
mi_parse constructor.
This is a partial revert of the commit:
commit fde3f93adb
Date: Mon Mar 20 10:56:55 2023 -0600
Introduce "static constructor" for mi_parse
which introduced the mi_parse::make functions, though after discussion
on the list the reasons for seem to have been lost[1]. Given there
are no test regressions when moving back to using the constructors, I
propose we should do that for now.
There should be no user visible changes after this commit.
[1] https://inbox.sourceware.org/gdb-patches/20230404-dap-loaded-sources-v2-5-93f229095e03@adacore.com/
Approved-By: Tom Tromey <tom@tromey.com>
492 lines
11 KiB
C
492 lines
11 KiB
C
/* MI Command Set - MI parser.
|
|
|
|
Copyright (C) 2000-2023 Free Software Foundation, Inc.
|
|
|
|
Contributed by Cygnus Solutions (a Red Hat company).
|
|
|
|
This file is part of GDB.
|
|
|
|
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 3 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, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "defs.h"
|
|
#include "mi-cmds.h"
|
|
#include "mi-parse.h"
|
|
#include "charset.h"
|
|
|
|
#include <ctype.h>
|
|
#include "cli/cli-utils.h"
|
|
#include "language.h"
|
|
|
|
static const char mi_no_values[] = "--no-values";
|
|
static const char mi_simple_values[] = "--simple-values";
|
|
static const char mi_all_values[] = "--all-values";
|
|
|
|
/* Like parse_escape, but leave the results as a host char, not a
|
|
target char. */
|
|
|
|
static int
|
|
mi_parse_escape (const char **string_ptr)
|
|
{
|
|
int c = *(*string_ptr)++;
|
|
|
|
switch (c)
|
|
{
|
|
case '\n':
|
|
return -2;
|
|
case 0:
|
|
(*string_ptr)--;
|
|
return 0;
|
|
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
{
|
|
int i = fromhex (c);
|
|
int count = 0;
|
|
|
|
while (++count < 3)
|
|
{
|
|
c = (**string_ptr);
|
|
if (isdigit (c) && c != '8' && c != '9')
|
|
{
|
|
(*string_ptr)++;
|
|
i *= 8;
|
|
i += fromhex (c);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
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:
|
|
break;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
void
|
|
mi_parse::parse_argv ()
|
|
{
|
|
/* If arguments were already computed (or were supplied at
|
|
construction), then there's no need to re-compute them. */
|
|
if (argv != nullptr)
|
|
return;
|
|
|
|
const char *chp = m_args.c_str ();
|
|
int argc = 0;
|
|
char **argv = XNEWVEC (char *, argc + 1);
|
|
|
|
argv[argc] = NULL;
|
|
while (1)
|
|
{
|
|
char *arg;
|
|
|
|
/* Skip leading white space. */
|
|
chp = skip_spaces (chp);
|
|
/* Three possibilities: EOF, quoted string, or other text. */
|
|
switch (*chp)
|
|
{
|
|
case '\0':
|
|
this->argv = argv;
|
|
this->argc = argc;
|
|
return;
|
|
case '"':
|
|
{
|
|
/* A quoted string. */
|
|
int len;
|
|
const char *start = chp + 1;
|
|
|
|
/* Determine the buffer size. */
|
|
chp = start;
|
|
len = 0;
|
|
while (*chp != '\0' && *chp != '"')
|
|
{
|
|
if (*chp == '\\')
|
|
{
|
|
chp++;
|
|
if (mi_parse_escape (&chp) <= 0)
|
|
{
|
|
/* Do not allow split lines or "\000". */
|
|
freeargv (argv);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
chp++;
|
|
len++;
|
|
}
|
|
/* Insist on a closing quote. */
|
|
if (*chp != '"')
|
|
{
|
|
freeargv (argv);
|
|
return;
|
|
}
|
|
/* Insist on trailing white space. */
|
|
if (chp[1] != '\0' && !isspace (chp[1]))
|
|
{
|
|
freeargv (argv);
|
|
return;
|
|
}
|
|
/* Create the buffer and copy characters in. */
|
|
arg = XNEWVEC (char, len + 1);
|
|
chp = start;
|
|
len = 0;
|
|
while (*chp != '\0' && *chp != '"')
|
|
{
|
|
if (*chp == '\\')
|
|
{
|
|
chp++;
|
|
arg[len] = mi_parse_escape (&chp);
|
|
}
|
|
else
|
|
arg[len] = *chp++;
|
|
len++;
|
|
}
|
|
arg[len] = '\0';
|
|
chp++; /* That closing quote. */
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
/* An unquoted string. Accumulate all non-blank
|
|
characters into a buffer. */
|
|
int len;
|
|
const char *start = chp;
|
|
|
|
while (*chp != '\0' && !isspace (*chp))
|
|
{
|
|
chp++;
|
|
}
|
|
len = chp - start;
|
|
arg = XNEWVEC (char, len + 1);
|
|
strncpy (arg, start, len);
|
|
arg[len] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
/* Append arg to argv. */
|
|
argv = XRESIZEVEC (char *, argv, argc + 2);
|
|
argv[argc++] = arg;
|
|
argv[argc] = NULL;
|
|
}
|
|
}
|
|
|
|
mi_parse::~mi_parse ()
|
|
{
|
|
freeargv (argv);
|
|
}
|
|
|
|
/* See mi-parse.h. */
|
|
|
|
const char *
|
|
mi_parse::args ()
|
|
{
|
|
/* If args were already computed, or if there is no pre-computed
|
|
argv, just return the args. */
|
|
if (!m_args.empty () || argv == nullptr)
|
|
return m_args.c_str ();
|
|
|
|
/* Compute args from argv. */
|
|
for (int i = 0; i < argc; ++i)
|
|
{
|
|
if (!m_args.empty ())
|
|
m_args += " ";
|
|
m_args += argv[i];
|
|
}
|
|
|
|
return m_args.c_str ();
|
|
}
|
|
|
|
/* See mi-parse.h. */
|
|
|
|
void
|
|
mi_parse::set_thread_group (const char *arg, char **endp)
|
|
{
|
|
if (thread_group != -1)
|
|
error (_("Duplicate '--thread-group' option"));
|
|
if (*arg != 'i')
|
|
error (_("Invalid thread group id"));
|
|
arg += 1;
|
|
thread_group = strtol (arg, endp, 10);
|
|
}
|
|
|
|
/* See mi-parse.h. */
|
|
|
|
void
|
|
mi_parse::set_thread (const char *arg, char **endp)
|
|
{
|
|
if (thread != -1)
|
|
error (_("Duplicate '--thread' option"));
|
|
thread = strtol (arg, endp, 10);
|
|
}
|
|
|
|
/* See mi-parse.h. */
|
|
|
|
void
|
|
mi_parse::set_frame (const char *arg, char **endp)
|
|
{
|
|
if (frame != -1)
|
|
error (_("Duplicate '--frame' option"));
|
|
frame = strtol (arg, endp, 10);
|
|
}
|
|
|
|
/* See mi-parse.h. */
|
|
|
|
void
|
|
mi_parse::set_language (const char *arg, const char **endp)
|
|
{
|
|
std::string lang_name = extract_arg (&arg);
|
|
|
|
language = language_enum (lang_name.c_str ());
|
|
if (language == language_unknown)
|
|
error (_("Invalid --language argument: %s"), lang_name.c_str ());
|
|
|
|
if (endp != nullptr)
|
|
*endp = arg;
|
|
}
|
|
|
|
/* See mi-parse.h. */
|
|
|
|
mi_parse::mi_parse (const char *cmd, std::string *token)
|
|
{
|
|
const char *chp;
|
|
|
|
/* Before starting, skip leading white space. */
|
|
cmd = skip_spaces (cmd);
|
|
|
|
/* Find/skip any token and then extract it. */
|
|
for (chp = cmd; *chp >= '0' && *chp <= '9'; chp++)
|
|
;
|
|
*token = std::string (cmd, chp - cmd);
|
|
|
|
/* This wasn't a real MI command. Return it as a CLI_COMMAND. */
|
|
if (*chp != '-')
|
|
{
|
|
chp = skip_spaces (chp);
|
|
this->command = make_unique_xstrdup (chp);
|
|
this->op = CLI_COMMAND;
|
|
|
|
return;
|
|
}
|
|
|
|
/* Extract the command. */
|
|
{
|
|
const char *tmp = chp + 1; /* discard ``-'' */
|
|
|
|
for (; *chp && !isspace (*chp); chp++)
|
|
;
|
|
this->command = make_unique_xstrndup (tmp, chp - tmp);
|
|
}
|
|
|
|
/* Find the command in the MI table. */
|
|
this->cmd = mi_cmd_lookup (this->command.get ());
|
|
if (this->cmd == NULL)
|
|
throw_error (UNDEFINED_COMMAND_ERROR,
|
|
_("Undefined MI command: %s"), this->command.get ());
|
|
|
|
/* Skip white space following the command. */
|
|
chp = skip_spaces (chp);
|
|
|
|
/* Parse the --thread and --frame options, if present. At present,
|
|
some important commands, like '-break-*' are implemented by
|
|
forwarding to the CLI layer directly. We want to parse --thread
|
|
and --frame here, so as not to leave those option in the string
|
|
that will be passed to CLI.
|
|
|
|
Same for the --language option. */
|
|
|
|
for (;;)
|
|
{
|
|
const char *option;
|
|
size_t as = sizeof ("--all ") - 1;
|
|
size_t tgs = sizeof ("--thread-group ") - 1;
|
|
size_t ts = sizeof ("--thread ") - 1;
|
|
size_t fs = sizeof ("--frame ") - 1;
|
|
size_t ls = sizeof ("--language ") - 1;
|
|
|
|
if (strncmp (chp, "--all ", as) == 0)
|
|
{
|
|
this->all = 1;
|
|
chp += as;
|
|
}
|
|
/* See if --all is the last token in the input. */
|
|
if (strcmp (chp, "--all") == 0)
|
|
{
|
|
this->all = 1;
|
|
chp += strlen (chp);
|
|
}
|
|
if (strncmp (chp, "--thread-group ", tgs) == 0)
|
|
{
|
|
char *endp;
|
|
|
|
option = "--thread-group";
|
|
chp += tgs;
|
|
this->set_thread_group (chp, &endp);
|
|
chp = endp;
|
|
}
|
|
else if (strncmp (chp, "--thread ", ts) == 0)
|
|
{
|
|
char *endp;
|
|
|
|
option = "--thread";
|
|
chp += ts;
|
|
this->set_thread (chp, &endp);
|
|
chp = endp;
|
|
}
|
|
else if (strncmp (chp, "--frame ", fs) == 0)
|
|
{
|
|
char *endp;
|
|
|
|
option = "--frame";
|
|
chp += fs;
|
|
this->set_frame (chp, &endp);
|
|
chp = endp;
|
|
}
|
|
else if (strncmp (chp, "--language ", ls) == 0)
|
|
{
|
|
option = "--language";
|
|
chp += ls;
|
|
this->set_language (chp, &chp);
|
|
}
|
|
else
|
|
break;
|
|
|
|
if (*chp != '\0' && !isspace (*chp))
|
|
error (_("Invalid value for the '%s' option"), option);
|
|
chp = skip_spaces (chp);
|
|
}
|
|
|
|
/* Save the rest of the arguments for the command. */
|
|
this->m_args = chp;
|
|
|
|
/* Fully parsed, flag as an MI command. */
|
|
this->op = MI_COMMAND;
|
|
}
|
|
|
|
/* See mi-parse.h. */
|
|
|
|
mi_parse::mi_parse (gdb::unique_xmalloc_ptr<char> command,
|
|
std::vector<gdb::unique_xmalloc_ptr<char>> args)
|
|
{
|
|
this->command = std::move (command);
|
|
this->token = "";
|
|
|
|
if (this->command.get ()[0] != '-')
|
|
throw_error (UNDEFINED_COMMAND_ERROR,
|
|
_("MI command '%s' does not start with '-'"),
|
|
this->command.get ());
|
|
|
|
/* Find the command in the MI table. */
|
|
this->cmd = mi_cmd_lookup (this->command.get () + 1);
|
|
if (this->cmd == NULL)
|
|
throw_error (UNDEFINED_COMMAND_ERROR,
|
|
_("Undefined MI command: %s"), this->command.get ());
|
|
|
|
/* This over-allocates slightly, but it seems unimportant. */
|
|
this->argv = XCNEWVEC (char *, args.size () + 1);
|
|
|
|
for (size_t i = 0; i < args.size (); ++i)
|
|
{
|
|
const char *chp = args[i].get ();
|
|
|
|
/* See if --all is the last token in the input. */
|
|
if (strcmp (chp, "--all") == 0)
|
|
{
|
|
this->all = 1;
|
|
}
|
|
else if (strcmp (chp, "--thread-group") == 0)
|
|
{
|
|
++i;
|
|
if (i == args.size ())
|
|
error ("No argument to '--thread-group'");
|
|
this->set_thread_group (args[i].get (), nullptr);
|
|
}
|
|
else if (strcmp (chp, "--thread") == 0)
|
|
{
|
|
++i;
|
|
if (i == args.size ())
|
|
error ("No argument to '--thread'");
|
|
this->set_thread (args[i].get (), nullptr);
|
|
}
|
|
else if (strcmp (chp, "--frame") == 0)
|
|
{
|
|
++i;
|
|
if (i == args.size ())
|
|
error ("No argument to '--frame'");
|
|
this->set_frame (args[i].get (), nullptr);
|
|
}
|
|
else if (strcmp (chp, "--language") == 0)
|
|
{
|
|
++i;
|
|
if (i == args.size ())
|
|
error ("No argument to '--language'");
|
|
this->set_language (args[i].get (), nullptr);
|
|
}
|
|
else
|
|
this->argv[this->argc++] = args[i].release ();
|
|
}
|
|
|
|
/* Fully parsed, flag as an MI command. */
|
|
this->op = MI_COMMAND;
|
|
}
|
|
|
|
enum print_values
|
|
mi_parse_print_values (const char *name)
|
|
{
|
|
if (strcmp (name, "0") == 0
|
|
|| strcmp (name, mi_no_values) == 0)
|
|
return PRINT_NO_VALUES;
|
|
else if (strcmp (name, "1") == 0
|
|
|| strcmp (name, mi_all_values) == 0)
|
|
return PRINT_ALL_VALUES;
|
|
else if (strcmp (name, "2") == 0
|
|
|| strcmp (name, mi_simple_values) == 0)
|
|
return PRINT_SIMPLE_VALUES;
|
|
else
|
|
error (_("Unknown value for PRINT_VALUES: must be: \
|
|
0 or \"%s\", 1 or \"%s\", 2 or \"%s\""),
|
|
mi_no_values, mi_all_values, mi_simple_values);
|
|
}
|