| /* Handle lists of commands, their decoding and documentation, for GDB. | |
| Copyright (C) 1986-2024 Free Software Foundation, Inc. | |
| This program is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
| #include "symtab.h" | |
| #include <ctype.h> | |
| #include "gdbsupport/gdb_regex.h" | |
| #include "completer.h" | |
| #include "ui-out.h" | |
| #include "cli/cli-cmds.h" | |
| #include "cli/cli-decode.h" | |
| #include "cli/cli-style.h" | |
| #include "cli/cli-utils.h" | |
| #include <optional> | |
| /* Prototypes for local functions. */ | |
| static void undef_cmd_error (const char *, const char *); | |
| static cmd_list_element::aliases_list_type delete_cmd | |
| (const char *name, cmd_list_element **list, cmd_list_element **prehook, | |
| cmd_list_element **prehookee, cmd_list_element **posthook, | |
| cmd_list_element **posthookee); | |
| static struct cmd_list_element *find_cmd (const char *command, | |
| int len, | |
| struct cmd_list_element *clist, | |
| int ignore_help_classes, | |
| int *nfound); | |
| static void help_cmd_list (struct cmd_list_element *list, | |
| enum command_class theclass, | |
| bool recurse, | |
| struct ui_file *stream); | |
| static void help_all (struct ui_file *stream); | |
| static int lookup_cmd_composition_1 (const char *text, | |
| struct cmd_list_element **alias, | |
| struct cmd_list_element **prefix_cmd, | |
| struct cmd_list_element **cmd, | |
| struct cmd_list_element *cur_list); | |
| /* Look up a command whose 'subcommands' field is SUBCOMMANDS. Return the | |
| command if found, otherwise return NULL. */ | |
| static struct cmd_list_element * | |
| lookup_cmd_with_subcommands (cmd_list_element **subcommands, | |
| cmd_list_element *list) | |
| { | |
| struct cmd_list_element *p = NULL; | |
| for (p = list; p != NULL; p = p->next) | |
| { | |
| struct cmd_list_element *q; | |
| if (!p->is_prefix ()) | |
| continue; | |
| else if (p->subcommands == subcommands) | |
| { | |
| /* If we found an alias, we must return the aliased | |
| command. */ | |
| return p->is_alias () ? p->alias_target : p; | |
| } | |
| q = lookup_cmd_with_subcommands (subcommands, *(p->subcommands)); | |
| if (q != NULL) | |
| return q; | |
| } | |
| return NULL; | |
| } | |
| static void | |
| print_help_for_command (const cmd_list_element &c, | |
| bool recurse, struct ui_file *stream); | |
| static void | |
| do_simple_func (const char *args, int from_tty, cmd_list_element *c) | |
| { | |
| c->function.simple_func (args, from_tty); | |
| } | |
| static void | |
| set_cmd_simple_func (struct cmd_list_element *cmd, cmd_simple_func_ftype *simple_func) | |
| { | |
| if (simple_func == NULL) | |
| cmd->func = NULL; | |
| else | |
| cmd->func = do_simple_func; | |
| cmd->function.simple_func = simple_func; | |
| } | |
| int | |
| cmd_simple_func_eq (struct cmd_list_element *cmd, cmd_simple_func_ftype *simple_func) | |
| { | |
| return (cmd->func == do_simple_func | |
| && cmd->function.simple_func == simple_func); | |
| } | |
| void | |
| set_cmd_completer (struct cmd_list_element *cmd, completer_ftype *completer) | |
| { | |
| cmd->completer = completer; /* Ok. */ | |
| } | |
| /* See definition in commands.h. */ | |
| void | |
| set_cmd_completer_handle_brkchars (struct cmd_list_element *cmd, | |
| completer_handle_brkchars_ftype *func) | |
| { | |
| cmd->completer_handle_brkchars = func; | |
| } | |
| /* See cli-decode.h. */ | |
| std::string | |
| cmd_list_element::prefixname_no_space () const | |
| { | |
| if (!this->is_prefix ()) | |
| /* Not a prefix command. */ | |
| return ""; | |
| std::string prefixname; | |
| if (this->prefix != nullptr) | |
| { | |
| prefixname = this->prefix->prefixname_no_space (); | |
| prefixname += " "; | |
| } | |
| prefixname += this->name; | |
| return prefixname; | |
| } | |
| /* See cli-decode.h. */ | |
| std::string | |
| cmd_list_element::prefixname () const | |
| { | |
| std::string result = prefixname_no_space (); | |
| if (!result.empty ()) | |
| result += " "; | |
| return result; | |
| } | |
| /* See cli/cli-decode.h. */ | |
| std::vector<std::string> | |
| cmd_list_element::command_components () const | |
| { | |
| std::vector<std::string> result; | |
| if (this->prefix != nullptr) | |
| result = this->prefix->command_components (); | |
| result.emplace_back (this->name); | |
| return result; | |
| } | |
| /* Add element named NAME. | |
| Space for NAME and DOC must be allocated by the caller. | |
| THECLASS is the top level category into which commands are broken down | |
| for "help" purposes. | |
| FUN should be the function to execute the command; | |
| it will get a character string as argument, with leading | |
| and trailing blanks already eliminated. | |
| DOC is a documentation string for the command. | |
| Its first line should be a complete sentence. | |
| It should start with ? for a command that is an abbreviation | |
| or with * for a command that most users don't need to know about. | |
| Add this command to command list *LIST. | |
| Returns a pointer to the added command (not necessarily the head | |
| of *LIST). */ | |
| static struct cmd_list_element * | |
| do_add_cmd (const char *name, enum command_class theclass, | |
| const char *doc, struct cmd_list_element **list) | |
| { | |
| struct cmd_list_element *c = new struct cmd_list_element (name, theclass, | |
| doc); | |
| /* Turn each alias of the old command into an alias of the new | |
| command. */ | |
| c->aliases = delete_cmd (name, list, &c->hook_pre, &c->hookee_pre, | |
| &c->hook_post, &c->hookee_post); | |
| for (cmd_list_element &alias : c->aliases) | |
| alias.alias_target = c; | |
| if (c->hook_pre) | |
| c->hook_pre->hookee_pre = c; | |
| if (c->hookee_pre) | |
| c->hookee_pre->hook_pre = c; | |
| if (c->hook_post) | |
| c->hook_post->hookee_post = c; | |
| if (c->hookee_post) | |
| c->hookee_post->hook_post = c; | |
| if (*list == NULL || strcmp ((*list)->name, name) >= 0) | |
| { | |
| c->next = *list; | |
| *list = c; | |
| } | |
| else | |
| { | |
| cmd_list_element *p = *list; | |
| while (p->next && strcmp (p->next->name, name) <= 0) | |
| { | |
| p = p->next; | |
| } | |
| c->next = p->next; | |
| p->next = c; | |
| } | |
| /* Search the prefix cmd of C, and assigns it to C->prefix. | |
| See also add_prefix_cmd and update_prefix_field_of_prefixed_commands. */ | |
| cmd_list_element *prefixcmd = lookup_cmd_with_subcommands (list, cmdlist); | |
| c->prefix = prefixcmd; | |
| return c; | |
| } | |
| struct cmd_list_element * | |
| add_cmd (const char *name, enum command_class theclass, | |
| const char *doc, struct cmd_list_element **list) | |
| { | |
| cmd_list_element *result = do_add_cmd (name, theclass, doc, list); | |
| result->func = NULL; | |
| result->function.simple_func = NULL; | |
| return result; | |
| } | |
| struct cmd_list_element * | |
| add_cmd (const char *name, enum command_class theclass, | |
| cmd_simple_func_ftype *fun, | |
| const char *doc, struct cmd_list_element **list) | |
| { | |
| cmd_list_element *result = do_add_cmd (name, theclass, doc, list); | |
| set_cmd_simple_func (result, fun); | |
| return result; | |
| } | |
| /* Add an element with a suppress notification to the LIST of commands. */ | |
| struct cmd_list_element * | |
| add_cmd_suppress_notification (const char *name, enum command_class theclass, | |
| cmd_simple_func_ftype *fun, const char *doc, | |
| struct cmd_list_element **list, | |
| bool *suppress_notification) | |
| { | |
| struct cmd_list_element *element; | |
| element = add_cmd (name, theclass, fun, doc, list); | |
| element->suppress_notification = suppress_notification; | |
| return element; | |
| } | |
| /* Deprecates a command CMD. | |
| REPLACEMENT is the name of the command which should be used in | |
| place of this command, or NULL if no such command exists. | |
| This function does not check to see if command REPLACEMENT exists | |
| since gdb may not have gotten around to adding REPLACEMENT when | |
| this function is called. | |
| Returns a pointer to the deprecated command. */ | |
| struct cmd_list_element * | |
| deprecate_cmd (struct cmd_list_element *cmd, const char *replacement) | |
| { | |
| cmd->cmd_deprecated = 1; | |
| cmd->deprecated_warn_user = 1; | |
| if (replacement != NULL) | |
| cmd->replacement = replacement; | |
| else | |
| cmd->replacement = NULL; | |
| return cmd; | |
| } | |
| struct cmd_list_element * | |
| add_alias_cmd (const char *name, cmd_list_element *target, | |
| enum command_class theclass, int abbrev_flag, | |
| struct cmd_list_element **list) | |
| { | |
| gdb_assert (target != nullptr); | |
| struct cmd_list_element *c = add_cmd (name, theclass, target->doc, list); | |
| /* If TARGET->DOC can be freed, we should make another copy. */ | |
| if (target->doc_allocated) | |
| { | |
| c->doc = xstrdup (target->doc); | |
| c->doc_allocated = 1; | |
| } | |
| /* NOTE: Both FUNC and all the FUNCTIONs need to be copied. */ | |
| c->func = target->func; | |
| c->function = target->function; | |
| c->subcommands = target->subcommands; | |
| c->allow_unknown = target->allow_unknown; | |
| c->abbrev_flag = abbrev_flag; | |
| c->alias_target = target; | |
| target->aliases.push_front (*c); | |
| return c; | |
| } | |
| /* Update the prefix field of all sub-commands of the prefix command C. | |
| We must do this when a prefix command is defined as the GDB init sequence | |
| does not guarantee that a prefix command is created before its sub-commands. | |
| For example, break-catch-sig.c initialization runs before breakpoint.c | |
| initialization, but it is breakpoint.c that creates the "catch" command used | |
| by the "catch signal" command created by break-catch-sig.c. */ | |
| static void | |
| update_prefix_field_of_prefixed_commands (struct cmd_list_element *c) | |
| { | |
| for (cmd_list_element *p = *c->subcommands; p != NULL; p = p->next) | |
| { | |
| p->prefix = c; | |
| /* We must recursively update the prefix field to cover | |
| e.g. 'info auto-load libthread-db' where the creation | |
| order was: | |
| libthread-db | |
| auto-load | |
| info | |
| In such a case, when 'auto-load' was created by do_add_cmd, | |
| the 'libthread-db' prefix field could not be updated, as the | |
| 'auto-load' command was not yet reachable by | |
| lookup_cmd_for_subcommands (list, cmdlist) | |
| that searches from the top level 'cmdlist'. */ | |
| if (p->is_prefix ()) | |
| update_prefix_field_of_prefixed_commands (p); | |
| } | |
| } | |
| /* Like add_cmd but adds an element for a command prefix: a name that | |
| should be followed by a subcommand to be looked up in another | |
| command list. SUBCOMMANDS should be the address of the variable | |
| containing that list. */ | |
| struct cmd_list_element * | |
| add_prefix_cmd (const char *name, enum command_class theclass, | |
| cmd_simple_func_ftype *fun, | |
| const char *doc, struct cmd_list_element **subcommands, | |
| int allow_unknown, struct cmd_list_element **list) | |
| { | |
| struct cmd_list_element *c = add_cmd (name, theclass, fun, doc, list); | |
| c->subcommands = subcommands; | |
| c->allow_unknown = allow_unknown; | |
| /* Now that prefix command C is defined, we need to set the prefix field | |
| of all prefixed commands that were defined before C itself was defined. */ | |
| update_prefix_field_of_prefixed_commands (c); | |
| return c; | |
| } | |
| /* A helper function for add_basic_prefix_cmd. This is a command | |
| function that just forwards to help_list. */ | |
| static void | |
| do_prefix_cmd (const char *args, int from_tty, struct cmd_list_element *c) | |
| { | |
| /* Look past all aliases. */ | |
| while (c->is_alias ()) | |
| c = c->alias_target; | |
| help_list (*c->subcommands, c->prefixname_no_space ().c_str (), | |
| all_commands, gdb_stdout); | |
| } | |
| /* See command.h. */ | |
| struct cmd_list_element * | |
| add_basic_prefix_cmd (const char *name, enum command_class theclass, | |
| const char *doc, struct cmd_list_element **subcommands, | |
| int allow_unknown, struct cmd_list_element **list) | |
| { | |
| struct cmd_list_element *cmd = add_prefix_cmd (name, theclass, nullptr, | |
| doc, subcommands, | |
| allow_unknown, list); | |
| cmd->func = do_prefix_cmd; | |
| return cmd; | |
| } | |
| /* A helper function for add_show_prefix_cmd. This is a command | |
| function that just forwards to cmd_show_list. */ | |
| static void | |
| do_show_prefix_cmd (const char *args, int from_tty, struct cmd_list_element *c) | |
| { | |
| cmd_show_list (*c->subcommands, from_tty); | |
| } | |
| /* See command.h. */ | |
| struct cmd_list_element * | |
| add_show_prefix_cmd (const char *name, enum command_class theclass, | |
| const char *doc, struct cmd_list_element **subcommands, | |
| int allow_unknown, struct cmd_list_element **list) | |
| { | |
| struct cmd_list_element *cmd = add_prefix_cmd (name, theclass, nullptr, | |
| doc, subcommands, | |
| allow_unknown, list); | |
| cmd->func = do_show_prefix_cmd; | |
| return cmd; | |
| } | |
| /* See command.h. */ | |
| set_show_commands | |
| add_setshow_prefix_cmd (const char *name, command_class theclass, | |
| const char *set_doc, const char *show_doc, | |
| cmd_list_element **set_subcommands_list, | |
| cmd_list_element **show_subcommands_list, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| set_show_commands cmds; | |
| cmds.set = add_basic_prefix_cmd (name, theclass, set_doc, | |
| set_subcommands_list, 0, | |
| set_list); | |
| cmds.show = add_show_prefix_cmd (name, theclass, show_doc, | |
| show_subcommands_list, 0, | |
| show_list); | |
| return cmds; | |
| } | |
| /* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the | |
| new command list element. */ | |
| struct cmd_list_element * | |
| add_prefix_cmd_suppress_notification | |
| (const char *name, enum command_class theclass, | |
| cmd_simple_func_ftype *fun, | |
| const char *doc, struct cmd_list_element **subcommands, | |
| int allow_unknown, struct cmd_list_element **list, | |
| bool *suppress_notification) | |
| { | |
| struct cmd_list_element *element | |
| = add_prefix_cmd (name, theclass, fun, doc, subcommands, | |
| allow_unknown, list); | |
| element->suppress_notification = suppress_notification; | |
| return element; | |
| } | |
| /* Like add_prefix_cmd but sets the abbrev_flag on the new command. */ | |
| struct cmd_list_element * | |
| add_abbrev_prefix_cmd (const char *name, enum command_class theclass, | |
| cmd_simple_func_ftype *fun, const char *doc, | |
| struct cmd_list_element **subcommands, | |
| int allow_unknown, struct cmd_list_element **list) | |
| { | |
| struct cmd_list_element *c = add_cmd (name, theclass, fun, doc, list); | |
| c->subcommands = subcommands; | |
| c->allow_unknown = allow_unknown; | |
| c->abbrev_flag = 1; | |
| return c; | |
| } | |
| /* This is an empty "simple func". */ | |
| void | |
| not_just_help_class_command (const char *args, int from_tty) | |
| { | |
| } | |
| /* This is an empty cmd func. */ | |
| static void | |
| empty_func (const char *args, int from_tty, cmd_list_element *c) | |
| { | |
| } | |
| /* Add element named NAME to command list LIST (the list for set/show | |
| or some sublist thereof). | |
| TYPE is set_cmd or show_cmd. | |
| THECLASS is as in add_cmd. | |
| VAR_TYPE is the kind of thing we are setting. | |
| EXTRA_LITERALS if non-NULL define extra literals to be accepted in lieu of | |
| a number for integer variables. | |
| ARGS is a pre-validated type-erased reference to the variable being | |
| controlled by this command. | |
| DOC is the documentation string. */ | |
| static struct cmd_list_element * | |
| add_set_or_show_cmd (const char *name, | |
| enum cmd_types type, | |
| enum command_class theclass, | |
| var_types var_type, | |
| const literal_def *extra_literals, | |
| const setting::erased_args &arg, | |
| const char *doc, | |
| struct cmd_list_element **list) | |
| { | |
| struct cmd_list_element *c = add_cmd (name, theclass, doc, list); | |
| gdb_assert (type == set_cmd || type == show_cmd); | |
| c->type = type; | |
| c->var.emplace (var_type, extra_literals, arg); | |
| /* This needs to be something besides NULL so that this isn't | |
| treated as a help class. */ | |
| c->func = empty_func; | |
| return c; | |
| } | |
| /* Add element named NAME to both command lists SET_LIST and SHOW_LIST. | |
| THECLASS is as in add_cmd. VAR_TYPE is the kind of thing we are | |
| setting. EXTRA_LITERALS if non-NULL define extra literals to be | |
| accepted in lieu of a number for integer variables. ARGS is a | |
| pre-validated type-erased reference to the variable being controlled | |
| by this command. SET_FUNC and SHOW_FUNC are the callback functions | |
| (if non-NULL). SET_DOC, SHOW_DOC and HELP_DOC are the documentation | |
| strings. | |
| Return the newly created set and show commands. */ | |
| static set_show_commands | |
| add_setshow_cmd_full_erased (const char *name, | |
| enum command_class theclass, | |
| var_types var_type, | |
| const literal_def *extra_literals, | |
| const setting::erased_args &args, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| struct cmd_list_element *set; | |
| struct cmd_list_element *show; | |
| gdb::unique_xmalloc_ptr<char> full_set_doc; | |
| gdb::unique_xmalloc_ptr<char> full_show_doc; | |
| if (help_doc != NULL) | |
| { | |
| full_set_doc = xstrprintf ("%s\n%s", set_doc, help_doc); | |
| full_show_doc = xstrprintf ("%s\n%s", show_doc, help_doc); | |
| } | |
| else | |
| { | |
| full_set_doc = make_unique_xstrdup (set_doc); | |
| full_show_doc = make_unique_xstrdup (show_doc); | |
| } | |
| set = add_set_or_show_cmd (name, set_cmd, theclass, var_type, | |
| extra_literals, args, | |
| full_set_doc.release (), set_list); | |
| set->doc_allocated = 1; | |
| if (set_func != NULL) | |
| set->func = set_func; | |
| show = add_set_or_show_cmd (name, show_cmd, theclass, var_type, | |
| extra_literals, args, | |
| full_show_doc.release (), show_list); | |
| show->doc_allocated = 1; | |
| show->show_value_func = show_func; | |
| /* Disable the default symbol completer. Doesn't make much sense | |
| for the "show" command to complete on anything. */ | |
| set_cmd_completer (show, nullptr); | |
| return {set, show}; | |
| } | |
| /* Completes on integer commands that support extra literals. */ | |
| static void | |
| integer_literals_completer (struct cmd_list_element *c, | |
| completion_tracker &tracker, | |
| const char *text, const char *word) | |
| { | |
| const literal_def *extra_literals = c->var->extra_literals (); | |
| if (*text == '\0') | |
| { | |
| tracker.add_completion (make_unique_xstrdup ("NUMBER")); | |
| for (const literal_def *l = extra_literals; | |
| l->literal != nullptr; | |
| l++) | |
| tracker.add_completion (make_unique_xstrdup (l->literal)); | |
| } | |
| else | |
| for (const literal_def *l = extra_literals; | |
| l->literal != nullptr; | |
| l++) | |
| if (startswith (l->literal, text)) | |
| tracker.add_completion (make_unique_xstrdup (l->literal)); | |
| } | |
| /* Add element named NAME to both command lists SET_LIST and SHOW_LIST. | |
| THECLASS is as in add_cmd. VAR_TYPE is the kind of thing we are | |
| setting. VAR is address of the variable being controlled by this | |
| command. EXTRA_LITERALS if non-NULL define extra literals to be | |
| accepted in lieu of a number for integer variables. If nullptr is | |
| given as VAR, then both SET_SETTING_FUNC and GET_SETTING_FUNC must | |
| be provided. SET_SETTING_FUNC and GET_SETTING_FUNC are callbacks | |
| used to access and modify the underlying property, whatever its | |
| storage is. SET_FUNC and SHOW_FUNC are the callback functions | |
| (if non-NULL). SET_DOC, SHOW_DOC and HELP_DOC are the | |
| documentation strings. | |
| Return the newly created set and show commands. */ | |
| template<typename T> | |
| static set_show_commands | |
| add_setshow_cmd_full (const char *name, | |
| enum command_class theclass, | |
| var_types var_type, T *var, | |
| const literal_def *extra_literals, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| typename setting_func_types<T>::set set_setting_func, | |
| typename setting_func_types<T>::get get_setting_func, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| auto erased_args | |
| = setting::erase_args (var_type, var, | |
| set_setting_func, get_setting_func); | |
| auto cmds = add_setshow_cmd_full_erased (name, | |
| theclass, | |
| var_type, extra_literals, | |
| erased_args, | |
| set_doc, show_doc, | |
| help_doc, | |
| set_func, | |
| show_func, | |
| set_list, | |
| show_list); | |
| if (extra_literals != nullptr) | |
| set_cmd_completer (cmds.set, integer_literals_completer); | |
| return cmds; | |
| } | |
| /* Same as above but omitting EXTRA_LITERALS. */ | |
| template<typename T> | |
| static set_show_commands | |
| add_setshow_cmd_full (const char *name, | |
| enum command_class theclass, | |
| var_types var_type, T *var, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| typename setting_func_types<T>::set set_setting_func, | |
| typename setting_func_types<T>::get get_setting_func, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| return add_setshow_cmd_full (name, theclass, var_type, var, nullptr, | |
| set_doc, show_doc, help_doc, | |
| set_setting_func, get_setting_func, | |
| set_func, show_func, set_list, show_list); | |
| } | |
| /* Add element named NAME to command list LIST (the list for set or | |
| some sublist thereof). THECLASS is as in add_cmd. ENUMLIST is a list | |
| of strings which may follow NAME. VAR is address of the variable | |
| which will contain the matching string (from ENUMLIST). */ | |
| set_show_commands | |
| add_setshow_enum_cmd (const char *name, | |
| enum command_class theclass, | |
| const char *const *enumlist, | |
| const char **var, | |
| const char *set_doc, | |
| const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| /* We require *VAR to be initialized before this call, and | |
| furthermore it must be == to one of the values in ENUMLIST. */ | |
| gdb_assert (var != nullptr && *var != nullptr); | |
| for (int i = 0; ; ++i) | |
| { | |
| gdb_assert (enumlist[i] != nullptr); | |
| if (*var == enumlist[i]) | |
| break; | |
| } | |
| set_show_commands commands | |
| = add_setshow_cmd_full<const char *> (name, theclass, var_enum, var, | |
| set_doc, show_doc, help_doc, | |
| nullptr, nullptr, set_func, | |
| show_func, set_list, show_list); | |
| commands.set->enums = enumlist; | |
| return commands; | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_enum_cmd (const char *name, command_class theclass, | |
| const char *const *enumlist, const char *set_doc, | |
| const char *show_doc, const char *help_doc, | |
| setting_func_types<const char *>::set set_func, | |
| setting_func_types<const char *>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| auto cmds = add_setshow_cmd_full<const char *> (name, theclass, var_enum, | |
| nullptr, set_doc, show_doc, | |
| help_doc, set_func, get_func, | |
| nullptr, show_func, set_list, | |
| show_list); | |
| cmds.set->enums = enumlist; | |
| return cmds; | |
| } | |
| /* See cli-decode.h. */ | |
| void | |
| complete_on_color (completion_tracker &tracker, | |
| const char *text, const char *word) | |
| { | |
| complete_on_enum (tracker, ui_file_style::basic_color_enums.data (), | |
| text, word); | |
| if (*text == '\0') | |
| { | |
| /* Convenience to let the user know what the option | |
| can accept. Note there's no common prefix between | |
| the strings on purpose, so that complete_on_enum doesn't do | |
| a partial match. */ | |
| tracker.add_completion (make_unique_xstrdup ("NUMBER")); | |
| tracker.add_completion (make_unique_xstrdup ("#RRGGBB")); | |
| } | |
| } | |
| /* Completer used in color commands. */ | |
| static void | |
| color_completer (struct cmd_list_element *ignore, | |
| completion_tracker &tracker, | |
| const char *text, const char *word) | |
| { | |
| complete_on_color (tracker, text, word); | |
| } | |
| /* Add element named NAME to command list LIST (the list for set or | |
| some sublist thereof). CLASS is as in add_cmd. VAR is address | |
| of the variable which will contain the color. */ | |
| set_show_commands | |
| add_setshow_color_cmd (const char *name, | |
| enum command_class theclass, | |
| ui_file_style::color *var, | |
| const char *set_doc, | |
| const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| set_show_commands commands = add_setshow_cmd_full<ui_file_style::color> | |
| (name, theclass, var_color, var, | |
| set_doc, show_doc, help_doc, | |
| nullptr, nullptr, set_func, show_func, | |
| set_list, show_list); | |
| set_cmd_completer (commands.set, color_completer); | |
| return commands; | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_color_cmd (const char *name, command_class theclass, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<ui_file_style::color>::set set_func, | |
| setting_func_types<ui_file_style::color>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| auto cmds = add_setshow_cmd_full<ui_file_style::color> | |
| (name, theclass, var_color, nullptr, | |
| set_doc, show_doc, help_doc, | |
| set_func, get_func, nullptr, show_func, | |
| set_list, show_list); | |
| set_cmd_completer (cmds.set, color_completer); | |
| return cmds; | |
| } | |
| /* See cli-decode.h. */ | |
| const char * const auto_boolean_enums[] = { "on", "off", "auto", NULL }; | |
| /* Add an auto-boolean command named NAME to both the set and show | |
| command list lists. THECLASS is as in add_cmd. VAR is address of the | |
| variable which will contain the value. DOC is the documentation | |
| string. FUNC is the corresponding callback. */ | |
| set_show_commands | |
| add_setshow_auto_boolean_cmd (const char *name, | |
| enum command_class theclass, | |
| enum auto_boolean *var, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| set_show_commands commands | |
| = add_setshow_cmd_full<enum auto_boolean> (name, theclass, var_auto_boolean, | |
| var, set_doc, show_doc, help_doc, | |
| nullptr, nullptr, set_func, | |
| show_func, set_list, show_list); | |
| commands.set->enums = auto_boolean_enums; | |
| return commands; | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_auto_boolean_cmd (const char *name, command_class theclass, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<enum auto_boolean>::set set_func, | |
| setting_func_types<enum auto_boolean>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| auto cmds = add_setshow_cmd_full<enum auto_boolean> (name, theclass, | |
| var_auto_boolean, | |
| nullptr, set_doc, | |
| show_doc, help_doc, | |
| set_func, get_func, | |
| nullptr, show_func, | |
| set_list, show_list); | |
| cmds.set->enums = auto_boolean_enums; | |
| return cmds; | |
| } | |
| /* See cli-decode.h. */ | |
| const char * const boolean_enums[] = { "on", "off", NULL }; | |
| /* Add element named NAME to both the set and show command LISTs (the | |
| list for set/show or some sublist thereof). THECLASS is as in | |
| add_cmd. VAR is address of the variable which will contain the | |
| value. SET_DOC and SHOW_DOC are the documentation strings. | |
| Returns the new command element. */ | |
| set_show_commands | |
| add_setshow_boolean_cmd (const char *name, enum command_class theclass, bool *var, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| set_show_commands commands | |
| = add_setshow_cmd_full<bool> (name, theclass, var_boolean, var, | |
| set_doc, show_doc, help_doc, | |
| nullptr, nullptr, set_func, show_func, | |
| set_list, show_list); | |
| commands.set->enums = boolean_enums; | |
| return commands; | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_boolean_cmd (const char *name, command_class theclass, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<bool>::set set_func, | |
| setting_func_types<bool>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| auto cmds = add_setshow_cmd_full<bool> (name, theclass, var_boolean, nullptr, | |
| set_doc, show_doc, help_doc, | |
| set_func, get_func, nullptr, | |
| show_func, set_list, show_list); | |
| cmds.set->enums = boolean_enums; | |
| return cmds; | |
| } | |
| /* Add element named NAME to both the set and show command LISTs (the | |
| list for set/show or some sublist thereof). */ | |
| set_show_commands | |
| add_setshow_filename_cmd (const char *name, enum command_class theclass, | |
| std::string *var, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| set_show_commands commands | |
| = add_setshow_cmd_full<std::string> (name, theclass, var_filename, var, | |
| set_doc, show_doc, help_doc, | |
| nullptr, nullptr, set_func, | |
| show_func, set_list, show_list); | |
| set_cmd_completer (commands.set, deprecated_filename_completer); | |
| return commands; | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_filename_cmd (const char *name, command_class theclass, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<std::string>::set set_func, | |
| setting_func_types<std::string>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| auto cmds = add_setshow_cmd_full<std::string> (name, theclass, var_filename, | |
| nullptr, set_doc, show_doc, | |
| help_doc, set_func, get_func, | |
| nullptr, show_func, set_list, | |
| show_list); | |
| set_cmd_completer (cmds.set, deprecated_filename_completer); | |
| return cmds; | |
| } | |
| /* Add element named NAME to both the set and show command LISTs (the | |
| list for set/show or some sublist thereof). */ | |
| set_show_commands | |
| add_setshow_string_cmd (const char *name, enum command_class theclass, | |
| std::string *var, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| set_show_commands commands | |
| = add_setshow_cmd_full<std::string> (name, theclass, var_string, var, | |
| set_doc, show_doc, help_doc, | |
| nullptr, nullptr, set_func, | |
| show_func, set_list, show_list); | |
| /* Disable the default symbol completer. */ | |
| set_cmd_completer (commands.set, nullptr); | |
| return commands; | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_string_cmd (const char *name, command_class theclass, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<std::string>::set set_func, | |
| setting_func_types<std::string>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| auto cmds = add_setshow_cmd_full<std::string> (name, theclass, var_string, | |
| nullptr, set_doc, show_doc, | |
| help_doc, set_func, get_func, | |
| nullptr, show_func, set_list, | |
| show_list); | |
| /* Disable the default symbol completer. */ | |
| set_cmd_completer (cmds.set, nullptr); | |
| return cmds; | |
| } | |
| /* Add element named NAME to both the set and show command LISTs (the | |
| list for set/show or some sublist thereof). */ | |
| set_show_commands | |
| add_setshow_string_noescape_cmd (const char *name, enum command_class theclass, | |
| std::string *var, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| set_show_commands commands | |
| = add_setshow_cmd_full<std::string> (name, theclass, var_string_noescape, | |
| var, set_doc, show_doc, help_doc, | |
| nullptr, nullptr, set_func, show_func, | |
| set_list, show_list); | |
| /* Disable the default symbol completer. */ | |
| set_cmd_completer (commands.set, nullptr); | |
| return commands; | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_string_noescape_cmd (const char *name, command_class theclass, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<std::string>::set set_func, | |
| setting_func_types<std::string>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| auto cmds = add_setshow_cmd_full<std::string> (name, theclass, | |
| var_string_noescape, nullptr, | |
| set_doc, show_doc, help_doc, | |
| set_func, get_func, | |
| nullptr, show_func, set_list, | |
| show_list); | |
| /* Disable the default symbol completer. */ | |
| set_cmd_completer (cmds.set, nullptr); | |
| return cmds; | |
| } | |
| /* Add element named NAME to both the set and show command LISTs (the | |
| list for set/show or some sublist thereof). */ | |
| set_show_commands | |
| add_setshow_optional_filename_cmd (const char *name, enum command_class theclass, | |
| std::string *var, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| set_show_commands commands | |
| = add_setshow_cmd_full<std::string> (name, theclass, var_optional_filename, | |
| var, set_doc, show_doc, help_doc, | |
| nullptr, nullptr, set_func, show_func, | |
| set_list, show_list); | |
| set_cmd_completer (commands.set, deprecated_filename_completer); | |
| return commands; | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_optional_filename_cmd (const char *name, command_class theclass, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<std::string>::set set_func, | |
| setting_func_types<std::string>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| auto cmds = | |
| add_setshow_cmd_full<std::string> (name, theclass, var_optional_filename, | |
| nullptr, set_doc, show_doc, help_doc, | |
| set_func, get_func, nullptr, show_func, | |
| set_list,show_list); | |
| set_cmd_completer (cmds.set, deprecated_filename_completer); | |
| return cmds; | |
| } | |
| /* Add element named NAME to both the set and show command LISTs (the | |
| list for set/show or some sublist thereof). THECLASS is as in | |
| add_cmd. VAR is address of the variable which will contain the | |
| value. SET_DOC and SHOW_DOC are the documentation strings. This | |
| function is only used in Python API. Please don't use it elsewhere. */ | |
| set_show_commands | |
| add_setshow_integer_cmd (const char *name, enum command_class theclass, | |
| int *var, const literal_def *extra_literals, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| set_show_commands commands | |
| = add_setshow_cmd_full<int> (name, theclass, var_integer, var, | |
| extra_literals, set_doc, show_doc, | |
| help_doc, nullptr, nullptr, set_func, | |
| show_func, set_list, show_list); | |
| return commands; | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_integer_cmd (const char *name, command_class theclass, | |
| const literal_def *extra_literals, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<int>::set set_func, | |
| setting_func_types<int>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| auto cmds = add_setshow_cmd_full<int> (name, theclass, var_integer, nullptr, | |
| extra_literals, set_doc, show_doc, | |
| help_doc, set_func, get_func, nullptr, | |
| show_func, set_list, show_list); | |
| return cmds; | |
| } | |
| /* Accept `unlimited' or 0, translated internally to INT_MAX. */ | |
| const literal_def integer_unlimited_literals[] = | |
| { | |
| { "unlimited", INT_MAX, 0 }, | |
| { nullptr } | |
| }; | |
| /* Same as above but using `integer_unlimited_literals', with a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_integer_cmd (const char *name, enum command_class theclass, | |
| int *var, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| set_show_commands commands | |
| = add_setshow_cmd_full<int> (name, theclass, var_integer, var, | |
| integer_unlimited_literals, | |
| set_doc, show_doc, help_doc, | |
| nullptr, nullptr, set_func, | |
| show_func, set_list, show_list); | |
| return commands; | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_integer_cmd (const char *name, command_class theclass, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<int>::set set_func, | |
| setting_func_types<int>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| auto cmds = add_setshow_cmd_full<int> (name, theclass, var_integer, nullptr, | |
| integer_unlimited_literals, | |
| set_doc, show_doc, help_doc, set_func, | |
| get_func, nullptr, show_func, set_list, | |
| show_list); | |
| return cmds; | |
| } | |
| /* Add element named NAME to both the set and show command LISTs (the | |
| list for set/show or some sublist thereof). CLASS is as in | |
| add_cmd. VAR is address of the variable which will contain the | |
| value. SET_DOC and SHOW_DOC are the documentation strings. */ | |
| set_show_commands | |
| add_setshow_pinteger_cmd (const char *name, enum command_class theclass, | |
| int *var, const literal_def *extra_literals, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| set_show_commands commands | |
| = add_setshow_cmd_full<int> (name, theclass, var_pinteger, var, | |
| extra_literals, set_doc, show_doc, | |
| help_doc, nullptr, nullptr, set_func, | |
| show_func, set_list, show_list); | |
| return commands; | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_pinteger_cmd (const char *name, command_class theclass, | |
| const literal_def *extra_literals, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<int>::set set_func, | |
| setting_func_types<int>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| auto cmds = add_setshow_cmd_full<int> (name, theclass, var_pinteger, nullptr, | |
| extra_literals, set_doc, show_doc, | |
| help_doc, set_func, get_func, nullptr, | |
| show_func, set_list, show_list); | |
| return cmds; | |
| } | |
| /* Add element named NAME to both the set and show command LISTs (the | |
| list for set/show or some sublist thereof). THECLASS is as in | |
| add_cmd. VAR is address of the variable which will contain the | |
| value. SET_DOC and SHOW_DOC are the documentation strings. */ | |
| set_show_commands | |
| add_setshow_uinteger_cmd (const char *name, enum command_class theclass, | |
| unsigned int *var, const literal_def *extra_literals, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| set_show_commands commands | |
| = add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger, var, | |
| extra_literals, set_doc, show_doc, | |
| help_doc, nullptr, nullptr, set_func, | |
| show_func, set_list, show_list); | |
| return commands; | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_uinteger_cmd (const char *name, command_class theclass, | |
| const literal_def *extra_literals, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<unsigned int>::set set_func, | |
| setting_func_types<unsigned int>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| auto cmds = add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger, | |
| nullptr, extra_literals, | |
| set_doc, show_doc, help_doc, | |
| set_func, get_func, nullptr, | |
| show_func, set_list, | |
| show_list); | |
| return cmds; | |
| } | |
| /* Accept `unlimited' or 0, translated internally to UINT_MAX. */ | |
| const literal_def uinteger_unlimited_literals[] = | |
| { | |
| { "unlimited", UINT_MAX, 0 }, | |
| { nullptr } | |
| }; | |
| /* Same as above but using `uinteger_unlimited_literals', with a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_uinteger_cmd (const char *name, enum command_class theclass, | |
| unsigned int *var, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| set_show_commands commands | |
| = add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger, var, | |
| uinteger_unlimited_literals, | |
| set_doc, show_doc, help_doc, nullptr, | |
| nullptr, set_func, show_func, | |
| set_list, show_list); | |
| return commands; | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_uinteger_cmd (const char *name, command_class theclass, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<unsigned int>::set set_func, | |
| setting_func_types<unsigned int>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| auto cmds = add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger, | |
| nullptr, | |
| uinteger_unlimited_literals, | |
| set_doc, show_doc, help_doc, | |
| set_func, get_func, nullptr, | |
| show_func, set_list, | |
| show_list); | |
| return cmds; | |
| } | |
| /* Add element named NAME to both the set and show command LISTs (the | |
| list for set/show or some sublist thereof). THECLASS is as in | |
| add_cmd. VAR is address of the variable which will contain the | |
| value. SET_DOC and SHOW_DOC are the documentation strings. */ | |
| set_show_commands | |
| add_setshow_zinteger_cmd (const char *name, enum command_class theclass, | |
| int *var, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| return add_setshow_cmd_full<int> (name, theclass, var_integer, var, | |
| set_doc, show_doc, help_doc, | |
| nullptr, nullptr, set_func, | |
| show_func, set_list, show_list); | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_zinteger_cmd (const char *name, command_class theclass, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<int>::set set_func, | |
| setting_func_types<int>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| return add_setshow_cmd_full<int> (name, theclass, var_integer, nullptr, | |
| set_doc, show_doc, help_doc, set_func, | |
| get_func, nullptr, show_func, set_list, | |
| show_list); | |
| } | |
| /* Accept `unlimited' or -1, using -1 internally. */ | |
| const literal_def pinteger_unlimited_literals[] = | |
| { | |
| { "unlimited", -1, -1 }, | |
| { nullptr } | |
| }; | |
| /* Same as above but using `pinteger_unlimited_literals', with a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_zuinteger_unlimited_cmd (const char *name, | |
| enum command_class theclass, | |
| int *var, | |
| const char *set_doc, | |
| const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| set_show_commands commands | |
| = add_setshow_cmd_full<int> (name, theclass, var_pinteger, var, | |
| pinteger_unlimited_literals, | |
| set_doc, show_doc, help_doc, nullptr, | |
| nullptr, set_func, show_func, set_list, | |
| show_list); | |
| return commands; | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_zuinteger_unlimited_cmd (const char *name, command_class theclass, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<int>::set set_func, | |
| setting_func_types<int>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| auto cmds | |
| = add_setshow_cmd_full<int> (name, theclass, var_pinteger, nullptr, | |
| pinteger_unlimited_literals, | |
| set_doc, show_doc, help_doc, set_func, | |
| get_func, nullptr, show_func, set_list, | |
| show_list); | |
| return cmds; | |
| } | |
| /* Add element named NAME to both the set and show command LISTs (the | |
| list for set/show or some sublist thereof). THECLASS is as in | |
| add_cmd. VAR is address of the variable which will contain the | |
| value. SET_DOC and SHOW_DOC are the documentation strings. */ | |
| set_show_commands | |
| add_setshow_zuinteger_cmd (const char *name, enum command_class theclass, | |
| unsigned int *var, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| cmd_func_ftype *set_func, | |
| show_value_ftype *show_func, | |
| struct cmd_list_element **set_list, | |
| struct cmd_list_element **show_list) | |
| { | |
| return add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger, | |
| var, set_doc, show_doc, help_doc, | |
| nullptr, nullptr, set_func, | |
| show_func, set_list, show_list); | |
| } | |
| /* Same as above but using a getter and a setter function instead of a pointer | |
| to a global storage buffer. */ | |
| set_show_commands | |
| add_setshow_zuinteger_cmd (const char *name, command_class theclass, | |
| const char *set_doc, const char *show_doc, | |
| const char *help_doc, | |
| setting_func_types<unsigned int>::set set_func, | |
| setting_func_types<unsigned int>::get get_func, | |
| show_value_ftype *show_func, | |
| cmd_list_element **set_list, | |
| cmd_list_element **show_list) | |
| { | |
| return add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger, | |
| nullptr, set_doc, show_doc, | |
| help_doc, set_func, get_func, | |
| nullptr, show_func, set_list, | |
| show_list); | |
| } | |
| /* Remove the command named NAME from the command list. Return the list | |
| commands which were aliased to the deleted command. The various *HOOKs are | |
| set to the pre- and post-hook commands for the deleted command. If the | |
| command does not have a hook, the corresponding out parameter is set to | |
| NULL. */ | |
| static cmd_list_element::aliases_list_type | |
| delete_cmd (const char *name, struct cmd_list_element **list, | |
| struct cmd_list_element **prehook, | |
| struct cmd_list_element **prehookee, | |
| struct cmd_list_element **posthook, | |
| struct cmd_list_element **posthookee) | |
| { | |
| struct cmd_list_element *iter; | |
| struct cmd_list_element **previous_chain_ptr; | |
| cmd_list_element::aliases_list_type aliases; | |
| *prehook = NULL; | |
| *prehookee = NULL; | |
| *posthook = NULL; | |
| *posthookee = NULL; | |
| previous_chain_ptr = list; | |
| for (iter = *previous_chain_ptr; iter; iter = *previous_chain_ptr) | |
| { | |
| if (strcmp (iter->name, name) == 0) | |
| { | |
| if (iter->destroyer) | |
| iter->destroyer (iter, iter->context ()); | |
| if (iter->hookee_pre) | |
| iter->hookee_pre->hook_pre = 0; | |
| *prehook = iter->hook_pre; | |
| *prehookee = iter->hookee_pre; | |
| if (iter->hookee_post) | |
| iter->hookee_post->hook_post = 0; | |
| *posthook = iter->hook_post; | |
| *posthookee = iter->hookee_post; | |
| /* Update the link. */ | |
| *previous_chain_ptr = iter->next; | |
| aliases = std::move (iter->aliases); | |
| /* If this command was an alias, remove it from the list of | |
| aliases. */ | |
| if (iter->is_alias ()) | |
| { | |
| auto it = iter->alias_target->aliases.iterator_to (*iter); | |
| iter->alias_target->aliases.erase (it); | |
| } | |
| delete iter; | |
| /* We won't see another command with the same name. */ | |
| break; | |
| } | |
| else | |
| previous_chain_ptr = &iter->next; | |
| } | |
| return aliases; | |
| } | |
| /* Shorthands to the commands above. */ | |
| /* Add an element to the list of info subcommands. */ | |
| struct cmd_list_element * | |
| add_info (const char *name, cmd_simple_func_ftype *fun, const char *doc) | |
| { | |
| return add_cmd (name, class_info, fun, doc, &infolist); | |
| } | |
| /* Add an alias to the list of info subcommands. */ | |
| cmd_list_element * | |
| add_info_alias (const char *name, cmd_list_element *target, int abbrev_flag) | |
| { | |
| return add_alias_cmd (name, target, class_run, abbrev_flag, &infolist); | |
| } | |
| /* Add an element to the list of commands. */ | |
| struct cmd_list_element * | |
| add_com (const char *name, enum command_class theclass, | |
| cmd_simple_func_ftype *fun, | |
| const char *doc) | |
| { | |
| return add_cmd (name, theclass, fun, doc, &cmdlist); | |
| } | |
| /* Add an alias or abbreviation command to the list of commands. | |
| For aliases predefined by GDB (such as bt), THECLASS must be | |
| different of class_alias, as class_alias is used to identify | |
| user defined aliases. */ | |
| cmd_list_element * | |
| add_com_alias (const char *name, cmd_list_element *target, | |
| command_class theclass, int abbrev_flag) | |
| { | |
| return add_alias_cmd (name, target, theclass, abbrev_flag, &cmdlist); | |
| } | |
| /* Add an element with a suppress notification to the list of commands. */ | |
| struct cmd_list_element * | |
| add_com_suppress_notification (const char *name, enum command_class theclass, | |
| cmd_simple_func_ftype *fun, const char *doc, | |
| bool *suppress_notification) | |
| { | |
| return add_cmd_suppress_notification (name, theclass, fun, doc, | |
| &cmdlist, suppress_notification); | |
| } | |
| /* Print the prefix of C followed by name of C in command style. */ | |
| static void | |
| fput_command_name_styled (const cmd_list_element &c, struct ui_file *stream) | |
| { | |
| std::string prefixname | |
| = c.prefix == nullptr ? "" : c.prefix->prefixname (); | |
| fprintf_styled (stream, command_style.style (), "%s%s", | |
| prefixname.c_str (), c.name); | |
| } | |
| /* True if ALIAS has a user-defined documentation. */ | |
| static bool | |
| user_documented_alias (const cmd_list_element &alias) | |
| { | |
| gdb_assert (alias.is_alias ()); | |
| /* Alias is user documented if it has an allocated documentation | |
| that differs from the aliased command. */ | |
| return (alias.doc_allocated | |
| && strcmp (alias.doc, alias.alias_target->doc) != 0); | |
| } | |
| /* Print the definition of alias C using title style for alias | |
| and aliased command. */ | |
| static void | |
| fput_alias_definition_styled (const cmd_list_element &c, | |
| struct ui_file *stream) | |
| { | |
| gdb_assert (c.is_alias ()); | |
| gdb_puts (" alias ", stream); | |
| fput_command_name_styled (c, stream); | |
| gdb_printf (stream, " = "); | |
| fput_command_name_styled (*c.alias_target, stream); | |
| gdb_printf (stream, " %s\n", c.default_args.c_str ()); | |
| } | |
| /* Print the definition of CMD aliases not deprecated and having default args | |
| and not specifically documented by the user. */ | |
| static void | |
| fput_aliases_definition_styled (const cmd_list_element &cmd, | |
| struct ui_file *stream) | |
| { | |
| for (const cmd_list_element &alias : cmd.aliases) | |
| if (!alias.cmd_deprecated | |
| && !user_documented_alias (alias) | |
| && !alias.default_args.empty ()) | |
| fput_alias_definition_styled (alias, stream); | |
| } | |
| /* If C has one or more aliases, style print the name of C and the name of its | |
| aliases not documented specifically by the user, separated by commas. | |
| If ALWAYS_FPUT_C_NAME, print the name of C even if it has no aliases. | |
| If one or more names are printed, POSTFIX is printed after the last name. | |
| */ | |
| static void | |
| fput_command_names_styled (const cmd_list_element &c, | |
| bool always_fput_c_name, const char *postfix, | |
| struct ui_file *stream) | |
| { | |
| /* First, check if we are going to print something. That is, either if | |
| ALWAYS_FPUT_C_NAME is true or if there exists at least one non-deprecated | |
| alias not documented specifically by the user. */ | |
| auto print_alias = [] (const cmd_list_element &alias) | |
| { | |
| return !alias.cmd_deprecated && !user_documented_alias (alias); | |
| }; | |
| bool print_something = always_fput_c_name; | |
| if (!print_something) | |
| for (const cmd_list_element &alias : c.aliases) | |
| { | |
| if (!print_alias (alias)) | |
| continue; | |
| print_something = true; | |
| break; | |
| } | |
| if (print_something) | |
| fput_command_name_styled (c, stream); | |
| for (const cmd_list_element &alias : c.aliases) | |
| { | |
| if (!print_alias (alias)) | |
| continue; | |
| gdb_puts (", ", stream); | |
| stream->wrap_here (3); | |
| fput_command_name_styled (alias, stream); | |
| } | |
| if (print_something) | |
| gdb_puts (postfix, stream); | |
| } | |
| /* If VERBOSE, print the full help for command C and highlight the | |
| documentation parts matching HIGHLIGHT, | |
| otherwise print only one-line help for command C. */ | |
| static void | |
| print_doc_of_command (const cmd_list_element &c, bool verbose, | |
| compiled_regex &highlight, struct ui_file *stream) | |
| { | |
| /* When printing the full documentation, add a line to separate | |
| this documentation from the previous command help, in the likely | |
| case that apropos finds several commands. */ | |
| if (verbose) | |
| gdb_puts ("\n", stream); | |
| fput_command_names_styled (c, true, | |
| verbose ? "" : " -- ", stream); | |
| if (verbose) | |
| { | |
| gdb_puts ("\n", stream); | |
| fput_aliases_definition_styled (c, stream); | |
| fputs_highlighted (c.doc, highlight, stream); | |
| gdb_puts ("\n", stream); | |
| } | |
| else | |
| { | |
| print_doc_line (stream, c.doc, false); | |
| gdb_puts ("\n", stream); | |
| fput_aliases_definition_styled (c, stream); | |
| } | |
| } | |
| /* Recursively walk the commandlist structures, and print out the | |
| documentation of commands that match our regex in either their | |
| name, or their documentation. | |
| If VERBOSE, prints the complete documentation and highlight the | |
| documentation parts matching REGEX, otherwise prints only | |
| the first line. | |
| */ | |
| void | |
| apropos_cmd (struct ui_file *stream, | |
| struct cmd_list_element *commandlist, | |
| bool verbose, compiled_regex ®ex) | |
| { | |
| struct cmd_list_element *c; | |
| int returnvalue; | |
| /* Walk through the commands. */ | |
| for (c=commandlist;c;c=c->next) | |
| { | |
| if (c->is_alias () && !user_documented_alias (*c)) | |
| { | |
| /* Command aliases/abbreviations not specifically documented by the | |
| user are skipped to ensure we print the doc of a command only once, | |
| when encountering the aliased command. */ | |
| continue; | |
| } | |
| returnvalue = -1; /* Needed to avoid double printing. */ | |
| if (c->name != NULL) | |
| { | |
| size_t name_len = strlen (c->name); | |
| /* Try to match against the name. */ | |
| returnvalue = regex.search (c->name, name_len, 0, name_len, NULL); | |
| if (returnvalue >= 0) | |
| print_doc_of_command (*c, verbose, regex, stream); | |
| /* Try to match against the name of the aliases. */ | |
| for (const cmd_list_element &alias : c->aliases) | |
| { | |
| name_len = strlen (alias.name); | |
| returnvalue = regex.search (alias.name, name_len, 0, name_len, NULL); | |
| if (returnvalue >= 0) | |
| { | |
| print_doc_of_command (*c, verbose, regex, stream); | |
| break; | |
| } | |
| } | |
| } | |
| if (c->doc != NULL && returnvalue < 0) | |
| { | |
| size_t doc_len = strlen (c->doc); | |
| /* Try to match against documentation. */ | |
| if (regex.search (c->doc, doc_len, 0, doc_len, NULL) >= 0) | |
| print_doc_of_command (*c, verbose, regex, stream); | |
| } | |
| /* Check if this command has subcommands. */ | |
| if (c->is_prefix ()) | |
| { | |
| /* Recursively call ourselves on the subcommand list, | |
| passing the right prefix in. */ | |
| apropos_cmd (stream, *c->subcommands, verbose, regex); | |
| } | |
| } | |
| } | |
| /* This command really has to deal with two things: | |
| 1) I want documentation on *this string* (usually called by | |
| "help commandname"). | |
| 2) I want documentation on *this list* (usually called by giving a | |
| command that requires subcommands. Also called by saying just | |
| "help".) | |
| I am going to split this into two separate commands, help_cmd and | |
| help_list. */ | |
| void | |
| help_cmd (const char *command, struct ui_file *stream) | |
| { | |
| struct cmd_list_element *c, *alias, *prefix_cmd, *c_cmd; | |
| if (!command) | |
| { | |
| help_list (cmdlist, "", all_classes, stream); | |
| return; | |
| } | |
| if (strcmp (command, "all") == 0) | |
| { | |
| help_all (stream); | |
| return; | |
| } | |
| const char *orig_command = command; | |
| c = lookup_cmd (&command, cmdlist, "", NULL, 0, 0); | |
| if (c == 0) | |
| return; | |
| lookup_cmd_composition (orig_command, &alias, &prefix_cmd, &c_cmd); | |
| /* There are three cases here. | |
| If c->subcommands is nonzero, we have a prefix command. | |
| Print its documentation, then list its subcommands. | |
| If c->func is non NULL, we really have a command. Print its | |
| documentation and return. | |
| If c->func is NULL, we have a class name. Print its | |
| documentation (as if it were a command) and then set class to the | |
| number of this class so that the commands in the class will be | |
| listed. */ | |
| if (alias == nullptr || !user_documented_alias (*alias)) | |
| { | |
| /* Case of a normal command, or an alias not explicitly | |
| documented by the user. */ | |
| /* If the user asked 'help somecommand' and there is no alias, | |
| the false indicates to not output the (single) command name. */ | |
| fput_command_names_styled (*c, false, "\n", stream); | |
| fput_aliases_definition_styled (*c, stream); | |
| gdb_puts (c->doc, stream); | |
| } | |
| else | |
| { | |
| /* Case of an alias explicitly documented by the user. | |
| Only output the alias definition and its explicit documentation. */ | |
| fput_alias_definition_styled (*alias, stream); | |
| fput_command_names_styled (*alias, false, "\n", stream); | |
| gdb_puts (alias->doc, stream); | |
| } | |
| gdb_puts ("\n", stream); | |
| if (!c->is_prefix () && !c->is_command_class_help ()) | |
| return; | |
| gdb_printf (stream, "\n"); | |
| /* If this is a prefix command, print it's subcommands. */ | |
| if (c->is_prefix ()) | |
| help_list (*c->subcommands, c->prefixname_no_space ().c_str (), | |
| all_commands, stream); | |
| /* If this is a class name, print all of the commands in the class. */ | |
| if (c->is_command_class_help ()) | |
| help_list (cmdlist, "", c->theclass, stream); | |
| if (c->hook_pre || c->hook_post) | |
| gdb_printf (stream, | |
| "\nThis command has a hook (or hooks) defined:\n"); | |
| if (c->hook_pre) | |
| gdb_printf (stream, | |
| "\tThis command is run after : %s (pre hook)\n", | |
| c->hook_pre->name); | |
| if (c->hook_post) | |
| gdb_printf (stream, | |
| "\tThis command is run before : %s (post hook)\n", | |
| c->hook_post->name); | |
| } | |
| /* Get a specific kind of help on a command list. | |
| LIST is the list. | |
| CMDTYPE is the prefix to use in the title string. It should not | |
| end in a space. | |
| THECLASS is the class with which to list the nodes of this list (see | |
| documentation for help_cmd_list below), As usual, ALL_COMMANDS for | |
| everything, ALL_CLASSES for just classes, and non-negative for only things | |
| in a specific class. | |
| and STREAM is the output stream on which to print things. | |
| If you call this routine with a class >= 0, it recurses. */ | |
| void | |
| help_list (struct cmd_list_element *list, const char *cmdtype, | |
| enum command_class theclass, struct ui_file *stream) | |
| { | |
| int len = strlen (cmdtype); | |
| const char *space = ""; | |
| const char *prefix = ""; | |
| if (len > 0) | |
| { | |
| prefix = "sub"; | |
| space = " "; | |
| } | |
| if (theclass == all_classes) | |
| gdb_printf (stream, "List of classes of %scommands:\n\n", | |
| prefix); | |
| else if (len == 0) | |
| gdb_printf (stream, "List of commands:\n\n"); | |
| else | |
| gdb_printf (stream, "List of \"%ps\" %scommands:\n\n", | |
| styled_string (command_style.style (), cmdtype), | |
| prefix); | |
| help_cmd_list (list, theclass, theclass >= 0, stream); | |
| if (theclass == all_classes) | |
| { | |
| gdb_printf (stream, "\n\ | |
| Type \"%p[help%s%s%p]\" followed by a class name for a list of commands in ", | |
| command_style.style ().ptr (), | |
| space, cmdtype, | |
| nullptr); | |
| stream->wrap_here (0); | |
| gdb_printf (stream, "that class."); | |
| gdb_printf (stream, "\n\ | |
| Type \"%ps\" for the list of all commands.", | |
| styled_string (command_style.style (), "help all")); | |
| } | |
| gdb_printf (stream, "\nType \"%p[help%s%s%p]\" followed by %scommand name ", | |
| command_style.style ().ptr (), space, cmdtype, nullptr, | |
| prefix); | |
| stream->wrap_here (0); | |
| gdb_puts ("for ", stream); | |
| stream->wrap_here (0); | |
| gdb_puts ("full ", stream); | |
| stream->wrap_here (0); | |
| gdb_puts ("documentation.\n", stream); | |
| gdb_printf (stream, | |
| "Type \"%ps\" to search " | |
| "for commands related to \"word\".\n", | |
| styled_string (command_style.style (), "apropos word")); | |
| gdb_printf (stream, "Type \"%ps\" for full documentation", | |
| styled_string (command_style.style (), "apropos -v word")); | |
| stream->wrap_here (0); | |
| gdb_puts (" of commands related to \"word\".\n", stream); | |
| gdb_puts ("Command name abbreviations are allowed if unambiguous.\n", | |
| stream); | |
| } | |
| static void | |
| help_all (struct ui_file *stream) | |
| { | |
| struct cmd_list_element *c; | |
| int seen_unclassified = 0; | |
| for (c = cmdlist; c; c = c->next) | |
| { | |
| if (c->abbrev_flag) | |
| continue; | |
| /* If this is a class name, print all of the commands in the | |
| class. */ | |
| if (c->is_command_class_help ()) | |
| { | |
| gdb_printf (stream, "\nCommand class: %s\n\n", c->name); | |
| help_cmd_list (cmdlist, c->theclass, true, stream); | |
| } | |
| } | |
| /* While it's expected that all commands are in some class, | |
| as a safety measure, we'll print commands outside of any | |
| class at the end. */ | |
| for (c = cmdlist; c; c = c->next) | |
| { | |
| if (c->abbrev_flag) | |
| continue; | |
| if (c->theclass == no_class) | |
| { | |
| if (!seen_unclassified) | |
| { | |
| gdb_printf (stream, "\nUnclassified commands\n\n"); | |
| seen_unclassified = 1; | |
| } | |
| print_help_for_command (*c, true, stream); | |
| } | |
| } | |
| } | |
| /* See cli-decode.h. */ | |
| void | |
| print_doc_line (struct ui_file *stream, const char *str, | |
| bool for_value_prefix) | |
| { | |
| static char *line_buffer = 0; | |
| static int line_size; | |
| const char *p; | |
| if (!line_buffer) | |
| { | |
| line_size = 80; | |
| line_buffer = (char *) xmalloc (line_size); | |
| } | |
| /* Searches for the first end of line or the end of STR. */ | |
| p = str; | |
| while (*p && *p != '\n') | |
| p++; | |
| if (p - str > line_size - 1) | |
| { | |
| line_size = p - str + 1; | |
| xfree (line_buffer); | |
| line_buffer = (char *) xmalloc (line_size); | |
| } | |
| strncpy (line_buffer, str, p - str); | |
| if (for_value_prefix) | |
| { | |
| if (islower (line_buffer[0])) | |
| line_buffer[0] = toupper (line_buffer[0]); | |
| gdb_assert (p > str); | |
| if (line_buffer[p - str - 1] == '.') | |
| line_buffer[p - str - 1] = '\0'; | |
| else | |
| line_buffer[p - str] = '\0'; | |
| } | |
| else | |
| line_buffer[p - str] = '\0'; | |
| gdb_puts (line_buffer, stream); | |
| } | |
| /* Print one-line help for command C. | |
| If RECURSE is non-zero, also print one-line descriptions | |
| of all prefixed subcommands. */ | |
| static void | |
| print_help_for_command (const cmd_list_element &c, | |
| bool recurse, struct ui_file *stream) | |
| { | |
| fput_command_names_styled (c, true, " -- ", stream); | |
| print_doc_line (stream, c.doc, false); | |
| gdb_puts ("\n", stream); | |
| if (!c.default_args.empty ()) | |
| fput_alias_definition_styled (c, stream); | |
| fput_aliases_definition_styled (c, stream); | |
| if (recurse | |
| && c.is_prefix () | |
| && c.abbrev_flag == 0) | |
| /* Subcommands of a prefix command typically have 'all_commands' | |
| as class. If we pass CLASS to recursive invocation, | |
| most often we won't see anything. */ | |
| help_cmd_list (*c.subcommands, all_commands, true, stream); | |
| } | |
| /* | |
| * Implement a help command on command list LIST. | |
| * RECURSE should be non-zero if this should be done recursively on | |
| * all sublists of LIST. | |
| * STREAM is the stream upon which the output should be written. | |
| * THECLASS should be: | |
| * A non-negative class number to list only commands in that | |
| * ALL_COMMANDS to list all commands in list. | |
| * ALL_CLASSES to list all classes in list. | |
| * | |
| * Note that aliases are only shown when THECLASS is class_alias. | |
| * In the other cases, the aliases will be shown together with their | |
| * aliased command. | |
| * | |
| * Note that RECURSE will be active on *all* sublists, not just the | |
| * ones selected by the criteria above (ie. the selection mechanism | |
| * is at the low level, not the high-level). | |
| */ | |
| static void | |
| help_cmd_list (struct cmd_list_element *list, enum command_class theclass, | |
| bool recurse, struct ui_file *stream) | |
| { | |
| struct cmd_list_element *c; | |
| for (c = list; c; c = c->next) | |
| { | |
| if (c->abbrev_flag == 1 || c->cmd_deprecated) | |
| { | |
| /* Do not show abbreviations or deprecated commands. */ | |
| continue; | |
| } | |
| if (c->is_alias () && theclass != class_alias) | |
| { | |
| /* Do not show an alias, unless specifically showing the | |
| list of aliases: for all other classes, an alias is | |
| shown (if needed) together with its aliased command. */ | |
| continue; | |
| } | |
| if (theclass == all_commands | |
| || (theclass == all_classes && c->is_command_class_help ()) | |
| || (theclass == c->theclass && !c->is_command_class_help ())) | |
| { | |
| /* show C when | |
| - showing all commands | |
| - showing all classes and C is a help class | |
| - showing commands of THECLASS and C is not the help class */ | |
| /* If we show the class_alias and C is an alias, do not recurse, | |
| as this would show the (possibly very long) not very useful | |
| list of sub-commands of the aliased command. */ | |
| print_help_for_command | |
| (*c, | |
| recurse && (theclass != class_alias || !c->is_alias ()), | |
| stream); | |
| continue; | |
| } | |
| if (recurse | |
| && (theclass == class_user || theclass == class_alias) | |
| && c->is_prefix ()) | |
| { | |
| /* User-defined commands or aliases may be subcommands. */ | |
| help_cmd_list (*c->subcommands, theclass, recurse, stream); | |
| continue; | |
| } | |
| /* Do not show C or recurse on C, e.g. because C does not belong to | |
| THECLASS or because C is a help class. */ | |
| } | |
| } | |
| /* Search the input clist for 'command'. Return the command if | |
| found (or NULL if not), and return the number of commands | |
| found in nfound. */ | |
| static struct cmd_list_element * | |
| find_cmd (const char *command, int len, struct cmd_list_element *clist, | |
| int ignore_help_classes, int *nfound) | |
| { | |
| struct cmd_list_element *found, *c; | |
| found = NULL; | |
| *nfound = 0; | |
| for (c = clist; c; c = c->next) | |
| if (!strncmp (command, c->name, len) | |
| && (!ignore_help_classes || !c->is_command_class_help ())) | |
| { | |
| found = c; | |
| (*nfound)++; | |
| if (c->name[len] == '\0') | |
| { | |
| *nfound = 1; | |
| break; | |
| } | |
| } | |
| return found; | |
| } | |
| /* Return the length of command name in TEXT. */ | |
| int | |
| find_command_name_length (const char *text) | |
| { | |
| const char *p = text; | |
| /* Treating underscores as part of command words is important | |
| so that "set args_foo()" doesn't get interpreted as | |
| "set args _foo()". */ | |
| /* Some characters are only used for TUI specific commands. | |
| However, they are always allowed for the sake of consistency. | |
| Note that this is larger than the character set allowed when | |
| creating user-defined commands. */ | |
| /* Recognize the single character commands so that, e.g., "!ls" | |
| works as expected. */ | |
| if (*p == '!' || *p == '|') | |
| return 1; | |
| while (valid_cmd_char_p (*p) | |
| /* Characters used by TUI specific commands. */ | |
| || *p == '+' || *p == '<' || *p == '>' || *p == '$') | |
| p++; | |
| return p - text; | |
| } | |
| /* See command.h. */ | |
| bool | |
| valid_cmd_char_p (int c) | |
| { | |
| /* Alas "42" is a legitimate user-defined command. | |
| In the interests of not breaking anything we preserve that. */ | |
| return isalnum (c) || c == '-' || c == '_' || c == '.'; | |
| } | |
| /* See command.h. */ | |
| bool | |
| valid_user_defined_cmd_name_p (const char *name) | |
| { | |
| const char *p; | |
| if (*name == '\0') | |
| return false; | |
| for (p = name; *p != '\0'; ++p) | |
| { | |
| if (valid_cmd_char_p (*p)) | |
| ; /* Ok. */ | |
| else | |
| return false; | |
| } | |
| return true; | |
| } | |
| /* See command.h. */ | |
| struct cmd_list_element * | |
| lookup_cmd_1 (const char **text, struct cmd_list_element *clist, | |
| struct cmd_list_element **result_list, std::string *default_args, | |
| int ignore_help_classes, bool lookup_for_completion_p) | |
| { | |
| char *command; | |
| int len, nfound; | |
| struct cmd_list_element *found, *c; | |
| bool found_alias = false; | |
| const char *line = *text; | |
| while (**text == ' ' || **text == '\t') | |
| (*text)++; | |
| /* Identify the name of the command. */ | |
| len = find_command_name_length (*text); | |
| /* If nothing but whitespace, return 0. */ | |
| if (len == 0) | |
| return 0; | |
| /* *text and p now bracket the first command word to lookup (and | |
| it's length is len). We copy this into a local temporary. */ | |
| command = (char *) alloca (len + 1); | |
| memcpy (command, *text, len); | |
| command[len] = '\0'; | |
| /* Look it up. */ | |
| found = 0; | |
| nfound = 0; | |
| found = find_cmd (command, len, clist, ignore_help_classes, &nfound); | |
| /* If nothing matches, we have a simple failure. */ | |
| if (nfound == 0) | |
| return 0; | |
| if (nfound > 1) | |
| { | |
| if (result_list != nullptr) | |
| /* Will be modified in calling routine | |
| if we know what the prefix command is. */ | |
| *result_list = 0; | |
| if (default_args != nullptr) | |
| *default_args = std::string (); | |
| return CMD_LIST_AMBIGUOUS; /* Ambiguous. */ | |
| } | |
| /* We've matched something on this list. Move text pointer forward. */ | |
| *text += len; | |
| if (found->is_alias ()) | |
| { | |
| /* We drop the alias (abbreviation) in favor of the command it | |
| is pointing to. If the alias is deprecated, though, we need to | |
| warn the user about it before we drop it. Note that while we | |
| are warning about the alias, we may also warn about the command | |
| itself and we will adjust the appropriate DEPRECATED_WARN_USER | |
| flags. */ | |
| if (found->deprecated_warn_user && !lookup_for_completion_p) | |
| deprecated_cmd_warning (line, clist); | |
| /* Return the default_args of the alias, not the default_args | |
| of the command it is pointing to. */ | |
| if (default_args != nullptr) | |
| *default_args = found->default_args; | |
| found = found->alias_target; | |
| found_alias = true; | |
| } | |
| /* If we found a prefix command, keep looking. */ | |
| if (found->is_prefix ()) | |
| { | |
| c = lookup_cmd_1 (text, *found->subcommands, result_list, default_args, | |
| ignore_help_classes, lookup_for_completion_p); | |
| if (!c) | |
| { | |
| /* Didn't find anything; this is as far as we got. */ | |
| if (result_list != nullptr) | |
| *result_list = clist; | |
| if (!found_alias && default_args != nullptr) | |
| *default_args = found->default_args; | |
| return found; | |
| } | |
| else if (c == CMD_LIST_AMBIGUOUS) | |
| { | |
| /* We've gotten this far properly, but the next step is | |
| ambiguous. We need to set the result list to the best | |
| we've found (if an inferior hasn't already set it). */ | |
| if (result_list != nullptr) | |
| if (!*result_list) | |
| /* This used to say *result_list = *found->subcommands. | |
| If that was correct, need to modify the documentation | |
| at the top of this function to clarify what is | |
| supposed to be going on. */ | |
| *result_list = found; | |
| /* For ambiguous commands, do not return any default_args args. */ | |
| if (default_args != nullptr) | |
| *default_args = std::string (); | |
| return c; | |
| } | |
| else | |
| { | |
| /* We matched! */ | |
| return c; | |
| } | |
| } | |
| else | |
| { | |
| if (result_list != nullptr) | |
| *result_list = clist; | |
| if (!found_alias && default_args != nullptr) | |
| *default_args = found->default_args; | |
| return found; | |
| } | |
| } | |
| /* All this hair to move the space to the front of cmdtype */ | |
| static void | |
| undef_cmd_error (const char *cmdtype, const char *q) | |
| { | |
| error (_("Undefined %scommand: \"%s\". Try \"help%s%.*s\"."), | |
| cmdtype, | |
| q, | |
| *cmdtype ? " " : "", | |
| (int) strlen (cmdtype) - 1, | |
| cmdtype); | |
| } | |
| /* Look up the contents of *LINE as a command in the command list LIST. | |
| LIST is a chain of struct cmd_list_element's. | |
| If it is found, return the struct cmd_list_element for that command, | |
| update *LINE to point after the command name, at the first argument | |
| and update *DEFAULT_ARGS (if DEFAULT_ARGS is not null) to the default | |
| args to prepend to the user provided args when running the command. | |
| Note that if the found cmd_list_element is found via an alias, | |
| the default args of the alias are returned. | |
| If not found, call error if ALLOW_UNKNOWN is zero | |
| otherwise (or if error returns) return zero. | |
| Call error if specified command is ambiguous, | |
| unless ALLOW_UNKNOWN is negative. | |
| CMDTYPE precedes the word "command" in the error message. | |
| If IGNORE_HELP_CLASSES is nonzero, ignore any command list | |
| elements which are actually help classes rather than commands (i.e. | |
| the function field of the struct cmd_list_element is 0). */ | |
| struct cmd_list_element * | |
| lookup_cmd (const char **line, struct cmd_list_element *list, | |
| const char *cmdtype, | |
| std::string *default_args, | |
| int allow_unknown, int ignore_help_classes) | |
| { | |
| struct cmd_list_element *last_list = 0; | |
| struct cmd_list_element *c; | |
| /* Note: Do not remove trailing whitespace here because this | |
| would be wrong for complete_command. Jim Kingdon */ | |
| if (!*line) | |
| error (_("Lack of needed %scommand"), cmdtype); | |
| c = lookup_cmd_1 (line, list, &last_list, default_args, ignore_help_classes); | |
| if (!c) | |
| { | |
| if (!allow_unknown) | |
| { | |
| char *q; | |
| int len = find_command_name_length (*line); | |
| q = (char *) alloca (len + 1); | |
| strncpy (q, *line, len); | |
| q[len] = '\0'; | |
| undef_cmd_error (cmdtype, q); | |
| } | |
| else | |
| return 0; | |
| } | |
| else if (c == CMD_LIST_AMBIGUOUS) | |
| { | |
| /* Ambigous. Local values should be off subcommands or called | |
| values. */ | |
| int local_allow_unknown = (last_list ? last_list->allow_unknown : | |
| allow_unknown); | |
| std::string local_cmdtype | |
| = last_list ? last_list->prefixname () : cmdtype; | |
| struct cmd_list_element *local_list = | |
| (last_list ? *(last_list->subcommands) : list); | |
| if (local_allow_unknown < 0) | |
| { | |
| if (last_list) | |
| return last_list; /* Found something. */ | |
| else | |
| return 0; /* Found nothing. */ | |
| } | |
| else | |
| { | |
| /* Report as error. */ | |
| int amb_len; | |
| char ambbuf[100]; | |
| for (amb_len = 0; | |
| ((*line)[amb_len] && (*line)[amb_len] != ' ' | |
| && (*line)[amb_len] != '\t'); | |
| amb_len++) | |
| ; | |
| ambbuf[0] = 0; | |
| for (c = local_list; c; c = c->next) | |
| if (!strncmp (*line, c->name, amb_len)) | |
| { | |
| if (strlen (ambbuf) + strlen (c->name) + 6 | |
| < (int) sizeof ambbuf) | |
| { | |
| if (strlen (ambbuf)) | |
| strcat (ambbuf, ", "); | |
| strcat (ambbuf, c->name); | |
| } | |
| else | |
| { | |
| strcat (ambbuf, ".."); | |
| break; | |
| } | |
| } | |
| error (_("Ambiguous %scommand \"%s\": %s."), | |
| local_cmdtype.c_str (), *line, ambbuf); | |
| } | |
| } | |
| else | |
| { | |
| if (c->type == set_cmd && **line != '\0' && !isspace (**line)) | |
| error (_("Argument must be preceded by space.")); | |
| /* We've got something. It may still not be what the caller | |
| wants (if this command *needs* a subcommand). */ | |
| while (**line == ' ' || **line == '\t') | |
| (*line)++; | |
| if (c->is_prefix () && **line && !c->allow_unknown) | |
| undef_cmd_error (c->prefixname ().c_str (), *line); | |
| /* Seems to be what he wants. Return it. */ | |
| return c; | |
| } | |
| return 0; | |
| } | |
| /* See command.h. */ | |
| struct cmd_list_element * | |
| lookup_cmd_exact (const char *name, | |
| struct cmd_list_element *list, | |
| bool ignore_help_classes) | |
| { | |
| const char *tem = name; | |
| struct cmd_list_element *cmd = lookup_cmd (&tem, list, "", NULL, -1, | |
| ignore_help_classes); | |
| if (cmd != nullptr && strcmp (name, cmd->name) != 0) | |
| cmd = nullptr; | |
| return cmd; | |
| } | |
| /* We are here presumably because an alias or command in TEXT is | |
| deprecated and a warning message should be generated. This | |
| function decodes TEXT and potentially generates a warning message | |
| as outlined below. | |
| Example for 'set endian big' which has a fictitious alias 'seb'. | |
| If alias wasn't used in TEXT, and the command is deprecated: | |
| "warning: 'set endian big' is deprecated." | |
| If alias was used, and only the alias is deprecated: | |
| "warning: 'seb' an alias for the command 'set endian big' is deprecated." | |
| If alias was used and command is deprecated (regardless of whether | |
| the alias itself is deprecated: | |
| "warning: 'set endian big' (seb) is deprecated." | |
| After the message has been sent, clear the appropriate flags in the | |
| command and/or the alias so the user is no longer bothered. | |
| */ | |
| void | |
| deprecated_cmd_warning (const char *text, struct cmd_list_element *list) | |
| { | |
| struct cmd_list_element *alias = nullptr; | |
| struct cmd_list_element *cmd = nullptr; | |
| /* Return if text doesn't evaluate to a command. We place this lookup | |
| within its own scope so that the PREFIX_CMD local is not visible | |
| later in this function. The value returned in PREFIX_CMD is based on | |
| the prefix found in TEXT, and is our case this prefix can be missing | |
| in some situations (when LIST is not the global CMDLIST). | |
| It is better for our purposes to use the prefix commands directly from | |
| the ALIAS and CMD results. */ | |
| { | |
| struct cmd_list_element *prefix_cmd = nullptr; | |
| if (!lookup_cmd_composition_1 (text, &alias, &prefix_cmd, &cmd, list)) | |
| return; | |
| } | |
| /* Return if nothing is deprecated. */ | |
| if (!((alias != nullptr ? alias->deprecated_warn_user : 0) | |
| || cmd->deprecated_warn_user)) | |
| return; | |
| /* Join command prefix (if any) and the command name. */ | |
| std::string tmp_cmd_str; | |
| if (cmd->prefix != nullptr) | |
| tmp_cmd_str += cmd->prefix->prefixname (); | |
| tmp_cmd_str += std::string (cmd->name); | |
| /* Display the appropriate first line, this warns that the thing the user | |
| entered is deprecated. */ | |
| if (alias != nullptr) | |
| { | |
| /* Join the alias prefix (if any) and the alias name. */ | |
| std::string tmp_alias_str; | |
| if (alias->prefix != nullptr) | |
| tmp_alias_str += alias->prefix->prefixname (); | |
| tmp_alias_str += std::string (alias->name); | |
| if (cmd->cmd_deprecated) | |
| gdb_printf (_("Warning: command '%ps' (%ps) is deprecated.\n"), | |
| styled_string (command_style.style (), | |
| tmp_cmd_str.c_str ()), | |
| styled_string (command_style.style (), | |
| tmp_alias_str.c_str ())); | |
| else | |
| gdb_printf (_("Warning: '%ps', an alias for the command '%ps', " | |
| "is deprecated.\n"), | |
| styled_string (command_style.style (), | |
| tmp_alias_str.c_str ()), | |
| styled_string (command_style.style (), | |
| tmp_cmd_str.c_str ())); | |
| } | |
| else | |
| gdb_printf (_("Warning: command '%ps' is deprecated.\n"), | |
| styled_string (command_style.style (), | |
| tmp_cmd_str.c_str ())); | |
| /* Now display a second line indicating what the user should use instead. | |
| If it is only the alias that is deprecated, we want to indicate the | |
| new alias, otherwise we'll indicate the new command. */ | |
| const char *replacement; | |
| if (alias != nullptr && !cmd->cmd_deprecated) | |
| replacement = alias->replacement; | |
| else | |
| replacement = cmd->replacement; | |
| if (replacement != nullptr) | |
| gdb_printf (_("Use '%ps'.\n\n"), | |
| styled_string (command_style.style (), | |
| replacement)); | |
| else | |
| gdb_printf (_("No alternative known.\n\n")); | |
| /* We've warned you, now we'll keep quiet. */ | |
| if (alias != nullptr) | |
| alias->deprecated_warn_user = 0; | |
| cmd->deprecated_warn_user = 0; | |
| } | |
| /* Look up the contents of TEXT as a command in the command list CUR_LIST. | |
| Return 1 on success, 0 on failure. | |
| If TEXT refers to an alias, *ALIAS will point to that alias. | |
| If TEXT is a subcommand (i.e. one that is preceded by a prefix | |
| command) set *PREFIX_CMD. | |
| Set *CMD to point to the command TEXT indicates, or to | |
| CMD_LIST_AMBIGUOUS if there are multiple possible matches. | |
| If any of *ALIAS, *PREFIX_CMD, or *CMD cannot be determined or do not | |
| exist, they are NULL when we return. | |
| */ | |
| static int | |
| lookup_cmd_composition_1 (const char *text, | |
| struct cmd_list_element **alias, | |
| struct cmd_list_element **prefix_cmd, | |
| struct cmd_list_element **cmd, | |
| struct cmd_list_element *cur_list) | |
| { | |
| *alias = nullptr; | |
| *prefix_cmd = cur_list->prefix; | |
| *cmd = nullptr; | |
| text = skip_spaces (text); | |
| /* Go through as many command lists as we need to, to find the command | |
| TEXT refers to. */ | |
| while (1) | |
| { | |
| /* Identify the name of the command. */ | |
| int len = find_command_name_length (text); | |
| /* If nothing but whitespace, return. */ | |
| if (len == 0) | |
| return 0; | |
| /* TEXT is the start of the first command word to lookup (and | |
| it's length is LEN). We copy this into a local temporary. */ | |
| std::string command (text, len); | |
| /* Look it up. */ | |
| int nfound = 0; | |
| *cmd = find_cmd (command.c_str (), len, cur_list, 1, &nfound); | |
| /* We only handle the case where a single command was found. */ | |
| if (nfound > 1) | |
| { | |
| *cmd = CMD_LIST_AMBIGUOUS; | |
| return 0; | |
| } | |
| else if (*cmd == nullptr) | |
| return 0; | |
| else | |
| { | |
| if ((*cmd)->is_alias ()) | |
| { | |
| /* If the command was actually an alias, we note that an | |
| alias was used (by assigning *ALIAS) and we set *CMD. */ | |
| *alias = *cmd; | |
| *cmd = (*cmd)->alias_target; | |
| } | |
| } | |
| text += len; | |
| text = skip_spaces (text); | |
| if ((*cmd)->is_prefix () && *text != '\0') | |
| { | |
| cur_list = *(*cmd)->subcommands; | |
| *prefix_cmd = *cmd; | |
| } | |
| else | |
| return 1; | |
| } | |
| } | |
| /* Look up the contents of TEXT as a command in the command list 'cmdlist'. | |
| Return 1 on success, 0 on failure. | |
| If TEXT refers to an alias, *ALIAS will point to that alias. | |
| If TEXT is a subcommand (i.e. one that is preceded by a prefix | |
| command) set *PREFIX_CMD. | |
| Set *CMD to point to the command TEXT indicates, or to | |
| CMD_LIST_AMBIGUOUS if there are multiple possible matches. | |
| If any of *ALIAS, *PREFIX_CMD, or *CMD cannot be determined or do not | |
| exist, they are NULL when we return. | |
| */ | |
| int | |
| lookup_cmd_composition (const char *text, | |
| struct cmd_list_element **alias, | |
| struct cmd_list_element **prefix_cmd, | |
| struct cmd_list_element **cmd) | |
| { | |
| return lookup_cmd_composition_1 (text, alias, prefix_cmd, cmd, cmdlist); | |
| } | |
| /* Helper function for SYMBOL_COMPLETION_FUNCTION. */ | |
| /* Return a vector of char pointers which point to the different | |
| possible completions in LIST of TEXT. | |
| WORD points in the same buffer as TEXT, and completions should be | |
| returned relative to this position. For example, suppose TEXT is | |
| "foo" and we want to complete to "foobar". If WORD is "oo", return | |
| "oobar"; if WORD is "baz/foo", return "baz/foobar". */ | |
| void | |
| complete_on_cmdlist (struct cmd_list_element *list, | |
| completion_tracker &tracker, | |
| const char *text, const char *word, | |
| int ignore_help_classes) | |
| { | |
| struct cmd_list_element *ptr; | |
| int textlen = strlen (text); | |
| int pass; | |
| int saw_deprecated_match = 0; | |
| /* We do one or two passes. In the first pass, we skip deprecated | |
| commands. If we see no matching commands in the first pass, and | |
| if we did happen to see a matching deprecated command, we do | |
| another loop to collect those. */ | |
| for (pass = 0; pass < 2; ++pass) | |
| { | |
| bool got_matches = false; | |
| for (ptr = list; ptr; ptr = ptr->next) | |
| if (!strncmp (ptr->name, text, textlen) | |
| && !ptr->abbrev_flag | |
| && (!ignore_help_classes || !ptr->is_command_class_help () | |
| || ptr->is_prefix ())) | |
| { | |
| if (pass == 0) | |
| { | |
| if (ptr->cmd_deprecated) | |
| { | |
| saw_deprecated_match = 1; | |
| continue; | |
| } | |
| } | |
| tracker.add_completion | |
| (make_completion_match_str (ptr->name, text, word)); | |
| got_matches = true; | |
| } | |
| if (got_matches) | |
| break; | |
| /* If we saw no matching deprecated commands in the first pass, | |
| just bail out. */ | |
| if (!saw_deprecated_match) | |
| break; | |
| } | |
| } | |
| /* Helper function for SYMBOL_COMPLETION_FUNCTION. */ | |
| /* Add the different possible completions in ENUMLIST of TEXT. | |
| WORD points in the same buffer as TEXT, and completions should be | |
| returned relative to this position. For example, suppose TEXT is "foo" | |
| and we want to complete to "foobar". If WORD is "oo", return | |
| "oobar"; if WORD is "baz/foo", return "baz/foobar". */ | |
| void | |
| complete_on_enum (completion_tracker &tracker, | |
| const char *const *enumlist, | |
| const char *text, const char *word) | |
| { | |
| int textlen = strlen (text); | |
| int i; | |
| const char *name; | |
| for (i = 0; (name = enumlist[i]) != NULL; i++) | |
| if (strncmp (name, text, textlen) == 0) | |
| tracker.add_completion (make_completion_match_str (name, text, word)); | |
| } | |
| /* Call the command function. */ | |
| void | |
| cmd_func (struct cmd_list_element *cmd, const char *args, int from_tty) | |
| { | |
| if (!cmd->is_command_class_help ()) | |
| { | |
| std::optional<scoped_restore_tmpl<bool>> restore_suppress; | |
| if (cmd->suppress_notification != NULL) | |
| restore_suppress.emplace (cmd->suppress_notification, true); | |
| cmd->func (args, from_tty, cmd); | |
| } | |
| else | |
| error (_("Invalid command")); | |
| } | |
| int | |
| cli_user_command_p (struct cmd_list_element *cmd) | |
| { | |
| return cmd->theclass == class_user && cmd->func == do_simple_func; | |
| } | |
| /* See cli-decode.h. */ | |
| ui_file_style::color | |
| parse_cli_var_color (const char **args) | |
| { | |
| /* Do a "set" command. ARG is nullptr if no argument, or the | |
| text of the argument. */ | |
| if (args == nullptr || *args == nullptr || **args == '\0') | |
| { | |
| std::string msg; | |
| for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i) | |
| { | |
| msg.append ("\""); | |
| msg.append (ui_file_style::basic_color_enums[i]); | |
| msg.append ("\", "); | |
| } | |
| error (_("Requires an argument. Valid arguments are %sinteger from -1 " | |
| "to 255 or an RGB hex triplet in a format #RRGGBB"), | |
| msg.c_str ()); | |
| } | |
| const char *p = skip_to_space (*args); | |
| size_t len = p - *args; | |
| int nmatches = 0; | |
| ui_file_style::basic_color match = ui_file_style::NONE; | |
| for (int i = 0; ui_file_style::basic_color_enums[i]; ++i) | |
| if (strncmp (*args, ui_file_style::basic_color_enums[i], len) == 0) | |
| { | |
| match = static_cast<ui_file_style::basic_color> (i - 1); | |
| if (ui_file_style::basic_color_enums[i][len] == '\0') | |
| { | |
| nmatches = 1; | |
| break; /* Exact match. */ | |
| } | |
| else | |
| nmatches++; | |
| } | |
| if (nmatches == 1) | |
| { | |
| *args += len; | |
| return ui_file_style::color (match); | |
| } | |
| if (nmatches > 1) | |
| error (_("Ambiguous item \"%.*s\"."), (int) len, *args); | |
| if (**args != '#') | |
| { | |
| ULONGEST num = get_ulongest (args); | |
| if (num > 255) | |
| error (_("integer %s out of range"), pulongest (num)); | |
| return ui_file_style::color (color_space::XTERM_256COLOR, | |
| static_cast<int> (num)); | |
| } | |
| /* Try to parse #RRGGBB string. */ | |
| if (len != 7) | |
| error_no_arg (_("invalid RGB hex triplet format")); | |
| uint32_t rgb; | |
| uint8_t r, g, b; | |
| int scanned_chars = 0; | |
| int parsed_args = sscanf (*args, "#%6" SCNx32 "%n", | |
| &rgb, &scanned_chars); | |
| if (parsed_args != 1 || scanned_chars != 7) | |
| error_no_arg (_("invalid RGB hex triplet format")); | |
| gdb_assert ((rgb >> 24) == 0); | |
| r = (rgb >> 16) & 0xff; | |
| g = (rgb >> 8) & 0xff; | |
| b = rgb & 0xff; | |
| *args += len; | |
| return ui_file_style::color (r, g, b); | |
| } | |
| /* See cli-decode.h. */ | |
| ui_file_style::color | |
| parse_var_color (const char *arg) | |
| { | |
| const char *end_arg = arg; | |
| ui_file_style::color color = parse_cli_var_color (&end_arg); | |
| int len = end_arg - arg; | |
| const char *after = skip_spaces (end_arg); | |
| if (*after != '\0') | |
| error (_("Junk after item \"%.*s\": %s"), len, arg, after); | |
| return color; | |
| } |