From patchwork Sun Oct 20 10:27:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philippe Waroquiers X-Patchwork-Id: 35183 Received: (qmail 102243 invoked by alias); 20 Oct 2019 10:27:36 -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 102230 invoked by uid 89); 20 Oct 2019 10:27:35 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-20.5 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.1 spammy=ele, tout, tracker, user-visible X-HELO: mailsec103.isp.belgacom.be Received: from mailsec103.isp.belgacom.be (HELO mailsec103.isp.belgacom.be) (195.238.20.99) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sun, 20 Oct 2019 10:27:28 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=skynet.be; i=@skynet.be; q=dns/txt; s=securemail; t=1571567247; x=1603103247; h=message-id:subject:from:to:date:in-reply-to:references: mime-version; bh=SFpXZqaAtGR1FACOm0OPhBKWgQmWBilPv29Pt1s3VT0=; b=ZCNh6sMlbUo4jpEJhASSxislcp91RmQExJd3wyxG2HoFN7w409WhE9a/ /jDTeZeZOU9IfUBGglOLxhTAMijDLA==; Received: from unknown (HELO md) ([109.131.135.220]) by relay.skynet.be with ESMTP/TLS/AES256-GCM-SHA384; 20 Oct 2019 12:27:24 +0200 Message-ID: Subject: Re: PING^2 Re: [RFAv3 0/3] Allow the user to define default leading args for commands and aliases From: Philippe Waroquiers To: gdb-patches@sourceware.org Date: Sun, 20 Oct 2019 12:27:24 +0200 In-Reply-To: <4b0d0cbd16fbd675e32408884ee4ce3c36e20068.camel@skynet.be> References: <20190915192655.27801-1-philippe.waroquiers@skynet.be> <4b0d0cbd16fbd675e32408884ee4ce3c36e20068.camel@skynet.be> User-Agent: Evolution 3.30.5-1.1 MIME-Version: 1.0 X-IsSubscribed: yes Ping ? Thanks Philippe (patch attached rebased on current master) On Sun, 2019-09-29 at 17:31 +0200, Philippe Waroquiers wrote: > Ping > Thanks > Philippe > > On Sun, 2019-09-15 at 21:26 +0200, Philippe Waroquiers wrote: > > Allow the user to define default leading args for commands and aliases. > > > > This patch series implements, tests and documents the following commands: > > > > set leading-args COMMAND [LEADING-ARGS...] > > show leading-args [COMMAND] > > set enable-leading-args [on|off] > > show enable-leading-args > > > > > > It also changes the alias command to be: > > alias [-a] [--] ALIAS = COMMAND [LEADING-ARGS...] > > > > > > Using the above leading-args commands and arguments, you can > > define default arguments for commands or define powerful aliases. > > > > For example: > > > > alias bt_ALL = backtrace -entry-values both -frame-arg all -past-main -past-entry -full > > > > defines the alias bt_ALL that will give as much information as possible > > in a backtrace. > > > > Leading args can usefully be combined with the 'with' command, such as: > > > > Make "wLapPeu" an alias of 2 nested "with": > > alias wLapPeu = with language pascal -- with print elements unlimited -- > > > > or have an alias pp10 to pretty print an expression with a maximum of > > 10 elements: > > > > alias pp10 = with print pretty -- with print elem 10 -- print > > > > This patch series also adds a completer for 'alias'. > > > > > > This is the 3rd version ot the patch series. > > > > Compared to the second version, it handles the preliminary comments > > of Pedro (and is rebased to the last master). > > I have for the moment kept the wording 'leading-args' but can change > > it to default-args if that is preferrable. > > The reason I have used leading-args is because these are default leading > > args: the user can add specific arguments after these leading args. > > > > For what concerns changing 'alias -a' to 'alias -abbreviation": > > This can for sure be done (and be backward compatible). This can however > > be done as a separate patch (I still also need to do a similar change > > for the 'qcs' flags). > > > > The second version changes were: > > * Removed the = separator between COMMAND and LEADING-ARGS > > * Converted alias command to use the option framework. > > * Show the leading args of aliases in 'help aliases'. > > > > Note 1: a preliminary version of this idea (using a command called > > 'add-args') was discussed in > > https://sourceware.org/ml/gdb-patches/2019-06/msg00395.html > > where Tom suggested to make it more GDB like, using 'set' command. > > > > Note 2: Following another comment of Tom, there is no '=' character > > to separate COMMAND from its leading args. > > If ever this would be ambiguous in some cases, we could introduce > > an optional '=' character to separate COMMAND from its LEADING-ARGS. > > Similarly, the alias command has no separator between COMMAND > > and its LEADING-ARGS. > > > > > > From 06175a32537f8bc701663fba2a6b8cc1ed9f973d Mon Sep 17 00:00:00 2001 From: Philippe Waroquiers Date: Wed, 19 Jun 2019 12:49:55 +0200 Subject: [PATCH 1/3] leading-args: allow to define default command/alias arguments Currently, a user can define an alias, but cannot have default arguments for this alias. This patch provides the following: * A new command 'set leading-args' that can define default leading args to prepend to the command or alias arguments before executing it. Note that leading args can be used for an alias and can nicely combine nested "with" (see below the changes for the alias command). (gdb) help set leading-args Set or clear leading args automatically preprended to a command or alias. Usage: set leading-args COMMAND [LEADING-ARGS...] Set or clear the leading 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 leading arguments, so you can specify different leading arguments for a command and for each of its aliases. Without the [LEADING-ARGS...], clears COMMAND leading arguments. Note that 'set leading-args' command has a completer to help typing COMMAND and its leading-args. * A new command 'show leading-args'. (gdb) help show leading-args Show the leading args of a command, or of all commands. Usage: show leading-args [COMMAND] Show the leading args of COMMAND. Without COMMAND, show the leading args of all commands. * The 'alias' command is modified so as to directly accept leading-args. (gdb) help alias Define a new command that is an alias of an existing command. Usage: alias [-a] [--] ALIAS = COMMAND [LEADING-ARGS...] ALIAS is the name of the alias command to create. COMMAND is the command being aliased to. If "-a" is specified, the command is an abbreviation, and will not appear in help command list output. You can optionally provide LEADING-ARGS to define at the same time ALIAS and its leading args. This is the equivalent of: alias ALIAS = COMMAND set leading-args ALIAS LEADING-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-leading-args'. This can be used if the user wants to temporarily disable the usage of leading args. 'show enable-leading-args' shows the value of 'enable-leading-args'. The main intended usage of this setting is to use it in the "with" command: as set leading-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 2019-09-15 Philippe Waroquiers * cli/cli-cmds.c (leading_args_cmd_completer) (lookup_cmd_for_leading_args, set_leading_args_command) (show_leading_args, show_leading_args_command_1) (show_leading_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 LEADING-ARGS... arguments. Use option framework. (_initialize_cli_cmds): Install 'set|show leading-args' commands. Update alias command help. (show_user, valid_command_p): Add NULL for new leading_args lookup_cmd argument. * cli/cli-decode.c (help_cmd): Show leading args if command has some. (lookup_cmd_1, lookup_cmd): New argument leading_args. (add_alias_cmd): Add NULL for new leading_args lookup_cmd argument. (print_help_for_command): Show leading args. * cli/cli-decode.h (struct cmd_list_element): New member leading_args. xfree leading_args in destructor. * cli/cli-script.c (process_next_line, do_define_command): Add NULL for new leading_args lookup_cmd argument. * command.h: Declare new leading_args argument in lookup_cmd and lookup_cmd_1. * completer.c (complete_line_internal_1): Add NULL for new leading_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_leading_args_var): New flag. (execute_command): Prepend leading_args if command has some. (set_verbose): Add NULL for new leading_args lookup_cmd or lookup_cmd_1 argument. (init_main): Install 'set|show enable-leading-args. * tracepoint.c (validate_actionline, encode_actions_1): Add NULL for new leading_args lookup_cmd or lookup_cmd_1 argument. --- gdb/cli/cli-cmds.c | 323 ++++++++++++++++++++++++++++++++++---- gdb/cli/cli-decode.c | 91 +++++++++-- gdb/cli/cli-decode.h | 6 + gdb/cli/cli-script.c | 10 +- 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, 438 insertions(+), 70 deletions(-) diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index a39ea22604..87bacd131c 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -51,6 +51,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" @@ -231,6 +232,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); @@ -325,6 +327,146 @@ with_command_completer (struct cmd_list_element *ignore, with_command_completer_1 ("set ", tracker, text); } +/* Completer for the "set|show leading-args" commands. */ + +static void +leading_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 leading 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_leading_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 leading 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 leading args + can be specified. What remains after having fond LCMD + is either spaces, or the leading 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 leading-args" command. */ + +static void +set_leading_args_command (const char *arg, int from_tty) +{ + struct cmd_list_element *prefix_cmd; + struct cmd_list_element *cmd = lookup_cmd_for_leading_args (&arg, &prefix_cmd); + + const char *leading_args = skip_spaces (arg); + + if (*leading_args == '\0') + leading_args = nullptr; + + if (cmd->leading_args) + xfree ((char *) cmd->leading_args); + if (leading_args == nullptr) + cmd->leading_args = nullptr; + else + cmd->leading_args = xstrdup (leading_args); +} + +/* Print a message showing C's name and its leading args or + if C has no leading args. + If SILENT_FOR_NO_LEADING_ARGS, does nothing if C has no leading args. */ + +static void +show_leading_args (struct cmd_list_element *c, + const char *prefix, + bool silent_for_no_leading_args) +{ + if (c->leading_args != nullptr || !silent_for_no_leading_args) + { + fputs_filtered ("leading-args ", gdb_stdout); + fprintf_styled (gdb_stdout, title_style.style (), + "%s%s", + prefix == nullptr ? "" : prefix, c->name); + fprintf_filtered (gdb_stdout, " = %s\n", + c->leading_args == nullptr + ? "" : c->leading_args); + } +} + +/* Recursively traverse COMMANDLIST and prints a message showing + the leading-args of the commands that have non-null leading args. + PREFIX is the prefix that led to COMMANDLIST. */ + +static void +show_leading_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_leading_args (c, prefix, true); + /* If C has subcommands, recursively search if its subcommands + have leading args. + Do not recurse for abbreviations to avoid duplicates + in the output. */ + if (c->prefixlist != NULL && !c->abbrev_flag) + show_leading_args_command_1 (*c->prefixlist, c->prefixname); + } +} + +/* Implementation of the "show leading-args" command. */ + +static void +show_leading_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_leading_args (&arg, + &prefix_cmd); + + show_leading_args (c, + prefix_cmd == nullptr ? "" : prefix_cmd->prefixname, + false); + } + else + show_leading_args_command_1 (cmdlist, ""); +} + /* Provide documentation on command or list given by COMMAND. FROM_TTY is ignored. */ @@ -1543,7 +1685,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); @@ -1575,6 +1717,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 [LEADING-ARGS...]" as if the user is + typing COMMAND LEADING-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. @@ -1609,7 +1816,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; @@ -1627,7 +1834,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 [LEADING-ARGS...]")); } /* Make an alias of an existing command. */ @@ -1635,8 +1842,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; @@ -1647,24 +1859,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 *leading_args = equals + 1; + struct cmd_list_element *c_command_prefix; + + lookup_cmd_for_leading_args (&leading_args, &c_command_prefix); + std::string command_argv_str (equals + 1, + leading_args == nullptr + ? strlen (equals + 1) + : leading_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') @@ -1699,6 +1905,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 @@ -1711,8 +1919,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 { @@ -1732,19 +1940,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->leading_args == nullptr); + if (leading_args != nullptr) + { + leading_args = skip_spaces (leading_args); + + if (*leading_args != '\0') + alias_cmd->leading_args = xstrdup (leading_args); } } @@ -2059,6 +2278,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 ("leading-args", class_support, set_leading_args_command, _("\ +Set or clear leading args automatically prepended to a command or alias.\n\ +Usage: set leading-args COMMAND [LEADING-ARGS...]\n\ +Set or clear the leading 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 leading arguments, so you can specify different\n\ +leading arguments for a command and for each of its aliases.\n\ +Without the [= LEADING-ARGS...], clears COMMAND leading arguments."), + &setlist); + set_cmd_completer_handle_brkchars (c, leading_args_cmd_completer); + + c = add_cmd ("leading-args", class_support, show_leading_args_command, _("\ +Show the leading args of a command, or of all commands.\n\ +Usage: show leading-args [COMMAND]\n\ +Show the leading args of COMMAND. Without COMMAND, show the leading args\n\ +of all commands."), + &showlist); + set_cmd_completer_handle_brkchars (c, leading_args_cmd_completer); + add_cmd ("version", no_set_class, show_version, _("Show what version of GDB this is."), &showlist); @@ -2210,19 +2449,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 [LEADING-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%\ +You can optionally provide LEADING-ARGS to define at the same time\n\ +ALIAS and its leading args. This is the equivalent of:\n\ + alias ALIAS = COMMAND\n\ + set leading-args ALIAS LEADING-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 debffbc0f7..b6c87180d9 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 leading 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 leading_args. */ + + struct cmd_list_element *alias, *prefix_cmd, *cmd; + const char *name, *leading_args; + + lookup_cmd_composition (initial_command, + &alias, &prefix_cmd, &cmd); + gdb_assert (cmd != nullptr); + gdb_assert (cmd == c); + + if (alias == nullptr) + { + name = c->name; + leading_args = c->leading_args; + } + else + { + name = alias->name; + leading_args = alias->leading_args; + } + if (leading_args != nullptr) + { + fputs_filtered ("leading-args ", stream); + fprintf_styled (stream, title_style.style (), + "%s%s", + prefix_cmd == nullptr ? "" : prefix_cmd->prefixname, + name); + fprintf_filtered (stream, " = %s\n", leading_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->leading_args != nullptr) + { + fputs_filtered (" leading-args ", stream); + fprintf_styled (stream, title_style.style (), + "%s%s", prefix, c->name); + fprintf_filtered (stream, " = %s\n", c->leading_args); + } if (recurse && c->prefixlist != 0 @@ -1418,8 +1461,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 LEADING_ARGS is not null and the found command has leading args defined, + then *LEADING_ARGS points at these leading args. + If the located command was an abbreviation, this routine returns the base - command of the abbreviation. + command of the abbreviation. Note that *LEADING_ARGS will point at the + leading args defined for the alias. It does no error reporting whatsoever; control will always return to the superior routine. @@ -1446,11 +1493,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 **leading_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') @@ -1486,6 +1535,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 (leading_args != NULL) + *leading_args = nullptr; return CMD_LIST_AMBIGUOUS; /* Ambiguous. */ } @@ -1501,22 +1552,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 leading_args of the alias, not the leading_args + of the command it is pointing to. */ + if (leading_args != NULL) + *leading_args = found->leading_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); + leading_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 && leading_args != NULL) + *leading_args = found->leading_args; return found; } else if (c == CMD_LIST_AMBIGUOUS) @@ -1531,6 +1590,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 leading_args args. */ + if (leading_args != nullptr) + *leading_args = nullptr; return c; } else @@ -1543,6 +1605,8 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist, { if (result_list != NULL) *result_list = clist; + if (!found_alias && leading_args != NULL) + *leading_args = found->leading_args; return found; } } @@ -1562,21 +1626,28 @@ 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 *LEADING_ARGS (if LEADING_ARGS is not null) to point at + the leading 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 leading 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 INGNORE_HELP_CLASSES is nonzero, ignore any command list + 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, + const char **leading_args, int allow_unknown, int ignore_help_classes) { struct cmd_list_element *last_list = 0; @@ -1588,7 +1659,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, leading_args, ignore_help_classes); if (!c) { diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h index 2ec4a97d81..b02076191f 100644 --- a/gdb/cli/cli-decode.h +++ b/gdb/cli/cli-decode.h @@ -69,6 +69,8 @@ struct cmd_list_element { if (doc && doc_allocated) xfree ((char *) doc); + if (leading_args) + xfree ((char *) leading_args); } DISABLE_COPY_AND_ASSIGN (cmd_list_element); @@ -176,6 +178,10 @@ struct cmd_list_element /* Hook for another command to be executed after this command. */ struct cmd_list_element *hook_post = nullptr; + /* Leading arguments to automatically prepend to the user + provided arguments when running this command or alias. */ + const char *leading_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 3137955265..959c527ee0 100644 --- a/gdb/cli/cli-script.c +++ b/gdb/cli/cli-script.c @@ -970,7 +970,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'; @@ -1327,7 +1327,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 ()); @@ -1383,7 +1383,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; @@ -1418,7 +1418,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) @@ -1487,7 +1487,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); diff --git a/gdb/command.h b/gdb/command.h index cf190ef649..044e3319d4 100644 --- a/gdb/command.h +++ b/gdb/command.h @@ -244,11 +244,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 07facfb012..b88bdbdeca 100644 --- a/gdb/completer.c +++ b/gdb/completer.c @@ -1341,7 +1341,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 f2fa40e453..8cf70f6e52 100644 --- a/gdb/guile/scm-cmd.c +++ b/gdb/guile/scm-cmd.c @@ -515,7 +515,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 6d24c2c37b..adadb42e10 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 465d3a10e5..e63ae9b40a 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -3146,7 +3146,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); @@ -3159,7 +3159,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); @@ -3178,7 +3178,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 ade2aa1b37..af41c7c459 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 87d1888c52..70e328080c 100644 --- a/gdb/python/py-cmd.c +++ b/gdb/python/py-cmd.c @@ -396,7 +396,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 4794366583..6233f119ef 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 5e1745db44..7a841f0500 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -14294,10 +14294,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 15d4fab8be..f546a3c667 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -533,6 +533,11 @@ set_repeat_arguments (const char *args) repeat_arguments = args; } +/* Flag for whether we want to use the configured leading args. + Default is yes. */ + +static bool enable_leading_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. */ @@ -561,6 +566,8 @@ execute_command (const char *p, int from_tty) { const char *cmd = p; const char *arg; + const char *leading_args; + std::string leading_args_and_arg; int was_sync = current_ui->prompt_state == PROMPT_BLOCKED; line = p; @@ -568,15 +575,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, "", &leading_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 (leading_args != nullptr && enable_leading_args_var) + { + if (*p) + leading_args_and_arg = std::string (leading_args) + + ' ' + std::string (p); + else + leading_args_and_arg = std::string (leading_args); + arg = leading_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 @@ -1883,7 +1902,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) @@ -2113,6 +2132,19 @@ EMACS-like or VI-like commands like control-P or ESC."), show_editing, &setlist, &showlist); + add_setshow_boolean_cmd ("enable-leading-args", class_support, + &enable_leading_args_var, _("\ +Set whether GDB prepends leading args when running a command.\n\ +The leading args for a command are configured using\n\ +\"set leading-args COMMAND = LEADING-ARGS..."), _("\ +Show whether GDB prepends leading args when running a command."), _("\ +Use \"on\" to enable the usage of the configured leading args,\n\ +and \"off\" to disable it.\n\ +Without an argument, leading 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 4db70607ef..40e6b03663 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); @@ -1309,7 +1309,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); @@ -2679,7 +2679,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); -- 2.20.1 From 90dcc6725bfc1d4d3e285e902c2fb3863002c53a Mon Sep 17 00:00:00 2001 From: Philippe Waroquiers Date: Sun, 23 Jun 2019 23:13:57 +0200 Subject: [PATCH 2/3] Add tests for new leading-args related commands and arguments. Test the new leading-args behaviour and completion. Note that gdb.base/leading-args.exp is somewhat copied from with.exp (the test of the with command), while leading-exp.c is a plain copy of with.c. gdb/testsuite/ChangeLog 2019-09-15 Philippe Waroquiers * gdb.base/leading-args.exp: New test. * gdb.base/leading.c: New file. * gdb.base/alias.exp: Update expected error msg for alias foo=bar. --- gdb/testsuite/gdb.base/alias.exp | 2 +- gdb/testsuite/gdb.base/leading-args.c | 41 +++++++ gdb/testsuite/gdb.base/leading-args.exp | 151 ++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 gdb/testsuite/gdb.base/leading-args.c create mode 100644 gdb/testsuite/gdb.base/leading-args.exp diff --git a/gdb/testsuite/gdb.base/alias.exp b/gdb/testsuite/gdb.base/alias.exp index 20722e5dea..764d3ff8ff 100644 --- a/gdb/testsuite/gdb.base/alias.exp +++ b/gdb/testsuite/gdb.base/alias.exp @@ -56,7 +56,7 @@ test_abbrev_alias set6 "alias -a -- set6 = set" 46 test_abbrev_alias -a "alias -a -- -a = set" 47 gdb_test "alias set2=set" "already exists: set2" -gdb_test "alias foo=bar" "Invalid command to alias to: bar" +gdb_test "alias foo=bar" "Undefined command: \"bar\". Try \"help\"." gdb_test_no_output "alias spe = set p elem" gdb_test_no_output "spe 50" diff --git a/gdb/testsuite/gdb.base/leading-args.c b/gdb/testsuite/gdb.base/leading-args.c new file mode 100644 index 0000000000..c6426625d4 --- /dev/null +++ b/gdb/testsuite/gdb.base/leading-args.c @@ -0,0 +1,41 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2019 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 . */ + +int xxx1 = 123; + +struct S +{ + int a; + int b; + int c; +}; + +struct S g_s = {1, 2, 3}; + +static void +inc () +{ + g_s.a++;; +} + +int +main () +{ + inc (); + + return 0; +} diff --git a/gdb/testsuite/gdb.base/leading-args.exp b/gdb/testsuite/gdb.base/leading-args.exp new file mode 100644 index 0000000000..ecc308fb51 --- /dev/null +++ b/gdb/testsuite/gdb.base/leading-args.exp @@ -0,0 +1,151 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2019 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 . + +# Test the "leading-args" related commands and arguments. + +load_lib completion-support.exp + +standard_testfile .c + +if {[build_executable "failed to prepare" $testfile $srcfile debug]} { + return -1 +} + +clean_restart $binfile + +# Basic/core tests using user-visible commands. +with_test_prefix "basics" { + # Define an alias to pretty print something. + gdb_test "print g_s" " = {a = 1, b = 2, c = 3}" "simple print" + gdb_test_no_output "alias PP = print -pretty --" "alias PP" + gdb_test "show leading-args PP" "leading-args PP = -pretty --" + gdb_test "PP g_s" \ + [multi_line \ + " = {" \ + " a = 1," \ + " b = 2," \ + " c = 3" \ + "}"] + + # Define leading-args for "inspect", should not impact "print". + gdb_test_no_output "set leading-args inspect -pretty --" "inspect -pretty" + gdb_test "show leading-args inspect" "leading-args inspect = -pretty --" + gdb_test "print g_s" " = {a = 1, b = 2, c = 3}" "simple print not impacted" + gdb_test "inspect g_s" \ + [multi_line \ + " = {" \ + " a = 1," \ + " b = 2," \ + " c = 3" \ + "}"] + + # Set hex as default FMT for print. + gdb_test_no_output "set leading-args print -- /x" "print hex" + gdb_test "inspect g_s" \ + [multi_line \ + " = {" \ + " a = 1," \ + " b = 2," \ + " c = 3" \ + "}"] "inspect g_s still pretty" + gdb_test "print g_s" " = {a = 0x1, b = 0x2, c = 0x3}" "simple print hex" + + # Test disabling leading args. + gdb_test "show enable-leading-args" \ + "Whether GDB prepends leading args when running a command is on\." + gdb_test_no_output "set enable-leading-args off" + gdb_test "print g_s" " = {a = 1, b = 2, c = 3}" \ + "simple print with enable-leading-args off" + gdb_test_no_output "set enable-leading-args on" + + + # Define leading-args for an alias of frame apply all + gdb_test_no_output "alias frame apply tout = frame apply all -past-entry -past-main" \ + "alias frame apply tout" + gdb_test "show leading-args frame apply tout" \ + "leading-args frame apply tout = -past-entry -past-main" + + # Show all leading args. + gdb_test "show leading-args" \ + [multi_line \ + "leading-args PP = -pretty --" \ + "leading-args frame apply tout = -past-entry -past-main" \ + "leading-args inspect = -pretty --" \ + "leading-args print = -- /x"] "show leading-args" + + # Clear the leading-args of "inspect". + gdb_test_no_output "set leading-args inspect" "clear inspect leading-args" + gdb_test "show leading-args inspect" "leading-args inspect = " + gdb_test "inspect g_s" " = {a = 1, b = 2, c = 3}" "simple inspect" + +} + +# Check errors. +with_test_prefix "errors" { + # Try an unknown root setting. + gdb_test "set leading-args xxxx yyyy -someoption" \ + "Undefined command: \"xxxx\". Try \"help\"\\." + + # Try ambiguous command. + gdb_test "set leading-args a" \ + "Ambiguous command \"a\":.*" "ambiguous a" + gdb_test "set leading-args frame a" \ + "Ambiguous frame command \"a\":.*" "ambiguous frame a" +} + + +# Check completion. +with_test_prefix "completion" { + test_gdb_complete_unique \ + "alias set pri" \ + "alias set print" + + test_gdb_complete_unique \ + "alias set print items = set pri" \ + "alias set print items = set print" + + test_gdb_complete_unique \ + "alias set print items = set print ele" \ + "alias set print items = set print elements" + + test_gdb_complete_unique \ + "alias btfu = backt" \ + "alias btfu = backtrace" + + test_gdb_complete_unique \ + "alias btfu = backtrace -fu" \ + "alias btfu = backtrace -full" + + test_gdb_complete_unique \ + "alias btfu = backtrace -full -past-e" \ + "alias btfu = backtrace -full -past-entry" + + gdb_test_no_output "alias btfu = backtrace -full -past-entry" \ + "alias btfu" + + # Test completion of 'set leading-args' (sharing most + # of the code that alias command uses to complete after + # the first =. + test_gdb_complete_unique \ + "set leading-args btf" \ + "set leading-args btfu" + + test_gdb_complete_unique \ + "set leading-args btfu -frame-a" \ + "set leading-args btfu -frame-arguments" + +} -- 2.20.1 From 65bd84e184d6d8f82cf6f12312db3718b2cb08ee Mon Sep 17 00:00:00 2001 From: Philippe Waroquiers Date: Tue, 25 Jun 2019 00:50:29 +0200 Subject: [PATCH 3/3] NEWS and documentation for leading-args related concept and commands. gdb/ChangeLog 2019-09-15 Philippe Waroquiers * NEWS: Mention new leading-args commands. Mention change to the alias command. gdb/doc/ChangeLog 2019-09-15 Philippe Waroquiers * gdb.texinfo (Command leading args): New node documenting 'set|show leading-args' and 'set|show enable-leading-args'. (Aliases): Document the new 'LEADING-ARGS...' option. --- gdb/NEWS | 18 +++++++ gdb/doc/gdb.texinfo | 126 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 143 insertions(+), 1 deletion(-) diff --git a/gdb/NEWS b/gdb/NEWS index 25e67e43c8..47a755ca8c 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -96,6 +96,18 @@ w SETTING [VALUE] [-- COMMAND] maint with SETTING [VALUE] [-- COMMAND] Like "with", but works with "maintenance set" settings. +set leading-args COMMAND [LEADING-ARGS...] +show leading-args [COMMAND] +set enable-leading-args [on|off] +show enable-leading-args + GDB can now automatically prepend leading args to a command or alias. + This allows to set default arguments or options for the GDB commands + or define easier to use aliases. + For example, 'set leading-args backtrace -full -frame-arguments all' + ensures that backtrace will automatically use the options -full + -frame-arguments all, without having to retype them for each backtrace + command. + set may-call-functions [on|off] show may-call-functions This controls whether GDB will attempt to call functions in @@ -174,6 +186,12 @@ info sources [-dirname | -basename] [--] [REGEXP] allow to restrict matching respectively to the dirname and basename parts of the files. +alias [-a] [--] ALIAS = COMMAND [LEADING-ARGS...] + The alias command can now directly define leading-args + to prepend to arguments provided by the user on the command line. + See 'set leading-args COMMAND [LEADING-ARGS...]' for more + information about leading args concept. + show style The "show style" and its subcommands are now styling a style name in their output using its own style, to help diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 1208e4f615..30732886db 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -1564,6 +1564,7 @@ show you the alternatives available, if there is more than one possibility). * Command Settings:: How to change default behavior of commands * Completion:: Command completion * Command Options:: Command options +* Command leading args:: Automatically prepend arguments to commands and aliases * Help:: How to ask @value{GDBN} for help @end menu @@ -1984,6 +1985,116 @@ uppercase. (For more on using the @code{print} command, see @ref{Data, ,Examining Data}.) +@node Command leading args +@section Automatically prepend arguments to commands and aliases + +You can tell @value{GDBN} to always add some default options or +arguments to a command. + +@cindex command options, automatically prepend +@cindex command, default arguments + +@table @code +@kindex set leading-args +@item set leading-args @var{command} [@var{leading-args@dots{}}] + +If you repetitively use the same arguments or options for a command, +you can tell @value{GDBN} to automatically prepend these arguments +or options to the arguments you type explicitely. + +For example, if you always want to have the command @code{thread apply all} +working on the threads in ascending order and to continue in case it +encounters an error, you can tell @value{GDBN} to automatically preprend +the @code{-ascending} and @code{-c} options by using: + +@smallexample +(@value{GDBP}) set leading-args thread apply all -ascending -c +@end smallexample + +Once you have set these leading args, any time you type +the @code{thread apply all} followed by @code{some arguments}, +@value{GDBN} will execute @code{thread apply all -ascending -c some arguments}. + +As usual, unambiguous abbreviations can be used for @var{command} +and @var{leading-args}. + +Commands and their aliases do not share their leading args. +So, for example, you can configure the commands @code{bt}, @code{where}, +@code{backtrace} and @code{info stack} to output different levels +of information and define a new alias @code{bt_ALL} showing all possible +information using: +@smallexample +(@value{GDBP}) set leading-args bt -entry-values no -frame-arguments none +(@value{GDBP}) set leading-args where -entry-values no -frame-argu scalars +(@value{GDBP}) set leading-args backtrace -entry-values no -frame-argu all +(@value{GDBP}) set leading-args info stack -entry-val both -fr all +(@value{GDBP}) alias bt_ALL = backtrace +(@value{GDBP}) set leading-args bt_ALL -entry-values both -frame-arg all \ +-past-main -past-entry -full +@end smallexample + +You can define an alias and specify its leading args in one command using the shorter to type: +@smallexample +(@value{GDBP}) alias bt_ALL = backtrace -entry-values both -frame-arg all \ +-past-main -past-entry -full +@end smallexample +(For more on using the @code{alias} command, see @ref{Aliases}.) + +Leading args are not limited to the arguments and options of @var{command}, +but can specify nested commands if @var{command} accepts such a nested command +as argument. +For example, the below defines @code{faalocalsoftype} that can be used to list +the frames having locals of a certain type, together with the matching local +vars: +@smallexample +(@value{GDBP}) alias faalocalsoftype = frame apply all info locals -q -t +(@value{GDBP}) faalocalsoftype int +#1 0x55554f5e in sleeper_or_burner (v=0xdf50) at sleepers.c:86 +i = 0 +ret = 21845 +@end smallexample + +This is also very useful to define an alias for a set of nested @code{with} +commands to have a particular combination of temporary settings. For example, +the below defines the alias @code{pp10} that pretty prints an expression +argument, with a maximum of 10 elements if the expression is a string or +an array: +@smallexample +(@value{GDBP}) alias pp10 = with print pretty -- with print elem 10 -- print +@end smallexample + + +Use @code{set leading-args @var{command}} (without giving @var{LEADING-ARGS}) +to clear the leading args of @var{command}. + +@item show leading-args [@var{command}] + +Use @code{show leading-args @var{command}} to show the current values of +the leading args for @var{command}. For example: +@smallexample +(@value{GDBP}) show leading-args backtrace +leading-args backtrace = -entry-values no -frame-arguments all +(@value{GDBP}) show leading-args break +leading-args break = +@end smallexample + +To show all the commands and aliases that have some leading args configured, +use the command +@code{show leading-args} without giving a @var{command} argument. + +@item set enable-leading-args @r{[}on|off@r{]} +@itemx show enable-leading-args +By default, @value{GDBN} will use the configured leading args. +This can be disabled using @code{set enable-leading-args off}. +The leading args of all commands are not cleared, but +will not be used till you do @code{set enable-leading-args on}. +This can be useful in user defined commands to ensure only the specified +set of options are used by a command launched by the user-defined command. +You can also use this setting in the @code{with} command, to temporarily +run a command without its possibly configured leading args. + +@end table + @node Help @section Getting Help @cindex online documentation @@ -27150,7 +27261,7 @@ You can define a new alias with the @samp{alias} command. @table @code @kindex alias -@item alias [-a] [--] @var{ALIAS} = @var{COMMAND} +@item alias [-a] [--] @var{ALIAS} = @var{COMMAND} [LEADING-ARGS...] @end table @@ -27168,6 +27279,19 @@ lists displayed by the @samp{help} command. The @samp{--} option specifies the end of options, and is useful when @var{ALIAS} begins with a dash. +You can specify @var{leading-args} for your alias. +These @var{leading-args} will be automatically added before the alias +arguments typed explicitely on the command line. + +For example, the below defines an alias @code{btfullall} that shows all local +variables and all frame arguments: +@smallexample +(@value{GDBP}) alias btfullall = backtrace -full -frame-arguments all +@end smallexample + +For more information about @var{leading-args}, see @ref{Command leading args, +,Automatically prepend arguments to commands and aliases} + Here is a simple example showing how to make an abbreviation of a command so that there is less to type. Suppose you were tired of typing @samp{disas}, the current