Commit | Line | Data |
---|---|---|
e9cafbcc TT |
1 | /* CLI utilities. |
2 | ||
e2882c85 | 3 | Copyright (C) 2011-2018 Free Software Foundation, Inc. |
e9cafbcc TT |
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" | |
e9cafbcc TT |
22 | #include "value.h" |
23 | ||
24 | #include <ctype.h> | |
25 | ||
5d5658a1 | 26 | /* See documentation in cli-utils.h. */ |
e9cafbcc | 27 | |
5d5658a1 | 28 | int |
e799154c | 29 | get_number_trailer (const char **pp, int trailer) |
e9cafbcc TT |
30 | { |
31 | int retval = 0; /* default */ | |
e799154c | 32 | const char *p = *pp; |
95e95a6d PA |
33 | bool negative = false; |
34 | ||
35 | if (*p == '-') | |
36 | { | |
37 | ++p; | |
38 | negative = true; | |
39 | } | |
e9cafbcc TT |
40 | |
41 | if (*p == '$') | |
42 | { | |
3bd0f5ef | 43 | struct value *val = value_from_history_ref (p, &p); |
e9cafbcc | 44 | |
3bd0f5ef | 45 | if (val) /* Value history reference */ |
e9cafbcc | 46 | { |
3bd0f5ef MS |
47 | if (TYPE_CODE (value_type (val)) == TYPE_CODE_INT) |
48 | retval = value_as_long (val); | |
49 | else | |
50 | { | |
2217da06 | 51 | printf_filtered (_("History value must have integer type.\n")); |
3bd0f5ef MS |
52 | retval = 0; |
53 | } | |
54 | } | |
55 | else /* Convenience variable */ | |
56 | { | |
57 | /* Internal variable. Make a copy of the name, so we can | |
58 | null-terminate it to pass to lookup_internalvar(). */ | |
59 | char *varname; | |
e799154c | 60 | const char *start = ++p; |
3bd0f5ef MS |
61 | LONGEST val; |
62 | ||
63 | while (isalnum (*p) || *p == '_') | |
64 | p++; | |
65 | varname = (char *) alloca (p - start + 1); | |
66 | strncpy (varname, start, p - start); | |
67 | varname[p - start] = '\0'; | |
68 | if (get_internalvar_integer (lookup_internalvar (varname), &val)) | |
69 | retval = (int) val; | |
70 | else | |
71 | { | |
72 | printf_filtered (_("Convenience variable must " | |
73 | "have integer value.\n")); | |
74 | retval = 0; | |
75 | } | |
e9cafbcc TT |
76 | } |
77 | } | |
78 | else | |
79 | { | |
95e95a6d | 80 | const char *p1 = p; |
e9cafbcc TT |
81 | while (*p >= '0' && *p <= '9') |
82 | ++p; | |
95e95a6d | 83 | if (p == p1) |
e9cafbcc TT |
84 | /* There is no number here. (e.g. "cond a == b"). */ |
85 | { | |
86 | /* Skip non-numeric token. */ | |
87 | while (*p && !isspace((int) *p)) | |
88 | ++p; | |
89 | /* Return zero, which caller must interpret as error. */ | |
90 | retval = 0; | |
91 | } | |
92 | else | |
95e95a6d | 93 | retval = atoi (p1); |
e9cafbcc TT |
94 | } |
95 | if (!(isspace (*p) || *p == '\0' || *p == trailer)) | |
96 | { | |
97 | /* Trailing junk: return 0 and let caller print error msg. */ | |
98 | while (!(isspace (*p) || *p == '\0' || *p == trailer)) | |
99 | ++p; | |
100 | retval = 0; | |
101 | } | |
f1735a53 | 102 | p = skip_spaces (p); |
e9cafbcc | 103 | *pp = p; |
95e95a6d | 104 | return negative ? -retval : retval; |
e9cafbcc TT |
105 | } |
106 | ||
107 | /* See documentation in cli-utils.h. */ | |
108 | ||
109 | int | |
f1735a53 | 110 | get_number (const char **pp) |
e9cafbcc TT |
111 | { |
112 | return get_number_trailer (pp, '\0'); | |
113 | } | |
114 | ||
115 | /* See documentation in cli-utils.h. */ | |
116 | ||
e799154c TT |
117 | int |
118 | get_number (char **pp) | |
119 | { | |
120 | int result; | |
121 | const char *p = *pp; | |
122 | ||
123 | result = get_number_trailer (&p, '\0'); | |
124 | *pp = (char *) p; | |
125 | return result; | |
126 | } | |
127 | ||
128 | /* See documentation in cli-utils.h. */ | |
129 | ||
bfd28288 PA |
130 | number_or_range_parser::number_or_range_parser (const char *string) |
131 | { | |
132 | init (string); | |
133 | } | |
134 | ||
135 | /* See documentation in cli-utils.h. */ | |
136 | ||
197f0a60 | 137 | void |
bfd28288 | 138 | number_or_range_parser::init (const char *string) |
e9cafbcc | 139 | { |
bfd28288 PA |
140 | m_cur_tok = string; |
141 | m_last_retval = 0; | |
142 | m_end_value = 0; | |
143 | m_end_ptr = NULL; | |
144 | m_in_range = false; | |
197f0a60 TT |
145 | } |
146 | ||
147 | /* See documentation in cli-utils.h. */ | |
e9cafbcc | 148 | |
197f0a60 | 149 | int |
bfd28288 | 150 | number_or_range_parser::get_number () |
197f0a60 | 151 | { |
bfd28288 | 152 | if (m_in_range) |
71ef29a8 PA |
153 | { |
154 | /* All number-parsing has already been done. Return the next | |
155 | integer value (one greater than the saved previous value). | |
156 | Do not advance the token pointer until the end of range is | |
157 | reached. */ | |
158 | ||
bfd28288 | 159 | if (++m_last_retval == m_end_value) |
71ef29a8 PA |
160 | { |
161 | /* End of range reached; advance token pointer. */ | |
bfd28288 PA |
162 | m_cur_tok = m_end_ptr; |
163 | m_in_range = false; | |
71ef29a8 PA |
164 | } |
165 | } | |
bfd28288 | 166 | else if (*m_cur_tok != '-') |
e9cafbcc | 167 | { |
bfd28288 | 168 | /* Default case: state->m_cur_tok is pointing either to a solo |
197f0a60 | 169 | number, or to the first number of a range. */ |
bfd28288 | 170 | m_last_retval = get_number_trailer (&m_cur_tok, '-'); |
529c08b2 PW |
171 | /* If get_number_trailer has found a -, it might be the start |
172 | of a command option. So, do not parse a range if the - is | |
173 | followed by an alpha. */ | |
174 | if (*m_cur_tok == '-' && !isalpha (*(m_cur_tok + 1))) | |
e9cafbcc | 175 | { |
e799154c | 176 | const char **temp; |
e9cafbcc TT |
177 | |
178 | /* This is the start of a range (<number1> - <number2>). | |
179 | Skip the '-', parse and remember the second number, | |
180 | and also remember the end of the final token. */ | |
181 | ||
bfd28288 | 182 | temp = &m_end_ptr; |
f1735a53 TT |
183 | m_end_ptr = skip_spaces (m_cur_tok + 1); |
184 | m_end_value = ::get_number (temp); | |
bfd28288 | 185 | if (m_end_value < m_last_retval) |
e9cafbcc TT |
186 | { |
187 | error (_("inverted range")); | |
188 | } | |
bfd28288 | 189 | else if (m_end_value == m_last_retval) |
e9cafbcc TT |
190 | { |
191 | /* Degenerate range (number1 == number2). Advance the | |
192 | token pointer so that the range will be treated as a | |
bfd28288 PA |
193 | single number. */ |
194 | m_cur_tok = m_end_ptr; | |
e9cafbcc TT |
195 | } |
196 | else | |
bfd28288 | 197 | m_in_range = true; |
e9cafbcc TT |
198 | } |
199 | } | |
e9cafbcc | 200 | else |
529c08b2 PW |
201 | { |
202 | if (isdigit (*(m_cur_tok + 1))) | |
203 | error (_("negative value")); | |
204 | if (*(m_cur_tok + 1) == '$') | |
205 | { | |
206 | /* Convenience variable. */ | |
207 | m_last_retval = ::get_number (&m_cur_tok); | |
208 | if (m_last_retval < 0) | |
209 | error (_("negative value")); | |
210 | } | |
211 | } | |
bfd28288 | 212 | return m_last_retval; |
e9cafbcc TT |
213 | } |
214 | ||
71ef29a8 PA |
215 | /* See documentation in cli-utils.h. */ |
216 | ||
217 | void | |
bfd28288 PA |
218 | number_or_range_parser::setup_range (int start_value, int end_value, |
219 | const char *end_ptr) | |
71ef29a8 PA |
220 | { |
221 | gdb_assert (start_value > 0); | |
222 | ||
bfd28288 PA |
223 | m_in_range = true; |
224 | m_end_ptr = end_ptr; | |
225 | m_last_retval = start_value - 1; | |
226 | m_end_value = end_value; | |
71ef29a8 PA |
227 | } |
228 | ||
529c08b2 PW |
229 | /* See documentation in cli-utils.h. */ |
230 | ||
231 | bool | |
232 | number_or_range_parser::finished () const | |
233 | { | |
234 | /* Parsing is finished when at end of string or null string, | |
235 | or we are not in a range and not in front of an integer, negative | |
236 | integer, convenience var or negative convenience var. */ | |
237 | return (m_cur_tok == NULL || *m_cur_tok == '\0' | |
238 | || (!m_in_range | |
239 | && !(isdigit (*m_cur_tok) || *m_cur_tok == '$') | |
240 | && !(*m_cur_tok == '-' | |
241 | && (isdigit (m_cur_tok[1]) || m_cur_tok[1] == '$')))); | |
242 | } | |
243 | ||
aea5b279 MS |
244 | /* Accept a number and a string-form list of numbers such as is |
245 | accepted by get_number_or_range. Return TRUE if the number is | |
246 | in the list. | |
247 | ||
248 | By definition, an empty list includes all numbers. This is to | |
249 | be interpreted as typing a command such as "delete break" with | |
250 | no arguments. */ | |
251 | ||
252 | int | |
e799154c | 253 | number_is_in_list (const char *list, int number) |
aea5b279 MS |
254 | { |
255 | if (list == NULL || *list == '\0') | |
256 | return 1; | |
257 | ||
bfd28288 | 258 | number_or_range_parser parser (list); |
529c08b2 PW |
259 | |
260 | if (parser.finished ()) | |
261 | error (_("Arguments must be numbers or '$' variables.")); | |
bfd28288 | 262 | while (!parser.finished ()) |
298f437a | 263 | { |
bfd28288 | 264 | int gotnum = parser.get_number (); |
aea5b279 | 265 | |
298f437a | 266 | if (gotnum == 0) |
529c08b2 | 267 | error (_("Arguments must be numbers or '$' variables.")); |
298f437a MS |
268 | if (gotnum == number) |
269 | return 1; | |
270 | } | |
aea5b279 MS |
271 | return 0; |
272 | } | |
273 | ||
e9cafbcc TT |
274 | /* See documentation in cli-utils.h. */ |
275 | ||
63160a43 PA |
276 | const char * |
277 | remove_trailing_whitespace (const char *start, const char *s) | |
c00f8484 KS |
278 | { |
279 | while (s > start && isspace (*(s - 1))) | |
280 | --s; | |
281 | ||
282 | return s; | |
283 | } | |
55aa24fb SDJ |
284 | |
285 | /* See documentation in cli-utils.h. */ | |
286 | ||
cb791d59 | 287 | std::string |
f1735a53 | 288 | extract_arg (const char **arg) |
55aa24fb | 289 | { |
b5be8ce0 | 290 | const char *result; |
55aa24fb SDJ |
291 | |
292 | if (!*arg) | |
cb791d59 | 293 | return std::string (); |
55aa24fb SDJ |
294 | |
295 | /* Find the start of the argument. */ | |
f1735a53 | 296 | *arg = skip_spaces (*arg); |
55aa24fb | 297 | if (!**arg) |
cb791d59 | 298 | return std::string (); |
55aa24fb SDJ |
299 | result = *arg; |
300 | ||
301 | /* Find the end of the argument. */ | |
f1735a53 | 302 | *arg = skip_to_space (*arg + 1); |
55aa24fb SDJ |
303 | |
304 | if (result == *arg) | |
cb791d59 | 305 | return std::string (); |
55aa24fb | 306 | |
cb791d59 | 307 | return std::string (result, *arg - result); |
b5be8ce0 JB |
308 | } |
309 | ||
310 | /* See documentation in cli-utils.h. */ | |
311 | ||
cb791d59 | 312 | std::string |
b5be8ce0 JB |
313 | extract_arg (char **arg) |
314 | { | |
315 | const char *arg_const = *arg; | |
cb791d59 | 316 | std::string result; |
55aa24fb | 317 | |
f1735a53 | 318 | result = extract_arg (&arg_const); |
b5be8ce0 JB |
319 | *arg += arg_const - *arg; |
320 | return result; | |
55aa24fb | 321 | } |
e6f0bce7 HZ |
322 | |
323 | /* See documentation in cli-utils.h. */ | |
324 | ||
325 | int | |
63160a43 | 326 | check_for_argument (const char **str, const char *arg, int arg_len) |
e6f0bce7 HZ |
327 | { |
328 | if (strncmp (*str, arg, arg_len) == 0 | |
329 | && ((*str)[arg_len] == '\0' || isspace ((*str)[arg_len]))) | |
330 | { | |
331 | *str += arg_len; | |
332 | return 1; | |
333 | } | |
334 | return 0; | |
335 | } | |
529c08b2 PW |
336 | |
337 | /* See documentation in cli-utils.h. */ | |
338 | ||
339 | int | |
340 | parse_flags (const char **str, const char *flags) | |
341 | { | |
342 | const char *p = skip_spaces (*str); | |
343 | ||
344 | if (p[0] == '-' | |
345 | && isalpha (p[1]) | |
346 | && (p[2] == '\0' || isspace (p[2]))) | |
347 | { | |
348 | const char pf = p[1]; | |
349 | const char *f = flags; | |
350 | ||
351 | while (*f != '\0') | |
352 | { | |
353 | if (*f == pf) | |
354 | { | |
355 | *str = skip_spaces (p + 2); | |
356 | return f - flags + 1; | |
357 | } | |
358 | f++; | |
359 | } | |
360 | } | |
361 | ||
362 | return 0; | |
363 | } | |
364 | ||
365 | /* See documentation in cli-utils.h. */ | |
366 | ||
367 | bool | |
368 | parse_flags_qcs (const char *which_command, const char **str, | |
369 | qcs_flags *flags) | |
370 | { | |
371 | switch (parse_flags (str, "qcs")) | |
372 | { | |
373 | case 0: | |
374 | return false; | |
375 | case 1: | |
376 | flags->quiet = true; | |
377 | break; | |
378 | case 2: | |
379 | flags->cont = true; | |
380 | break; | |
381 | case 3: | |
382 | flags->silent = true; | |
383 | break; | |
384 | default: | |
385 | gdb_assert_not_reached ("int qcs flag out of bound"); | |
386 | } | |
387 | ||
388 | if (flags->cont && flags->silent) | |
389 | error (_("%s: -c and -s are mutually exclusive"), which_command); | |
390 | ||
391 | return true; | |
392 | } |