1 /* MI Command Set - MI parser. 2 3 Copyright (C) 2000-2023 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 "defs.h" 23 #include "mi-cmds.h" 24 #include "mi-parse.h" 25 #include "charset.h" 26 27 #include <ctype.h> 28 #include "cli/cli-utils.h" 29 #include "language.h" 30 31 static const char mi_no_values[] = "--no-values"; 32 static const char mi_simple_values[] = "--simple-values"; 33 static const char mi_all_values[] = "--all-values"; 34 35 /* Like parse_escape, but leave the results as a host char, not a 36 target char. */ 37 38 static int 39 mi_parse_escape (const char **string_ptr) 40 { 41 int c = *(*string_ptr)++; 42 43 switch (c) 44 { 45 case '\n': 46 return -2; 47 case 0: 48 (*string_ptr)--; 49 return 0; 50 51 case '0': 52 case '1': 53 case '2': 54 case '3': 55 case '4': 56 case '5': 57 case '6': 58 case '7': 59 { 60 int i = fromhex (c); 61 int count = 0; 62 63 while (++count < 3) 64 { 65 c = (**string_ptr); 66 if (isdigit (c) && c != '8' && c != '9') 67 { 68 (*string_ptr)++; 69 i *= 8; 70 i += fromhex (c); 71 } 72 else 73 { 74 break; 75 } 76 } 77 return i; 78 } 79 80 case 'a': 81 c = '\a'; 82 break; 83 case 'b': 84 c = '\b'; 85 break; 86 case 'f': 87 c = '\f'; 88 break; 89 case 'n': 90 c = '\n'; 91 break; 92 case 'r': 93 c = '\r'; 94 break; 95 case 't': 96 c = '\t'; 97 break; 98 case 'v': 99 c = '\v'; 100 break; 101 102 default: 103 break; 104 } 105 106 return c; 107 } 108 109 void 110 mi_parse_argv (const char *args, struct mi_parse *parse) 111 { 112 const char *chp = args; 113 int argc = 0; 114 char **argv = XNEWVEC (char *, argc + 1); 115 116 argv[argc] = NULL; 117 while (1) 118 { 119 char *arg; 120 121 /* Skip leading white space. */ 122 chp = skip_spaces (chp); 123 /* Three possibilities: EOF, quoted string, or other text. */ 124 switch (*chp) 125 { 126 case '\0': 127 parse->argv = argv; 128 parse->argc = argc; 129 return; 130 case '"': 131 { 132 /* A quoted string. */ 133 int len; 134 const char *start = chp + 1; 135 136 /* Determine the buffer size. */ 137 chp = start; 138 len = 0; 139 while (*chp != '\0' && *chp != '"') 140 { 141 if (*chp == '\\') 142 { 143 chp++; 144 if (mi_parse_escape (&chp) <= 0) 145 { 146 /* Do not allow split lines or "\000". */ 147 freeargv (argv); 148 return; 149 } 150 } 151 else 152 chp++; 153 len++; 154 } 155 /* Insist on a closing quote. */ 156 if (*chp != '"') 157 { 158 freeargv (argv); 159 return; 160 } 161 /* Insist on trailing white space. */ 162 if (chp[1] != '\0' && !isspace (chp[1])) 163 { 164 freeargv (argv); 165 return; 166 } 167 /* Create the buffer and copy characters in. */ 168 arg = XNEWVEC (char, len + 1); 169 chp = start; 170 len = 0; 171 while (*chp != '\0' && *chp != '"') 172 { 173 if (*chp == '\\') 174 { 175 chp++; 176 arg[len] = mi_parse_escape (&chp); 177 } 178 else 179 arg[len] = *chp++; 180 len++; 181 } 182 arg[len] = '\0'; 183 chp++; /* That closing quote. */ 184 break; 185 } 186 default: 187 { 188 /* An unquoted string. Accumulate all non-blank 189 characters into a buffer. */ 190 int len; 191 const char *start = chp; 192 193 while (*chp != '\0' && !isspace (*chp)) 194 { 195 chp++; 196 } 197 len = chp - start; 198 arg = XNEWVEC (char, len + 1); 199 strncpy (arg, start, len); 200 arg[len] = '\0'; 201 break; 202 } 203 } 204 /* Append arg to argv. */ 205 argv = XRESIZEVEC (char *, argv, argc + 2); 206 argv[argc++] = arg; 207 argv[argc] = NULL; 208 } 209 } 210 211 mi_parse::mi_parse () 212 : op (MI_COMMAND), 213 command (NULL), 214 token (NULL), 215 cmd (NULL), 216 cmd_start (NULL), 217 args (NULL), 218 argv (NULL), 219 argc (0), 220 all (0), 221 thread_group (-1), 222 thread (-1), 223 frame (-1), 224 language (language_unknown) 225 { 226 } 227 228 mi_parse::~mi_parse () 229 { 230 xfree (command); 231 xfree (token); 232 xfree (args); 233 freeargv (argv); 234 } 235 236 std::unique_ptr<struct mi_parse> 237 mi_parse (const char *cmd, char **token) 238 { 239 const char *chp; 240 241 std::unique_ptr<struct mi_parse> parse (new struct mi_parse); 242 243 /* Before starting, skip leading white space. */ 244 cmd = skip_spaces (cmd); 245 246 /* Find/skip any token and then extract it. */ 247 for (chp = cmd; *chp >= '0' && *chp <= '9'; chp++) 248 ; 249 *token = (char *) xmalloc (chp - cmd + 1); 250 memcpy (*token, cmd, (chp - cmd)); 251 (*token)[chp - cmd] = '\0'; 252 253 /* This wasn't a real MI command. Return it as a CLI_COMMAND. */ 254 if (*chp != '-') 255 { 256 chp = skip_spaces (chp); 257 parse->command = xstrdup (chp); 258 parse->op = CLI_COMMAND; 259 260 return parse; 261 } 262 263 /* Extract the command. */ 264 { 265 const char *tmp = chp + 1; /* discard ``-'' */ 266 267 for (; *chp && !isspace (*chp); chp++) 268 ; 269 parse->command = (char *) xmalloc (chp - tmp + 1); 270 memcpy (parse->command, tmp, chp - tmp); 271 parse->command[chp - tmp] = '\0'; 272 } 273 274 /* Find the command in the MI table. */ 275 parse->cmd = mi_cmd_lookup (parse->command); 276 if (parse->cmd == NULL) 277 throw_error (UNDEFINED_COMMAND_ERROR, 278 _("Undefined MI command: %s"), parse->command); 279 280 /* Skip white space following the command. */ 281 chp = skip_spaces (chp); 282 283 /* Parse the --thread and --frame options, if present. At present, 284 some important commands, like '-break-*' are implemented by 285 forwarding to the CLI layer directly. We want to parse --thread 286 and --frame here, so as not to leave those option in the string 287 that will be passed to CLI. 288 289 Same for the --language option. */ 290 291 for (;;) 292 { 293 const char *option; 294 size_t as = sizeof ("--all ") - 1; 295 size_t tgs = sizeof ("--thread-group ") - 1; 296 size_t ts = sizeof ("--thread ") - 1; 297 size_t fs = sizeof ("--frame ") - 1; 298 size_t ls = sizeof ("--language ") - 1; 299 300 if (strncmp (chp, "--all ", as) == 0) 301 { 302 parse->all = 1; 303 chp += as; 304 } 305 /* See if --all is the last token in the input. */ 306 if (strcmp (chp, "--all") == 0) 307 { 308 parse->all = 1; 309 chp += strlen (chp); 310 } 311 if (strncmp (chp, "--thread-group ", tgs) == 0) 312 { 313 char *endp; 314 315 option = "--thread-group"; 316 if (parse->thread_group != -1) 317 error (_("Duplicate '--thread-group' option")); 318 chp += tgs; 319 if (*chp != 'i') 320 error (_("Invalid thread group id")); 321 chp += 1; 322 parse->thread_group = strtol (chp, &endp, 10); 323 chp = endp; 324 } 325 else if (strncmp (chp, "--thread ", ts) == 0) 326 { 327 char *endp; 328 329 option = "--thread"; 330 if (parse->thread != -1) 331 error (_("Duplicate '--thread' option")); 332 chp += ts; 333 parse->thread = strtol (chp, &endp, 10); 334 chp = endp; 335 } 336 else if (strncmp (chp, "--frame ", fs) == 0) 337 { 338 char *endp; 339 340 option = "--frame"; 341 if (parse->frame != -1) 342 error (_("Duplicate '--frame' option")); 343 chp += fs; 344 parse->frame = strtol (chp, &endp, 10); 345 chp = endp; 346 } 347 else if (strncmp (chp, "--language ", ls) == 0) 348 { 349 option = "--language"; 350 chp += ls; 351 std::string lang_name = extract_arg (&chp); 352 353 parse->language = language_enum (lang_name.c_str ()); 354 if (parse->language == language_unknown 355 || parse->language == language_auto) 356 error (_("Invalid --language argument: %s"), lang_name.c_str ()); 357 } 358 else 359 break; 360 361 if (*chp != '\0' && !isspace (*chp)) 362 error (_("Invalid value for the '%s' option"), option); 363 chp = skip_spaces (chp); 364 } 365 366 /* Save the rest of the arguments for the command. */ 367 parse->args = xstrdup (chp); 368 369 /* Fully parsed, flag as an MI command. */ 370 parse->op = MI_COMMAND; 371 return parse; 372 } 373 374 enum print_values 375 mi_parse_print_values (const char *name) 376 { 377 if (strcmp (name, "0") == 0 378 || strcmp (name, mi_no_values) == 0) 379 return PRINT_NO_VALUES; 380 else if (strcmp (name, "1") == 0 381 || strcmp (name, mi_all_values) == 0) 382 return PRINT_ALL_VALUES; 383 else if (strcmp (name, "2") == 0 384 || strcmp (name, mi_simple_values) == 0) 385 return PRINT_SIMPLE_VALUES; 386 else 387 error (_("Unknown value for PRINT_VALUES: must be: \ 388 0 or \"%s\", 1 or \"%s\", 2 or \"%s\""), 389 mi_no_values, mi_all_values, mi_simple_values); 390 } 391