]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/mi/mi-parse.c
gdb, gdbserver, gdbsupport: remove includes of early headers
[thirdparty/binutils-gdb.git] / gdb / mi / mi-parse.c
1 /* MI Command Set - MI parser.
2
3 Copyright (C) 2000-2024 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 #include "charset.h"
25
26 #include <ctype.h>
27 #include "cli/cli-utils.h"
28 #include "language.h"
29
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";
33
34 /* Like parse_escape, but leave the results as a host char, not a
35 target char. */
36
37 static int
38 mi_parse_escape (const char **string_ptr)
39 {
40 int c = *(*string_ptr)++;
41
42 switch (c)
43 {
44 case '\n':
45 return -2;
46 case 0:
47 (*string_ptr)--;
48 return 0;
49
50 case '0':
51 case '1':
52 case '2':
53 case '3':
54 case '4':
55 case '5':
56 case '6':
57 case '7':
58 {
59 int i = fromhex (c);
60 int count = 0;
61
62 while (++count < 3)
63 {
64 c = (**string_ptr);
65 if (isdigit (c) && c != '8' && c != '9')
66 {
67 (*string_ptr)++;
68 i *= 8;
69 i += fromhex (c);
70 }
71 else
72 {
73 break;
74 }
75 }
76 return i;
77 }
78
79 case 'a':
80 c = '\a';
81 break;
82 case 'b':
83 c = '\b';
84 break;
85 case 'f':
86 c = '\f';
87 break;
88 case 'n':
89 c = '\n';
90 break;
91 case 'r':
92 c = '\r';
93 break;
94 case 't':
95 c = '\t';
96 break;
97 case 'v':
98 c = '\v';
99 break;
100
101 default:
102 break;
103 }
104
105 return c;
106 }
107
108 void
109 mi_parse::parse_argv ()
110 {
111 /* If arguments were already computed (or were supplied at
112 construction), then there's no need to re-compute them. */
113 if (argv != nullptr)
114 return;
115
116 const char *chp = m_args.c_str ();
117 int argc = 0;
118 char **argv = XNEWVEC (char *, argc + 1);
119
120 argv[argc] = NULL;
121 while (1)
122 {
123 char *arg;
124
125 /* Skip leading white space. */
126 chp = skip_spaces (chp);
127 /* Three possibilities: EOF, quoted string, or other text. */
128 switch (*chp)
129 {
130 case '\0':
131 this->argv = argv;
132 this->argc = argc;
133 return;
134 case '"':
135 {
136 /* A quoted string. */
137 int len;
138 const char *start = chp + 1;
139
140 /* Determine the buffer size. */
141 chp = start;
142 len = 0;
143 while (*chp != '\0' && *chp != '"')
144 {
145 if (*chp == '\\')
146 {
147 chp++;
148 if (mi_parse_escape (&chp) <= 0)
149 {
150 /* Do not allow split lines or "\000". */
151 freeargv (argv);
152 return;
153 }
154 }
155 else
156 chp++;
157 len++;
158 }
159 /* Insist on a closing quote. */
160 if (*chp != '"')
161 {
162 freeargv (argv);
163 return;
164 }
165 /* Insist on trailing white space. */
166 if (chp[1] != '\0' && !isspace (chp[1]))
167 {
168 freeargv (argv);
169 return;
170 }
171 /* Create the buffer and copy characters in. */
172 arg = XNEWVEC (char, len + 1);
173 chp = start;
174 len = 0;
175 while (*chp != '\0' && *chp != '"')
176 {
177 if (*chp == '\\')
178 {
179 chp++;
180 arg[len] = mi_parse_escape (&chp);
181 }
182 else
183 arg[len] = *chp++;
184 len++;
185 }
186 arg[len] = '\0';
187 chp++; /* That closing quote. */
188 break;
189 }
190 default:
191 {
192 /* An unquoted string. Accumulate all non-blank
193 characters into a buffer. */
194 int len;
195 const char *start = chp;
196
197 while (*chp != '\0' && !isspace (*chp))
198 {
199 chp++;
200 }
201 len = chp - start;
202 arg = XNEWVEC (char, len + 1);
203 strncpy (arg, start, len);
204 arg[len] = '\0';
205 break;
206 }
207 }
208 /* Append arg to argv. */
209 argv = XRESIZEVEC (char *, argv, argc + 2);
210 argv[argc++] = arg;
211 argv[argc] = NULL;
212 }
213 }
214
215 mi_parse::~mi_parse ()
216 {
217 freeargv (argv);
218 }
219
220 /* See mi-parse.h. */
221
222 const char *
223 mi_parse::args ()
224 {
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 ();
229
230 /* Compute args from argv. */
231 for (int i = 0; i < argc; ++i)
232 {
233 if (!m_args.empty ())
234 m_args += " ";
235 m_args += argv[i];
236 }
237
238 return m_args.c_str ();
239 }
240
241 /* See mi-parse.h. */
242
243 void
244 mi_parse::set_thread_group (const char *arg, char **endp)
245 {
246 if (thread_group != -1)
247 error (_("Duplicate '--thread-group' option"));
248 if (*arg != 'i')
249 error (_("Invalid thread group id"));
250 arg += 1;
251 thread_group = strtol (arg, endp, 10);
252 }
253
254 /* See mi-parse.h. */
255
256 void
257 mi_parse::set_thread (const char *arg, char **endp)
258 {
259 if (thread != -1)
260 error (_("Duplicate '--thread' option"));
261 thread = strtol (arg, endp, 10);
262 }
263
264 /* See mi-parse.h. */
265
266 void
267 mi_parse::set_frame (const char *arg, char **endp)
268 {
269 if (frame != -1)
270 error (_("Duplicate '--frame' option"));
271 frame = strtol (arg, endp, 10);
272 }
273
274 /* See mi-parse.h. */
275
276 void
277 mi_parse::set_language (const char *arg, const char **endp)
278 {
279 std::string lang_name = extract_arg (&arg);
280
281 language = language_enum (lang_name.c_str ());
282 if (language == language_unknown)
283 error (_("Invalid --language argument: %s"), lang_name.c_str ());
284
285 if (endp != nullptr)
286 *endp = arg;
287 }
288
289 /* See mi-parse.h. */
290
291 mi_parse::mi_parse (const char *cmd, std::string *token)
292 {
293 const char *chp;
294
295 /* Before starting, skip leading white space. */
296 cmd = skip_spaces (cmd);
297
298 /* Find/skip any token and then extract it. */
299 for (chp = cmd; *chp >= '0' && *chp <= '9'; chp++)
300 ;
301 *token = std::string (cmd, chp - cmd);
302
303 /* This wasn't a real MI command. Return it as a CLI_COMMAND. */
304 if (*chp != '-')
305 {
306 chp = skip_spaces (chp);
307 this->command = make_unique_xstrdup (chp);
308 this->op = CLI_COMMAND;
309
310 return;
311 }
312
313 /* Extract the command. */
314 {
315 const char *tmp = chp + 1; /* discard ``-'' */
316
317 for (; *chp && !isspace (*chp); chp++)
318 ;
319 this->command = make_unique_xstrndup (tmp, chp - tmp);
320 }
321
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 ());
327
328 /* Skip white space following the command. */
329 chp = skip_spaces (chp);
330
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.
336
337 Same for the --language option. */
338
339 for (;;)
340 {
341 const char *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;
347
348 if (strncmp (chp, "--all ", as) == 0)
349 {
350 this->all = 1;
351 chp += as;
352 }
353 /* See if --all is the last token in the input. */
354 if (strcmp (chp, "--all") == 0)
355 {
356 this->all = 1;
357 chp += strlen (chp);
358 }
359 if (strncmp (chp, "--thread-group ", tgs) == 0)
360 {
361 char *endp;
362
363 option = "--thread-group";
364 chp += tgs;
365 this->set_thread_group (chp, &endp);
366 chp = endp;
367 }
368 else if (strncmp (chp, "--thread ", ts) == 0)
369 {
370 char *endp;
371
372 option = "--thread";
373 chp += ts;
374 this->set_thread (chp, &endp);
375 chp = endp;
376 }
377 else if (strncmp (chp, "--frame ", fs) == 0)
378 {
379 char *endp;
380
381 option = "--frame";
382 chp += fs;
383 this->set_frame (chp, &endp);
384 chp = endp;
385 }
386 else if (strncmp (chp, "--language ", ls) == 0)
387 {
388 option = "--language";
389 chp += ls;
390 this->set_language (chp, &chp);
391 }
392 else
393 break;
394
395 if (*chp != '\0' && !isspace (*chp))
396 error (_("Invalid value for the '%s' option"), option);
397 chp = skip_spaces (chp);
398 }
399
400 /* Save the rest of the arguments for the command. */
401 this->m_args = chp;
402
403 /* Fully parsed, flag as an MI command. */
404 this->op = MI_COMMAND;
405 }
406
407 /* See mi-parse.h. */
408
409 mi_parse::mi_parse (gdb::unique_xmalloc_ptr<char> command,
410 std::vector<gdb::unique_xmalloc_ptr<char>> args)
411 {
412 this->command = std::move (command);
413 this->token = "";
414
415 if (this->command.get ()[0] != '-')
416 throw_error (UNDEFINED_COMMAND_ERROR,
417 _("MI command '%s' does not start with '-'"),
418 this->command.get ());
419
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 ());
425
426 /* This over-allocates slightly, but it seems unimportant. */
427 this->argv = XCNEWVEC (char *, args.size () + 1);
428
429 for (size_t i = 0; i < args.size (); ++i)
430 {
431 const char *chp = args[i].get ();
432
433 /* See if --all is the last token in the input. */
434 if (strcmp (chp, "--all") == 0)
435 {
436 this->all = 1;
437 }
438 else if (strcmp (chp, "--thread-group") == 0)
439 {
440 ++i;
441 if (i == args.size ())
442 error ("No argument to '--thread-group'");
443 this->set_thread_group (args[i].get (), nullptr);
444 }
445 else if (strcmp (chp, "--thread") == 0)
446 {
447 ++i;
448 if (i == args.size ())
449 error ("No argument to '--thread'");
450 this->set_thread (args[i].get (), nullptr);
451 }
452 else if (strcmp (chp, "--frame") == 0)
453 {
454 ++i;
455 if (i == args.size ())
456 error ("No argument to '--frame'");
457 this->set_frame (args[i].get (), nullptr);
458 }
459 else if (strcmp (chp, "--language") == 0)
460 {
461 ++i;
462 if (i == args.size ())
463 error ("No argument to '--language'");
464 this->set_language (args[i].get (), nullptr);
465 }
466 else
467 this->argv[this->argc++] = args[i].release ();
468 }
469
470 /* Fully parsed, flag as an MI command. */
471 this->op = MI_COMMAND;
472 }
473
474 enum print_values
475 mi_parse_print_values (const char *name)
476 {
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;
486 else
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);
490 }