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