1 /* MI Command Set - MI parser. 2 3 Copyright (C) 2000, 2001, 2002, 2007, 2008, 2009 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 27 #include <ctype.h> 28 #include "gdb_string.h" 29 30 static void 31 mi_parse_argv (char *args, struct mi_parse *parse) 32 { 33 char *chp = args; 34 int argc = 0; 35 char **argv = xmalloc ((argc + 1) * sizeof (char *)); 36 argv[argc] = NULL; 37 while (1) 38 { 39 char *arg; 40 /* skip leading white space */ 41 while (isspace (*chp)) 42 chp++; 43 /* Three possibilities: EOF, quoted string, or other text. */ 44 switch (*chp) 45 { 46 case '\0': 47 parse->argv = argv; 48 parse->argc = argc; 49 return; 50 case '"': 51 { 52 /* A quoted string. */ 53 int len; 54 char *start = chp + 1; 55 /* Determine the buffer size. */ 56 chp = start; 57 len = 0; 58 while (*chp != '\0' && *chp != '"') 59 { 60 if (*chp == '\\') 61 { 62 chp++; 63 if (parse_escape (&chp) <= 0) 64 { 65 /* Do not allow split lines or "\000" */ 66 freeargv (argv); 67 return; 68 } 69 } 70 else 71 chp++; 72 len++; 73 } 74 /* Insist on a closing quote. */ 75 if (*chp != '"') 76 { 77 freeargv (argv); 78 return; 79 } 80 /* Insist on trailing white space. */ 81 if (chp[1] != '\0' && !isspace (chp[1])) 82 { 83 freeargv (argv); 84 return; 85 } 86 /* create the buffer. */ 87 arg = xmalloc ((len + 1) * sizeof (char)); 88 /* And copy the characters in. */ 89 chp = start; 90 len = 0; 91 while (*chp != '\0' && *chp != '"') 92 { 93 if (*chp == '\\') 94 { 95 chp++; 96 arg[len] = parse_escape (&chp); 97 } 98 else 99 arg[len] = *chp++; 100 len++; 101 } 102 arg[len] = '\0'; 103 chp++; /* that closing quote. */ 104 break; 105 } 106 default: 107 { 108 /* An unquoted string. Accumulate all non blank 109 characters into a buffer. */ 110 int len; 111 char *start = chp; 112 while (*chp != '\0' && !isspace (*chp)) 113 { 114 chp++; 115 } 116 len = chp - start; 117 arg = xmalloc ((len + 1) * sizeof (char)); 118 strncpy (arg, start, len); 119 arg[len] = '\0'; 120 break; 121 } 122 } 123 /* Append arg to argv. */ 124 argv = xrealloc (argv, (argc + 2) * sizeof (char *)); 125 argv[argc++] = arg; 126 argv[argc] = NULL; 127 } 128 } 129 130 131 void 132 mi_parse_free (struct mi_parse *parse) 133 { 134 if (parse == NULL) 135 return; 136 if (parse->command != NULL) 137 xfree (parse->command); 138 if (parse->token != NULL) 139 xfree (parse->token); 140 if (parse->args != NULL) 141 xfree (parse->args); 142 if (parse->argv != NULL) 143 freeargv (parse->argv); 144 xfree (parse); 145 } 146 147 148 struct mi_parse * 149 mi_parse (char *cmd) 150 { 151 char *chp; 152 struct mi_parse *parse = XMALLOC (struct mi_parse); 153 memset (parse, 0, sizeof (*parse)); 154 parse->thread = -1; 155 parse->frame = -1; 156 157 /* Before starting, skip leading white space. */ 158 while (isspace (*cmd)) 159 cmd++; 160 161 /* Find/skip any token and then extract it. */ 162 for (chp = cmd; *chp >= '0' && *chp <= '9'; chp++) 163 ; 164 parse->token = xmalloc ((chp - cmd + 1) * sizeof (char *)); 165 memcpy (parse->token, cmd, (chp - cmd)); 166 parse->token[chp - cmd] = '\0'; 167 168 /* This wasn't a real MI command. Return it as a CLI_COMMAND. */ 169 if (*chp != '-') 170 { 171 while (isspace (*chp)) 172 chp++; 173 parse->command = xstrdup (chp); 174 parse->op = CLI_COMMAND; 175 return parse; 176 } 177 178 /* Extract the command. */ 179 { 180 char *tmp = chp + 1; /* discard ``-'' */ 181 for (; *chp && !isspace (*chp); chp++) 182 ; 183 parse->command = xmalloc ((chp - tmp + 1) * sizeof (char *)); 184 memcpy (parse->command, tmp, chp - tmp); 185 parse->command[chp - tmp] = '\0'; 186 } 187 188 /* Find the command in the MI table. */ 189 parse->cmd = mi_lookup (parse->command); 190 if (parse->cmd == NULL) 191 { 192 /* FIXME: This should be a function call. */ 193 fprintf_unfiltered 194 (raw_stdout, 195 "%s^error,msg=\"Undefined MI command: %s\"\n", 196 parse->token, parse->command); 197 mi_parse_free (parse); 198 return NULL; 199 } 200 201 /* Skip white space following the command. */ 202 while (isspace (*chp)) 203 chp++; 204 205 /* Parse the --thread and --frame options, if present. At present, 206 some important commands, like '-break-*' are implemented by forwarding 207 to the CLI layer directly. We want to parse --thread and --frame 208 here, so as not to leave those option in the string that will be passed 209 to CLI. */ 210 for (;;) 211 { 212 char *start = chp; 213 size_t ts = sizeof ("--thread ") - 1; 214 size_t fs = sizeof ("--frame ") - 1; 215 if (strncmp (chp, "--thread ", ts) == 0) 216 { 217 if (parse->thread != -1) 218 error ("Duplicate '--thread' option"); 219 chp += ts; 220 parse->thread = strtol (chp, &chp, 10); 221 } 222 else if (strncmp (chp, "--frame ", fs) == 0) 223 { 224 if (parse->frame != -1) 225 error ("Duplicate '--frame' option"); 226 chp += fs; 227 parse->frame = strtol (chp, &chp, 10); 228 } 229 else 230 break; 231 232 if (*chp != '\0' && !isspace (*chp)) 233 error ("Invalid value for the '%s' option", 234 start[2] == 't' ? "--thread" : "--frame"); 235 while (isspace (*chp)) 236 chp++; 237 } 238 239 /* For new argv commands, attempt to return the parsed argument 240 list. */ 241 if (parse->cmd->argv_func != NULL) 242 { 243 mi_parse_argv (chp, parse); 244 if (parse->argv == NULL) 245 { 246 /* FIXME: This should be a function call. */ 247 fprintf_unfiltered 248 (raw_stdout, 249 "%s^error,msg=\"Problem parsing arguments: %s %s\"\n", 250 parse->token, parse->command, chp); 251 mi_parse_free (parse); 252 return NULL; 253 } 254 } 255 256 /* FIXME: DELETE THIS */ 257 /* For CLI commands, also return the remainder of the 258 command line as a single string. */ 259 if (parse->cmd->cli.cmd != NULL) 260 parse->args = xstrdup (chp); 261 262 /* Fully parsed. */ 263 parse->op = MI_COMMAND; 264 return parse; 265 } 266