1/* CLI utilities.
2
3   Copyright (c) 2011 Free Software Foundation, Inc.
4
5   This file is part of GDB.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20#include "defs.h"
21#include "cli/cli-utils.h"
22#include "gdb_string.h"
23#include "value.h"
24#include "gdb_assert.h"
25
26#include <ctype.h>
27
28/* *PP is a string denoting a number.  Get the number of the.  Advance
29   *PP after the string and any trailing whitespace.
30
31   Currently the string can either be a number, or "$" followed by the
32   name of a convenience variable, or ("$" or "$$") followed by digits.
33
34   TRAILER is a character which can be found after the number; most
35   commonly this is `-'.  If you don't want a trailer, use \0.  */
36
37static int
38get_number_trailer (char **pp, int trailer)
39{
40  int retval = 0;	/* default */
41  char *p = *pp;
42
43  if (*p == '$')
44    {
45      struct value *val = value_from_history_ref (p, &p);
46
47      if (val)	/* Value history reference */
48	{
49	  if (TYPE_CODE (value_type (val)) == TYPE_CODE_INT)
50	    retval = value_as_long (val);
51	  else
52	    {
53	      printf_filtered (_("History value must have integer type."));
54	      retval = 0;
55	    }
56	}
57      else	/* Convenience variable */
58	{
59	  /* Internal variable.  Make a copy of the name, so we can
60	     null-terminate it to pass to lookup_internalvar().  */
61	  char *varname;
62	  char *start = ++p;
63	  LONGEST val;
64
65	  while (isalnum (*p) || *p == '_')
66	    p++;
67	  varname = (char *) alloca (p - start + 1);
68	  strncpy (varname, start, p - start);
69	  varname[p - start] = '\0';
70	  if (get_internalvar_integer (lookup_internalvar (varname), &val))
71	    retval = (int) val;
72	  else
73	    {
74	      printf_filtered (_("Convenience variable must "
75				 "have integer value.\n"));
76	      retval = 0;
77	    }
78	}
79    }
80  else
81    {
82      if (*p == '-')
83	++p;
84      while (*p >= '0' && *p <= '9')
85	++p;
86      if (p == *pp)
87	/* There is no number here.  (e.g. "cond a == b").  */
88	{
89	  /* Skip non-numeric token.  */
90	  while (*p && !isspace((int) *p))
91	    ++p;
92	  /* Return zero, which caller must interpret as error.  */
93	  retval = 0;
94	}
95      else
96	retval = atoi (*pp);
97    }
98  if (!(isspace (*p) || *p == '\0' || *p == trailer))
99    {
100      /* Trailing junk: return 0 and let caller print error msg.  */
101      while (!(isspace (*p) || *p == '\0' || *p == trailer))
102	++p;
103      retval = 0;
104    }
105  p = skip_spaces (p);
106  *pp = p;
107  return retval;
108}
109
110/* See documentation in cli-utils.h.  */
111
112int
113get_number (char **pp)
114{
115  return get_number_trailer (pp, '\0');
116}
117
118/* See documentation in cli-utils.h.  */
119
120void
121init_number_or_range (struct get_number_or_range_state *state,
122		      char *string)
123{
124  memset (state, 0, sizeof (*state));
125  state->string = string;
126}
127
128/* See documentation in cli-utils.h.  */
129
130int
131get_number_or_range (struct get_number_or_range_state *state)
132{
133  if (*state->string != '-')
134    {
135      /* Default case: state->string is pointing either to a solo
136	 number, or to the first number of a range.  */
137      state->last_retval = get_number_trailer (&state->string, '-');
138      if (*state->string == '-')
139	{
140	  char **temp;
141
142	  /* This is the start of a range (<number1> - <number2>).
143	     Skip the '-', parse and remember the second number,
144	     and also remember the end of the final token.  */
145
146	  temp = &state->end_ptr;
147	  state->end_ptr = skip_spaces (state->string + 1);
148	  state->end_value = get_number (temp);
149	  if (state->end_value < state->last_retval)
150	    {
151	      error (_("inverted range"));
152	    }
153	  else if (state->end_value == state->last_retval)
154	    {
155	      /* Degenerate range (number1 == number2).  Advance the
156		 token pointer so that the range will be treated as a
157		 single number.  */
158	      state->string = state->end_ptr;
159	    }
160	  else
161	    state->in_range = 1;
162	}
163    }
164  else if (! state->in_range)
165    error (_("negative value"));
166  else
167    {
168      /* state->string points to the '-' that betokens a range.  All
169	 number-parsing has already been done.  Return the next
170	 integer value (one greater than the saved previous value).
171	 Do not advance the token pointer until the end of range
172	 is reached.  */
173
174      if (++state->last_retval == state->end_value)
175	{
176	  /* End of range reached; advance token pointer.  */
177	  state->string = state->end_ptr;
178	  state->in_range = 0;
179	}
180    }
181  state->finished = *state->string == '\0';
182  return state->last_retval;
183}
184
185/* Accept a number and a string-form list of numbers such as is
186   accepted by get_number_or_range.  Return TRUE if the number is
187   in the list.
188
189   By definition, an empty list includes all numbers.  This is to
190   be interpreted as typing a command such as "delete break" with
191   no arguments.  */
192
193int
194number_is_in_list (char *list, int number)
195{
196  struct get_number_or_range_state state;
197
198  if (list == NULL || *list == '\0')
199    return 1;
200
201  init_number_or_range (&state, list);
202  while (!state.finished)
203    {
204      int gotnum = get_number_or_range (&state);
205
206      if (gotnum == 0)
207	error (_("Args must be numbers or '$' variables."));
208      if (gotnum == number)
209	return 1;
210    }
211  return 0;
212}
213
214/* See documentation in cli-utils.h.  */
215
216char *
217skip_spaces (char *chp)
218{
219  if (chp == NULL)
220    return NULL;
221  while (*chp && isspace (*chp))
222    chp++;
223  return chp;
224}
225
226/* See documentation in cli-utils.h.  */
227
228char *
229skip_to_space (char *chp)
230{
231  if (chp == NULL)
232    return NULL;
233  while (*chp && !isspace (*chp))
234    chp++;
235  return chp;
236}
237
238/* See documentation in cli-utils.h.  */
239
240char *
241remove_trailing_whitespace (const char *start, char *s)
242{
243  while (s > start && isspace (*(s - 1)))
244    --s;
245
246  return s;
247}
248