1 /* MI Command Set - MI parser.
3 Copyright (C) 2000-2024 Free Software Foundation, Inc.
5 Contributed by Cygnus Solutions (a Red Hat company).
7 This file is part of GDB.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
27 #include "cli/cli-utils.h"
30 static const char mi_no_values
[] = "--no-values";
31 static const char mi_simple_values
[] = "--simple-values";
32 static const char mi_all_values
[] = "--all-values";
34 /* Like parse_escape, but leave the results as a host char, not a
38 mi_parse_escape (const char **string_ptr
)
40 int c
= *(*string_ptr
)++;
65 if (isdigit (c
) && c
!= '8' && c
!= '9')
109 mi_parse::parse_argv ()
111 /* If arguments were already computed (or were supplied at
112 construction), then there's no need to re-compute them. */
116 const char *chp
= m_args
.c_str ();
118 char **argv
= XNEWVEC (char *, argc
+ 1);
125 /* Skip leading white space. */
126 chp
= skip_spaces (chp
);
127 /* Three possibilities: EOF, quoted string, or other text. */
136 /* A quoted string. */
138 const char *start
= chp
+ 1;
140 /* Determine the buffer size. */
143 while (*chp
!= '\0' && *chp
!= '"')
148 if (mi_parse_escape (&chp
) <= 0)
150 /* Do not allow split lines or "\000". */
159 /* Insist on a closing quote. */
165 /* Insist on trailing white space. */
166 if (chp
[1] != '\0' && !isspace (chp
[1]))
171 /* Create the buffer and copy characters in. */
172 arg
= XNEWVEC (char, len
+ 1);
175 while (*chp
!= '\0' && *chp
!= '"')
180 arg
[len
] = mi_parse_escape (&chp
);
187 chp
++; /* That closing quote. */
192 /* An unquoted string. Accumulate all non-blank
193 characters into a buffer. */
195 const char *start
= chp
;
197 while (*chp
!= '\0' && !isspace (*chp
))
202 arg
= XNEWVEC (char, len
+ 1);
203 strncpy (arg
, start
, len
);
208 /* Append arg to argv. */
209 argv
= XRESIZEVEC (char *, argv
, argc
+ 2);
215 mi_parse::~mi_parse ()
220 /* See mi-parse.h. */
225 /* If args were already computed, or if there is no pre-computed
226 argv, just return the args. */
227 if (!m_args
.empty () || argv
== nullptr)
228 return m_args
.c_str ();
230 /* Compute args from argv. */
231 for (int i
= 0; i
< argc
; ++i
)
233 if (!m_args
.empty ())
238 return m_args
.c_str ();
241 /* See mi-parse.h. */
244 mi_parse::set_thread_group (const char *arg
, char **endp
)
246 if (thread_group
!= -1)
247 error (_("Duplicate '--thread-group' option"));
249 error (_("Invalid thread group id"));
251 thread_group
= strtol (arg
, endp
, 10);
254 /* See mi-parse.h. */
257 mi_parse::set_thread (const char *arg
, char **endp
)
260 error (_("Duplicate '--thread' option"));
261 thread
= strtol (arg
, endp
, 10);
264 /* See mi-parse.h. */
267 mi_parse::set_frame (const char *arg
, char **endp
)
270 error (_("Duplicate '--frame' option"));
271 frame
= strtol (arg
, endp
, 10);
274 /* See mi-parse.h. */
277 mi_parse::set_language (const char *arg
, const char **endp
)
279 std::string lang_name
= extract_arg (&arg
);
281 language
= language_enum (lang_name
.c_str ());
282 if (language
== language_unknown
)
283 error (_("Invalid --language argument: %s"), lang_name
.c_str ());
289 /* See mi-parse.h. */
291 mi_parse::mi_parse (const char *cmd
, std::string
*token
)
295 /* Before starting, skip leading white space. */
296 cmd
= skip_spaces (cmd
);
298 /* Find/skip any token and then extract it. */
299 for (chp
= cmd
; *chp
>= '0' && *chp
<= '9'; chp
++)
301 *token
= std::string (cmd
, chp
- cmd
);
303 /* This wasn't a real MI command. Return it as a CLI_COMMAND. */
306 chp
= skip_spaces (chp
);
307 this->command
= make_unique_xstrdup (chp
);
308 this->op
= CLI_COMMAND
;
313 /* Extract the command. */
315 const char *tmp
= chp
+ 1; /* discard ``-'' */
317 for (; *chp
&& !isspace (*chp
); chp
++)
319 this->command
= make_unique_xstrndup (tmp
, chp
- tmp
);
322 /* Find the command in the MI table. */
323 this->cmd
= mi_cmd_lookup (this->command
.get ());
324 if (this->cmd
== NULL
)
325 throw_error (UNDEFINED_COMMAND_ERROR
,
326 _("Undefined MI command: %s"), this->command
.get ());
328 /* Skip white space following the command. */
329 chp
= skip_spaces (chp
);
331 /* Parse the --thread and --frame options, if present. At present,
332 some important commands, like '-break-*' are implemented by
333 forwarding to the CLI layer directly. We want to parse --thread
334 and --frame here, so as not to leave those option in the string
335 that will be passed to CLI.
337 Same for the --language option. */
342 size_t as
= sizeof ("--all ") - 1;
343 size_t tgs
= sizeof ("--thread-group ") - 1;
344 size_t ts
= sizeof ("--thread ") - 1;
345 size_t fs
= sizeof ("--frame ") - 1;
346 size_t ls
= sizeof ("--language ") - 1;
348 if (strncmp (chp
, "--all ", as
) == 0)
353 /* See if --all is the last token in the input. */
354 if (strcmp (chp
, "--all") == 0)
359 if (strncmp (chp
, "--thread-group ", tgs
) == 0)
363 option
= "--thread-group";
365 this->set_thread_group (chp
, &endp
);
368 else if (strncmp (chp
, "--thread ", ts
) == 0)
374 this->set_thread (chp
, &endp
);
377 else if (strncmp (chp
, "--frame ", fs
) == 0)
383 this->set_frame (chp
, &endp
);
386 else if (strncmp (chp
, "--language ", ls
) == 0)
388 option
= "--language";
390 this->set_language (chp
, &chp
);
395 if (*chp
!= '\0' && !isspace (*chp
))
396 error (_("Invalid value for the '%s' option"), option
);
397 chp
= skip_spaces (chp
);
400 /* Save the rest of the arguments for the command. */
403 /* Fully parsed, flag as an MI command. */
404 this->op
= MI_COMMAND
;
407 /* See mi-parse.h. */
409 mi_parse::mi_parse (gdb::unique_xmalloc_ptr
<char> command
,
410 std::vector
<gdb::unique_xmalloc_ptr
<char>> args
)
412 this->command
= std::move (command
);
415 if (this->command
.get ()[0] != '-')
416 throw_error (UNDEFINED_COMMAND_ERROR
,
417 _("MI command '%s' does not start with '-'"),
418 this->command
.get ());
420 /* Find the command in the MI table. */
421 this->cmd
= mi_cmd_lookup (this->command
.get () + 1);
422 if (this->cmd
== NULL
)
423 throw_error (UNDEFINED_COMMAND_ERROR
,
424 _("Undefined MI command: %s"), this->command
.get ());
426 /* This over-allocates slightly, but it seems unimportant. */
427 this->argv
= XCNEWVEC (char *, args
.size () + 1);
429 for (size_t i
= 0; i
< args
.size (); ++i
)
431 const char *chp
= args
[i
].get ();
433 /* See if --all is the last token in the input. */
434 if (strcmp (chp
, "--all") == 0)
438 else if (strcmp (chp
, "--thread-group") == 0)
441 if (i
== args
.size ())
442 error ("No argument to '--thread-group'");
443 this->set_thread_group (args
[i
].get (), nullptr);
445 else if (strcmp (chp
, "--thread") == 0)
448 if (i
== args
.size ())
449 error ("No argument to '--thread'");
450 this->set_thread (args
[i
].get (), nullptr);
452 else if (strcmp (chp
, "--frame") == 0)
455 if (i
== args
.size ())
456 error ("No argument to '--frame'");
457 this->set_frame (args
[i
].get (), nullptr);
459 else if (strcmp (chp
, "--language") == 0)
462 if (i
== args
.size ())
463 error ("No argument to '--language'");
464 this->set_language (args
[i
].get (), nullptr);
467 this->argv
[this->argc
++] = args
[i
].release ();
470 /* Fully parsed, flag as an MI command. */
471 this->op
= MI_COMMAND
;
475 mi_parse_print_values (const char *name
)
477 if (strcmp (name
, "0") == 0
478 || strcmp (name
, mi_no_values
) == 0)
479 return PRINT_NO_VALUES
;
480 else if (strcmp (name
, "1") == 0
481 || strcmp (name
, mi_all_values
) == 0)
482 return PRINT_ALL_VALUES
;
483 else if (strcmp (name
, "2") == 0
484 || strcmp (name
, mi_simple_values
) == 0)
485 return PRINT_SIMPLE_VALUES
;
487 error (_("Unknown value for PRINT_VALUES: must be: \
488 0 or \"%s\", 1 or \"%s\", 2 or \"%s\""),
489 mi_no_values
, mi_all_values
, mi_simple_values
);