]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/mi/mi-parse.c
gdb/linux-nat: initialize lwp_info::syscall_state
[thirdparty/binutils-gdb.git] / gdb / mi / mi-parse.c
CommitLineData
fb40c209 1/* MI Command Set - MI parser.
349c5d5f 2
d01e8234 3 Copyright (C) 2000-2025 Free Software Foundation, Inc.
349c5d5f 4
ab91fdd5 5 Contributed by Cygnus Solutions (a Red Hat company).
fb40c209
AC
6
7 This file is part of GDB.
8
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
a9762ec7 11 the Free Software Foundation; either version 3 of the License, or
fb40c209
AC
12 (at your option) any later version.
13
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.
18
19 You should have received a copy of the GNU General Public License
a9762ec7 20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
fb40c209 21
fb40c209
AC
22#include "mi-cmds.h"
23#include "mi-parse.h"
24
25#include <ctype.h>
529480d0 26#include "cli/cli-utils.h"
403cb6b1 27#include "language.h"
fb40c209 28
44d100c3
TT
29static const char mi_no_values[] = "--no-values";
30static const char mi_simple_values[] = "--simple-values";
31static const char mi_all_values[] = "--all-values";
87967e27 32
f870a310
TT
33/* Like parse_escape, but leave the results as a host char, not a
34 target char. */
35
36static int
ee047554 37mi_parse_escape (const char **string_ptr)
f870a310
TT
38{
39 int c = *(*string_ptr)++;
102040f0 40
f870a310
TT
41 switch (c)
42 {
43 case '\n':
44 return -2;
45 case 0:
46 (*string_ptr)--;
47 return 0;
48
49 case '0':
50 case '1':
51 case '2':
52 case '3':
53 case '4':
54 case '5':
55 case '6':
56 case '7':
57 {
2b531492 58 int i = fromhex (c);
f870a310 59 int count = 0;
102040f0 60
f870a310
TT
61 while (++count < 3)
62 {
63 c = (**string_ptr);
64 if (isdigit (c) && c != '8' && c != '9')
65 {
66 (*string_ptr)++;
67 i *= 8;
2b531492 68 i += fromhex (c);
f870a310
TT
69 }
70 else
71 {
72 break;
73 }
74 }
75 return i;
76 }
77
78 case 'a':
79 c = '\a';
80 break;
81 case 'b':
82 c = '\b';
83 break;
84 case 'f':
85 c = '\f';
86 break;
87 case 'n':
88 c = '\n';
89 break;
90 case 'r':
91 c = '\r';
92 break;
93 case 't':
94 c = '\t';
95 break;
96 case 'v':
97 c = '\v';
98 break;
99
100 default:
101 break;
102 }
103
104 return c;
105}
106
1f6c8c33 107void
7df1df79 108mi_parse::parse_argv ()
fb40c209 109{
e7a2797e
TT
110 /* If arguments were already computed (or were supplied at
111 construction), then there's no need to re-compute them. */
112 if (argv != nullptr)
113 return;
114
fde3f93a 115 const char *chp = m_args.c_str ();
fb40c209 116 int argc = 0;
8d749320 117 char **argv = XNEWVEC (char *, argc + 1);
102040f0 118
fb40c209
AC
119 argv[argc] = NULL;
120 while (1)
121 {
122 char *arg;
102040f0 123
2b03b41d 124 /* Skip leading white space. */
f1735a53 125 chp = skip_spaces (chp);
fb40c209
AC
126 /* Three possibilities: EOF, quoted string, or other text. */
127 switch (*chp)
128 {
129 case '\0':
7df1df79
TT
130 this->argv = argv;
131 this->argc = argc;
fb40c209
AC
132 return;
133 case '"':
134 {
2b03b41d 135 /* A quoted string. */
fb40c209 136 int len;
ee047554 137 const char *start = chp + 1;
102040f0 138
2b03b41d 139 /* Determine the buffer size. */
fb40c209
AC
140 chp = start;
141 len = 0;
142 while (*chp != '\0' && *chp != '"')
143 {
144 if (*chp == '\\')
145 {
146 chp++;
f870a310 147 if (mi_parse_escape (&chp) <= 0)
fb40c209 148 {
2b03b41d 149 /* Do not allow split lines or "\000". */
fb40c209
AC
150 freeargv (argv);
151 return;
152 }
153 }
154 else
155 chp++;
156 len++;
157 }
2b03b41d 158 /* Insist on a closing quote. */
fb40c209
AC
159 if (*chp != '"')
160 {
161 freeargv (argv);
162 return;
163 }
2b03b41d 164 /* Insist on trailing white space. */
fb40c209
AC
165 if (chp[1] != '\0' && !isspace (chp[1]))
166 {
167 freeargv (argv);
168 return;
169 }
2b03b41d 170 /* Create the buffer and copy characters in. */
224c3ddb 171 arg = XNEWVEC (char, len + 1);
fb40c209
AC
172 chp = start;
173 len = 0;
174 while (*chp != '\0' && *chp != '"')
175 {
176 if (*chp == '\\')
177 {
178 chp++;
f870a310 179 arg[len] = mi_parse_escape (&chp);
fb40c209
AC
180 }
181 else
182 arg[len] = *chp++;
183 len++;
184 }
185 arg[len] = '\0';
2b03b41d 186 chp++; /* That closing quote. */
fb40c209
AC
187 break;
188 }
189 default:
190 {
2b03b41d
SS
191 /* An unquoted string. Accumulate all non-blank
192 characters into a buffer. */
fb40c209 193 int len;
ee047554 194 const char *start = chp;
102040f0 195
fb40c209
AC
196 while (*chp != '\0' && !isspace (*chp))
197 {
198 chp++;
199 }
200 len = chp - start;
224c3ddb 201 arg = XNEWVEC (char, len + 1);
fb40c209
AC
202 strncpy (arg, start, len);
203 arg[len] = '\0';
204 break;
205 }
206 }
2b03b41d 207 /* Append arg to argv. */
224c3ddb 208 argv = XRESIZEVEC (char *, argv, argc + 2);
fb40c209
AC
209 argv[argc++] = arg;
210 argv[argc] = NULL;
211 }
212}
213
4d89769a 214mi_parse::~mi_parse ()
305aeedc 215{
4d89769a 216 freeargv (argv);
305aeedc 217}
fb40c209 218
6b2cb925
TT
219/* See mi-parse.h. */
220
e7a2797e
TT
221const char *
222mi_parse::args ()
223{
224 /* If args were already computed, or if there is no pre-computed
225 argv, just return the args. */
226 if (!m_args.empty () || argv == nullptr)
227 return m_args.c_str ();
228
229 /* Compute args from argv. */
230 for (int i = 0; i < argc; ++i)
231 {
232 if (!m_args.empty ())
233 m_args += " ";
234 m_args += argv[i];
235 }
236
237 return m_args.c_str ();
238}
239
240/* See mi-parse.h. */
241
6b2cb925
TT
242void
243mi_parse::set_thread_group (const char *arg, char **endp)
244{
245 if (thread_group != -1)
246 error (_("Duplicate '--thread-group' option"));
247 if (*arg != 'i')
248 error (_("Invalid thread group id"));
249 arg += 1;
250 thread_group = strtol (arg, endp, 10);
251}
252
253/* See mi-parse.h. */
254
255void
256mi_parse::set_thread (const char *arg, char **endp)
257{
258 if (thread != -1)
259 error (_("Duplicate '--thread' option"));
260 thread = strtol (arg, endp, 10);
261}
262
263/* See mi-parse.h. */
264
265void
266mi_parse::set_frame (const char *arg, char **endp)
267{
268 if (frame != -1)
269 error (_("Duplicate '--frame' option"));
270 frame = strtol (arg, endp, 10);
271}
272
273/* See mi-parse.h. */
274
275void
276mi_parse::set_language (const char *arg, const char **endp)
277{
278 std::string lang_name = extract_arg (&arg);
279
280 language = language_enum (lang_name.c_str ());
281 if (language == language_unknown)
282 error (_("Invalid --language argument: %s"), lang_name.c_str ());
283
284 if (endp != nullptr)
285 *endp = arg;
286}
287
951dbdfe
AB
288/* See mi-parse.h. */
289
290mi_parse::mi_parse (const char *cmd, std::string *token)
fb40c209 291{
ee047554 292 const char *chp;
102040f0 293
2b03b41d 294 /* Before starting, skip leading white space. */
f1735a53 295 cmd = skip_spaces (cmd);
fb40c209 296
2b03b41d 297 /* Find/skip any token and then extract it. */
fb40c209
AC
298 for (chp = cmd; *chp >= '0' && *chp <= '9'; chp++)
299 ;
550194db 300 *token = std::string (cmd, chp - cmd);
fb40c209 301
2b03b41d 302 /* This wasn't a real MI command. Return it as a CLI_COMMAND. */
fb40c209
AC
303 if (*chp != '-')
304 {
f1735a53 305 chp = skip_spaces (chp);
951dbdfe
AB
306 this->command = make_unique_xstrdup (chp);
307 this->op = CLI_COMMAND;
305aeedc 308
951dbdfe 309 return;
fb40c209
AC
310 }
311
2b03b41d 312 /* Extract the command. */
fb40c209 313 {
ee047554 314 const char *tmp = chp + 1; /* discard ``-'' */
102040f0 315
fb40c209
AC
316 for (; *chp && !isspace (*chp); chp++)
317 ;
951dbdfe 318 this->command = make_unique_xstrndup (tmp, chp - tmp);
fb40c209
AC
319 }
320
2b03b41d 321 /* Find the command in the MI table. */
951dbdfe
AB
322 this->cmd = mi_cmd_lookup (this->command.get ());
323 if (this->cmd == NULL)
2ea126fa 324 throw_error (UNDEFINED_COMMAND_ERROR,
951dbdfe 325 _("Undefined MI command: %s"), this->command.get ());
fb40c209 326
2b03b41d 327 /* Skip white space following the command. */
f1735a53 328 chp = skip_spaces (chp);
fb40c209 329
1e92afda 330 /* Parse the --thread and --frame options, if present. At present,
2b03b41d
SS
331 some important commands, like '-break-*' are implemented by
332 forwarding to the CLI layer directly. We want to parse --thread
333 and --frame here, so as not to leave those option in the string
403cb6b1
JB
334 that will be passed to CLI.
335
336 Same for the --language option. */
337
1e92afda
VP
338 for (;;)
339 {
30a7f059 340 const char *option;
a79b8f6e
VP
341 size_t as = sizeof ("--all ") - 1;
342 size_t tgs = sizeof ("--thread-group ") - 1;
1e92afda
VP
343 size_t ts = sizeof ("--thread ") - 1;
344 size_t fs = sizeof ("--frame ") - 1;
403cb6b1 345 size_t ls = sizeof ("--language ") - 1;
102040f0 346
a79b8f6e
VP
347 if (strncmp (chp, "--all ", as) == 0)
348 {
951dbdfe 349 this->all = 1;
a79b8f6e
VP
350 chp += as;
351 }
352 /* See if --all is the last token in the input. */
353 if (strcmp (chp, "--all") == 0)
354 {
951dbdfe 355 this->all = 1;
dda83cd7
SM
356 chp += strlen (chp);
357 }
a79b8f6e
VP
358 if (strncmp (chp, "--thread-group ", tgs) == 0)
359 {
ee047554
KS
360 char *endp;
361
30a7f059 362 option = "--thread-group";
a79b8f6e 363 chp += tgs;
951dbdfe 364 this->set_thread_group (chp, &endp);
ee047554 365 chp = endp;
a79b8f6e 366 }
8e0e408a 367 else if (strncmp (chp, "--thread ", ts) == 0)
1e92afda 368 {
ee047554
KS
369 char *endp;
370
30a7f059 371 option = "--thread";
1e92afda 372 chp += ts;
951dbdfe 373 this->set_thread (chp, &endp);
ee047554 374 chp = endp;
1e92afda
VP
375 }
376 else if (strncmp (chp, "--frame ", fs) == 0)
377 {
ee047554
KS
378 char *endp;
379
30a7f059 380 option = "--frame";
1e92afda 381 chp += fs;
951dbdfe 382 this->set_frame (chp, &endp);
ee047554 383 chp = endp;
1e92afda 384 }
403cb6b1
JB
385 else if (strncmp (chp, "--language ", ls) == 0)
386 {
403cb6b1
JB
387 option = "--language";
388 chp += ls;
951dbdfe 389 this->set_language (chp, &chp);
403cb6b1 390 }
1e92afda
VP
391 else
392 break;
393
394 if (*chp != '\0' && !isspace (*chp))
30a7f059 395 error (_("Invalid value for the '%s' option"), option);
f1735a53 396 chp = skip_spaces (chp);
1e92afda
VP
397 }
398
1f6c8c33 399 /* Save the rest of the arguments for the command. */
951dbdfe 400 this->m_args = chp;
fb40c209 401
2b03b41d 402 /* Fully parsed, flag as an MI command. */
951dbdfe 403 this->op = MI_COMMAND;
fb40c209 404}
87967e27 405
e7a2797e
TT
406/* See mi-parse.h. */
407
951dbdfe
AB
408mi_parse::mi_parse (gdb::unique_xmalloc_ptr<char> command,
409 std::vector<gdb::unique_xmalloc_ptr<char>> args)
e7a2797e 410{
951dbdfe
AB
411 this->command = std::move (command);
412 this->token = "";
e7a2797e 413
951dbdfe 414 if (this->command.get ()[0] != '-')
e7a2797e
TT
415 throw_error (UNDEFINED_COMMAND_ERROR,
416 _("MI command '%s' does not start with '-'"),
951dbdfe 417 this->command.get ());
e7a2797e
TT
418
419 /* Find the command in the MI table. */
951dbdfe
AB
420 this->cmd = mi_cmd_lookup (this->command.get () + 1);
421 if (this->cmd == NULL)
e7a2797e 422 throw_error (UNDEFINED_COMMAND_ERROR,
951dbdfe 423 _("Undefined MI command: %s"), this->command.get ());
e7a2797e
TT
424
425 /* This over-allocates slightly, but it seems unimportant. */
951dbdfe 426 this->argv = XCNEWVEC (char *, args.size () + 1);
e7a2797e
TT
427
428 for (size_t i = 0; i < args.size (); ++i)
429 {
430 const char *chp = args[i].get ();
431
432 /* See if --all is the last token in the input. */
433 if (strcmp (chp, "--all") == 0)
434 {
951dbdfe 435 this->all = 1;
e7a2797e
TT
436 }
437 else if (strcmp (chp, "--thread-group") == 0)
438 {
439 ++i;
440 if (i == args.size ())
441 error ("No argument to '--thread-group'");
951dbdfe 442 this->set_thread_group (args[i].get (), nullptr);
e7a2797e
TT
443 }
444 else if (strcmp (chp, "--thread") == 0)
445 {
446 ++i;
447 if (i == args.size ())
448 error ("No argument to '--thread'");
951dbdfe 449 this->set_thread (args[i].get (), nullptr);
e7a2797e
TT
450 }
451 else if (strcmp (chp, "--frame") == 0)
452 {
453 ++i;
454 if (i == args.size ())
455 error ("No argument to '--frame'");
951dbdfe 456 this->set_frame (args[i].get (), nullptr);
e7a2797e
TT
457 }
458 else if (strcmp (chp, "--language") == 0)
459 {
460 ++i;
461 if (i == args.size ())
462 error ("No argument to '--language'");
951dbdfe 463 this->set_language (args[i].get (), nullptr);
e7a2797e
TT
464 }
465 else
951dbdfe 466 this->argv[this->argc++] = args[i].release ();
e7a2797e
TT
467 }
468
469 /* Fully parsed, flag as an MI command. */
951dbdfe 470 this->op = MI_COMMAND;
e7a2797e
TT
471}
472
87967e27
YQ
473enum print_values
474mi_parse_print_values (const char *name)
475{
476 if (strcmp (name, "0") == 0
477 || strcmp (name, mi_no_values) == 0)
478 return PRINT_NO_VALUES;
479 else if (strcmp (name, "1") == 0
480 || strcmp (name, mi_all_values) == 0)
481 return PRINT_ALL_VALUES;
482 else if (strcmp (name, "2") == 0
483 || strcmp (name, mi_simple_values) == 0)
484 return PRINT_SIMPLE_VALUES;
485 else
486 error (_("Unknown value for PRINT_VALUES: must be: \
4870 or \"%s\", 1 or \"%s\", 2 or \"%s\""),
488 mi_no_values, mi_all_values, mi_simple_values);
489}