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