xref: /dragonfly/contrib/gdb-7/gdb/mi/mi-parse.c (revision fb5b3747)
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