]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame_incremental - gdb/mi/mi-parse.c
Automatic date update in version.in
[thirdparty/binutils-gdb.git] / gdb / mi / mi-parse.c
... / ...
CommitLineData
1/* MI Command Set - MI parser.
2
3 Copyright (C) 2000-2025 Free Software Foundation, Inc.
4
5 Contributed by Cygnus Solutions (a Red Hat company).
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
11 the Free Software Foundation; either version 3 of the License, or
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
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21
22#include "mi-cmds.h"
23#include "mi-parse.h"
24
25#include <ctype.h>
26#include "cli/cli-utils.h"
27#include "language.h"
28
29static const char mi_no_values[] = "--no-values";
30static const char mi_simple_values[] = "--simple-values";
31static const char mi_all_values[] = "--all-values";
32
33/* Like parse_escape, but leave the results as a host char, not a
34 target char. */
35
36static int
37mi_parse_escape (const char **string_ptr)
38{
39 int c = *(*string_ptr)++;
40
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 {
58 int i = fromhex (c);
59 int count = 0;
60
61 while (++count < 3)
62 {
63 c = (**string_ptr);
64 if (isdigit (c) && c != '8' && c != '9')
65 {
66 (*string_ptr)++;
67 i *= 8;
68 i += fromhex (c);
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
107void
108mi_parse::parse_argv ()
109{
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
115 const char *chp = m_args.c_str ();
116 int argc = 0;
117 char **argv = XNEWVEC (char *, argc + 1);
118
119 argv[argc] = NULL;
120 while (1)
121 {
122 char *arg;
123
124 /* Skip leading white space. */
125 chp = skip_spaces (chp);
126 /* Three possibilities: EOF, quoted string, or other text. */
127 switch (*chp)
128 {
129 case '\0':
130 this->argv = argv;
131 this->argc = argc;
132 return;
133 case '"':
134 {
135 /* A quoted string. */
136 int len;
137 const char *start = chp + 1;
138
139 /* Determine the buffer size. */
140 chp = start;
141 len = 0;
142 while (*chp != '\0' && *chp != '"')
143 {
144 if (*chp == '\\')
145 {
146 chp++;
147 if (mi_parse_escape (&chp) <= 0)
148 {
149 /* Do not allow split lines or "\000". */
150 freeargv (argv);
151 return;
152 }
153 }
154 else
155 chp++;
156 len++;
157 }
158 /* Insist on a closing quote. */
159 if (*chp != '"')
160 {
161 freeargv (argv);
162 return;
163 }
164 /* Insist on trailing white space. */
165 if (chp[1] != '\0' && !isspace (chp[1]))
166 {
167 freeargv (argv);
168 return;
169 }
170 /* Create the buffer and copy characters in. */
171 arg = XNEWVEC (char, len + 1);
172 chp = start;
173 len = 0;
174 while (*chp != '\0' && *chp != '"')
175 {
176 if (*chp == '\\')
177 {
178 chp++;
179 arg[len] = mi_parse_escape (&chp);
180 }
181 else
182 arg[len] = *chp++;
183 len++;
184 }
185 arg[len] = '\0';
186 chp++; /* That closing quote. */
187 break;
188 }
189 default:
190 {
191 /* An unquoted string. Accumulate all non-blank
192 characters into a buffer. */
193 int len;
194 const char *start = chp;
195
196 while (*chp != '\0' && !isspace (*chp))
197 {
198 chp++;
199 }
200 len = chp - start;
201 arg = XNEWVEC (char, len + 1);
202 strncpy (arg, start, len);
203 arg[len] = '\0';
204 break;
205 }
206 }
207 /* Append arg to argv. */
208 argv = XRESIZEVEC (char *, argv, argc + 2);
209 argv[argc++] = arg;
210 argv[argc] = NULL;
211 }
212}
213
214mi_parse::~mi_parse ()
215{
216 freeargv (argv);
217}
218
219/* See mi-parse.h. */
220
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
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
288/* See mi-parse.h. */
289
290mi_parse::mi_parse (const char *cmd, std::string *token)
291{
292 const char *chp;
293
294 /* Before starting, skip leading white space. */
295 cmd = skip_spaces (cmd);
296
297 /* Find/skip any token and then extract it. */
298 for (chp = cmd; *chp >= '0' && *chp <= '9'; chp++)
299 ;
300 *token = std::string (cmd, chp - cmd);
301
302 /* This wasn't a real MI command. Return it as a CLI_COMMAND. */
303 if (*chp != '-')
304 {
305 chp = skip_spaces (chp);
306 this->command = make_unique_xstrdup (chp);
307 this->op = CLI_COMMAND;
308
309 return;
310 }
311
312 /* Extract the command. */
313 {
314 const char *tmp = chp + 1; /* discard ``-'' */
315
316 for (; *chp && !isspace (*chp); chp++)
317 ;
318 this->command = make_unique_xstrndup (tmp, chp - tmp);
319 }
320
321 /* Find the command in the MI table. */
322 this->cmd = mi_cmd_lookup (this->command.get ());
323 if (this->cmd == NULL)
324 throw_error (UNDEFINED_COMMAND_ERROR,
325 _("Undefined MI command: %s"), this->command.get ());
326
327 /* Skip white space following the command. */
328 chp = skip_spaces (chp);
329
330 /* Parse the --thread and --frame options, if present. At present,
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
334 that will be passed to CLI.
335
336 Same for the --language option. */
337
338 for (;;)
339 {
340 const char *option;
341 size_t as = sizeof ("--all ") - 1;
342 size_t tgs = sizeof ("--thread-group ") - 1;
343 size_t ts = sizeof ("--thread ") - 1;
344 size_t fs = sizeof ("--frame ") - 1;
345 size_t ls = sizeof ("--language ") - 1;
346
347 if (strncmp (chp, "--all ", as) == 0)
348 {
349 this->all = 1;
350 chp += as;
351 }
352 /* See if --all is the last token in the input. */
353 if (strcmp (chp, "--all") == 0)
354 {
355 this->all = 1;
356 chp += strlen (chp);
357 }
358 if (strncmp (chp, "--thread-group ", tgs) == 0)
359 {
360 char *endp;
361
362 option = "--thread-group";
363 chp += tgs;
364 this->set_thread_group (chp, &endp);
365 chp = endp;
366 }
367 else if (strncmp (chp, "--thread ", ts) == 0)
368 {
369 char *endp;
370
371 option = "--thread";
372 chp += ts;
373 this->set_thread (chp, &endp);
374 chp = endp;
375 }
376 else if (strncmp (chp, "--frame ", fs) == 0)
377 {
378 char *endp;
379
380 option = "--frame";
381 chp += fs;
382 this->set_frame (chp, &endp);
383 chp = endp;
384 }
385 else if (strncmp (chp, "--language ", ls) == 0)
386 {
387 option = "--language";
388 chp += ls;
389 this->set_language (chp, &chp);
390 }
391 else
392 break;
393
394 if (*chp != '\0' && !isspace (*chp))
395 error (_("Invalid value for the '%s' option"), option);
396 chp = skip_spaces (chp);
397 }
398
399 /* Save the rest of the arguments for the command. */
400 this->m_args = chp;
401
402 /* Fully parsed, flag as an MI command. */
403 this->op = MI_COMMAND;
404}
405
406/* See mi-parse.h. */
407
408mi_parse::mi_parse (gdb::unique_xmalloc_ptr<char> command,
409 std::vector<gdb::unique_xmalloc_ptr<char>> args)
410{
411 this->command = std::move (command);
412 this->token = "";
413
414 if (this->command.get ()[0] != '-')
415 throw_error (UNDEFINED_COMMAND_ERROR,
416 _("MI command '%s' does not start with '-'"),
417 this->command.get ());
418
419 /* Find the command in the MI table. */
420 this->cmd = mi_cmd_lookup (this->command.get () + 1);
421 if (this->cmd == NULL)
422 throw_error (UNDEFINED_COMMAND_ERROR,
423 _("Undefined MI command: %s"), this->command.get ());
424
425 /* This over-allocates slightly, but it seems unimportant. */
426 this->argv = XCNEWVEC (char *, args.size () + 1);
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 {
435 this->all = 1;
436 }
437 else if (strcmp (chp, "--thread-group") == 0)
438 {
439 ++i;
440 if (i == args.size ())
441 error ("No argument to '--thread-group'");
442 this->set_thread_group (args[i].get (), nullptr);
443 }
444 else if (strcmp (chp, "--thread") == 0)
445 {
446 ++i;
447 if (i == args.size ())
448 error ("No argument to '--thread'");
449 this->set_thread (args[i].get (), nullptr);
450 }
451 else if (strcmp (chp, "--frame") == 0)
452 {
453 ++i;
454 if (i == args.size ())
455 error ("No argument to '--frame'");
456 this->set_frame (args[i].get (), nullptr);
457 }
458 else if (strcmp (chp, "--language") == 0)
459 {
460 ++i;
461 if (i == args.size ())
462 error ("No argument to '--language'");
463 this->set_language (args[i].get (), nullptr);
464 }
465 else
466 this->argv[this->argc++] = args[i].release ();
467 }
468
469 /* Fully parsed, flag as an MI command. */
470 this->op = MI_COMMAND;
471}
472
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}