xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/cli/cli-utils.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /* CLI utilities.
2 
3    Copyright (C) 2011-2016 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 "value.h"
23 
24 #include <ctype.h>
25 
26 /* See documentation in cli-utils.h.  */
27 
28 int
29 get_number_trailer (const char **pp, int trailer)
30 {
31   int retval = 0;	/* default */
32   const char *p = *pp;
33 
34   if (*p == '$')
35     {
36       struct value *val = value_from_history_ref (p, &p);
37 
38       if (val)	/* Value history reference */
39 	{
40 	  if (TYPE_CODE (value_type (val)) == TYPE_CODE_INT)
41 	    retval = value_as_long (val);
42 	  else
43 	    {
44 	      printf_filtered (_("History value must have integer type.\n"));
45 	      retval = 0;
46 	    }
47 	}
48       else	/* Convenience variable */
49 	{
50 	  /* Internal variable.  Make a copy of the name, so we can
51 	     null-terminate it to pass to lookup_internalvar().  */
52 	  char *varname;
53 	  const char *start = ++p;
54 	  LONGEST val;
55 
56 	  while (isalnum (*p) || *p == '_')
57 	    p++;
58 	  varname = (char *) alloca (p - start + 1);
59 	  strncpy (varname, start, p - start);
60 	  varname[p - start] = '\0';
61 	  if (get_internalvar_integer (lookup_internalvar (varname), &val))
62 	    retval = (int) val;
63 	  else
64 	    {
65 	      printf_filtered (_("Convenience variable must "
66 				 "have integer value.\n"));
67 	      retval = 0;
68 	    }
69 	}
70     }
71   else
72     {
73       if (*p == '-')
74 	++p;
75       while (*p >= '0' && *p <= '9')
76 	++p;
77       if (p == *pp)
78 	/* There is no number here.  (e.g. "cond a == b").  */
79 	{
80 	  /* Skip non-numeric token.  */
81 	  while (*p && !isspace((int) *p))
82 	    ++p;
83 	  /* Return zero, which caller must interpret as error.  */
84 	  retval = 0;
85 	}
86       else
87 	retval = atoi (*pp);
88     }
89   if (!(isspace (*p) || *p == '\0' || *p == trailer))
90     {
91       /* Trailing junk: return 0 and let caller print error msg.  */
92       while (!(isspace (*p) || *p == '\0' || *p == trailer))
93 	++p;
94       retval = 0;
95     }
96   p = skip_spaces_const (p);
97   *pp = p;
98   return retval;
99 }
100 
101 /* See documentation in cli-utils.h.  */
102 
103 int
104 get_number_const (const char **pp)
105 {
106   return get_number_trailer (pp, '\0');
107 }
108 
109 /* See documentation in cli-utils.h.  */
110 
111 int
112 get_number (char **pp)
113 {
114   int result;
115   const char *p = *pp;
116 
117   result = get_number_trailer (&p, '\0');
118   *pp = (char *) p;
119   return result;
120 }
121 
122 /* See documentation in cli-utils.h.  */
123 
124 void
125 init_number_or_range (struct get_number_or_range_state *state,
126 		      const char *string)
127 {
128   memset (state, 0, sizeof (*state));
129   state->string = string;
130 }
131 
132 /* See documentation in cli-utils.h.  */
133 
134 int
135 get_number_or_range (struct get_number_or_range_state *state)
136 {
137   if (state->in_range)
138     {
139       /* All number-parsing has already been done.  Return the next
140 	 integer value (one greater than the saved previous value).
141 	 Do not advance the token pointer until the end of range is
142 	 reached.  */
143 
144       if (++state->last_retval == state->end_value)
145 	{
146 	  /* End of range reached; advance token pointer.  */
147 	  state->string = state->end_ptr;
148 	  state->in_range = 0;
149 	}
150     }
151   else if (*state->string != '-')
152     {
153       /* Default case: state->string is pointing either to a solo
154 	 number, or to the first number of a range.  */
155       state->last_retval = get_number_trailer (&state->string, '-');
156       if (*state->string == '-')
157 	{
158 	  const char **temp;
159 
160 	  /* This is the start of a range (<number1> - <number2>).
161 	     Skip the '-', parse and remember the second number,
162 	     and also remember the end of the final token.  */
163 
164 	  temp = &state->end_ptr;
165 	  state->end_ptr = skip_spaces_const (state->string + 1);
166 	  state->end_value = get_number_const (temp);
167 	  if (state->end_value < state->last_retval)
168 	    {
169 	      error (_("inverted range"));
170 	    }
171 	  else if (state->end_value == state->last_retval)
172 	    {
173 	      /* Degenerate range (number1 == number2).  Advance the
174 		 token pointer so that the range will be treated as a
175 		 single number.  */
176 	      state->string = state->end_ptr;
177 	    }
178 	  else
179 	    state->in_range = 1;
180 	}
181     }
182   else
183     error (_("negative value"));
184   state->finished = *state->string == '\0';
185   return state->last_retval;
186 }
187 
188 /* See documentation in cli-utils.h.  */
189 
190 void
191 number_range_setup_range (struct get_number_or_range_state *state,
192 			  int start_value, int end_value, const char *end_ptr)
193 {
194   gdb_assert (start_value > 0);
195 
196   state->in_range = 1;
197   state->end_ptr = end_ptr;
198   state->last_retval = start_value - 1;
199   state->end_value = end_value;
200 }
201 
202 /* Accept a number and a string-form list of numbers such as is
203    accepted by get_number_or_range.  Return TRUE if the number is
204    in the list.
205 
206    By definition, an empty list includes all numbers.  This is to
207    be interpreted as typing a command such as "delete break" with
208    no arguments.  */
209 
210 int
211 number_is_in_list (const char *list, int number)
212 {
213   struct get_number_or_range_state state;
214 
215   if (list == NULL || *list == '\0')
216     return 1;
217 
218   init_number_or_range (&state, list);
219   while (!state.finished)
220     {
221       int gotnum = get_number_or_range (&state);
222 
223       if (gotnum == 0)
224 	error (_("Args must be numbers or '$' variables."));
225       if (gotnum == number)
226 	return 1;
227     }
228   return 0;
229 }
230 
231 /* See documentation in cli-utils.h.  */
232 
233 char *
234 remove_trailing_whitespace (const char *start, char *s)
235 {
236   while (s > start && isspace (*(s - 1)))
237     --s;
238 
239   return s;
240 }
241 
242 /* See documentation in cli-utils.h.  */
243 
244 char *
245 extract_arg_const (const char **arg)
246 {
247   const char *result;
248 
249   if (!*arg)
250     return NULL;
251 
252   /* Find the start of the argument.  */
253   *arg = skip_spaces_const (*arg);
254   if (!**arg)
255     return NULL;
256   result = *arg;
257 
258   /* Find the end of the argument.  */
259   *arg = skip_to_space_const (*arg + 1);
260 
261   if (result == *arg)
262     return NULL;
263 
264   return savestring (result, *arg - result);
265 }
266 
267 /* See documentation in cli-utils.h.  */
268 
269 char *
270 extract_arg (char **arg)
271 {
272   const char *arg_const = *arg;
273   char *result;
274 
275   result = extract_arg_const (&arg_const);
276   *arg += arg_const - *arg;
277   return result;
278 }
279 
280 /* See documentation in cli-utils.h.  */
281 
282 int
283 check_for_argument (char **str, char *arg, int arg_len)
284 {
285   if (strncmp (*str, arg, arg_len) == 0
286       && ((*str)[arg_len] == '\0' || isspace ((*str)[arg_len])))
287     {
288       *str += arg_len;
289       return 1;
290     }
291   return 0;
292 }
293