1 /* CLI utilities.
2 
3    Copyright (C) 2011-2020 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 ULONGEST
get_ulongest(const char ** pp,int trailer)29 get_ulongest (const char **pp, int trailer)
30 {
31   LONGEST retval = 0;	/* default */
32   const char *p = *pp;
33 
34   if (*p == '$')
35     {
36       value *val = value_from_history_ref (p, &p);
37 
38       if (val != NULL)	/* Value history reference */
39 	{
40 	  if (value_type (val)->code () == TYPE_CODE_INT)
41 	    retval = value_as_long (val);
42 	  else
43 	    error (_("History value must have integer type."));
44 	}
45       else	/* Convenience variable */
46 	{
47 	  /* Internal variable.  Make a copy of the name, so we can
48 	     null-terminate it to pass to lookup_internalvar().  */
49 	  const char *start = ++p;
50 	  while (isalnum (*p) || *p == '_')
51 	    p++;
52 	  std::string varname (start, p - start);
53 	  if (!get_internalvar_integer (lookup_internalvar (varname.c_str ()),
54 				       &retval))
55 	    error (_("Convenience variable $%s does not have integer value."),
56 		   varname.c_str ());
57 	}
58     }
59   else
60     {
61       const char *end = p;
62       retval = strtoulst (p, &end, 0);
63       if (p == end)
64 	{
65 	  /* There is no number here.  (e.g. "cond a == b").  */
66 	  error (_("Expected integer at: %s"), p);
67 	}
68       p = end;
69     }
70 
71   if (!(isspace (*p) || *p == '\0' || *p == trailer))
72     error (_("Trailing junk at: %s"), p);
73   p = skip_spaces (p);
74   *pp = p;
75   return retval;
76 }
77 
78 /* See documentation in cli-utils.h.  */
79 
80 int
get_number_trailer(const char ** pp,int trailer)81 get_number_trailer (const char **pp, int trailer)
82 {
83   int retval = 0;	/* default */
84   const char *p = *pp;
85   bool negative = false;
86 
87   if (*p == '-')
88     {
89       ++p;
90       negative = true;
91     }
92 
93   if (*p == '$')
94     {
95       struct value *val = value_from_history_ref (p, &p);
96 
97       if (val)	/* Value history reference */
98 	{
99 	  if (value_type (val)->code () == TYPE_CODE_INT)
100 	    retval = value_as_long (val);
101 	  else
102 	    {
103 	      printf_filtered (_("History value must have integer type.\n"));
104 	      retval = 0;
105 	    }
106 	}
107       else	/* Convenience variable */
108 	{
109 	  /* Internal variable.  Make a copy of the name, so we can
110 	     null-terminate it to pass to lookup_internalvar().  */
111 	  char *varname;
112 	  const char *start = ++p;
113 	  LONGEST longest_val;
114 
115 	  while (isalnum (*p) || *p == '_')
116 	    p++;
117 	  varname = (char *) alloca (p - start + 1);
118 	  strncpy (varname, start, p - start);
119 	  varname[p - start] = '\0';
120 	  if (get_internalvar_integer (lookup_internalvar (varname),
121 				       &longest_val))
122 	    retval = (int) longest_val;
123 	  else
124 	    {
125 	      printf_filtered (_("Convenience variable must "
126 				 "have integer value.\n"));
127 	      retval = 0;
128 	    }
129 	}
130     }
131   else
132     {
133       const char *p1 = p;
134       while (*p >= '0' && *p <= '9')
135 	++p;
136       if (p == p1)
137 	/* There is no number here.  (e.g. "cond a == b").  */
138 	{
139 	  /* Skip non-numeric token.  */
140 	  while (*p && !isspace((int) *p))
141 	    ++p;
142 	  /* Return zero, which caller must interpret as error.  */
143 	  retval = 0;
144 	}
145       else
146 	retval = atoi (p1);
147     }
148   if (!(isspace (*p) || *p == '\0' || *p == trailer))
149     {
150       /* Trailing junk: return 0 and let caller print error msg.  */
151       while (!(isspace (*p) || *p == '\0' || *p == trailer))
152 	++p;
153       retval = 0;
154     }
155   p = skip_spaces (p);
156   *pp = p;
157   return negative ? -retval : retval;
158 }
159 
160 /* See documentation in cli-utils.h.  */
161 
162 int
get_number(const char ** pp)163 get_number (const char **pp)
164 {
165   return get_number_trailer (pp, '\0');
166 }
167 
168 /* See documentation in cli-utils.h.  */
169 
170 int
get_number(char ** pp)171 get_number (char **pp)
172 {
173   int result;
174   const char *p = *pp;
175 
176   result = get_number_trailer (&p, '\0');
177   *pp = (char *) p;
178   return result;
179 }
180 
181 /* See documentation in cli-utils.h.  */
182 
183 void
report_unrecognized_option_error(const char * command,const char * args)184 report_unrecognized_option_error (const char *command, const char *args)
185 {
186   std::string option = extract_arg (&args);
187 
188   error (_("Unrecognized option '%s' to %s command.  "
189 	   "Try \"help %s\"."), option.c_str (),
190 	 command, command);
191 }
192 
193 /* See documentation in cli-utils.h.  */
194 
195 const char *
info_print_args_help(const char * prefix,const char * entity_kind,bool document_n_flag)196 info_print_args_help (const char *prefix,
197 		      const char *entity_kind,
198 		      bool document_n_flag)
199 {
200   return xstrprintf (_("\
201 %sIf NAMEREGEXP is provided, only prints the %s whose name\n\
202 matches NAMEREGEXP.\n\
203 If -t TYPEREGEXP is provided, only prints the %s whose type\n\
204 matches TYPEREGEXP.  Note that the matching is done with the type\n\
205 printed by the 'whatis' command.\n\
206 By default, the command might produce headers and/or messages indicating\n\
207 why no %s can be printed.\n\
208 The flag -q disables the production of these headers and messages.%s"),
209 		     prefix, entity_kind, entity_kind, entity_kind,
210 		     (document_n_flag ? _("\n\
211 By default, the command will include non-debug symbols in the output;\n\
212 these can be excluded using the -n flag.") : ""));
213 }
214 
215 /* See documentation in cli-utils.h.  */
216 
number_or_range_parser(const char * string)217 number_or_range_parser::number_or_range_parser (const char *string)
218 {
219   init (string);
220 }
221 
222 /* See documentation in cli-utils.h.  */
223 
224 void
init(const char * string)225 number_or_range_parser::init (const char *string)
226 {
227   m_cur_tok = string;
228   m_last_retval = 0;
229   m_end_value = 0;
230   m_end_ptr = NULL;
231   m_in_range = false;
232 }
233 
234 /* See documentation in cli-utils.h.  */
235 
236 int
get_number()237 number_or_range_parser::get_number ()
238 {
239   if (m_in_range)
240     {
241       /* All number-parsing has already been done.  Return the next
242 	 integer value (one greater than the saved previous value).
243 	 Do not advance the token pointer until the end of range is
244 	 reached.  */
245 
246       if (++m_last_retval == m_end_value)
247 	{
248 	  /* End of range reached; advance token pointer.  */
249 	  m_cur_tok = m_end_ptr;
250 	  m_in_range = false;
251 	}
252     }
253   else if (*m_cur_tok != '-')
254     {
255       /* Default case: state->m_cur_tok is pointing either to a solo
256 	 number, or to the first number of a range.  */
257       m_last_retval = get_number_trailer (&m_cur_tok, '-');
258       /* If get_number_trailer has found a '-' preceded by a space, it
259 	 might be the start of a command option.  So, do not parse a
260 	 range if the '-' is followed by an alpha or another '-'.  We
261 	 might also be completing something like
262 	 "frame apply level 0 -" and we prefer treating that "-" as an
263 	 option rather than an incomplete range, so check for end of
264 	 string as well.  */
265       if (m_cur_tok[0] == '-'
266 	  && !(isspace (m_cur_tok[-1])
267 	       && (isalpha (m_cur_tok[1])
268 		   || m_cur_tok[1] == '-'
269 		   || m_cur_tok[1] == '\0')))
270 	{
271 	  const char **temp;
272 
273 	  /* This is the start of a range (<number1> - <number2>).
274 	     Skip the '-', parse and remember the second number,
275 	     and also remember the end of the final token.  */
276 
277 	  temp = &m_end_ptr;
278 	  m_end_ptr = skip_spaces (m_cur_tok + 1);
279 	  m_end_value = ::get_number (temp);
280 	  if (m_end_value < m_last_retval)
281 	    {
282 	      error (_("inverted range"));
283 	    }
284 	  else if (m_end_value == m_last_retval)
285 	    {
286 	      /* Degenerate range (number1 == number2).  Advance the
287 		 token pointer so that the range will be treated as a
288 		 single number.  */
289 	      m_cur_tok = m_end_ptr;
290 	    }
291 	  else
292 	    m_in_range = true;
293 	}
294     }
295   else
296     {
297       if (isdigit (*(m_cur_tok + 1)))
298 	error (_("negative value"));
299       if (*(m_cur_tok + 1) == '$')
300 	{
301 	  /* Convenience variable.  */
302 	  m_last_retval = ::get_number (&m_cur_tok);
303 	  if (m_last_retval < 0)
304 	    error (_("negative value"));
305 	}
306     }
307   return m_last_retval;
308 }
309 
310 /* See documentation in cli-utils.h.  */
311 
312 void
setup_range(int start_value,int end_value,const char * end_ptr)313 number_or_range_parser::setup_range (int start_value, int end_value,
314 				     const char *end_ptr)
315 {
316   gdb_assert (start_value > 0);
317 
318   m_in_range = true;
319   m_end_ptr = end_ptr;
320   m_last_retval = start_value - 1;
321   m_end_value = end_value;
322 }
323 
324 /* See documentation in cli-utils.h.  */
325 
326 bool
finished()327 number_or_range_parser::finished () const
328 {
329   /* Parsing is finished when at end of string or null string,
330      or we are not in a range and not in front of an integer, negative
331      integer, convenience var or negative convenience var.  */
332   return (m_cur_tok == NULL || *m_cur_tok == '\0'
333 	  || (!m_in_range
334 	      && !(isdigit (*m_cur_tok) || *m_cur_tok == '$')
335 	      && !(*m_cur_tok == '-'
336 		   && (isdigit (m_cur_tok[1]) || m_cur_tok[1] == '$'))));
337 }
338 
339 /* Accept a number and a string-form list of numbers such as is
340    accepted by get_number_or_range.  Return TRUE if the number is
341    in the list.
342 
343    By definition, an empty list includes all numbers.  This is to
344    be interpreted as typing a command such as "delete break" with
345    no arguments.  */
346 
347 int
number_is_in_list(const char * list,int number)348 number_is_in_list (const char *list, int number)
349 {
350   if (list == NULL || *list == '\0')
351     return 1;
352 
353   number_or_range_parser parser (list);
354 
355   if (parser.finished ())
356     error (_("Arguments must be numbers or '$' variables."));
357   while (!parser.finished ())
358     {
359       int gotnum = parser.get_number ();
360 
361       if (gotnum == 0)
362 	error (_("Arguments must be numbers or '$' variables."));
363       if (gotnum == number)
364 	return 1;
365     }
366   return 0;
367 }
368 
369 /* See documentation in cli-utils.h.  */
370 
371 const char *
remove_trailing_whitespace(const char * start,const char * s)372 remove_trailing_whitespace (const char *start, const char *s)
373 {
374   while (s > start && isspace (*(s - 1)))
375     --s;
376 
377   return s;
378 }
379 
380 /* See documentation in cli-utils.h.  */
381 
382 std::string
extract_arg(const char ** arg)383 extract_arg (const char **arg)
384 {
385   const char *result;
386 
387   if (!*arg)
388     return std::string ();
389 
390   /* Find the start of the argument.  */
391   *arg = skip_spaces (*arg);
392   if (!**arg)
393     return std::string ();
394   result = *arg;
395 
396   /* Find the end of the argument.  */
397   *arg = skip_to_space (*arg + 1);
398 
399   if (result == *arg)
400     return std::string ();
401 
402   return std::string (result, *arg - result);
403 }
404 
405 /* See documentation in cli-utils.h.  */
406 
407 std::string
extract_arg(char ** arg)408 extract_arg (char **arg)
409 {
410   const char *arg_const = *arg;
411   std::string result;
412 
413   result = extract_arg (&arg_const);
414   *arg += arg_const - *arg;
415   return result;
416 }
417 
418 /* See documentation in cli-utils.h.  */
419 
420 int
check_for_argument(const char ** str,const char * arg,int arg_len)421 check_for_argument (const char **str, const char *arg, int arg_len)
422 {
423   if (strncmp (*str, arg, arg_len) == 0
424       && ((*str)[arg_len] == '\0' || isspace ((*str)[arg_len])))
425     {
426       *str += arg_len;
427       *str = skip_spaces (*str);
428       return 1;
429     }
430   return 0;
431 }
432 
433 /* See documentation in cli-utils.h.  */
434 
435 void
validate_flags_qcs(const char * which_command,qcs_flags * flags)436 validate_flags_qcs (const char *which_command, qcs_flags *flags)
437 {
438   if (flags->cont && flags->silent)
439     error (_("%s: -c and -s are mutually exclusive"), which_command);
440 }
441 
442