xref: /dragonfly/contrib/gdb-7/gdb/mi/mi-parse.c (revision e96fb831)
1 /* MI Command Set - MI parser.
2 
3    Copyright (C) 2000, 2001, 2002, 2007, 2008, 2009, 2010, 2011
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 /* A cleanup that calls mi_parse_free.  */
227 
228 static void
229 mi_parse_cleanup (void *arg)
230 {
231   mi_parse_free (arg);
232 }
233 
234 struct mi_parse *
235 mi_parse (char *cmd, char **token)
236 {
237   char *chp;
238   struct mi_parse *parse = XMALLOC (struct mi_parse);
239   struct cleanup *cleanup;
240 
241   memset (parse, 0, sizeof (*parse));
242   parse->all = 0;
243   parse->thread_group = -1;
244   parse->thread = -1;
245   parse->frame = -1;
246 
247   cleanup = make_cleanup (mi_parse_cleanup, parse);
248 
249   /* Before starting, skip leading white space. */
250   while (isspace (*cmd))
251     cmd++;
252 
253   /* Find/skip any token and then extract it. */
254   for (chp = cmd; *chp >= '0' && *chp <= '9'; chp++)
255     ;
256   *token = xmalloc ((chp - cmd + 1) * sizeof (char *));
257   memcpy (*token, cmd, (chp - cmd));
258   (*token)[chp - cmd] = '\0';
259 
260   /* This wasn't a real MI command.  Return it as a CLI_COMMAND. */
261   if (*chp != '-')
262     {
263       while (isspace (*chp))
264 	chp++;
265       parse->command = xstrdup (chp);
266       parse->op = CLI_COMMAND;
267 
268       discard_cleanups (cleanup);
269 
270       return parse;
271     }
272 
273   /* Extract the command. */
274   {
275     char *tmp = chp + 1;	/* discard ``-'' */
276 
277     for (; *chp && !isspace (*chp); chp++)
278       ;
279     parse->command = xmalloc ((chp - tmp + 1) * sizeof (char *));
280     memcpy (parse->command, tmp, chp - tmp);
281     parse->command[chp - tmp] = '\0';
282   }
283 
284   /* Find the command in the MI table. */
285   parse->cmd = mi_lookup (parse->command);
286   if (parse->cmd == NULL)
287     error (_("Undefined MI command: %s"), parse->command);
288 
289   /* Skip white space following the command. */
290   while (isspace (*chp))
291     chp++;
292 
293   /* Parse the --thread and --frame options, if present.  At present,
294      some important commands, like '-break-*' are implemented by forwarding
295      to the CLI layer directly.  We want to parse --thread and --frame
296      here, so as not to leave those option in the string that will be passed
297      to CLI.  */
298   for (;;)
299     {
300       const char *option;
301       size_t as = sizeof ("--all ") - 1;
302       size_t tgs = sizeof ("--thread-group ") - 1;
303       size_t ts = sizeof ("--thread ") - 1;
304       size_t fs = sizeof ("--frame ") - 1;
305 
306       if (strncmp (chp, "--all ", as) == 0)
307 	{
308 	  parse->all = 1;
309 	  chp += as;
310 	}
311       /* See if --all is the last token in the input.  */
312       if (strcmp (chp, "--all") == 0)
313 	{
314           parse->all = 1;
315           chp += strlen (chp);
316         }
317       if (strncmp (chp, "--thread-group ", tgs) == 0)
318 	{
319 	  option = "--thread-group";
320 	  if (parse->thread_group != -1)
321 	    error (_("Duplicate '--thread-group' option"));
322 	  chp += tgs;
323 	  if (*chp != 'i')
324 	    error (_("Invalid thread group id"));
325 	  chp += 1;
326 	  parse->thread_group = strtol (chp, &chp, 10);
327 	}
328       else if (strncmp (chp, "--thread ", ts) == 0)
329 	{
330 	  option = "--thread";
331 	  if (parse->thread != -1)
332 	    error (_("Duplicate '--thread' option"));
333 	  chp += ts;
334 	  parse->thread = strtol (chp, &chp, 10);
335 	}
336       else if (strncmp (chp, "--frame ", fs) == 0)
337 	{
338 	  option = "--frame";
339 	  if (parse->frame != -1)
340 	    error (_("Duplicate '--frame' option"));
341 	  chp += fs;
342 	  parse->frame = strtol (chp, &chp, 10);
343 	}
344       else
345 	break;
346 
347       if (*chp != '\0' && !isspace (*chp))
348 	error (_("Invalid value for the '%s' option"), option);
349       while (isspace (*chp))
350 	chp++;
351     }
352 
353   /* For new argv commands, attempt to return the parsed argument
354      list. */
355   if (parse->cmd->argv_func != NULL)
356     {
357       mi_parse_argv (chp, parse);
358       if (parse->argv == NULL)
359 	error (_("Problem parsing arguments: %s %s"), parse->command, chp);
360     }
361 
362   /* FIXME: DELETE THIS */
363   /* For CLI commands, also return the remainder of the
364      command line as a single string. */
365   if (parse->cmd->cli.cmd != NULL)
366     parse->args = xstrdup (chp);
367 
368   discard_cleanups (cleanup);
369 
370   /* Fully parsed. */
371   parse->op = MI_COMMAND;
372   return parse;
373 }
374