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