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