xref: /dragonfly/contrib/gdb-7/gdb/mi/mi-parse.c (revision c3762235)
1 /* MI Command Set - MI parser.
2 
3    Copyright (C) 2000-2013 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 #include "cli/cli-utils.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 (const 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 (const char *args, struct mi_parse *parse)
107 {
108   const 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       chp = skip_spaces_const (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 	    const 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 and copy characters in.  */
164 	    arg = xmalloc ((len + 1) * sizeof (char));
165 	    chp = start;
166 	    len = 0;
167 	    while (*chp != '\0' && *chp != '"')
168 	      {
169 		if (*chp == '\\')
170 		  {
171 		    chp++;
172 		    arg[len] = mi_parse_escape (&chp);
173 		  }
174 		else
175 		  arg[len] = *chp++;
176 		len++;
177 	      }
178 	    arg[len] = '\0';
179 	    chp++;		/* That closing quote.  */
180 	    break;
181 	  }
182 	default:
183 	  {
184 	    /* An unquoted string.  Accumulate all non-blank
185 	       characters into a buffer.  */
186 	    int len;
187 	    const char *start = chp;
188 
189 	    while (*chp != '\0' && !isspace (*chp))
190 	      {
191 		chp++;
192 	      }
193 	    len = chp - start;
194 	    arg = xmalloc ((len + 1) * sizeof (char));
195 	    strncpy (arg, start, len);
196 	    arg[len] = '\0';
197 	    break;
198 	  }
199 	}
200       /* Append arg to argv.  */
201       argv = xrealloc (argv, (argc + 2) * sizeof (char *));
202       argv[argc++] = arg;
203       argv[argc] = NULL;
204     }
205 }
206 
207 void
208 mi_parse_free (struct mi_parse *parse)
209 {
210   if (parse == NULL)
211     return;
212   if (parse->command != NULL)
213     xfree (parse->command);
214   if (parse->token != NULL)
215     xfree (parse->token);
216   if (parse->args != NULL)
217     xfree (parse->args);
218   if (parse->argv != NULL)
219     freeargv (parse->argv);
220   xfree (parse);
221 }
222 
223 /* A cleanup that calls mi_parse_free.  */
224 
225 static void
226 mi_parse_cleanup (void *arg)
227 {
228   mi_parse_free (arg);
229 }
230 
231 struct mi_parse *
232 mi_parse (const char *cmd, char **token)
233 {
234   const char *chp;
235   struct mi_parse *parse = XMALLOC (struct mi_parse);
236   struct cleanup *cleanup;
237 
238   memset (parse, 0, sizeof (*parse));
239   parse->all = 0;
240   parse->thread_group = -1;
241   parse->thread = -1;
242   parse->frame = -1;
243 
244   cleanup = make_cleanup (mi_parse_cleanup, parse);
245 
246   /* Before starting, skip leading white space.  */
247   cmd = skip_spaces_const (cmd);
248 
249   /* Find/skip any token and then extract it.  */
250   for (chp = cmd; *chp >= '0' && *chp <= '9'; chp++)
251     ;
252   *token = xmalloc (chp - cmd + 1);
253   memcpy (*token, cmd, (chp - cmd));
254   (*token)[chp - cmd] = '\0';
255 
256   /* This wasn't a real MI command.  Return it as a CLI_COMMAND.  */
257   if (*chp != '-')
258     {
259       chp = skip_spaces_const (chp);
260       parse->command = xstrdup (chp);
261       parse->op = CLI_COMMAND;
262 
263       discard_cleanups (cleanup);
264 
265       return parse;
266     }
267 
268   /* Extract the command.  */
269   {
270     const char *tmp = chp + 1;	/* discard ``-'' */
271 
272     for (; *chp && !isspace (*chp); chp++)
273       ;
274     parse->command = xmalloc (chp - tmp + 1);
275     memcpy (parse->command, tmp, chp - tmp);
276     parse->command[chp - tmp] = '\0';
277   }
278 
279   /* Find the command in the MI table.  */
280   parse->cmd = mi_lookup (parse->command);
281   if (parse->cmd == NULL)
282     error (_("Undefined MI command: %s"), parse->command);
283 
284   /* Skip white space following the command.  */
285   chp = skip_spaces_const (chp);
286 
287   /* Parse the --thread and --frame options, if present.  At present,
288      some important commands, like '-break-*' are implemented by
289      forwarding to the CLI layer directly.  We want to parse --thread
290      and --frame here, so as not to leave those option in the string
291      that will be passed to CLI.  */
292   for (;;)
293     {
294       const char *option;
295       size_t as = sizeof ("--all ") - 1;
296       size_t tgs = sizeof ("--thread-group ") - 1;
297       size_t ts = sizeof ("--thread ") - 1;
298       size_t fs = sizeof ("--frame ") - 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
348 	break;
349 
350       if (*chp != '\0' && !isspace (*chp))
351 	error (_("Invalid value for the '%s' option"), option);
352       chp = skip_spaces_const (chp);
353     }
354 
355   /* For new argv commands, attempt to return the parsed argument
356      list.  */
357   if (parse->cmd->argv_func != NULL)
358     {
359       mi_parse_argv (chp, parse);
360       if (parse->argv == NULL)
361 	error (_("Problem parsing arguments: %s %s"), parse->command, chp);
362     }
363 
364   /* FIXME: DELETE THIS */
365   /* For CLI commands, also return the remainder of the
366      command line as a single string. */
367   if (parse->cmd->cli.cmd != NULL)
368     parse->args = xstrdup (chp);
369 
370   discard_cleanups (cleanup);
371 
372   /* Fully parsed, flag as an MI command.  */
373   parse->op = MI_COMMAND;
374   return parse;
375 }
376