1 /* MI Command Set - MI parser. 2 3 Copyright (C) 2000, 2001, 2002, 2007, 2008, 2009, 2010 4 Free Software Foundation, Inc. 5 6 Contributed by Cygnus Solutions (a Red Hat company). 7 8 This file is part of GDB. 9 10 This program is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 3 of the License, or 13 (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 22 23 #include "defs.h" 24 #include "mi-cmds.h" 25 #include "mi-parse.h" 26 #include "charset.h" 27 28 #include <ctype.h> 29 #include "gdb_string.h" 30 31 /* Like parse_escape, but leave the results as a host char, not a 32 target char. */ 33 34 static int 35 mi_parse_escape (char **string_ptr) 36 { 37 int c = *(*string_ptr)++; 38 39 switch (c) 40 { 41 case '\n': 42 return -2; 43 case 0: 44 (*string_ptr)--; 45 return 0; 46 47 case '0': 48 case '1': 49 case '2': 50 case '3': 51 case '4': 52 case '5': 53 case '6': 54 case '7': 55 { 56 int i = host_hex_value (c); 57 int count = 0; 58 59 while (++count < 3) 60 { 61 c = (**string_ptr); 62 if (isdigit (c) && c != '8' && c != '9') 63 { 64 (*string_ptr)++; 65 i *= 8; 66 i += host_hex_value (c); 67 } 68 else 69 { 70 break; 71 } 72 } 73 return i; 74 } 75 76 case 'a': 77 c = '\a'; 78 break; 79 case 'b': 80 c = '\b'; 81 break; 82 case 'f': 83 c = '\f'; 84 break; 85 case 'n': 86 c = '\n'; 87 break; 88 case 'r': 89 c = '\r'; 90 break; 91 case 't': 92 c = '\t'; 93 break; 94 case 'v': 95 c = '\v'; 96 break; 97 98 default: 99 break; 100 } 101 102 return c; 103 } 104 105 static void 106 mi_parse_argv (char *args, struct mi_parse *parse) 107 { 108 char *chp = args; 109 int argc = 0; 110 char **argv = xmalloc ((argc + 1) * sizeof (char *)); 111 112 argv[argc] = NULL; 113 while (1) 114 { 115 char *arg; 116 117 /* skip leading white space */ 118 while (isspace (*chp)) 119 chp++; 120 /* Three possibilities: EOF, quoted string, or other text. */ 121 switch (*chp) 122 { 123 case '\0': 124 parse->argv = argv; 125 parse->argc = argc; 126 return; 127 case '"': 128 { 129 /* A quoted string. */ 130 int len; 131 char *start = chp + 1; 132 133 /* Determine the buffer size. */ 134 chp = start; 135 len = 0; 136 while (*chp != '\0' && *chp != '"') 137 { 138 if (*chp == '\\') 139 { 140 chp++; 141 if (mi_parse_escape (&chp) <= 0) 142 { 143 /* Do not allow split lines or "\000" */ 144 freeargv (argv); 145 return; 146 } 147 } 148 else 149 chp++; 150 len++; 151 } 152 /* Insist on a closing quote. */ 153 if (*chp != '"') 154 { 155 freeargv (argv); 156 return; 157 } 158 /* Insist on trailing white space. */ 159 if (chp[1] != '\0' && !isspace (chp[1])) 160 { 161 freeargv (argv); 162 return; 163 } 164 /* create the buffer. */ 165 arg = xmalloc ((len + 1) * sizeof (char)); 166 /* And copy the characters in. */ 167 chp = start; 168 len = 0; 169 while (*chp != '\0' && *chp != '"') 170 { 171 if (*chp == '\\') 172 { 173 chp++; 174 arg[len] = mi_parse_escape (&chp); 175 } 176 else 177 arg[len] = *chp++; 178 len++; 179 } 180 arg[len] = '\0'; 181 chp++; /* that closing quote. */ 182 break; 183 } 184 default: 185 { 186 /* An unquoted string. Accumulate all non blank 187 characters into a buffer. */ 188 int len; 189 char *start = chp; 190 191 while (*chp != '\0' && !isspace (*chp)) 192 { 193 chp++; 194 } 195 len = chp - start; 196 arg = xmalloc ((len + 1) * sizeof (char)); 197 strncpy (arg, start, len); 198 arg[len] = '\0'; 199 break; 200 } 201 } 202 /* Append arg to argv. */ 203 argv = xrealloc (argv, (argc + 2) * sizeof (char *)); 204 argv[argc++] = arg; 205 argv[argc] = NULL; 206 } 207 } 208 209 210 void 211 mi_parse_free (struct mi_parse *parse) 212 { 213 if (parse == NULL) 214 return; 215 if (parse->command != NULL) 216 xfree (parse->command); 217 if (parse->token != NULL) 218 xfree (parse->token); 219 if (parse->args != NULL) 220 xfree (parse->args); 221 if (parse->argv != NULL) 222 freeargv (parse->argv); 223 xfree (parse); 224 } 225 226 227 struct mi_parse * 228 mi_parse (char *cmd) 229 { 230 char *chp; 231 struct mi_parse *parse = XMALLOC (struct mi_parse); 232 233 memset (parse, 0, sizeof (*parse)); 234 parse->all = 0; 235 parse->thread_group = -1; 236 parse->thread = -1; 237 parse->frame = -1; 238 239 /* Before starting, skip leading white space. */ 240 while (isspace (*cmd)) 241 cmd++; 242 243 /* Find/skip any token and then extract it. */ 244 for (chp = cmd; *chp >= '0' && *chp <= '9'; chp++) 245 ; 246 parse->token = xmalloc ((chp - cmd + 1) * sizeof (char *)); 247 memcpy (parse->token, cmd, (chp - cmd)); 248 parse->token[chp - cmd] = '\0'; 249 250 /* This wasn't a real MI command. Return it as a CLI_COMMAND. */ 251 if (*chp != '-') 252 { 253 while (isspace (*chp)) 254 chp++; 255 parse->command = xstrdup (chp); 256 parse->op = CLI_COMMAND; 257 return parse; 258 } 259 260 /* Extract the command. */ 261 { 262 char *tmp = chp + 1; /* discard ``-'' */ 263 264 for (; *chp && !isspace (*chp); chp++) 265 ; 266 parse->command = xmalloc ((chp - tmp + 1) * sizeof (char *)); 267 memcpy (parse->command, tmp, chp - tmp); 268 parse->command[chp - tmp] = '\0'; 269 } 270 271 /* Find the command in the MI table. */ 272 parse->cmd = mi_lookup (parse->command); 273 if (parse->cmd == NULL) 274 { 275 /* FIXME: This should be a function call. */ 276 fprintf_unfiltered 277 (raw_stdout, 278 "%s^error,msg=\"Undefined MI command: %s\"\n", 279 parse->token, parse->command); 280 mi_parse_free (parse); 281 return NULL; 282 } 283 284 /* Skip white space following the command. */ 285 while (isspace (*chp)) 286 chp++; 287 288 /* Parse the --thread and --frame options, if present. At present, 289 some important commands, like '-break-*' are implemented by forwarding 290 to the CLI layer directly. We want to parse --thread and --frame 291 here, so as not to leave those option in the string that will be passed 292 to CLI. */ 293 for (;;) 294 { 295 char *start = chp; 296 size_t as = sizeof ("--all ") - 1; 297 size_t tgs = sizeof ("--thread-group ") - 1; 298 size_t ts = sizeof ("--thread ") - 1; 299 size_t fs = sizeof ("--frame ") - 1; 300 301 if (strncmp (chp, "--all ", as) == 0) 302 { 303 parse->all = 1; 304 chp += as; 305 } 306 /* See if --all is the last token in the input. */ 307 if (strcmp (chp, "--all") == 0) 308 { 309 parse->all = 1; 310 chp += strlen (chp); 311 } 312 if (strncmp (chp, "--thread-group ", tgs) == 0) 313 { 314 if (parse->thread_group != -1) 315 error (_("Duplicate '--thread-group' option")); 316 chp += tgs; 317 if (*chp != 'i') 318 error (_("Invalid thread group id")); 319 chp += 1; 320 parse->thread_group = strtol (chp, &chp, 10); 321 } 322 if (strncmp (chp, "--thread ", ts) == 0) 323 { 324 if (parse->thread != -1) 325 error (_("Duplicate '--thread' option")); 326 chp += ts; 327 parse->thread = strtol (chp, &chp, 10); 328 } 329 else if (strncmp (chp, "--frame ", fs) == 0) 330 { 331 if (parse->frame != -1) 332 error (_("Duplicate '--frame' option")); 333 chp += fs; 334 parse->frame = strtol (chp, &chp, 10); 335 } 336 else 337 break; 338 339 if (*chp != '\0' && !isspace (*chp)) 340 error (_("Invalid value for the '%s' option"), 341 start[2] == 't' ? "--thread" : "--frame"); 342 while (isspace (*chp)) 343 chp++; 344 } 345 346 /* For new argv commands, attempt to return the parsed argument 347 list. */ 348 if (parse->cmd->argv_func != NULL) 349 { 350 mi_parse_argv (chp, parse); 351 if (parse->argv == NULL) 352 { 353 /* FIXME: This should be a function call. */ 354 fprintf_unfiltered 355 (raw_stdout, 356 "%s^error,msg=\"Problem parsing arguments: %s %s\"\n", 357 parse->token, parse->command, chp); 358 mi_parse_free (parse); 359 return NULL; 360 } 361 } 362 363 /* FIXME: DELETE THIS */ 364 /* For CLI commands, also return the remainder of the 365 command line as a single string. */ 366 if (parse->cmd->cli.cmd != NULL) 367 parse->args = xstrdup (chp); 368 369 /* Fully parsed. */ 370 parse->op = MI_COMMAND; 371 return parse; 372 } 373