Home | History | Annotate | Line # | Download | only in mi
      1   1.1  christos /* MI Command Set - MI parser.
      2   1.1  christos 
      3  1.11  christos    Copyright (C) 2000-2024 Free Software Foundation, Inc.
      4   1.1  christos 
      5   1.1  christos    Contributed by Cygnus Solutions (a Red Hat company).
      6   1.1  christos 
      7   1.1  christos    This file is part of GDB.
      8   1.1  christos 
      9   1.1  christos    This program is free software; you can redistribute it and/or modify
     10   1.1  christos    it under the terms of the GNU General Public License as published by
     11   1.1  christos    the Free Software Foundation; either version 3 of the License, or
     12   1.1  christos    (at your option) any later version.
     13   1.1  christos 
     14   1.1  christos    This program is distributed in the hope that it will be useful,
     15   1.1  christos    but WITHOUT ANY WARRANTY; without even the implied warranty of
     16   1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17   1.1  christos    GNU General Public License for more details.
     18   1.1  christos 
     19   1.1  christos    You should have received a copy of the GNU General Public License
     20   1.1  christos    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     21   1.1  christos 
     22   1.1  christos #include "mi-cmds.h"
     23   1.1  christos #include "mi-parse.h"
     24   1.1  christos #include "charset.h"
     25   1.1  christos 
     26   1.1  christos #include <ctype.h>
     27   1.1  christos #include "cli/cli-utils.h"
     28   1.1  christos #include "language.h"
     29   1.1  christos 
     30   1.1  christos static const char mi_no_values[] = "--no-values";
     31   1.1  christos static const char mi_simple_values[] = "--simple-values";
     32   1.1  christos static const char mi_all_values[] = "--all-values";
     33   1.1  christos 
     34   1.1  christos /* Like parse_escape, but leave the results as a host char, not a
     35   1.1  christos    target char.  */
     36   1.1  christos 
     37   1.1  christos static int
     38   1.1  christos mi_parse_escape (const char **string_ptr)
     39   1.1  christos {
     40   1.1  christos   int c = *(*string_ptr)++;
     41   1.1  christos 
     42   1.1  christos   switch (c)
     43   1.1  christos     {
     44   1.1  christos       case '\n':
     45   1.1  christos 	return -2;
     46   1.1  christos       case 0:
     47   1.1  christos 	(*string_ptr)--;
     48   1.1  christos 	return 0;
     49   1.1  christos 
     50   1.1  christos       case '0':
     51   1.1  christos       case '1':
     52   1.1  christos       case '2':
     53   1.1  christos       case '3':
     54   1.1  christos       case '4':
     55   1.1  christos       case '5':
     56   1.1  christos       case '6':
     57   1.1  christos       case '7':
     58   1.1  christos 	{
     59  1.10  christos 	  int i = fromhex (c);
     60   1.1  christos 	  int count = 0;
     61   1.1  christos 
     62   1.1  christos 	  while (++count < 3)
     63   1.1  christos 	    {
     64   1.1  christos 	      c = (**string_ptr);
     65   1.1  christos 	      if (isdigit (c) && c != '8' && c != '9')
     66   1.1  christos 		{
     67   1.1  christos 		  (*string_ptr)++;
     68   1.1  christos 		  i *= 8;
     69  1.10  christos 		  i += fromhex (c);
     70   1.1  christos 		}
     71   1.1  christos 	      else
     72   1.1  christos 		{
     73   1.1  christos 		  break;
     74   1.1  christos 		}
     75   1.1  christos 	    }
     76   1.1  christos 	  return i;
     77   1.1  christos 	}
     78   1.1  christos 
     79   1.1  christos     case 'a':
     80   1.1  christos       c = '\a';
     81   1.1  christos       break;
     82   1.1  christos     case 'b':
     83   1.1  christos       c = '\b';
     84   1.1  christos       break;
     85   1.1  christos     case 'f':
     86   1.1  christos       c = '\f';
     87   1.1  christos       break;
     88   1.1  christos     case 'n':
     89   1.1  christos       c = '\n';
     90   1.1  christos       break;
     91   1.1  christos     case 'r':
     92   1.1  christos       c = '\r';
     93   1.1  christos       break;
     94   1.1  christos     case 't':
     95   1.1  christos       c = '\t';
     96   1.1  christos       break;
     97   1.1  christos     case 'v':
     98   1.1  christos       c = '\v';
     99   1.1  christos       break;
    100   1.1  christos 
    101   1.1  christos     default:
    102   1.1  christos       break;
    103   1.1  christos     }
    104   1.1  christos 
    105   1.1  christos   return c;
    106   1.1  christos }
    107   1.1  christos 
    108  1.10  christos void
    109  1.11  christos mi_parse::parse_argv ()
    110   1.1  christos {
    111  1.11  christos   /* If arguments were already computed (or were supplied at
    112  1.11  christos      construction), then there's no need to re-compute them.  */
    113  1.11  christos   if (argv != nullptr)
    114  1.11  christos     return;
    115  1.11  christos 
    116  1.11  christos   const char *chp = m_args.c_str ();
    117   1.1  christos   int argc = 0;
    118   1.6  christos   char **argv = XNEWVEC (char *, argc + 1);
    119   1.1  christos 
    120   1.1  christos   argv[argc] = NULL;
    121   1.1  christos   while (1)
    122   1.1  christos     {
    123   1.1  christos       char *arg;
    124   1.1  christos 
    125   1.1  christos       /* Skip leading white space.  */
    126   1.8  christos       chp = skip_spaces (chp);
    127   1.1  christos       /* Three possibilities: EOF, quoted string, or other text. */
    128   1.1  christos       switch (*chp)
    129   1.1  christos 	{
    130   1.1  christos 	case '\0':
    131  1.11  christos 	  this->argv = argv;
    132  1.11  christos 	  this->argc = argc;
    133   1.1  christos 	  return;
    134   1.1  christos 	case '"':
    135   1.1  christos 	  {
    136   1.1  christos 	    /* A quoted string.  */
    137   1.1  christos 	    int len;
    138   1.1  christos 	    const char *start = chp + 1;
    139   1.1  christos 
    140   1.1  christos 	    /* Determine the buffer size.  */
    141   1.1  christos 	    chp = start;
    142   1.1  christos 	    len = 0;
    143   1.1  christos 	    while (*chp != '\0' && *chp != '"')
    144   1.1  christos 	      {
    145   1.1  christos 		if (*chp == '\\')
    146   1.1  christos 		  {
    147   1.1  christos 		    chp++;
    148   1.1  christos 		    if (mi_parse_escape (&chp) <= 0)
    149   1.1  christos 		      {
    150   1.1  christos 			/* Do not allow split lines or "\000".  */
    151   1.1  christos 			freeargv (argv);
    152   1.1  christos 			return;
    153   1.1  christos 		      }
    154   1.1  christos 		  }
    155   1.1  christos 		else
    156   1.1  christos 		  chp++;
    157   1.1  christos 		len++;
    158   1.1  christos 	      }
    159   1.1  christos 	    /* Insist on a closing quote.  */
    160   1.1  christos 	    if (*chp != '"')
    161   1.1  christos 	      {
    162   1.1  christos 		freeargv (argv);
    163   1.1  christos 		return;
    164   1.1  christos 	      }
    165   1.1  christos 	    /* Insist on trailing white space.  */
    166   1.1  christos 	    if (chp[1] != '\0' && !isspace (chp[1]))
    167   1.1  christos 	      {
    168   1.1  christos 		freeargv (argv);
    169   1.1  christos 		return;
    170   1.1  christos 	      }
    171   1.1  christos 	    /* Create the buffer and copy characters in.  */
    172   1.6  christos 	    arg = XNEWVEC (char, len + 1);
    173   1.1  christos 	    chp = start;
    174   1.1  christos 	    len = 0;
    175   1.1  christos 	    while (*chp != '\0' && *chp != '"')
    176   1.1  christos 	      {
    177   1.1  christos 		if (*chp == '\\')
    178   1.1  christos 		  {
    179   1.1  christos 		    chp++;
    180   1.1  christos 		    arg[len] = mi_parse_escape (&chp);
    181   1.1  christos 		  }
    182   1.1  christos 		else
    183   1.1  christos 		  arg[len] = *chp++;
    184   1.1  christos 		len++;
    185   1.1  christos 	      }
    186   1.1  christos 	    arg[len] = '\0';
    187   1.1  christos 	    chp++;		/* That closing quote.  */
    188   1.1  christos 	    break;
    189   1.1  christos 	  }
    190   1.1  christos 	default:
    191   1.1  christos 	  {
    192   1.1  christos 	    /* An unquoted string.  Accumulate all non-blank
    193   1.1  christos 	       characters into a buffer.  */
    194   1.1  christos 	    int len;
    195   1.1  christos 	    const char *start = chp;
    196   1.1  christos 
    197   1.1  christos 	    while (*chp != '\0' && !isspace (*chp))
    198   1.1  christos 	      {
    199   1.1  christos 		chp++;
    200   1.1  christos 	      }
    201   1.1  christos 	    len = chp - start;
    202   1.6  christos 	    arg = XNEWVEC (char, len + 1);
    203   1.1  christos 	    strncpy (arg, start, len);
    204   1.1  christos 	    arg[len] = '\0';
    205   1.1  christos 	    break;
    206   1.1  christos 	  }
    207   1.1  christos 	}
    208   1.1  christos       /* Append arg to argv.  */
    209   1.6  christos       argv = XRESIZEVEC (char *, argv, argc + 2);
    210   1.1  christos       argv[argc++] = arg;
    211   1.1  christos       argv[argc] = NULL;
    212   1.1  christos     }
    213   1.1  christos }
    214   1.1  christos 
    215  1.11  christos mi_parse::~mi_parse ()
    216  1.11  christos {
    217  1.11  christos   freeargv (argv);
    218  1.11  christos }
    219  1.11  christos 
    220  1.11  christos /* See mi-parse.h.  */
    221  1.11  christos 
    222  1.11  christos const char *
    223  1.11  christos mi_parse::args ()
    224  1.11  christos {
    225  1.11  christos   /* If args were already computed, or if there is no pre-computed
    226  1.11  christos      argv, just return the args.  */
    227  1.11  christos   if (!m_args.empty () || argv == nullptr)
    228  1.11  christos     return  m_args.c_str ();
    229  1.11  christos 
    230  1.11  christos   /* Compute args from argv.  */
    231  1.11  christos   for (int i = 0; i < argc; ++i)
    232  1.11  christos     {
    233  1.11  christos       if (!m_args.empty ())
    234  1.11  christos 	m_args += " ";
    235  1.11  christos       m_args += argv[i];
    236  1.11  christos     }
    237  1.11  christos 
    238  1.11  christos   return m_args.c_str ();
    239  1.11  christos }
    240  1.11  christos 
    241  1.11  christos /* See mi-parse.h.  */
    242  1.11  christos 
    243  1.11  christos void
    244  1.11  christos mi_parse::set_thread_group (const char *arg, char **endp)
    245  1.11  christos {
    246  1.11  christos   if (thread_group != -1)
    247  1.11  christos     error (_("Duplicate '--thread-group' option"));
    248  1.11  christos   if (*arg != 'i')
    249  1.11  christos     error (_("Invalid thread group id"));
    250  1.11  christos   arg += 1;
    251  1.11  christos   thread_group = strtol (arg, endp, 10);
    252  1.11  christos }
    253  1.11  christos 
    254  1.11  christos /* See mi-parse.h.  */
    255  1.11  christos 
    256  1.11  christos void
    257  1.11  christos mi_parse::set_thread (const char *arg, char **endp)
    258  1.11  christos {
    259  1.11  christos   if (thread != -1)
    260  1.11  christos     error (_("Duplicate '--thread' option"));
    261  1.11  christos   thread = strtol (arg, endp, 10);
    262  1.11  christos }
    263  1.11  christos 
    264  1.11  christos /* See mi-parse.h.  */
    265  1.11  christos 
    266  1.11  christos void
    267  1.11  christos mi_parse::set_frame (const char *arg, char **endp)
    268   1.1  christos {
    269  1.11  christos   if (frame != -1)
    270  1.11  christos     error (_("Duplicate '--frame' option"));
    271  1.11  christos   frame = strtol (arg, endp, 10);
    272   1.1  christos }
    273   1.1  christos 
    274  1.11  christos /* See mi-parse.h.  */
    275  1.11  christos 
    276  1.11  christos void
    277  1.11  christos mi_parse::set_language (const char *arg, const char **endp)
    278   1.1  christos {
    279  1.11  christos   std::string lang_name = extract_arg (&arg);
    280  1.11  christos 
    281  1.11  christos   language = language_enum (lang_name.c_str ());
    282  1.11  christos   if (language == language_unknown)
    283  1.11  christos     error (_("Invalid --language argument: %s"), lang_name.c_str ());
    284  1.11  christos 
    285  1.11  christos   if (endp != nullptr)
    286  1.11  christos     *endp = arg;
    287   1.1  christos }
    288   1.1  christos 
    289  1.11  christos /* See mi-parse.h.  */
    290  1.11  christos 
    291  1.11  christos mi_parse::mi_parse (const char *cmd, std::string *token)
    292   1.1  christos {
    293   1.1  christos   const char *chp;
    294   1.1  christos 
    295   1.1  christos   /* Before starting, skip leading white space.  */
    296   1.8  christos   cmd = skip_spaces (cmd);
    297   1.1  christos 
    298   1.1  christos   /* Find/skip any token and then extract it.  */
    299   1.1  christos   for (chp = cmd; *chp >= '0' && *chp <= '9'; chp++)
    300   1.1  christos     ;
    301  1.11  christos   *token = std::string (cmd, chp - cmd);
    302   1.1  christos 
    303   1.1  christos   /* This wasn't a real MI command.  Return it as a CLI_COMMAND.  */
    304   1.1  christos   if (*chp != '-')
    305   1.1  christos     {
    306   1.8  christos       chp = skip_spaces (chp);
    307  1.11  christos       this->command = make_unique_xstrdup (chp);
    308  1.11  christos       this->op = CLI_COMMAND;
    309   1.1  christos 
    310  1.11  christos       return;
    311   1.1  christos     }
    312   1.1  christos 
    313   1.1  christos   /* Extract the command.  */
    314   1.1  christos   {
    315   1.1  christos     const char *tmp = chp + 1;	/* discard ``-'' */
    316   1.1  christos 
    317   1.1  christos     for (; *chp && !isspace (*chp); chp++)
    318   1.1  christos       ;
    319  1.11  christos     this->command = make_unique_xstrndup (tmp, chp - tmp);
    320   1.1  christos   }
    321   1.1  christos 
    322   1.1  christos   /* Find the command in the MI table.  */
    323  1.11  christos   this->cmd = mi_cmd_lookup (this->command.get ());
    324  1.11  christos   if (this->cmd == NULL)
    325   1.1  christos     throw_error (UNDEFINED_COMMAND_ERROR,
    326  1.11  christos 		 _("Undefined MI command: %s"), this->command.get ());
    327   1.1  christos 
    328   1.1  christos   /* Skip white space following the command.  */
    329   1.8  christos   chp = skip_spaces (chp);
    330   1.1  christos 
    331   1.1  christos   /* Parse the --thread and --frame options, if present.  At present,
    332   1.1  christos      some important commands, like '-break-*' are implemented by
    333   1.1  christos      forwarding to the CLI layer directly.  We want to parse --thread
    334   1.1  christos      and --frame here, so as not to leave those option in the string
    335   1.1  christos      that will be passed to CLI.
    336   1.1  christos 
    337   1.1  christos      Same for the --language option.  */
    338   1.1  christos 
    339   1.1  christos   for (;;)
    340   1.1  christos     {
    341   1.1  christos       const char *option;
    342   1.1  christos       size_t as = sizeof ("--all ") - 1;
    343   1.1  christos       size_t tgs = sizeof ("--thread-group ") - 1;
    344   1.1  christos       size_t ts = sizeof ("--thread ") - 1;
    345   1.1  christos       size_t fs = sizeof ("--frame ") - 1;
    346   1.1  christos       size_t ls = sizeof ("--language ") - 1;
    347   1.1  christos 
    348   1.1  christos       if (strncmp (chp, "--all ", as) == 0)
    349   1.1  christos 	{
    350  1.11  christos 	  this->all = 1;
    351   1.1  christos 	  chp += as;
    352   1.1  christos 	}
    353   1.1  christos       /* See if --all is the last token in the input.  */
    354   1.1  christos       if (strcmp (chp, "--all") == 0)
    355   1.1  christos 	{
    356  1.11  christos 	  this->all = 1;
    357  1.10  christos 	  chp += strlen (chp);
    358  1.10  christos 	}
    359   1.1  christos       if (strncmp (chp, "--thread-group ", tgs) == 0)
    360   1.1  christos 	{
    361   1.1  christos 	  char *endp;
    362   1.1  christos 
    363   1.1  christos 	  option = "--thread-group";
    364   1.1  christos 	  chp += tgs;
    365  1.11  christos 	  this->set_thread_group (chp, &endp);
    366   1.1  christos 	  chp = endp;
    367   1.1  christos 	}
    368   1.1  christos       else if (strncmp (chp, "--thread ", ts) == 0)
    369   1.1  christos 	{
    370   1.1  christos 	  char *endp;
    371   1.1  christos 
    372   1.1  christos 	  option = "--thread";
    373   1.1  christos 	  chp += ts;
    374  1.11  christos 	  this->set_thread (chp, &endp);
    375   1.1  christos 	  chp = endp;
    376   1.1  christos 	}
    377   1.1  christos       else if (strncmp (chp, "--frame ", fs) == 0)
    378   1.1  christos 	{
    379   1.1  christos 	  char *endp;
    380   1.1  christos 
    381   1.1  christos 	  option = "--frame";
    382   1.1  christos 	  chp += fs;
    383  1.11  christos 	  this->set_frame (chp, &endp);
    384   1.1  christos 	  chp = endp;
    385   1.1  christos 	}
    386   1.1  christos       else if (strncmp (chp, "--language ", ls) == 0)
    387   1.1  christos 	{
    388   1.1  christos 	  option = "--language";
    389   1.1  christos 	  chp += ls;
    390  1.11  christos 	  this->set_language (chp, &chp);
    391   1.1  christos 	}
    392   1.1  christos       else
    393   1.1  christos 	break;
    394   1.1  christos 
    395   1.1  christos       if (*chp != '\0' && !isspace (*chp))
    396   1.1  christos 	error (_("Invalid value for the '%s' option"), option);
    397   1.8  christos       chp = skip_spaces (chp);
    398   1.1  christos     }
    399   1.1  christos 
    400  1.10  christos   /* Save the rest of the arguments for the command.  */
    401  1.11  christos   this->m_args = chp;
    402  1.11  christos 
    403  1.11  christos   /* Fully parsed, flag as an MI command.  */
    404  1.11  christos   this->op = MI_COMMAND;
    405  1.11  christos }
    406  1.11  christos 
    407  1.11  christos /* See mi-parse.h.  */
    408  1.11  christos 
    409  1.11  christos mi_parse::mi_parse (gdb::unique_xmalloc_ptr<char> command,
    410  1.11  christos 		    std::vector<gdb::unique_xmalloc_ptr<char>> args)
    411  1.11  christos {
    412  1.11  christos   this->command = std::move (command);
    413  1.11  christos   this->token = "";
    414  1.11  christos 
    415  1.11  christos   if (this->command.get ()[0] != '-')
    416  1.11  christos     throw_error (UNDEFINED_COMMAND_ERROR,
    417  1.11  christos 		 _("MI command '%s' does not start with '-'"),
    418  1.11  christos 		 this->command.get ());
    419  1.11  christos 
    420  1.11  christos   /* Find the command in the MI table.  */
    421  1.11  christos   this->cmd = mi_cmd_lookup (this->command.get () + 1);
    422  1.11  christos   if (this->cmd == NULL)
    423  1.11  christos     throw_error (UNDEFINED_COMMAND_ERROR,
    424  1.11  christos 		 _("Undefined MI command: %s"), this->command.get ());
    425  1.11  christos 
    426  1.11  christos   /* This over-allocates slightly, but it seems unimportant.  */
    427  1.11  christos   this->argv = XCNEWVEC (char *, args.size () + 1);
    428  1.11  christos 
    429  1.11  christos   for (size_t i = 0; i < args.size (); ++i)
    430  1.11  christos     {
    431  1.11  christos       const char *chp = args[i].get ();
    432  1.11  christos 
    433  1.11  christos       /* See if --all is the last token in the input.  */
    434  1.11  christos       if (strcmp (chp, "--all") == 0)
    435  1.11  christos 	{
    436  1.11  christos 	  this->all = 1;
    437  1.11  christos 	}
    438  1.11  christos       else if (strcmp (chp, "--thread-group") == 0)
    439  1.11  christos 	{
    440  1.11  christos 	  ++i;
    441  1.11  christos 	  if (i == args.size ())
    442  1.11  christos 	    error ("No argument to '--thread-group'");
    443  1.11  christos 	  this->set_thread_group (args[i].get (), nullptr);
    444  1.11  christos 	}
    445  1.11  christos       else if (strcmp (chp, "--thread") == 0)
    446  1.11  christos 	{
    447  1.11  christos 	  ++i;
    448  1.11  christos 	  if (i == args.size ())
    449  1.11  christos 	    error ("No argument to '--thread'");
    450  1.11  christos 	  this->set_thread (args[i].get (), nullptr);
    451  1.11  christos 	}
    452  1.11  christos       else if (strcmp (chp, "--frame") == 0)
    453  1.11  christos 	{
    454  1.11  christos 	  ++i;
    455  1.11  christos 	  if (i == args.size ())
    456  1.11  christos 	    error ("No argument to '--frame'");
    457  1.11  christos 	  this->set_frame (args[i].get (), nullptr);
    458  1.11  christos 	}
    459  1.11  christos       else if (strcmp (chp, "--language") == 0)
    460  1.11  christos 	{
    461  1.11  christos 	  ++i;
    462  1.11  christos 	  if (i == args.size ())
    463  1.11  christos 	    error ("No argument to '--language'");
    464  1.11  christos 	  this->set_language (args[i].get (), nullptr);
    465  1.11  christos 	}
    466  1.11  christos       else
    467  1.11  christos 	this->argv[this->argc++] = args[i].release ();
    468  1.11  christos     }
    469   1.1  christos 
    470   1.1  christos   /* Fully parsed, flag as an MI command.  */
    471  1.11  christos   this->op = MI_COMMAND;
    472   1.1  christos }
    473   1.1  christos 
    474   1.1  christos enum print_values
    475   1.1  christos mi_parse_print_values (const char *name)
    476   1.1  christos {
    477   1.1  christos    if (strcmp (name, "0") == 0
    478   1.1  christos        || strcmp (name, mi_no_values) == 0)
    479   1.1  christos      return PRINT_NO_VALUES;
    480   1.1  christos    else if (strcmp (name, "1") == 0
    481   1.1  christos 	    || strcmp (name, mi_all_values) == 0)
    482   1.1  christos      return PRINT_ALL_VALUES;
    483   1.1  christos    else if (strcmp (name, "2") == 0
    484   1.1  christos 	    || strcmp (name, mi_simple_values) == 0)
    485   1.1  christos      return PRINT_SIMPLE_VALUES;
    486   1.1  christos    else
    487   1.1  christos      error (_("Unknown value for PRINT_VALUES: must be: \
    488   1.1  christos 0 or \"%s\", 1 or \"%s\", 2 or \"%s\""),
    489   1.1  christos 	    mi_no_values, mi_all_values, mi_simple_values);
    490   1.1  christos }
    491