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