]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/cli/cli-option.c
Update copyright year range in header of all files managed by GDB
[thirdparty/binutils-gdb.git] / gdb / cli / cli-option.c
CommitLineData
9d0faba9
PA
1/* CLI options framework, for GDB.
2
213516ef 3 Copyright (C) 2017-2023 Free Software Foundation, Inc.
9d0faba9
PA
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-option.h"
22#include "cli/cli-decode.h"
23#include "cli/cli-utils.h"
24#include "cli/cli-setshow.h"
25#include "command.h"
26#include <vector>
27
28namespace gdb {
29namespace option {
30
31/* An option's value. Which field is active depends on the option's
32 type. */
33union option_value
34{
35 /* For var_boolean options. */
36 bool boolean;
37
38 /* For var_uinteger options. */
39 unsigned int uinteger;
40
41 /* For var_zuinteger_unlimited options. */
42 int integer;
43
44 /* For var_enum options. */
45 const char *enumeration;
3d9be6f5
PA
46
47 /* For var_string options. This is malloc-allocated. */
e0700ba4 48 std::string *string;
9d0faba9
PA
49};
50
51/* Holds an options definition and its value. */
52struct option_def_and_value
53{
54 /* The option definition. */
55 const option_def &option;
56
57 /* A context. */
58 void *ctx;
59
60 /* The option's value, if any. */
61 gdb::optional<option_value> value;
3d9be6f5
PA
62
63 /* Constructor. */
64 option_def_and_value (const option_def &option_, void *ctx_,
65 gdb::optional<option_value> &&value_ = {})
66 : option (option_),
67 ctx (ctx_),
68 value (std::move (value_))
69 {
70 clear_value (option_, value_);
71 }
72
73 /* Move constructor. Need this because for some types the values
74 are allocated on the heap. */
75 option_def_and_value (option_def_and_value &&rval)
76 : option (rval.option),
77 ctx (rval.ctx),
78 value (std::move (rval.value))
79 {
80 clear_value (rval.option, rval.value);
81 }
82
83 DISABLE_COPY_AND_ASSIGN (option_def_and_value);
84
85 ~option_def_and_value ()
86 {
87 if (value.has_value ())
88 {
89 if (option.type == var_string)
e0700ba4 90 delete value->string;
3d9be6f5
PA
91 }
92 }
93
94private:
95
96 /* Clear the option_value, without releasing it. This is used after
97 the value has been moved to some other option_def_and_value
98 instance. This is needed because for some types the value is
99 allocated on the heap, so we must clear the pointer in the
100 source, to avoid a double free. */
101 static void clear_value (const option_def &option,
102 gdb::optional<option_value> &value)
103 {
104 if (value.has_value ())
105 {
106 if (option.type == var_string)
107 value->string = nullptr;
108 }
109 }
9d0faba9
PA
110};
111
41fc454c
PA
112static void save_option_value_in_ctx (gdb::optional<option_def_and_value> &ov);
113
9d0faba9
PA
114/* Info passed around when handling completion. */
115struct parse_option_completion_info
116{
117 /* The completion word. */
118 const char *word;
119
120 /* The tracker. */
121 completion_tracker &tracker;
122};
123
124/* If ARGS starts with "-", look for a "--" delimiter. If one is
125 found, then interpret everything up until the "--" as command line
126 options. Otherwise, interpret unknown input as the beginning of
127 the command's operands. */
128
129static const char *
130find_end_options_delimiter (const char *args)
131{
132 if (args[0] == '-')
133 {
134 const char *p = args;
135
136 p = skip_spaces (p);
137 while (*p)
138 {
139 if (check_for_argument (&p, "--"))
140 return p;
141 else
142 p = skip_to_space (p);
143 p = skip_spaces (p);
144 }
145 }
146
147 return nullptr;
148}
149
150/* Complete TEXT/WORD on all options in OPTIONS_GROUP. */
151
152static void
153complete_on_options (gdb::array_view<const option_def_group> options_group,
154 completion_tracker &tracker,
155 const char *text, const char *word)
156{
157 size_t textlen = strlen (text);
158 for (const auto &grp : options_group)
159 for (const auto &opt : grp.options)
160 if (strncmp (opt.name, text, textlen) == 0)
161 {
162 tracker.add_completion
163 (make_completion_match_str (opt.name, text, word));
164 }
165}
166
167/* See cli-option.h. */
168
169void
170complete_on_all_options (completion_tracker &tracker,
171 gdb::array_view<const option_def_group> options_group)
172{
173 static const char opt[] = "-";
174 complete_on_options (options_group, tracker, opt + 1, opt);
175}
176
177/* Parse ARGS, guided by OPTIONS_GROUP. HAVE_DELIMITER is true if the
178 whole ARGS line included the "--" options-terminator delimiter. */
179
180static gdb::optional<option_def_and_value>
181parse_option (gdb::array_view<const option_def_group> options_group,
182 process_options_mode mode,
183 bool have_delimiter,
184 const char **args,
185 parse_option_completion_info *completion = nullptr)
186{
187 if (*args == nullptr)
188 return {};
189 else if (**args != '-')
190 {
191 if (have_delimiter)
192 error (_("Unrecognized option at: %s"), *args);
193 return {};
194 }
195 else if (check_for_argument (args, "--"))
196 return {};
197
198 /* Skip the initial '-'. */
199 const char *arg = *args + 1;
200
201 const char *after = skip_to_space (arg);
202 size_t len = after - arg;
203 const option_def *match = nullptr;
204 void *match_ctx = nullptr;
205
206 for (const auto &grp : options_group)
207 {
208 for (const auto &o : grp.options)
209 {
210 if (strncmp (o.name, arg, len) == 0)
211 {
212 if (match != nullptr)
213 {
214 if (completion != nullptr && arg[len] == '\0')
215 {
216 complete_on_options (options_group,
217 completion->tracker,
218 arg, completion->word);
219 return {};
220 }
221
222 error (_("Ambiguous option at: -%s"), arg);
223 }
224
225 match = &o;
226 match_ctx = grp.ctx;
227
228 if ((isspace (arg[len]) || arg[len] == '\0')
229 && strlen (o.name) == len)
230 break; /* Exact match. */
231 }
232 }
233 }
234
235 if (match == nullptr)
236 {
237 if (have_delimiter || mode != PROCESS_OPTIONS_UNKNOWN_IS_OPERAND)
238 error (_("Unrecognized option at: %s"), *args);
239
240 return {};
241 }
242
243 if (completion != nullptr && arg[len] == '\0')
244 {
245 complete_on_options (options_group, completion->tracker,
246 arg, completion->word);
247 return {};
248 }
249
250 *args += 1 + len;
251 *args = skip_spaces (*args);
252 if (completion != nullptr)
253 completion->word = *args;
254
255 switch (match->type)
256 {
257 case var_boolean:
258 {
259 if (!match->have_argument)
260 {
261 option_value val;
262 val.boolean = true;
263 return option_def_and_value {*match, match_ctx, val};
264 }
265
266 const char *val_str = *args;
267 int res;
268
269 if (**args == '\0' && completion != nullptr)
270 {
271 /* Complete on both "on/off" and more options. */
272
273 if (mode == PROCESS_OPTIONS_REQUIRE_DELIMITER)
274 {
275 complete_on_enum (completion->tracker,
276 boolean_enums, val_str, val_str);
277 complete_on_all_options (completion->tracker, options_group);
278 }
279 return option_def_and_value {*match, match_ctx};
280 }
281 else if (**args == '-')
282 {
283 /* Treat:
284 "cmd -boolean-option -another-opt..."
285 as:
286 "cmd -boolean-option on -another-opt..."
287 */
288 res = 1;
289 }
290 else if (**args == '\0')
291 {
292 /* Treat:
293 (1) "cmd -boolean-option "
294 as:
295 (1) "cmd -boolean-option on"
296 */
297 res = 1;
298 }
299 else
300 {
301 res = parse_cli_boolean_value (args);
302 if (res < 0)
303 {
304 const char *end = skip_to_space (*args);
305 if (completion != nullptr)
306 {
307 if (*end == '\0')
308 {
309 complete_on_enum (completion->tracker,
310 boolean_enums, val_str, val_str);
311 return option_def_and_value {*match, match_ctx};
312 }
313 }
314
315 if (have_delimiter)
316 error (_("Value given for `-%s' is not a boolean: %.*s"),
317 match->name, (int) (end - val_str), val_str);
318 /* The user didn't separate options from operands
319 using "--", so treat this unrecognized value as the
320 start of the operands. This makes "frame apply all
321 -past-main CMD" work. */
322 return option_def_and_value {*match, match_ctx};
323 }
324 else if (completion != nullptr && **args == '\0')
325 {
326 /* While "cmd -boolean [TAB]" only offers "on" and
327 "off", the boolean option actually accepts "1",
328 "yes", etc. as boolean values. We complete on all
329 of those instead of BOOLEAN_ENUMS here to make
330 these work:
331
332 "p -object 1[TAB]" -> "p -object 1 "
333 "p -object ye[TAB]" -> "p -object yes "
334
335 Etc. Note that it's important that the space is
336 auto-appended. Otherwise, if we only completed on
337 on/off here, then it might look to the user like
338 "1" isn't valid, like:
339 "p -object 1[TAB]" -> "p -object 1" (i.e., nothing happens).
340 */
341 static const char *const all_boolean_enums[] = {
342 "on", "off",
343 "yes", "no",
344 "enable", "disable",
345 "0", "1",
346 nullptr,
347 };
348 complete_on_enum (completion->tracker, all_boolean_enums,
349 val_str, val_str);
350 return {};
351 }
352 }
353
354 option_value val;
355 val.boolean = res;
356 return option_def_and_value {*match, match_ctx, val};
357 }
358 case var_uinteger:
359 case var_zuinteger_unlimited:
360 {
361 if (completion != nullptr)
362 {
363 if (**args == '\0')
364 {
365 /* Convenience to let the user know what the option
366 can accept. Note there's no common prefix between
367 the strings on purpose, so that readline doesn't do
368 a partial match. */
369 completion->tracker.add_completion
370 (make_unique_xstrdup ("NUMBER"));
371 completion->tracker.add_completion
372 (make_unique_xstrdup ("unlimited"));
373 return {};
374 }
375 else if (startswith ("unlimited", *args))
376 {
377 completion->tracker.add_completion
378 (make_unique_xstrdup ("unlimited"));
379 return {};
380 }
381 }
382
383 if (match->type == var_zuinteger_unlimited)
384 {
385 option_value val;
386 val.integer = parse_cli_var_zuinteger_unlimited (args, false);
387 return option_def_and_value {*match, match_ctx, val};
388 }
389 else
390 {
391 option_value val;
392 val.uinteger = parse_cli_var_uinteger (match->type, args, false);
393 return option_def_and_value {*match, match_ctx, val};
394 }
395 }
396 case var_enum:
397 {
398 if (completion != nullptr)
399 {
400 const char *after_arg = skip_to_space (*args);
401 if (*after_arg == '\0')
402 {
403 complete_on_enum (completion->tracker,
404 match->enums, *args, *args);
41fc454c
PA
405 if (completion->tracker.have_completions ())
406 return {};
9d0faba9 407
41fc454c
PA
408 /* If we don't have completions, let the
409 non-completion path throw on invalid enum value
410 below, so that completion processing stops. */
9d0faba9
PA
411 }
412 }
413
414 if (check_for_argument (args, "--"))
415 {
416 /* Treat e.g., "backtrace -entry-values --" as if there
417 was no argument after "-entry-values". This makes
418 parse_cli_var_enum throw an error with a suggestion of
419 what are the valid options. */
420 args = nullptr;
421 }
422
423 option_value val;
424 val.enumeration = parse_cli_var_enum (args, match->enums);
425 return option_def_and_value {*match, match_ctx, val};
426 }
3d9be6f5
PA
427 case var_string:
428 {
429 if (check_for_argument (args, "--"))
430 {
431 /* Treat e.g., "maint test-options -string --" as if there
432 was no argument after "-string". */
433 error (_("-%s requires an argument"), match->name);
434 }
435
436 const char *arg_start = *args;
021d8588 437 std::string str = extract_string_maybe_quoted (args);
3d9be6f5
PA
438 if (*args == arg_start)
439 error (_("-%s requires an argument"), match->name);
440
441 option_value val;
e0700ba4 442 val.string = new std::string (std::move (str));
3d9be6f5
PA
443 return option_def_and_value {*match, match_ctx, val};
444 }
9d0faba9
PA
445
446 default:
447 /* Not yet. */
557b4d76 448 gdb_assert_not_reached ("option type not supported");
9d0faba9
PA
449 }
450
451 return {};
452}
453
454/* See cli-option.h. */
455
456bool
457complete_options (completion_tracker &tracker,
458 const char **args,
459 process_options_mode mode,
460 gdb::array_view<const option_def_group> options_group)
461{
462 const char *text = *args;
463
464 tracker.set_use_custom_word_point (true);
465
466 const char *delimiter = find_end_options_delimiter (text);
467 bool have_delimiter = delimiter != nullptr;
468
469 if (text[0] == '-' && (!have_delimiter || *delimiter == '\0'))
470 {
471 parse_option_completion_info completion_info {nullptr, tracker};
472
473 while (1)
474 {
475 *args = skip_spaces (*args);
476 completion_info.word = *args;
477
478 if (strcmp (*args, "-") == 0)
479 {
480 complete_on_options (options_group, tracker, *args + 1,
481 completion_info.word);
482 }
483 else if (strcmp (*args, "--") == 0)
484 {
485 tracker.add_completion (make_unique_xstrdup (*args));
486 }
487 else if (**args == '-')
488 {
489 gdb::optional<option_def_and_value> ov
490 = parse_option (options_group, mode, have_delimiter,
491 args, &completion_info);
492 if (!ov && !tracker.have_completions ())
493 {
494 tracker.advance_custom_word_point_by (*args - text);
495 return mode == PROCESS_OPTIONS_REQUIRE_DELIMITER;
496 }
497
498 if (ov
499 && ov->option.type == var_boolean
500 && !ov->value.has_value ())
501 {
502 /* Looked like a boolean option, but we failed to
503 parse the value. If this command requires a
504 delimiter, this value can't be the start of the
505 operands, so return true. Otherwise, if the
506 command doesn't require a delimiter return false
507 so that the caller tries to complete on the
508 operand. */
509 tracker.advance_custom_word_point_by (*args - text);
510 return mode == PROCESS_OPTIONS_REQUIRE_DELIMITER;
511 }
512
513 /* If we parsed an option with an argument, and reached
514 the end of the input string with no trailing space,
515 return true, so that our callers don't try to
516 complete anything by themselves. E.g., this makes it
517 so that with:
518
519 (gdb) frame apply all -limit 10[TAB]
520
521 we don't try to complete on command names. */
522 if (ov
523 && !tracker.have_completions ()
524 && **args == '\0'
525 && *args > text && !isspace ((*args)[-1]))
526 {
527 tracker.advance_custom_word_point_by
528 (*args - text);
529 return true;
530 }
41fc454c
PA
531
532 /* If the caller passed in a context, then it is
533 interested in the option argument values. */
534 if (ov && ov->ctx != nullptr)
535 save_option_value_in_ctx (ov);
9d0faba9
PA
536 }
537 else
538 {
539 tracker.advance_custom_word_point_by
540 (completion_info.word - text);
541
542 /* If the command requires a delimiter, but we haven't
543 seen one, then return true, so that the caller
544 doesn't try to complete on whatever follows options,
545 which for these commands should only be done if
546 there's a delimiter. */
547 if (mode == PROCESS_OPTIONS_REQUIRE_DELIMITER
548 && !have_delimiter)
549 {
550 /* If we reached the end of the input string, then
551 offer all options, since that's all the user can
552 type (plus "--"). */
553 if (completion_info.word[0] == '\0')
554 complete_on_all_options (tracker, options_group);
555 return true;
556 }
557 else
558 return false;
559 }
560
561 if (tracker.have_completions ())
562 {
563 tracker.advance_custom_word_point_by
564 (completion_info.word - text);
565 return true;
566 }
567 }
568 }
569 else if (delimiter != nullptr)
570 {
571 tracker.advance_custom_word_point_by (delimiter - text);
572 *args = delimiter;
573 return false;
574 }
575
576 return false;
577}
578
41fc454c
PA
579/* Save the parsed value in the option's context. */
580
581static void
582save_option_value_in_ctx (gdb::optional<option_def_and_value> &ov)
583{
584 switch (ov->option.type)
585 {
586 case var_boolean:
587 {
588 bool value = ov->value.has_value () ? ov->value->boolean : true;
589 *ov->option.var_address.boolean (ov->option, ov->ctx) = value;
590 }
591 break;
592 case var_uinteger:
593 *ov->option.var_address.uinteger (ov->option, ov->ctx)
594 = ov->value->uinteger;
595 break;
596 case var_zuinteger_unlimited:
597 *ov->option.var_address.integer (ov->option, ov->ctx)
598 = ov->value->integer;
599 break;
600 case var_enum:
601 *ov->option.var_address.enumeration (ov->option, ov->ctx)
602 = ov->value->enumeration;
603 break;
3d9be6f5
PA
604 case var_string:
605 *ov->option.var_address.string (ov->option, ov->ctx)
e0700ba4 606 = std::move (*ov->value->string);
3d9be6f5 607 break;
41fc454c
PA
608 default:
609 gdb_assert_not_reached ("unhandled option type");
610 }
611}
612
9d0faba9
PA
613/* See cli-option.h. */
614
615bool
616process_options (const char **args,
617 process_options_mode mode,
618 gdb::array_view<const option_def_group> options_group)
619{
620 if (*args == nullptr)
621 return false;
622
623 /* If ARGS starts with "-", look for a "--" sequence. If one is
624 found, then interpret everything up until the "--" as
625 'gdb::option'-style command line options. Otherwise, interpret
626 ARGS as possibly the command's operands. */
627 bool have_delimiter = find_end_options_delimiter (*args) != nullptr;
628
629 if (mode == PROCESS_OPTIONS_REQUIRE_DELIMITER && !have_delimiter)
630 return false;
631
632 bool processed_any = false;
633
634 while (1)
635 {
636 *args = skip_spaces (*args);
637
638 auto ov = parse_option (options_group, mode, have_delimiter, args);
639 if (!ov)
640 {
641 if (processed_any)
642 return true;
643 return false;
644 }
645
646 processed_any = true;
647
41fc454c 648 save_option_value_in_ctx (ov);
9d0faba9
PA
649 }
650}
651
652/* Helper for build_help. Return a fragment of a help string showing
653 OPT's possible values. Returns NULL if OPT doesn't take an
654 argument. */
655
656static const char *
657get_val_type_str (const option_def &opt, std::string &buffer)
658{
659 if (!opt.have_argument)
660 return nullptr;
661
662 switch (opt.type)
663 {
664 case var_boolean:
665 return "[on|off]";
666 case var_uinteger:
667 case var_zuinteger_unlimited:
668 return "NUMBER|unlimited";
669 case var_enum:
670 {
671 buffer = "";
672 for (size_t i = 0; opt.enums[i] != nullptr; i++)
673 {
674 if (i != 0)
675 buffer += "|";
676 buffer += opt.enums[i];
677 }
678 return buffer.c_str ();
679 }
3d9be6f5
PA
680 case var_string:
681 return "STRING";
9d0faba9
PA
682 default:
683 return nullptr;
684 }
685}
686
687/* Helper for build_help. Appends an indented version of DOC into
688 HELP. */
689
690static void
691append_indented_doc (const char *doc, std::string &help)
692{
693 const char *p = doc;
694 const char *n = strchr (p, '\n');
695
696 while (n != nullptr)
697 {
698 help += " ";
699 help.append (p, n - p + 1);
700 p = n + 1;
701 n = strchr (p, '\n');
702 }
703 help += " ";
704 help += p;
9d0faba9
PA
705}
706
707/* Fill HELP with an auto-generated "help" string fragment for
708 OPTIONS. */
709
710static void
711build_help_option (gdb::array_view<const option_def> options,
712 std::string &help)
713{
714 std::string buffer;
715
716 for (const auto &o : options)
717 {
718 if (o.set_doc == nullptr)
719 continue;
720
721 help += " -";
722 help += o.name;
723
724 const char *val_type_str = get_val_type_str (o, buffer);
725 if (val_type_str != nullptr)
726 {
727 help += ' ';
728 help += val_type_str;
729 }
730 help += "\n";
731 append_indented_doc (o.set_doc, help);
732 if (o.help_doc != nullptr)
590042fc
PW
733 {
734 help += "\n";
735 append_indented_doc (o.help_doc, help);
736 }
9d0faba9
PA
737 }
738}
739
740/* See cli-option.h. */
741
742std::string
743build_help (const char *help_tmpl,
744 gdb::array_view<const option_def_group> options_group)
745{
590042fc 746 bool need_newlines = false;
9d0faba9
PA
747 std::string help_str;
748
749 const char *p = strstr (help_tmpl, "%OPTIONS%");
750 help_str.assign (help_tmpl, p);
751
752 for (const auto &grp : options_group)
753 for (const auto &opt : grp.options)
590042fc
PW
754 {
755 if (need_newlines)
756 help_str += "\n\n";
757 else
758 need_newlines = true;
759 build_help_option (opt, help_str);
760 }
9d0faba9
PA
761
762 p += strlen ("%OPTIONS%");
763 help_str.append (p);
764
765 return help_str;
766}
767
768/* See cli-option.h. */
769
770void
771add_setshow_cmds_for_options (command_class cmd_class,
772 void *data,
773 gdb::array_view<const option_def> options,
774 struct cmd_list_element **set_list,
775 struct cmd_list_element **show_list)
776{
777 for (const auto &option : options)
778 {
779 if (option.type == var_boolean)
780 {
781 add_setshow_boolean_cmd (option.name, cmd_class,
782 option.var_address.boolean (option, data),
783 option.set_doc, option.show_doc,
784 option.help_doc,
785 nullptr, option.show_cmd_cb,
786 set_list, show_list);
787 }
788 else if (option.type == var_uinteger)
789 {
790 add_setshow_uinteger_cmd (option.name, cmd_class,
791 option.var_address.uinteger (option, data),
792 option.set_doc, option.show_doc,
793 option.help_doc,
794 nullptr, option.show_cmd_cb,
795 set_list, show_list);
796 }
797 else if (option.type == var_zuinteger_unlimited)
798 {
799 add_setshow_zuinteger_unlimited_cmd
800 (option.name, cmd_class,
801 option.var_address.integer (option, data),
802 option.set_doc, option.show_doc,
803 option.help_doc,
804 nullptr, option.show_cmd_cb,
805 set_list, show_list);
806 }
807 else if (option.type == var_enum)
808 {
809 add_setshow_enum_cmd (option.name, cmd_class,
810 option.enums,
811 option.var_address.enumeration (option, data),
812 option.set_doc, option.show_doc,
813 option.help_doc,
814 nullptr, option.show_cmd_cb,
815 set_list, show_list);
816 }
3d9be6f5
PA
817 else if (option.type == var_string)
818 {
819 add_setshow_string_cmd (option.name, cmd_class,
820 option.var_address.string (option, data),
821 option.set_doc, option.show_doc,
822 option.help_doc,
823 nullptr, option.show_cmd_cb,
824 set_list, show_list);
825 }
9d0faba9 826 else
557b4d76 827 gdb_assert_not_reached ("option type not handled");
9d0faba9
PA
828 }
829}
830
831} /* namespace option */
832} /* namespace gdb */