1/* MI Command Set - MI parser.
2
3   Copyright 2000, 2001, 2002 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 2 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, write to the Free Software
21   Foundation, Inc., 59 Temple Place - Suite 330,
22   Boston, MA 02111-1307, USA.  */
23
24#include "defs.h"
25#include "mi-cmds.h"
26#include "mi-parse.h"
27
28#include <ctype.h>
29#include "gdb_string.h"
30
31static void
32mi_parse_argv (char *args, struct mi_parse *parse)
33{
34  char *chp = args;
35  int argc = 0;
36  char **argv = xmalloc ((argc + 1) * sizeof (char *));
37  argv[argc] = NULL;
38  while (1)
39    {
40      char *arg;
41      /* skip leading white space */
42      while (isspace (*chp))
43	chp++;
44      /* Three possibilities: EOF, quoted string, or other text. */
45      switch (*chp)
46	{
47	case '\0':
48	  parse->argv = argv;
49	  parse->argc = argc;
50	  return;
51	case '"':
52	  {
53	    /* A quoted string. */
54	    int len;
55	    char *start = chp + 1;
56	    /* Determine the buffer size. */
57	    chp = start;
58	    len = 0;
59	    while (*chp != '\0' && *chp != '"')
60	      {
61		if (*chp == '\\')
62		  {
63		    chp++;
64		    if (parse_escape (&chp) <= 0)
65		      {
66			/* Do not allow split lines or "\000" */
67			freeargv (argv);
68			return;
69		      }
70		  }
71		else
72		  chp++;
73		len++;
74	      }
75	    /* Insist on a closing quote. */
76	    if (*chp != '"')
77	      {
78		freeargv (argv);
79		return;
80	      }
81	    /* Insist on trailing white space. */
82	    if (chp[1] != '\0' && !isspace (chp[1]))
83	      {
84		freeargv (argv);
85		return;
86	      }
87	    /* create the buffer. */
88	    arg = xmalloc ((len + 1) * sizeof (char));
89	    /* And copy the characters in. */
90	    chp = start;
91	    len = 0;
92	    while (*chp != '\0' && *chp != '"')
93	      {
94		if (*chp == '\\')
95		  {
96		    chp++;
97		    arg[len] = parse_escape (&chp);
98		  }
99		else
100		  arg[len] = *chp++;
101		len++;
102	      }
103	    arg[len] = '\0';
104	    chp++;		/* that closing quote. */
105	    break;
106	  }
107	default:
108	  {
109	    /* An unquoted string.  Accumulate all non blank
110	       characters into a buffer. */
111	    int len;
112	    char *start = chp;
113	    while (*chp != '\0' && !isspace (*chp))
114	      {
115		chp++;
116	      }
117	    len = chp - start;
118	    arg = xmalloc ((len + 1) * sizeof (char));
119	    strncpy (arg, start, len);
120	    arg[len] = '\0';
121	    break;
122	  }
123	}
124      /* Append arg to argv. */
125      argv = xrealloc (argv, (argc + 2) * sizeof (char *));
126      argv[argc++] = arg;
127      argv[argc] = NULL;
128    }
129}
130
131
132void
133mi_parse_free (struct mi_parse *parse)
134{
135  if (parse == NULL)
136    return;
137  if (parse->command != NULL)
138    xfree (parse->command);
139  if (parse->token != NULL)
140    xfree (parse->token);
141  if (parse->args != NULL)
142    xfree (parse->args);
143  if (parse->argv != NULL)
144    freeargv (parse->argv);
145  xfree (parse);
146}
147
148
149struct mi_parse *
150mi_parse (char *cmd)
151{
152  char *chp;
153  struct mi_parse *parse = XMALLOC (struct mi_parse);
154  memset (parse, 0, sizeof (*parse));
155
156  /* Before starting, skip leading white space. */
157  while (isspace (*cmd))
158    cmd++;
159
160  /* Find/skip any token and then extract it. */
161  for (chp = cmd; *chp >= '0' && *chp <= '9'; chp++)
162    ;
163  parse->token = xmalloc ((chp - cmd + 1) * sizeof (char *));
164  memcpy (parse->token, cmd, (chp - cmd));
165  parse->token[chp - cmd] = '\0';
166
167  /* This wasn't a real MI command.  Return it as a CLI_COMMAND. */
168  if (*chp != '-')
169    {
170      while (isspace (*chp))
171	chp++;
172      parse->command = xstrdup (chp);
173      parse->op = CLI_COMMAND;
174      return parse;
175    }
176
177  /* Extract the command. */
178  {
179    char *tmp = chp + 1;	/* discard ``-'' */
180    for (; *chp && !isspace (*chp); chp++)
181      ;
182    parse->command = xmalloc ((chp - tmp + 1) * sizeof (char *));
183    memcpy (parse->command, tmp, chp - tmp);
184    parse->command[chp - tmp] = '\0';
185  }
186
187  /* Find the command in the MI table. */
188  parse->cmd = mi_lookup (parse->command);
189  if (parse->cmd == NULL)
190    {
191      /* FIXME: This should be a function call. */
192      fprintf_unfiltered
193	(raw_stdout,
194	 "%s^error,msg=\"Undefined MI command: %s\"\n",
195	 parse->token, parse->command);
196      mi_parse_free (parse);
197      return NULL;
198    }
199
200  /* Skip white space following the command. */
201  while (isspace (*chp))
202    chp++;
203
204  /* For new argv commands, attempt to return the parsed argument
205     list. */
206  if (parse->cmd->argv_func != NULL)
207    {
208      mi_parse_argv (chp, parse);
209      if (parse->argv == NULL)
210	{
211	  /* FIXME: This should be a function call. */
212	  fprintf_unfiltered
213	    (raw_stdout,
214	     "%s^error,msg=\"Problem parsing arguments: %s %s\"\n",
215	     parse->token, parse->command, chp);
216	  mi_parse_free (parse);
217	  return NULL;
218	}
219    }
220
221  /* FIXME: DELETE THIS */
222  /* For CLI and old ARGS commands, also return the remainder of the
223     command line as a single string. */
224  if (parse->cmd->args_func != NULL
225      || parse->cmd->cli.cmd != NULL)
226    {
227      parse->args = xstrdup (chp);
228    }
229
230  /* Fully parsed. */
231  parse->op = MI_COMMAND;
232  return parse;
233}
234