From patchwork Sat Feb 8 14:09:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philippe Waroquiers X-Patchwork-Id: 37760 Received: (qmail 38071 invoked by alias); 8 Feb 2020 14:10:00 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 37942 invoked by uid 89); 8 Feb 2020 14:09:59 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-22.1 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.1 spammy=inferior-tty, inferiortty, superior, Walk X-HELO: mailsec114.isp.belgacom.be Received: from mailsec114.isp.belgacom.be (HELO mailsec114.isp.belgacom.be) (195.238.20.110) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 08 Feb 2020 14:09:53 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=skynet.be; i=@skynet.be; q=dns/txt; s=securemail; t=1581170993; x=1612706993; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=ZGdoHZdwX3x5PEy8iMFalGzJ6BllEpoXvZKkzWf2U4U=; b=wR6Tqr7xGv1QXmnmCnirk8SiFkQAItQ3qRSC3JJX9So11Yy4XSj5oXb9 heeBoiqdUvkGsZ/goxSG6z44At/QHg==; IronPort-SDR: x2QLZD8UFX3A3mrjtyc34xSJYpe4t8KSq2SQO9TqoF7BTKFXiZE5Rs4OeoXD4XuXcwQacSO3ai MwCQ9Iyg+iJx87B/Lp1MsLyXTY2VNP/TT2SgSnULCBUH7qnH3kMS13idUva7CMFEUfb7jiuvpr 3g4ZlKOJjLP0+tEMnBt+1O0b+j4nYbsaPwd/yx4xYU9jR/IRItQ8bD9elQyzHP6HNLFyDRijvC tTbT+2c1kArd8MFqhqccMFzeKsP4sqzfoP9xqcZH6AmRcku1WuOU6U6oiwNCDKT3ZNOQb5ihKt r5A= Received: from 156.47-242-81.adsl-dyn.isp.belgacom.be (HELO md.home) ([81.242.47.156]) by relay.skynet.be with ESMTP/TLS/ECDHE-RSA-AES128-GCM-SHA256; 08 Feb 2020 15:09:18 +0100 From: Philippe Waroquiers To: gdb-patches@sourceware.org Cc: Philippe Waroquiers Subject: [RFAv4 1/3] default-args: allow to define default command/alias arguments Date: Sat, 8 Feb 2020 15:09:11 +0100 Message-Id: <20200208140913.13298-2-philippe.waroquiers@skynet.be> In-Reply-To: <20200208140913.13298-1-philippe.waroquiers@skynet.be> References: <20200208140913.13298-1-philippe.waroquiers@skynet.be> MIME-Version: 1.0 X-IsSubscribed: yes Currently, a user can define an alias, but cannot have default arguments for this alias. This patch provides the following: * A new command 'set default-args' that can define default args to prepend to the command or alias arguments before executing it. Note that default args can be used for an alias and can nicely combine nested "with" (see below the changes for the alias command). (gdb) help set default-args Set or clear default args automatically prepended to a command or alias. Usage: set default-args COMMAND [DEFAULT-ARGS...] Set or clear the default arguments automatically prepended to the arguments the user provides when COMMAND is run. Note that COMMAND can be an alias. Commands and their aliases do not share their default arguments, so you can specify different default arguments for a command and for each of its aliases. Without the [= DEFAULT-ARGS...], clears COMMAND default arguments. Note that 'set default-args' command has a completer to help typing COMMAND and its default-args. * A new command 'show default-args'. (gdb) help show default-args Show the default args of a command, or of all commands. Usage: show default-args [COMMAND] Show the default args of COMMAND. Without COMMAND, show the default args of all commands. * The 'alias' command is modified so as to directly accept default-args. (gdb) help alias Define a new command that is an alias of an existing command. Usage: alias [-a] [--] ALIAS = COMMAND [DEFAULT-ARGS...] ALIAS is the name of the alias command to create. COMMAND is the command being aliased to. Options: -a Specify that ALIAS is an abbreviation of COMMAND. Abbreviations are not shown in command lists displayed by the 'help' command. You can optionally provide DEFAULT-ARGS to define at the same time ALIAS and its default args. This is the equivalent of: alias ALIAS = COMMAND set default-args ALIAS DEFAULT-ARGS... Examples: Make "spe" an alias of "set print elements": alias spe set print elements Make "elms" an alias of "elements" in the "set print" command: alias -a set print elms set print elements Make "btf" an alias of "backtrace -full -past-entry -past-main" : alias btf = backtrace -full -past-entry -past-main Make "wLapPeu" an alias of 2 nested "with": alias wLapPeu = with language pascal -- with print elements unlimited -- * 'alias' command now has a completer that helps to complete: - ALIAS (if the user defines an alias after a prefix), - the aliased COMMAND - the possible options for the aliased COMMAND. * A new setting 'set enable-default-args'. This can be used if the user wants to temporarily disable the usage of default args. 'show enable-default-args' shows the value of 'enable-default-args'. The main intended usage of this setting is to use it in the "with" command: as set default-args can add arguments to standard GDB commands, it is useful (e.g. in user defined commands) to be able to go back to the 'standard' behaviour of a command. gdb/ChangeLog YYYY-MM-DD Philippe Waroquiers * cli/cli-cmds.c (default_args_cmd_completer) (lookup_cmd_for_default_args, set_default_args_command) (show_default_args, show_default_args_command_1) (show_default_args_command, alias_command_completer) (make_alias_options_def_group): New functions. (alias_opts, alias_option_defs): New struct and array. (alias_usage_error): Update usage. (alias_command): Handles optional DEFAULT-ARGS... arguments. Use option framework. (_initialize_cli_cmds): Install 'set|show default-args' commands. Update alias command help. (show_user, valid_command_p): Add NULL for new default_args lookup_cmd argument. * cli/cli-decode.c (help_cmd): Show default args if command has some. (lookup_cmd_1, lookup_cmd): New argument default_args. (add_alias_cmd): Add NULL for new default_args lookup_cmd argument. (print_help_for_command): Show default args. * cli/cli-decode.h (struct cmd_list_element): New member default_args. xfree default_args in destructor. * cli/cli-script.c (process_next_line, do_define_command): Add NULL for new default_args lookup_cmd argument. * command.h: Declare new default_args argument in lookup_cmd and lookup_cmd_1. * completer.c (complete_line_internal_1): Add NULL for new default_args lookup_cmd or lookup_cmd_1 argument. * guile/scm-cmd.c (gdbscm_parse_command_name): Likewise. * guile/scm-param.c (add_setshow_generic, pascm_parameter_defined_p): Likewise. * infcmd.c (_initialize_infcmd): Likewise. * python/py-auto-load.c (gdbpy_initialize_auto_load): Likewise. * python/py-cmd.c (gdbpy_parse_command_name): Likewise. * python/py-param.c (add_setshow_generic): Likewise. * remote.c (_initialize_remote): Likewise. * top.c (enable_default_args_var): New flag. (execute_command): Prepend default_args if command has some. (set_verbose): Add NULL for new default_args lookup_cmd or lookup_cmd_1 argument. (init_main): Install 'set|show enable-default-args. * tracepoint.c (validate_actionline, encode_actions_1): Add NULL for new default_args lookup_cmd or lookup_cmd_1 argument. --- gdb/cli/cli-cmds.c | 325 ++++++++++++++++++++++++++++++++++---- gdb/cli/cli-decode.c | 89 +++++++++-- gdb/cli/cli-decode.h | 6 + gdb/cli/cli-script.c | 12 +- gdb/command.h | 2 + gdb/completer.c | 2 +- gdb/guile/scm-cmd.c | 2 +- gdb/guile/scm-param.c | 6 +- gdb/infcmd.c | 6 +- gdb/python/py-auto-load.c | 4 +- gdb/python/py-cmd.c | 2 +- gdb/python/py-param.c | 4 +- gdb/remote.c | 4 +- gdb/top.c | 40 ++++- gdb/tracepoint.c | 6 +- 15 files changed, 439 insertions(+), 71 deletions(-) diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index 6f324410e1..d8197aa740 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -50,6 +50,7 @@ #include "cli/cli-cmds.h" #include "cli/cli-style.h" #include "cli/cli-utils.h" +#include "cli/cli-style.h" #include "extension.h" #include "gdbsupport/pathstuff.h" @@ -230,6 +231,7 @@ with_command_1 (const char *set_cmd_prefix, nested_cmd = repeat_previous (); cmd_list_element *set_cmd = lookup_cmd (&args, setlist, set_cmd_prefix, + nullptr, /*allow_unknown=*/ 0, /*ignore_help_classes=*/ 1); gdb_assert (set_cmd != nullptr); @@ -324,6 +326,146 @@ with_command_completer (struct cmd_list_element *ignore, with_command_completer_1 ("set ", tracker, text); } +/* Completer for the "set|show default-args" commands. */ + +static void +default_args_cmd_completer (struct cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char *word) +{ + tracker.set_use_custom_word_point (true); + + complete_nested_command_line (tracker, text); +} + +/* Look up the contents of TEXT as a command usable with default args. + Throws an error if no such command is found. + Return the found command and advances TEXT past the found command. + If the found command is a postfix command, set *PREFIX_CMD to its prefix command. */ + +static struct cmd_list_element * +lookup_cmd_for_default_args (const char **text, + struct cmd_list_element **prefix_cmd) +{ + const char *orig_text = *text; + struct cmd_list_element *lcmd, *alias, *cmd; + + if (*text == nullptr || skip_spaces (*text) == nullptr) + error (_("COMMAND missing.")); + + /* We first use lookup_cmd to verify TEXT unambiguously identifies + a command. */ + lcmd = lookup_cmd (text, cmdlist, "", NULL, + /*allow_unknown=*/ 0, + /*ignore_help_classes=*/ 1); + + /* Note that we accept default args for prefix commands, + as a prefix command can also be a valid usable + command accepting some arguments. + For example, "thread apply" applies a command to a + list of thread ids, and is also the prefix command for + thread apply all. */ + + /* We have an unambiguous command for which default args + can be specified. What remains after having fond LCMD + is either spaces, or the default args character. */ + + /* We then use lookup_cmd_composition to detect if the user + has specified an alias, and find the possible prefix_cmd + of cmd. */ + lookup_cmd_composition + (std::string (orig_text, *text - orig_text).c_str (), + &alias, prefix_cmd, &cmd); + gdb_assert (cmd != nullptr); + gdb_assert (cmd == lcmd); + if (alias != nullptr) + cmd = alias; + + return cmd; +} + +/* Implementation of the "set default-args" command. */ + +static void +set_default_args_command (const char *arg, int from_tty) +{ + struct cmd_list_element *prefix_cmd; + struct cmd_list_element *cmd = lookup_cmd_for_default_args (&arg, &prefix_cmd); + + const char *default_args = skip_spaces (arg); + + if (*default_args == '\0') + default_args = nullptr; + + if (cmd->default_args) + xfree ((char *) cmd->default_args); + if (default_args == nullptr) + cmd->default_args = nullptr; + else + cmd->default_args = xstrdup (default_args); +} + +/* Print a message showing C's name and its default args or + if C has no default args. + If SILENT_FOR_NO_DEFAULT_ARGS, does nothing if C has no default args. */ + +static void +show_default_args (struct cmd_list_element *c, + const char *prefix, + bool silent_for_no_default_args) +{ + if (c->default_args != nullptr || !silent_for_no_default_args) + { + fputs_filtered ("default-args ", gdb_stdout); + fprintf_styled (gdb_stdout, title_style.style (), + "%s%s", + prefix == nullptr ? "" : prefix, c->name); + fprintf_filtered (gdb_stdout, " = %s\n", + c->default_args == nullptr + ? "" : c->default_args); + } +} + +/* Recursively traverse COMMANDLIST and prints a message showing + the default-args of the commands that have non-null default args. + PREFIX is the prefix that led to COMMANDLIST. */ + +static void +show_default_args_command_1 (struct cmd_list_element *commandlist, + const char *prefix) +{ + /* Walk through the commands. */ + for (cmd_list_element *c = commandlist; c; c = c->next) + { + show_default_args (c, prefix, true); + /* If C has subcommands, recursively search if its subcommands + have default args. + Do not recurse for abbreviations to avoid duplicates + in the output. */ + if (c->prefixlist != NULL && !c->abbrev_flag) + show_default_args_command_1 (*c->prefixlist, c->prefixname); + } +} + +/* Implementation of the "show default-args" command. */ + +static void +show_default_args_command (const char *arg, int from_tty) +{ + if (skip_spaces (arg) != nullptr) + { + struct cmd_list_element *prefix_cmd; + struct cmd_list_element *c = lookup_cmd_for_default_args (&arg, + &prefix_cmd); + + show_default_args (c, + prefix_cmd == nullptr ? "" : prefix_cmd->prefixname, + false); + } + else + show_default_args_command_1 (cmdlist, ""); +} + /* Provide documentation on command or list given by COMMAND. FROM_TTY is ignored. */ @@ -1550,7 +1692,7 @@ show_user (const char *args, int from_tty) { const char *comname = args; - c = lookup_cmd (&comname, cmdlist, "", 0, 1); + c = lookup_cmd (&comname, cmdlist, "", NULL, 0, 1); if (!cli_user_command_p (c)) error (_("Not a user command.")); show_user_1 (c, "", args, gdb_stdout); @@ -1582,6 +1724,71 @@ apropos_command (const char *arg, int from_tty) apropos_cmd (gdb_stdout, cmdlist, verbose, pattern, ""); } +/* The options for the "alias" command. */ + +struct alias_opts +{ + /* For "-a". */ + bool abbrev_flag = 0; +}; + +static const gdb::option::option_def alias_option_defs[] = { + + gdb::option::flag_option_def { + "a", + [] (alias_opts *opts) { return &opts->abbrev_flag; }, + N_("Specify that ALIAS is an abbreviation of COMMAND.\n\ +Abbreviations are not shown in command lists displayed by the 'help' command."), + }, + +}; + +/* Create an option_def_group for the "alias" options, with + A_OPTS as context. */ + +static inline gdb::option::option_def_group +make_alias_options_def_group (alias_opts *a_opts) +{ + return {{alias_option_defs}, a_opts}; +} + +/* Completer for the "alias_command". */ + +static void +alias_command_completer (struct cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char *word) +{ + const auto grp = make_alias_options_def_group (nullptr); + + tracker.set_use_custom_word_point (true); + + if (gdb::option::complete_options + (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp)) + return; + + const char *delim = strstr (text, "="); + + /* If we're past the "=" delimiter, complete the + "alias ALIAS = COMMAND [DEFAULT-ARGS...]" as if the user is + typing COMMAND DEFAULT-ARGS... */ + if (delim != text + && delim != nullptr + && isspace (delim[-1]) + && (isspace (delim[1]) || delim[1] == '\0')) + { + std::string new_text = std::string (delim + 1); + + tracker.advance_custom_word_point_by (delim + 1 - text); + complete_nested_command_line (tracker, new_text.c_str ()); + return; + } + + /* We're not yet past the "=" delimiter. Complete a command, as + the user might type an alias following a prefix command. */ + complete_nested_command_line (tracker, text); +} + /* Subroutine of alias_command to simplify it. Return the first N elements of ARGV flattened back to a string with a space separating each element. @@ -1616,7 +1823,7 @@ valid_command_p (const char *command) { struct cmd_list_element *c; - c = lookup_cmd_1 (& command, cmdlist, NULL, 1); + c = lookup_cmd_1 (& command, cmdlist, NULL, NULL, 1); if (c == NULL || c == (struct cmd_list_element *) -1) return false; @@ -1634,7 +1841,7 @@ valid_command_p (const char *command) static void alias_usage_error (void) { - error (_("Usage: alias [-a] [--] ALIAS = COMMAND")); + error (_("Usage: alias [-a] [--] ALIAS = COMMAND [DEFAULT-ARGS...]")); } /* Make an alias of an existing command. */ @@ -1642,8 +1849,13 @@ alias_usage_error (void) static void alias_command (const char *args, int from_tty) { + alias_opts a_opts; + + auto grp = make_alias_options_def_group (&a_opts); + gdb::option::process_options + (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp); + int i, alias_argc, command_argc; - int abbrev_flag = 0; const char *equals; const char *alias, *command; @@ -1654,24 +1866,18 @@ alias_command (const char *args, int from_tty) std::string args2 (args, equals - args); gdb_argv built_alias_argv (args2.c_str ()); - gdb_argv command_argv (equals + 1); + + const char *default_args = equals + 1; + struct cmd_list_element *c_command_prefix; + + lookup_cmd_for_default_args (&default_args, &c_command_prefix); + std::string command_argv_str (equals + 1, + default_args == nullptr + ? strlen (equals + 1) + : default_args - equals - 1); + gdb_argv command_argv (command_argv_str.c_str ()); char **alias_argv = built_alias_argv.get (); - while (alias_argv[0] != NULL) - { - if (strcmp (alias_argv[0], "-a") == 0) - { - ++alias_argv; - abbrev_flag = 1; - } - else if (strcmp (alias_argv[0], "--") == 0) - { - ++alias_argv; - break; - } - else - break; - } if (alias_argv[0] == NULL || command_argv[0] == NULL || *alias_argv[0] == '\0' || *command_argv[0] == '\0') @@ -1706,6 +1912,8 @@ alias_command (const char *args, int from_tty) if (valid_command_p (alias)) error (_("Alias already exists: %s"), alias); + struct cmd_list_element *alias_cmd; + /* If ALIAS is one word, it is an alias for the entire COMMAND. Example: alias spe = set print elements @@ -1718,8 +1926,8 @@ alias_command (const char *args, int from_tty) if (alias_argc == 1) { /* add_cmd requires *we* allocate space for name, hence the xstrdup. */ - add_com_alias (xstrdup (alias_argv[0]), command, class_alias, - abbrev_flag); + alias_cmd = add_com_alias (xstrdup (alias_argv[0]), command, class_alias, + a_opts.abbrev_flag); } else { @@ -1739,19 +1947,30 @@ alias_command (const char *args, int from_tty) alias_prefix = alias_prefix_string.c_str (); command_prefix = command_prefix_string.c_str (); - c_command = lookup_cmd_1 (& command_prefix, cmdlist, NULL, 1); + c_command = lookup_cmd_1 (& command_prefix, cmdlist, NULL, NULL, 1); /* We've already tried to look up COMMAND. */ gdb_assert (c_command != NULL && c_command != (struct cmd_list_element *) -1); gdb_assert (c_command->prefixlist != NULL); - c_alias = lookup_cmd_1 (& alias_prefix, cmdlist, NULL, 1); + c_alias = lookup_cmd_1 (& alias_prefix, cmdlist, NULL, NULL, 1); if (c_alias != c_command) error (_("ALIAS and COMMAND prefixes do not match.")); /* add_cmd requires *we* allocate space for name, hence the xstrdup. */ - add_alias_cmd (xstrdup (alias_argv[alias_argc - 1]), - command_argv[command_argc - 1], - class_alias, abbrev_flag, c_command->prefixlist); + alias_cmd = add_alias_cmd (xstrdup (alias_argv[alias_argc - 1]), + command_argv[command_argc - 1], + class_alias, a_opts.abbrev_flag, + c_command->prefixlist); + } + + gdb_assert (alias_cmd); + gdb_assert (alias_cmd->default_args == nullptr); + if (default_args != nullptr) + { + default_args = skip_spaces (default_args); + + if (*default_args != '\0') + alias_cmd->default_args = xstrdup (default_args); } } @@ -1940,7 +2159,7 @@ setting_cmd (const char *fnname, struct cmd_list_element *showlist, error (_("First argument of %s must be a string."), fnname); const char *a0 = (const char *) value_contents (argv[0]); - cmd_list_element *cmd = lookup_cmd (&a0, showlist, "", -1, 0); + cmd_list_element *cmd = lookup_cmd (&a0, showlist, "", NULL, -1, 0); if (cmd == nullptr || cmd_type (cmd) != show_cmd) error (_("First argument of %s must be a " @@ -2299,6 +2518,26 @@ You can supply a command number to start with, or a `+' to start after\n\ the previous command number shown."), &showlist); + c = add_cmd ("default-args", class_support, set_default_args_command, _("\ +Set or clear default args automatically prepended to a command or alias.\n\ +Usage: set default-args COMMAND [DEFAULT-ARGS...]\n\ +Set or clear the default arguments automatically prepended\n\ +to the arguments the user provides when COMMAND is run.\n\ +Note that COMMAND can be an alias. Commands and their aliases\n\ +do not share their default arguments, so you can specify different\n\ +default arguments for a command and for each of its aliases.\n\ +Without the [= DEFAULT-ARGS...], clears COMMAND default arguments."), + &setlist); + set_cmd_completer_handle_brkchars (c, default_args_cmd_completer); + + c = add_cmd ("default-args", class_support, show_default_args_command, _("\ +Show the default args of a command, or of all commands.\n\ +Usage: show default-args [COMMAND]\n\ +Show the default args of COMMAND. Without COMMAND, show the default args\n\ +of all commands."), + &showlist); + set_cmd_completer_handle_brkchars (c, default_args_cmd_completer); + add_cmd ("version", no_set_class, show_version, _("Show what version of GDB this is."), &showlist); @@ -2450,19 +2689,37 @@ When 'on', each command is displayed as it is executed."), NULL, &setlist, &showlist); - c = add_com ("alias", class_support, alias_command, _("\ + const auto alias_opts = make_alias_options_def_group (nullptr); + + static std::string alias_help + = gdb::option::build_help (_("\ Define a new command that is an alias of an existing command.\n\ -Usage: alias [-a] [--] ALIAS = COMMAND\n\ +Usage: alias [-a] [--] ALIAS = COMMAND [DEFAULT-ARGS...]\n\ ALIAS is the name of the alias command to create.\n\ COMMAND is the command being aliased to.\n\ -If \"-a\" is specified, the command is an abbreviation,\n\ -and will not appear in help command list output.\n\ +\n\ +Options:\n\ +%OPTIONS%\n\ +You can optionally provide DEFAULT-ARGS to define at the same time\n\ +ALIAS and its default args. This is the equivalent of:\n\ + alias ALIAS = COMMAND\n\ + set default-args ALIAS DEFAULT-ARGS...\n\ \n\ Examples:\n\ Make \"spe\" an alias of \"set print elements\":\n\ - alias spe = set print elements\n\ + alias spe set print elements\n\ Make \"elms\" an alias of \"elements\" in the \"set print\" command:\n\ - alias -a set print elms = set print elements")); + alias -a set print elms set print elements\n\ +Make \"btf\" an alias of \"backtrace -full -past-entry -past-main\" :\n\ + alias btf = backtrace -full -past-entry -past-main\n\ +Make \"wLapPeu\" an alias of 2 nested \"with\":\n\ + alias wLapPeu = with language pascal -- with print elements unlimited --"), + alias_opts); + + c = add_com ("alias", class_support, alias_command, + alias_help.c_str ()); + + set_cmd_completer_handle_brkchars (c, alias_command_completer); const char *source_help_text = xstrprintf (_("\ Read commands from a file named FILE.\n\ diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c index 7aecd9897e..8cfff843d2 100644 --- a/gdb/cli/cli-decode.c +++ b/gdb/cli/cli-decode.c @@ -343,7 +343,7 @@ add_alias_cmd (const char *name, const char *oldname, struct cmd_list_element *old; tmp = oldname; - old = lookup_cmd (&tmp, *list, "", 1, 1); + old = lookup_cmd (&tmp, *list, "", NULL, 1, 1); return add_alias_cmd (name, old, theclass, abbrev_flag, list); } @@ -1045,6 +1045,7 @@ void help_cmd (const char *command, struct ui_file *stream) { struct cmd_list_element *c; + const char *initial_command = command; if (!command) { @@ -1058,7 +1059,7 @@ help_cmd (const char *command, struct ui_file *stream) return; } - c = lookup_cmd (&command, cmdlist, "", 0, 0); + c = lookup_cmd (&command, cmdlist, "", NULL, 0, 0); if (c == 0) return; @@ -1078,6 +1079,41 @@ help_cmd (const char *command, struct ui_file *stream) fputs_filtered (c->doc, stream); fputs_filtered ("\n", stream); + if (c->func != nullptr) + { + /* Print the default args of the command if it has some. + Use lookup_cmd_composition to find if help was requested for + an alias. In this case, rather print the alias default_args. */ + + struct cmd_list_element *alias, *prefix_cmd, *cmd; + const char *name, *default_args; + + lookup_cmd_composition (initial_command, + &alias, &prefix_cmd, &cmd); + gdb_assert (cmd != nullptr); + gdb_assert (cmd == c); + + if (alias == nullptr) + { + name = c->name; + default_args = c->default_args; + } + else + { + name = alias->name; + default_args = alias->default_args; + } + if (default_args != nullptr) + { + fputs_filtered ("default-args ", stream); + fprintf_styled (stream, title_style.style (), + "%s%s", + prefix_cmd == nullptr ? "" : prefix_cmd->prefixname, + name); + fprintf_filtered (stream, " = %s\n", default_args); + } + } + if (c->prefixlist == 0 && c->func != NULL) return; fprintf_filtered (stream, "\n"); @@ -1271,6 +1307,13 @@ print_help_for_command (struct cmd_list_element *c, const char *prefix, fputs_filtered (" -- ", stream); print_doc_line (stream, c->doc, false); fputs_filtered ("\n", stream); + if (c->default_args != nullptr) + { + fputs_filtered (" default-args ", stream); + fprintf_styled (stream, title_style.style (), + "%s%s", prefix, c->name); + fprintf_filtered (stream, " = %s\n", c->default_args); + } if (recurse && c->prefixlist != 0 @@ -1422,8 +1465,12 @@ valid_user_defined_cmd_name_p (const char *name) the list in which there are ambiguous choices (and *TEXT will be set to the ambiguous text string). + if DEFAULT_ARGS is not null and the found command has default args defined, + then *DEFAULT_ARGS points at these default args. + If the located command was an abbreviation, this routine returns the base - command of the abbreviation. + command of the abbreviation. Note that *DEFAULT_ARGS will point at the + default args defined for the alias. It does no error reporting whatsoever; control will always return to the superior routine. @@ -1450,11 +1497,13 @@ valid_user_defined_cmd_name_p (const char *name) struct cmd_list_element * lookup_cmd_1 (const char **text, struct cmd_list_element *clist, - struct cmd_list_element **result_list, int ignore_help_classes) + struct cmd_list_element **result_list, const char **default_args, + int ignore_help_classes) { char *command; int len, nfound; struct cmd_list_element *found, *c; + bool found_alias = false; const char *line = *text; while (**text == ' ' || **text == '\t') @@ -1490,6 +1539,8 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist, /* Will be modified in calling routine if we know what the prefix command is. */ *result_list = 0; + if (default_args != NULL) + *default_args = nullptr; return CMD_LIST_AMBIGUOUS; /* Ambiguous. */ } @@ -1505,22 +1556,30 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist, 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) deprecated_cmd_warning (line); + + /* Return the default_args of the alias, not the default_args + of the command it is pointing to. */ + if (default_args != NULL) + *default_args = found->default_args; found = found->cmd_pointer; + found_alias = true; } /* If we found a prefix command, keep looking. */ if (found->prefixlist) { c = lookup_cmd_1 (text, *found->prefixlist, result_list, - ignore_help_classes); + default_args, ignore_help_classes); if (!c) { /* Didn't find anything; this is as far as we got. */ if (result_list != NULL) *result_list = clist; + if (!found_alias && default_args != NULL) + *default_args = found->default_args; return found; } else if (c == CMD_LIST_AMBIGUOUS) @@ -1535,6 +1594,9 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist, 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 = nullptr; return c; } else @@ -1547,6 +1609,8 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist, { if (result_list != NULL) *result_list = clist; + if (!found_alias && default_args != NULL) + *default_args = found->default_args; return found; } } @@ -1566,8 +1630,14 @@ undef_cmd_error (const char *cmdtype, const char *q) /* 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 - and update *LINE to point after the command name, at the first argument. + 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 point at + 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, @@ -1581,6 +1651,7 @@ undef_cmd_error (const char *cmdtype, const char *q) struct cmd_list_element * lookup_cmd (const char **line, struct cmd_list_element *list, const char *cmdtype, + const char **default_args, int allow_unknown, int ignore_help_classes) { struct cmd_list_element *last_list = 0; @@ -1592,7 +1663,7 @@ lookup_cmd (const char **line, struct cmd_list_element *list, if (!*line) error (_("Lack of needed %scommand"), cmdtype); - c = lookup_cmd_1 (line, list, &last_list, ignore_help_classes); + c = lookup_cmd_1 (line, list, &last_list, default_args, ignore_help_classes); if (!c) { diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h index 4f7c7701e4..92bff3b38c 100644 --- a/gdb/cli/cli-decode.h +++ b/gdb/cli/cli-decode.h @@ -72,6 +72,8 @@ struct cmd_list_element xfree ((char *) doc); if (name_allocated) xfree ((char *) name); + if (default_args) + xfree ((char *) default_args); } DISABLE_COPY_AND_ASSIGN (cmd_list_element); @@ -183,6 +185,10 @@ struct cmd_list_element /* Hook for another command to be executed after this command. */ struct cmd_list_element *hook_post = nullptr; + /* Default arguments to automatically prepend to the user + provided arguments when running this command or alias. */ + const char *default_args = nullptr; + /* Nonzero identifies a prefix command. For them, the address of the variable containing the list of subcommands. */ struct cmd_list_element **prefixlist = nullptr; diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c index c9d2378906..e912613785 100644 --- a/gdb/cli/cli-script.c +++ b/gdb/cli/cli-script.c @@ -974,7 +974,7 @@ process_next_line (const char *p, struct command_line **command, /* Resolve command abbreviations (e.g. 'ws' for 'while-stepping'). */ const char *cmd_name = p; struct cmd_list_element *cmd - = lookup_cmd_1 (&cmd_name, cmdlist, NULL, 1); + = lookup_cmd_1 (&cmd_name, cmdlist, NULL, NULL, 1); cmd_name = skip_spaces (cmd_name); bool inline_cmd = *cmd_name != '\0'; @@ -1331,7 +1331,7 @@ validate_comname (const char **comname) std::string prefix (*comname, last_word - 1); const char *tem = prefix.c_str (); - c = lookup_cmd (&tem, cmdlist, "", 0, 1); + c = lookup_cmd (&tem, cmdlist, "", NULL, 0, 1); if (c->prefixlist == NULL) error (_("\"%s\" is not a prefix command."), prefix.c_str ()); @@ -1387,7 +1387,7 @@ do_define_command (const char *comname, int from_tty, /* Look it up, and verify that we got an exact match. */ tem = comname; - c = lookup_cmd (&tem, *list, "", -1, 1); + c = lookup_cmd (&tem, *list, "", NULL, -1, 1); if (c && strcmp (comname, c->name) != 0) c = 0; @@ -1432,7 +1432,7 @@ do_define_command (const char *comname, int from_tty, { /* Look up cmd it hooks, and verify that we got an exact match. */ tem = comname + hook_name_size; - hookc = lookup_cmd (&tem, *list, "", -1, 0); + hookc = lookup_cmd (&tem, *list, "", NULL, -1, 0); if (hookc && strcmp (comname + hook_name_size, hookc->name) != 0) hookc = 0; if (!hookc && commands == nullptr) @@ -1518,7 +1518,7 @@ document_command (const char *comname, int from_tty) list = validate_comname (&comname); tem = comname; - c = lookup_cmd (&tem, *list, "", 0, 1); + c = lookup_cmd (&tem, *list, "", NULL, 0, 1); if (c->theclass != class_user) error (_("Command \"%s\" is built-in."), comfull); @@ -1567,7 +1567,7 @@ define_prefix_command (const char *comname, int from_tty) /* Look it up, and verify that we got an exact match. */ tem = comname; - c = lookup_cmd (&tem, *list, "", -1, 1); + c = lookup_cmd (&tem, *list, "", NULL, -1, 1); if (c != nullptr && strcmp (comname, c->name) != 0) c = nullptr; diff --git a/gdb/command.h b/gdb/command.h index 7f436c72c9..cb28dd94cb 100644 --- a/gdb/command.h +++ b/gdb/command.h @@ -252,11 +252,13 @@ extern enum cmd_types cmd_type (struct cmd_list_element *cmd); extern struct cmd_list_element *lookup_cmd (const char **, struct cmd_list_element *, const char *, + const char **, int, int); extern struct cmd_list_element *lookup_cmd_1 (const char **, struct cmd_list_element *, struct cmd_list_element **, + const char **, int); extern struct cmd_list_element *deprecate_cmd (struct cmd_list_element *, diff --git a/gdb/completer.c b/gdb/completer.c index 619fb8a028..5e9b13aa90 100644 --- a/gdb/completer.c +++ b/gdb/completer.c @@ -1324,7 +1324,7 @@ complete_line_internal_1 (completion_tracker &tracker, } else { - c = lookup_cmd_1 (&p, cmdlist, &result_list, ignore_help_classes); + c = lookup_cmd_1 (&p, cmdlist, &result_list, NULL, ignore_help_classes); } /* Move p up to the next interesting thing. */ diff --git a/gdb/guile/scm-cmd.c b/gdb/guile/scm-cmd.c index ec1adffa25..8fd2772df4 100644 --- a/gdb/guile/scm-cmd.c +++ b/gdb/guile/scm-cmd.c @@ -512,7 +512,7 @@ gdbscm_parse_command_name (const char *name, prefix_text[i + 1] = '\0'; prefix_text2 = prefix_text; - elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, 1); + elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, NULL, 1); if (elt == NULL || elt == CMD_LIST_AMBIGUOUS) { msg = xstrprintf (_("could not find command prefix '%s'"), prefix_text); diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c index b72766523a..62e2108740 100644 --- a/gdb/guile/scm-param.c +++ b/gdb/guile/scm-param.c @@ -466,13 +466,13 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class, /* Lookup created parameter, and register Scheme object against the parameter context. Perform this task against both lists. */ tmp_name = cmd_name; - param = lookup_cmd (&tmp_name, *show_list, "", 0, 1); + param = lookup_cmd (&tmp_name, *show_list, "", NULL, 0, 1); gdb_assert (param != NULL); set_cmd_context (param, self); *set_cmd = param; tmp_name = cmd_name; - param = lookup_cmd (&tmp_name, *set_list, "", 0, 1); + param = lookup_cmd (&tmp_name, *set_list, "", NULL, 0, 1); gdb_assert (param != NULL); set_cmd_context (param, self); *show_cmd = param; @@ -969,7 +969,7 @@ pascm_parameter_defined_p (const char *name, struct cmd_list_element *list) { struct cmd_list_element *c; - c = lookup_cmd_1 (&name, list, NULL, 1); + c = lookup_cmd_1 (&name, list, NULL, NULL, 1); /* If the name is ambiguous that's ok, it's a new parameter still. */ return c != NULL && c != CMD_LIST_AMBIGUOUS; diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 62890bde2a..fd754ea7f9 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -3191,7 +3191,7 @@ is restored."), show_inferior_tty_command, &setlist, &showlist); cmd_name = "inferior-tty"; - c = lookup_cmd (&cmd_name, setlist, "", -1, 1); + c = lookup_cmd (&cmd_name, setlist, "", NULL, -1, 1); gdb_assert (c != NULL); add_alias_cmd ("tty", c, class_alias, 0, &cmdlist); @@ -3204,7 +3204,7 @@ Follow this command with any number of args, to be passed to the program."), set_args_command, show_args_command, &setlist, &showlist); - c = lookup_cmd (&cmd_name, setlist, "", -1, 1); + c = lookup_cmd (&cmd_name, setlist, "", NULL, -1, 1); gdb_assert (c != NULL); set_cmd_completer (c, filename_completer); @@ -3223,7 +3223,7 @@ working directory."), set_cwd_command, show_cwd_command, &setlist, &showlist); - c = lookup_cmd (&cmd_name, setlist, "", -1, 1); + c = lookup_cmd (&cmd_name, setlist, "", NULL, -1, 1); gdb_assert (c != NULL); set_cmd_completer (c, filename_completer); diff --git a/gdb/python/py-auto-load.c b/gdb/python/py-auto-load.c index 2084fc43ff..56db946463 100644 --- a/gdb/python/py-auto-load.c +++ b/gdb/python/py-auto-load.c @@ -84,12 +84,12 @@ Show the debugger's behaviour regarding auto-loaded Python scripts, " NULL, NULL, show_auto_load_python_scripts, &setlist, &showlist); cmd_name = "auto-load-scripts"; - cmd = lookup_cmd (&cmd_name, setlist, "", -1, 1); + cmd = lookup_cmd (&cmd_name, setlist, "", NULL, -1, 1); deprecate_cmd (cmd, "set auto-load python-scripts"); /* It is needed because lookup_cmd updates the CMD_NAME pointer. */ cmd_name = "auto-load-scripts"; - cmd = lookup_cmd (&cmd_name, showlist, "", -1, 1); + cmd = lookup_cmd (&cmd_name, showlist, "", NULL, -1, 1); deprecate_cmd (cmd, "show auto-load python-scripts"); add_cmd ("python-scripts", class_info, info_auto_load_python_scripts, diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c index b822c14004..b8e2c365ee 100644 --- a/gdb/python/py-cmd.c +++ b/gdb/python/py-cmd.c @@ -390,7 +390,7 @@ gdbpy_parse_command_name (const char *name, std::string prefix_text (name, i + 1); prefix_text2 = prefix_text.c_str (); - elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, 1); + elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, NULL, 1); if (elt == NULL || elt == CMD_LIST_AMBIGUOUS) { PyErr_Format (PyExc_RuntimeError, _("Could not find command prefix %s."), diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c index 7b183cfa55..fb39187b18 100644 --- a/gdb/python/py-param.c +++ b/gdb/python/py-param.c @@ -569,12 +569,12 @@ add_setshow_generic (int parmclass, enum command_class cmdclass, /* Lookup created parameter, and register Python object against the parameter context. Perform this task against both lists. */ tmp_name = cmd_name; - param = lookup_cmd (&tmp_name, *show_list, "", 0, 1); + param = lookup_cmd (&tmp_name, *show_list, "", NULL, 0, 1); if (param) set_cmd_context (param, self); tmp_name = cmd_name; - param = lookup_cmd (&tmp_name, *set_list, "", 0, 1); + param = lookup_cmd (&tmp_name, *set_list, "", NULL, 0, 1); if (param) set_cmd_context (param, self); } diff --git a/gdb/remote.c b/gdb/remote.c index 34a8a08f56..3396a80b87 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -14373,10 +14373,10 @@ If set, a break, instead of a cntrl-c, is sent to the remote target."), set_remotebreak, show_remotebreak, &setlist, &showlist); cmd_name = "remotebreak"; - cmd = lookup_cmd (&cmd_name, setlist, "", -1, 1); + cmd = lookup_cmd (&cmd_name, setlist, "", NULL, -1, 1); deprecate_cmd (cmd, "set remote interrupt-sequence"); cmd_name = "remotebreak"; /* needed because lookup_cmd updates the pointer */ - cmd = lookup_cmd (&cmd_name, showlist, "", -1, 1); + cmd = lookup_cmd (&cmd_name, showlist, "", NULL, -1, 1); deprecate_cmd (cmd, "show remote interrupt-sequence"); add_setshow_enum_cmd ("interrupt-sequence", class_support, diff --git a/gdb/top.c b/gdb/top.c index f702af9acd..2042f3d473 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -549,6 +549,11 @@ set_repeat_arguments (const char *args) repeat_arguments = args; } +/* Flag for whether we want to use the configured default args. + Default is yes. */ + +static bool enable_default_args_var = true; + /* Execute the line P as a command, in the current user context. Pass FROM_TTY as second argument to the defining function. */ @@ -577,6 +582,8 @@ execute_command (const char *p, int from_tty) { const char *cmd = p; const char *arg; + const char *default_args; + std::string default_args_and_arg; int was_sync = current_ui->prompt_state == PROMPT_BLOCKED; line = p; @@ -584,15 +591,27 @@ execute_command (const char *p, int from_tty) /* If trace-commands is set then this will print this command. */ print_command_trace ("%s", p); - c = lookup_cmd (&cmd, cmdlist, "", 0, 1); + c = lookup_cmd (&cmd, cmdlist, "", &default_args, 0, 1); p = cmd; scoped_restore save_repeat_args = make_scoped_restore (&repeat_arguments, nullptr); const char *args_pointer = p; - /* Pass null arg rather than an empty one. */ - arg = *p ? p : 0; + if (default_args != nullptr && enable_default_args_var) + { + if (*p) + default_args_and_arg = std::string (default_args) + + ' ' + std::string (p); + else + default_args_and_arg = std::string (default_args); + arg = default_args_and_arg.c_str (); + } + else + { + /* Pass null arg rather than an empty one. */ + arg = *p ? p : 0; + } /* FIXME: cagney/2002-02-02: The c->type test is pretty dodgy while the is_complete_command(cfunc) test is just plain @@ -1935,7 +1954,7 @@ set_verbose (const char *args, int from_tty, struct cmd_list_element *c) const char *cmdname = "verbose"; struct cmd_list_element *showcmd; - showcmd = lookup_cmd_1 (&cmdname, showlist, NULL, 1); + showcmd = lookup_cmd_1 (&cmdname, showlist, NULL, NULL, 1); gdb_assert (showcmd != NULL && showcmd != CMD_LIST_AMBIGUOUS); if (c->doc && c->doc_allocated) @@ -2171,6 +2190,19 @@ EMACS-like or VI-like commands like control-P or ESC."), show_editing, &setlist, &showlist); + add_setshow_boolean_cmd ("enable-default-args", class_support, + &enable_default_args_var, _("\ +Set whether GDB prepends default args when running a command.\n\ +The default args for a command are configured using\n\ +\"set default-args COMMAND = DEFAULT-ARGS..."), _("\ +Show whether GDB prepends default args when running a command."), _("\ +Use \"on\" to enable the usage of the configured default args,\n\ +and \"off\" to disable it.\n\ +Without an argument, default args usage is enabled."), + NULL, + NULL, + &setlist, &showlist); + add_setshow_boolean_cmd ("save", no_class, &write_history_p, _("\ Set saving of the history record on exit."), _("\ Show saving of the history record on exit."), _("\ diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c index aa6bea4a8f..26df3e0f61 100644 --- a/gdb/tracepoint.c +++ b/gdb/tracepoint.c @@ -651,7 +651,7 @@ validate_actionline (const char *line, struct breakpoint *b) if (*p == '#') /* comment line */ return; - c = lookup_cmd (&p, cmdlist, "", -1, 1); + c = lookup_cmd (&p, cmdlist, "", NULL, -1, 1); if (c == 0) error (_("`%s' is not a tracepoint action, or is ambiguous."), p); @@ -1303,7 +1303,7 @@ encode_actions_1 (struct command_line *action, action_exp = action->line; action_exp = skip_spaces (action_exp); - cmd = lookup_cmd (&action_exp, cmdlist, "", -1, 1); + cmd = lookup_cmd (&action_exp, cmdlist, "", NULL, -1, 1); if (cmd == 0) error (_("Bad action list item: %s"), action_exp); @@ -2673,7 +2673,7 @@ trace_dump_actions (struct command_line *action, if (*action_exp == '#') /* comment line */ continue; - cmd = lookup_cmd (&action_exp, cmdlist, "", -1, 1); + cmd = lookup_cmd (&action_exp, cmdlist, "", NULL, -1, 1); if (cmd == 0) error (_("Bad action list item: %s"), action_exp);