[RFAv3,1/3] leading-args: allow to define default command/alias arguments

Message ID 20190915192655.27801-2-philippe.waroquiers@skynet.be
State New, archived
Headers

Commit Message

Philippe Waroquiers Sept. 15, 2019, 7:26 p.m. UTC
  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  <philippe.waroquiers@skynet.be>

	* 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(-)
  

Patch

diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 4fc656775c..9ef0b40fef 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -50,6 +50,7 @@ 
 #include "cli/cli-setshow.h"
 #include "cli/cli-cmds.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 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 <no leading args>
+   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
+			? "<no leading args>" : 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.  */
@@ -1539,7 +1681,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);
@@ -1571,6 +1713,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".  */
+  int abbrev_flag = 0;
+};
+
+static const gdb::option::option_def alias_option_defs[] = {
+
+  gdb::option::flag_option_def<alias_opts> {
+    "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.
@@ -1605,7 +1812,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;
@@ -1623,7 +1830,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.  */
@@ -1631,8 +1838,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;
 
@@ -1643,24 +1855,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')
@@ -1695,6 +1901,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
 
@@ -1707,8 +1915,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
     {
@@ -1728,19 +1936,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);
     }
 }
 
@@ -2055,6 +2274,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);
 
@@ -2206,19 +2445,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 7d4b39f467..846b48e327 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 4fc9c70259..e5f944f0bd 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -971,7 +971,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';
 
@@ -1328,7 +1328,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 ());
 
@@ -1384,7 +1384,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;
 
@@ -1419,7 +1419,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)
@@ -1488,7 +1488,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 2c608048d5..1df4d7fb57 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 53120cb23e..4d3ce6770a 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -463,13 +463,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;
@@ -966,7 +966,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 d7a7e6f933..4fcaae7161 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -3148,7 +3148,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);
 
@@ -3161,7 +3161,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);
 
@@ -3180,7 +3180,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 c7b9afdb11..559652de93 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 afeff581ee..87af351051 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -566,12 +566,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 ae06c4ba79..d8a1099540 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -14317,10 +14317,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 2b53640af0..a549b66c9c 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -535,6 +535,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 int enable_leading_args_var = 1;
+
 /* Execute the line P as a command, in the current user context.
    Pass FROM_TTY as second argument to the defining function.  */
 
@@ -563,6 +568,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;
@@ -570,15 +577,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
@@ -1885,7 +1904,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)
@@ -2115,6 +2134,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 2d5b9c705e..2c4042f87c 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -644,7 +644,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);
 
@@ -1302,7 +1302,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);
 
@@ -2672,7 +2672,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);