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