Commit | Line | Data |
---|---|---|
9d0faba9 PA |
1 | /* CLI options framework, for GDB. |
2 | ||
3 | Copyright (C) 2017-2019 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 "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 | ||
28 | namespace gdb { | |
29 | namespace option { | |
30 | ||
31 | /* An option's value. Which field is active depends on the option's | |
32 | type. */ | |
33 | union 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. */ | |
48 | char *string; | |
9d0faba9 PA |
49 | }; |
50 | ||
51 | /* Holds an options definition and its value. */ | |
52 | struct 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) | |
90 | xfree (value->string); | |
91 | } | |
92 | } | |
93 | ||
94 | private: | |
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 |
112 | static void save_option_value_in_ctx (gdb::optional<option_def_and_value> &ov); |
113 | ||
9d0faba9 PA |
114 | /* Info passed around when handling completion. */ |
115 | struct 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 | ||
129 | static const char * | |
130 | find_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 | ||
152 | static void | |
153 | complete_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 | ||
169 | void | |
170 | complete_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 | ||
180 | static gdb::optional<option_def_and_value> | |
181 | parse_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; | |
021d8588 | 442 | val.string = xstrdup (str.c_str ()); |
3d9be6f5 PA |
443 | return option_def_and_value {*match, match_ctx, val}; |
444 | } | |
9d0faba9 PA |
445 | |
446 | default: | |
447 | /* Not yet. */ | |
448 | gdb_assert_not_reached (_("option type not supported")); | |
449 | } | |
450 | ||
451 | return {}; | |
452 | } | |
453 | ||
454 | /* See cli-option.h. */ | |
455 | ||
456 | bool | |
457 | complete_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 | ||
581 | static void | |
582 | save_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) | |
606 | = ov->value->string; | |
607 | ov->value->string = nullptr; | |
608 | break; | |
41fc454c PA |
609 | default: |
610 | gdb_assert_not_reached ("unhandled option type"); | |
611 | } | |
612 | } | |
613 | ||
9d0faba9 PA |
614 | /* See cli-option.h. */ |
615 | ||
616 | bool | |
617 | process_options (const char **args, | |
618 | process_options_mode mode, | |
619 | gdb::array_view<const option_def_group> options_group) | |
620 | { | |
621 | if (*args == nullptr) | |
622 | return false; | |
623 | ||
624 | /* If ARGS starts with "-", look for a "--" sequence. If one is | |
625 | found, then interpret everything up until the "--" as | |
626 | 'gdb::option'-style command line options. Otherwise, interpret | |
627 | ARGS as possibly the command's operands. */ | |
628 | bool have_delimiter = find_end_options_delimiter (*args) != nullptr; | |
629 | ||
630 | if (mode == PROCESS_OPTIONS_REQUIRE_DELIMITER && !have_delimiter) | |
631 | return false; | |
632 | ||
633 | bool processed_any = false; | |
634 | ||
635 | while (1) | |
636 | { | |
637 | *args = skip_spaces (*args); | |
638 | ||
639 | auto ov = parse_option (options_group, mode, have_delimiter, args); | |
640 | if (!ov) | |
641 | { | |
642 | if (processed_any) | |
643 | return true; | |
644 | return false; | |
645 | } | |
646 | ||
647 | processed_any = true; | |
648 | ||
41fc454c | 649 | save_option_value_in_ctx (ov); |
9d0faba9 PA |
650 | } |
651 | } | |
652 | ||
653 | /* Helper for build_help. Return a fragment of a help string showing | |
654 | OPT's possible values. Returns NULL if OPT doesn't take an | |
655 | argument. */ | |
656 | ||
657 | static const char * | |
658 | get_val_type_str (const option_def &opt, std::string &buffer) | |
659 | { | |
660 | if (!opt.have_argument) | |
661 | return nullptr; | |
662 | ||
663 | switch (opt.type) | |
664 | { | |
665 | case var_boolean: | |
666 | return "[on|off]"; | |
667 | case var_uinteger: | |
668 | case var_zuinteger_unlimited: | |
669 | return "NUMBER|unlimited"; | |
670 | case var_enum: | |
671 | { | |
672 | buffer = ""; | |
673 | for (size_t i = 0; opt.enums[i] != nullptr; i++) | |
674 | { | |
675 | if (i != 0) | |
676 | buffer += "|"; | |
677 | buffer += opt.enums[i]; | |
678 | } | |
679 | return buffer.c_str (); | |
680 | } | |
3d9be6f5 PA |
681 | case var_string: |
682 | return "STRING"; | |
9d0faba9 PA |
683 | default: |
684 | return nullptr; | |
685 | } | |
686 | } | |
687 | ||
688 | /* Helper for build_help. Appends an indented version of DOC into | |
689 | HELP. */ | |
690 | ||
691 | static void | |
692 | append_indented_doc (const char *doc, std::string &help) | |
693 | { | |
694 | const char *p = doc; | |
695 | const char *n = strchr (p, '\n'); | |
696 | ||
697 | while (n != nullptr) | |
698 | { | |
699 | help += " "; | |
700 | help.append (p, n - p + 1); | |
701 | p = n + 1; | |
702 | n = strchr (p, '\n'); | |
703 | } | |
704 | help += " "; | |
705 | help += p; | |
9d0faba9 PA |
706 | } |
707 | ||
708 | /* Fill HELP with an auto-generated "help" string fragment for | |
709 | OPTIONS. */ | |
710 | ||
711 | static void | |
712 | build_help_option (gdb::array_view<const option_def> options, | |
713 | std::string &help) | |
714 | { | |
715 | std::string buffer; | |
716 | ||
717 | for (const auto &o : options) | |
718 | { | |
719 | if (o.set_doc == nullptr) | |
720 | continue; | |
721 | ||
722 | help += " -"; | |
723 | help += o.name; | |
724 | ||
725 | const char *val_type_str = get_val_type_str (o, buffer); | |
726 | if (val_type_str != nullptr) | |
727 | { | |
728 | help += ' '; | |
729 | help += val_type_str; | |
730 | } | |
731 | help += "\n"; | |
732 | append_indented_doc (o.set_doc, help); | |
733 | if (o.help_doc != nullptr) | |
590042fc PW |
734 | { |
735 | help += "\n"; | |
736 | append_indented_doc (o.help_doc, help); | |
737 | } | |
9d0faba9 PA |
738 | } |
739 | } | |
740 | ||
741 | /* See cli-option.h. */ | |
742 | ||
743 | std::string | |
744 | build_help (const char *help_tmpl, | |
745 | gdb::array_view<const option_def_group> options_group) | |
746 | { | |
590042fc | 747 | bool need_newlines = false; |
9d0faba9 PA |
748 | std::string help_str; |
749 | ||
750 | const char *p = strstr (help_tmpl, "%OPTIONS%"); | |
751 | help_str.assign (help_tmpl, p); | |
752 | ||
753 | for (const auto &grp : options_group) | |
754 | for (const auto &opt : grp.options) | |
590042fc PW |
755 | { |
756 | if (need_newlines) | |
757 | help_str += "\n\n"; | |
758 | else | |
759 | need_newlines = true; | |
760 | build_help_option (opt, help_str); | |
761 | } | |
9d0faba9 PA |
762 | |
763 | p += strlen ("%OPTIONS%"); | |
764 | help_str.append (p); | |
765 | ||
766 | return help_str; | |
767 | } | |
768 | ||
769 | /* See cli-option.h. */ | |
770 | ||
771 | void | |
772 | add_setshow_cmds_for_options (command_class cmd_class, | |
773 | void *data, | |
774 | gdb::array_view<const option_def> options, | |
775 | struct cmd_list_element **set_list, | |
776 | struct cmd_list_element **show_list) | |
777 | { | |
778 | for (const auto &option : options) | |
779 | { | |
780 | if (option.type == var_boolean) | |
781 | { | |
782 | add_setshow_boolean_cmd (option.name, cmd_class, | |
783 | option.var_address.boolean (option, data), | |
784 | option.set_doc, option.show_doc, | |
785 | option.help_doc, | |
786 | nullptr, option.show_cmd_cb, | |
787 | set_list, show_list); | |
788 | } | |
789 | else if (option.type == var_uinteger) | |
790 | { | |
791 | add_setshow_uinteger_cmd (option.name, cmd_class, | |
792 | option.var_address.uinteger (option, data), | |
793 | option.set_doc, option.show_doc, | |
794 | option.help_doc, | |
795 | nullptr, option.show_cmd_cb, | |
796 | set_list, show_list); | |
797 | } | |
798 | else if (option.type == var_zuinteger_unlimited) | |
799 | { | |
800 | add_setshow_zuinteger_unlimited_cmd | |
801 | (option.name, cmd_class, | |
802 | option.var_address.integer (option, data), | |
803 | option.set_doc, option.show_doc, | |
804 | option.help_doc, | |
805 | nullptr, option.show_cmd_cb, | |
806 | set_list, show_list); | |
807 | } | |
808 | else if (option.type == var_enum) | |
809 | { | |
810 | add_setshow_enum_cmd (option.name, cmd_class, | |
811 | option.enums, | |
812 | option.var_address.enumeration (option, data), | |
813 | option.set_doc, option.show_doc, | |
814 | option.help_doc, | |
815 | nullptr, option.show_cmd_cb, | |
816 | set_list, show_list); | |
817 | } | |
3d9be6f5 PA |
818 | else if (option.type == var_string) |
819 | { | |
820 | add_setshow_string_cmd (option.name, cmd_class, | |
821 | option.var_address.string (option, data), | |
822 | option.set_doc, option.show_doc, | |
823 | option.help_doc, | |
824 | nullptr, option.show_cmd_cb, | |
825 | set_list, show_list); | |
826 | } | |
9d0faba9 PA |
827 | else |
828 | gdb_assert_not_reached (_("option type not handled")); | |
829 | } | |
830 | } | |
831 | ||
832 | } /* namespace option */ | |
833 | } /* namespace gdb */ |