[RFAv6,1/3] default-args: allow to define default arguments for aliases

Message ID 20200516171947.10507-2-philippe.waroquiers@skynet.be
State Superseded
Headers show
Series
  • Allow the user to define default args for aliases
Related show

Commit Message

Simon Marchi via Gdb-patches May 16, 2020, 5:19 p.m.
Currently, a user can define an alias, but cannot have default
arguments for this alias.

This patch provides the following:

* A new command 'set default-args' that defines default args
  to prepend to the list of arguments explicitely given by the user
  when using an alias.
  Note that default args can be used for an alias and nicely
  combine nested "with" (see below the changes for the alias command).

        (gdb) help set default-args
        Set or clear default args of an alias.
        Usage: set default-args ALIAS [DEFAULT-ARGS...]
        Set or clear the default arguments automatically prepended
        to the list of arguments the user explicitly provides when the user-defined
        ALIAS is run.
        The different aliases of a command do not share their default arguments:
        Each alias of a command can be configured with different values.
        Without DEFAULT-ARGS..., clears ALIAS default arguments.
        (gdb)

  Note that 'set default-args' command has a completer to help typing
  ALIAS and its default-args.

The way 'set default-args' is implemented makes it trivial to set default
args also for GDB commands (such as "backtrace") and for GDB pre-defined
aliases (such as "bt").  It was however deemed better to not allow to
define default arguments for pre-defined commands and aliases, to avoid
users believing that e.g. default args for "backtrace" would apply to "bt".
The default-args concept and related new commands are implemented in such
a way that if needed default args can be allowed for GDB predefined commands
and aliases without changing the user interface and with small code
changes.

* A new command 'show default-args'.

        (gdb) help show default-args
        Show the default args of an alias, or of all aliases.
        Usage: show default-args [ALIAS]
        Show the default args of ALIAS.  Without ALIAS, show the default args
        of all commands.

* The 'alias' command is modified so as to directly accept default-args.

        (gdb) h alias
        Define a new command that is an alias of an existing command.
        Usage: alias [-a] [--] ALIAS = COMMAND [DEFAULT-ARGS...]
        ALIAS is the name of the alias command to create.
        COMMAND is the command being aliased to.
        If "-a" is specified, the command is an abbreviation,
        and will not be used in command completion.

        Options:
          -a
            Specify that ALIAS is an abbreviation of COMMAND.
            Abbreviations are not shown in command lists displayed by the 'help' command.
        You can optionally provide DEFAULT-ARGS to define at the same time
        ALIAS and its default args.  This is the equivalent of:
          alias ALIAS = COMMAND
          set default-args ALIAS DEFAULT-ARGS...

        Examples:
        Make "spe" an alias of "set print elements":
          alias spe set print elements
        Make "elms" an alias of "elements" in the "set print" command:
          alias -a set print elms set print elements
        Make "btf" an alias of "backtrace -full -past-entry -past-main" :
          alias btf = backtrace -full -past-entry -past-main
        Make "wLapPeu" an alias of 2 nested "with":
          alias wLapPeu = with language pascal -- with print elements unlimited --
        (gdb)

* '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.

* Help and apropos commands show the definitions of the aliases
  that have default arguments, e.g.
        (gdb) help backtrace
        backtrace, btf, where, bt
          alias btf = backtrace -full -past-entry -past-main
        Print backtrace of all stack frames, or innermost COUNT frames.
        Usage: backtrace [OPTION]... [QUALIFIER]... [COUNT | -COUNT]

        Options:
          -entry-values no|only|preferred|if-needed|both|compact|default
            Set printing of function arguments at function entry.
        ...

gdb/ChangeLog
YYYY-MM-DD  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

	* cli/cli-cmds.c (default_args_cmd_completer)
	(lookup_cmd_for_default_args, set_default_args_command)
	(show_default_args, show_default_args_command_1)
	(show_default_args_command, alias_command_completer)
	(make_alias_options_def_group): New functions.
	(alias_opts, alias_option_defs): New struct and array.
	(alias_usage_error): Update usage.
	(alias_command): Handles optional DEFAULT-ARGS... arguments.
	Use option framework.
	(_initialize_cli_cmds): Install 'set|show default-args' commands.
	Update alias command help.  Update aliases command help.
	(show_user, valid_command_p):
	Add NULL for new default_args lookup_cmd argument.
	* cli/cli-decode.c (help_cmd): Show default args if command has
	some.
	(lookup_cmd_1, lookup_cmd): New argument default_args.
	(add_alias_cmd):
	Add NULL for new default_args lookup_cmd argument.
	(print_help_for_command): Show default args under the layout
	  alias some_alias = some_aliased_cmd some_alias_default_arg.
	* cli/cli-decode.h (struct cmd_list_element): New member default_args.
	xfree default_args in destructor.
	* cli/cli-script.c (process_next_line, do_define_command):
	Add NULL for new default_args lookup_cmd argument.
	* command.h: Declare new default_args argument in lookup_cmd
	and lookup_cmd_1.
	* completer.c (complete_line_internal_1):
	Add NULL for new default_args lookup_cmd or lookup_cmd_1 argument.
	* guile/scm-cmd.c (gdbscm_parse_command_name): Likewise.
	* guile/scm-param.c (add_setshow_generic, pascm_parameter_defined_p):
	Likewise.
	* infcmd.c (_initialize_infcmd): Likewise.
	* python/py-auto-load.c (gdbpy_initialize_auto_load): Likewise.
	* python/py-cmd.c (gdbpy_parse_command_name): Likewise.
	* python/py-param.c (add_setshow_generic): Likewise.
	* remote.c (_initialize_remote): Likewise.
	* top.c (execute_command): Prepend default_args if command has some.
	(set_verbose):
	Add NULL for new default_args lookup_cmd or  lookup_cmd_1 argument.
	* tracepoint.c (validate_actionline, encode_actions_1):
	Add NULL for new default_args lookup_cmd or lookup_cmd_1 argument.
---
 gdb/cli/cli-cmds.c        | 328 ++++++++++++++++++++++++++++++++++----
 gdb/cli/cli-decode.c      | 135 +++++++++++++---
 gdb/cli/cli-decode.h      |   4 +
 gdb/cli/cli-script.c      |  12 +-
 gdb/command.h             |   2 +
 gdb/completer.c           |   2 +-
 gdb/guile/scm-cmd.c       |   2 +-
 gdb/guile/scm-param.c     |   6 +-
 gdb/infcmd.c              |   6 +-
 gdb/python/py-auto-load.c |   4 +-
 gdb/python/py-cmd.c       |   2 +-
 gdb/python/py-param.c     |   4 +-
 gdb/remote.c              |   4 +-
 gdb/top.c                 |  21 ++-
 gdb/tracepoint.c          |   6 +-
 15 files changed, 459 insertions(+), 79 deletions(-)

-- 
2.20.1

Comments

Simon Marchi June 8, 2020, 9:28 p.m. | #1
I don't remember if we talked about this earlier... but is this
the expected behavior when using an alias to define another alias?

(gdb) alias aaa = echo allo\n
(gdb) alias bbb = aaa bonjour\n
(gdb) aaa
allo
(gdb) bbb
bonjour
(gdb)

In other words, it's possible to use an alias in the definition of
another alias, but the default args of the first alias are ignored.
It's understandable when looking at the implementation, but is it
what users will expect?  The equivalent in the shell is different, so
I can imagine some users will be surprised:

$ alias aaa='echo allo'
$ alias bbb='aaa bonjour'
$ aaa
allo
$ bbb
allo bonjour

> @@ -315,6 +317,150 @@ with_command_completer (struct cmd_list_element *ignore,

>    with_command_completer_1 ("set ", tracker,  text);

>  }

>  

> +/* Completer for the "set|show default-args" commands.  */

> +

> +static void

> +default_args_cmd_completer (struct cmd_list_element *ignore,

> +			    completion_tracker &tracker,

> +			    const char *text, const char *word)

> +{

> +  tracker.set_use_custom_word_point (true);

> +

> +  complete_nested_command_line (tracker, text);

> +}

> +

> +/* Look up the contents of TEXT as a command usable with default args.

> +   Throws an error if no such command is found.

> +   Return the found command and advances TEXT past the found command.

> +   If the found command is a postfix command, set *PREFIX_CMD to its

> +   prefix command.  */

> +

> +static struct cmd_list_element *

> +lookup_cmd_for_default_args (const char **text,

> +			     struct cmd_list_element **prefix_cmd)

> +{

> +  const char *orig_text = *text;

> +  struct cmd_list_element *lcmd;

> +

> +  if (*text == nullptr || skip_spaces (*text) == nullptr)

> +    error (_("COMMAND missing."));


This should say ALIAS, since it says ALIAS in "help set default-args".

Simon
Simon Marchi via Gdb-patches June 9, 2020, 7:49 p.m. | #2
Hello Simon,

On Mon, 2020-06-08 at 17:28 -0400, Simon Marchi wrote:
> I don't remember if we talked about this earlier... but is this

> the expected behavior when using an alias to define another alias?


This point was not discussed but is the consequence of GDB
resolving an alias to point at the (end/final) command it is
aliasing, and e.g. not mplementing an alias as a kind of textual
replacement.
When asking the list of aliases, it similarly shows the 'final'
command it is aliasing, and not the alias that was used 
in the definition.

(gdb) alias aaa = echo bonjour
(gdb) alias bbb = aaa coucou
(gdb) help aliases
User-defined aliases of other commands.

List of commands:

aaa -- Print a constant string.  Give string as argument.
  alias aaa = echo bonjour
bbb -- Print a constant string.  Give string as argument.
  alias bbb = echo coucou

We might explicit this behaviour in the user manual.
(it might be possible to re-implement aliases as textual
replacement but that is a major rework, e.g. the
chain of aliases of a command has to be replaced
by something else, the aliased_command pointer is to point
at another chain of aliases, that must be evaluated "dynamically"
when executing the command, rather than resolved at definition time.


Note that GDB and a shell are already very different beasts
on many aspects e.g. :
  (gdb) set $xxx = "coucou"
  (gdb) echo $xxx
  $xxx(gdb) p $xxx
  $1 = "coucou"
  (gdb) 



> > +  if (*text == nullptr || skip_spaces (*text) == nullptr)

> > +    error (_("COMMAND missing."));

> 

> This should say ALIAS, since it says ALIAS in "help set default-args".

That is a left over of the version that allowed default args for
commands.  I will fix this.

Thanks for the comments

Philippe
Simon Marchi June 9, 2020, 10:59 p.m. | #3
On 2020-06-09 3:49 p.m., Philippe Waroquiers wrote:
> Hello Simon,

> 

> On Mon, 2020-06-08 at 17:28 -0400, Simon Marchi wrote:

>> I don't remember if we talked about this earlier... but is this

>> the expected behavior when using an alias to define another alias?

> 

> This point was not discussed but is the consequence of GDB

> resolving an alias to point at the (end/final) command it is

> aliasing, and e.g. not mplementing an alias as a kind of textual

> replacement.

> When asking the list of aliases, it similarly shows the 'final'

> command it is aliasing, and not the alias that was used 

> in the definition.

> 

> (gdb) alias aaa = echo bonjour

> (gdb) alias bbb = aaa coucou

> (gdb) help aliases

> User-defined aliases of other commands.

> 

> List of commands:

> 

> aaa -- Print a constant string.  Give string as argument.

>   alias aaa = echo bonjour

> bbb -- Print a constant string.  Give string as argument.

>   alias bbb = echo coucou

> 

> We might explicit this behaviour in the user manual.

> (it might be possible to re-implement aliases as textual

> replacement but that is a major rework, e.g. the

> chain of aliases of a command has to be replaced

> by something else, the aliased_command pointer is to point

> at another chain of aliases, that must be evaluated "dynamically"

> when executing the command, rather than resolved at definition time.


I understand that this is because of the current implementation, but I really
think that the behavior is unexpected from the point of view of the user.  If
default arguments don't add up when defining an alias based on another alias,
then I'd suggest preventing the user from creating aliases based on other
aliases.  The rationale being that there is no point in doing:

(gdb) alias aaa = print abc
(gdb) alias bbb = aaa def

over

(gdb) alias aaa = print abc
(gdb) alias bbb = print def

And the former would just cause confusion with the current behavior (even if
documented in the manual).  Plus, if we allow the former, we will be stuck with
this behavior, we won't be able to change it in the future if we want to
implement the behavior where the default arguments accumulate.

I have to admit that I didn't dig much in the implementation, but this is how I
would see it.  Right now, when you define `bbb`, bbb's cmd_list_element->cmd_pointer
points to print's cmd_list_element.  Would it be hard to make it point to aaa's
cmd_list_element instead?  This way, when we look up command `bbb`, we could resolve
the aliases recursively (or iteratively) and just build up the default args string
as each alias level is resolved.  If we take the example from above, when executing
command `bbb`, we would end up with default args "abc def".

I presume it's doable but not a simple change, so that's why I suggest for now to
just prevent defining aliases based on aliases, so we can keep the door open to
implementing it later.

> Note that GDB and a shell are already very different beasts

> on many aspects e.g. :

>   (gdb) set $xxx = "coucou"

>   (gdb) echo $xxx

>   $xxx(gdb) p $xxx

>   $1 = "coucou"

>   (gdb) 


Of course!  But it doesn't prevent us from stealing good ideas from the
shell :).

Simon
Simon Marchi via Gdb-patches June 10, 2020, 6:56 p.m. | #4
On Tue, 2020-06-09 at 18:59 -0400, Simon Marchi wrote:
> 

> I understand that this is because of the current implementation, but I really

> think that the behavior is unexpected from the point of view of the user.  If

> default arguments don't add up when defining an alias based on another alias,

> then I'd suggest preventing the user from creating aliases based on other

> aliases.  The rationale being that there is no point in doing:

> 

> (gdb) alias aaa = print abc

> (gdb) alias bbb = aaa def

> 

> over

> 

> (gdb) alias aaa = print abc

> (gdb) alias bbb = print def

> 

> And the former would just cause confusion with the current behavior (even if

> documented in the manual).  Plus, if we allow the former, we will be stuck with

> this behavior, we won't be able to change it in the future if we want to

> implement the behavior where the default arguments accumulate.

> 

> I have to admit that I didn't dig much in the implementation, but this is how I

> would see it.  Right now, when you define `bbb`, bbb's cmd_list_element->cmd_pointer

> points to print's cmd_list_element.  Would it be hard to make it point to aaa's

> cmd_list_element instead?  This way, when we look up command `bbb`, we could resolve

> the aliases recursively (or iteratively) and just build up the default args string

> as each alias level is resolved.  If we take the example from above, when executing

> command `bbb`, we would end up with default args "abc def".

Yes, to expand the args, we would have to scan a linked list of aliases.
the linked list should replace the current pointer to the aliased command.
But as far as I can see, there are a bunch of other things to rework
(such as the chain of alias of a command).
So, not a minor change (as you presume below).
> 

> I presume it's doable but not a simple change, so that's why I suggest for now to

> just prevent defining aliases based on aliases, so we can keep the door open to

> implementing it later.

Yes, that is a good idea.

I will implement this protection/prevention this week-end.
Note that to avoid having a backward incompatible change, I suggest to report an
error only when the new or the old alias have default args.
Otherwise, we will break .gdbinit of users that have done:
   alias aaa = backtrace
   alias bbb = aaa

Philippe
Simon Marchi June 11, 2020, 3:07 a.m. | #5
On 2020-06-10 2:56 p.m., Philippe Waroquiers wrote:
> On Tue, 2020-06-09 at 18:59 -0400, Simon Marchi wrote:

>>

>> I understand that this is because of the current implementation, but I really

>> think that the behavior is unexpected from the point of view of the user.  If

>> default arguments don't add up when defining an alias based on another alias,

>> then I'd suggest preventing the user from creating aliases based on other

>> aliases.  The rationale being that there is no point in doing:

>>

>> (gdb) alias aaa = print abc

>> (gdb) alias bbb = aaa def

>>

>> over

>>

>> (gdb) alias aaa = print abc

>> (gdb) alias bbb = print def

>>

>> And the former would just cause confusion with the current behavior (even if

>> documented in the manual).  Plus, if we allow the former, we will be stuck with

>> this behavior, we won't be able to change it in the future if we want to

>> implement the behavior where the default arguments accumulate.

>>

>> I have to admit that I didn't dig much in the implementation, but this is how I

>> would see it.  Right now, when you define `bbb`, bbb's cmd_list_element->cmd_pointer

>> points to print's cmd_list_element.  Would it be hard to make it point to aaa's

>> cmd_list_element instead?  This way, when we look up command `bbb`, we could resolve

>> the aliases recursively (or iteratively) and just build up the default args string

>> as each alias level is resolved.  If we take the example from above, when executing

>> command `bbb`, we would end up with default args "abc def".

> Yes, to expand the args, we would have to scan a linked list of aliases.

> the linked list should replace the current pointer to the aliased command.

> But as far as I can see, there are a bunch of other things to rework

> (such as the chain of alias of a command).

> So, not a minor change (as you presume below).

>>

>> I presume it's doable but not a simple change, so that's why I suggest for now to

>> just prevent defining aliases based on aliases, so we can keep the door open to

>> implementing it later.

> Yes, that is a good idea.

> 

> I will implement this protection/prevention this week-end.

> Note that to avoid having a backward incompatible change, I suggest to report an

> error only when the new or the old alias have default args.

> Otherwise, we will break .gdbinit of users that have done:

>    alias aaa = backtrace

>    alias bbb = aaa


Ah right, the alias command already exists so this is an existing use case.  That's
fine with me, since that will continue to work without change in behavior the day
we implement the default arg with aliases thing.

Simon

Patch

diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index eb6e32b046..7555b60d7a 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -50,6 +50,7 @@ 
 #include "cli/cli-cmds.h"
 #include "cli/cli-style.h"
 #include "cli/cli-utils.h"
+#include "cli/cli-style.h"
 
 #include "extension.h"
 #include "gdbsupport/pathstuff.h"
@@ -221,6 +222,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);
@@ -315,6 +317,150 @@  with_command_completer (struct cmd_list_element *ignore,
   with_command_completer_1 ("set ", tracker,  text);
 }
 
+/* Completer for the "set|show default-args" commands.  */
+
+static void
+default_args_cmd_completer (struct cmd_list_element *ignore,
+			    completion_tracker &tracker,
+			    const char *text, const char *word)
+{
+  tracker.set_use_custom_word_point (true);
+
+  complete_nested_command_line (tracker, text);
+}
+
+/* Look up the contents of TEXT as a command usable with default args.
+   Throws an error if no such command is found.
+   Return the found command and advances TEXT past the found command.
+   If the found command is a postfix command, set *PREFIX_CMD to its
+   prefix command.  */
+
+static struct cmd_list_element *
+lookup_cmd_for_default_args (const char **text,
+			     struct cmd_list_element **prefix_cmd)
+{
+  const char *orig_text = *text;
+  struct cmd_list_element *lcmd;
+
+  if (*text == nullptr || skip_spaces (*text) == nullptr)
+    error (_("COMMAND missing."));
+
+  /* We first use lookup_cmd to verify TEXT unambiguously identifies
+     a command.  */
+  lcmd = lookup_cmd (text, cmdlist, "", NULL,
+		     /*allow_unknown=*/ 0,
+		     /*ignore_help_classes=*/ 1);
+
+  /* Note that we accept default args for prefix commands,
+     as a prefix command can also be a valid usable
+     command accepting some arguments.
+     For example, "thread apply" applies a command to a
+     list of thread ids, and is also the prefix command for
+     thread apply all.  */
+
+  /* We have an unambiguous command for which default args
+     can be specified.  What remains after having fond LCMD
+     is either spaces, or the default args character.  */
+
+  /* We then use lookup_cmd_composition to detect if the user
+     has specified an alias, and find the possible prefix_cmd
+     of cmd.  */
+  struct cmd_list_element *alias, *cmd;
+  lookup_cmd_composition
+    (std::string (orig_text, *text - orig_text).c_str (),
+     &alias, prefix_cmd, &cmd);
+  gdb_assert (cmd != nullptr);
+  gdb_assert (cmd == lcmd);
+  if (alias != nullptr)
+    cmd = alias;
+
+  return cmd;
+}
+
+/* Implementation of the "set default-args" command.  */
+
+static void
+set_default_args_command (const char *arg, int from_tty)
+{
+  struct cmd_list_element *prefix_cmd;
+  struct cmd_list_element *cmd = lookup_cmd_for_default_args (&arg,
+							      &prefix_cmd);
+
+  /* Currently, we only accept to set default args for user-defined aliases.
+     This is deemed to avoid some user confusion when e.g. setting
+     default args for the "backtrace" command but then wrongly expecting
+     the "bt" command (a predefined alias) to use the default args specified
+     for the "backtrace" command.  In other words, we assume the user is not
+     supposed to know the difference between a command and its predefined
+     aliases.  */
+  if (cmd->theclass != class_alias)
+    error (_("Default args can only be specified for user-defined aliases."));
+
+  const char *default_args = skip_spaces (arg);
+  cmd->default_args = default_args;
+}
+
+/* Print a message showing C's name and its default args or <no default args>
+   if C has no default args.
+   If SILENT_FOR_NO_DEFAULT_ARGS, does nothing if C has no default args.  */
+
+static void
+show_default_args (struct cmd_list_element *c,
+		   const char *prefix,
+		   bool silent_for_no_default_args)
+{
+  if (!c->default_args.empty () || !silent_for_no_default_args)
+    {
+      fputs_filtered ("default-args ", gdb_stdout);
+      fprintf_styled (gdb_stdout, title_style.style (),
+		      "%s%s",
+		      prefix == nullptr ? "" : prefix, c->name);
+      fprintf_filtered (gdb_stdout, " = %s\n",
+			c->default_args.empty ()
+			? "<no default args>" : c->default_args.c_str ());
+    }
+}
+
+/* Recursively traverse COMMANDLIST and prints a message showing
+   the default-args of the commands that have non-null default args.
+   PREFIX is the prefix that led to COMMANDLIST.  */
+
+static void
+show_default_args_command_1 (struct cmd_list_element *commandlist,
+			     const char *prefix)
+{
+  /* Walk through the commands.  */
+  for (cmd_list_element *c = commandlist; c; c = c->next)
+    {
+      show_default_args (c, prefix, true);
+      /* If C has subcommands, recursively search if its subcommands
+	 have default args.
+	 Do not recurse for abbreviations to avoid duplicates
+	 in the output.  */
+      if (c->prefixlist != NULL && !c->abbrev_flag)
+	show_default_args_command_1 (*c->prefixlist, c->prefixname);
+    }
+}
+
+/* Implementation of the "show default-args" command.  */
+
+static void
+show_default_args_command (const char *arg, int from_tty)
+{
+  if (skip_spaces (arg) != nullptr)
+    {
+      struct cmd_list_element *prefix_cmd;
+      struct cmd_list_element *c = lookup_cmd_for_default_args (&arg,
+								&prefix_cmd);
+
+      show_default_args (c,
+			 prefix_cmd == nullptr ? "" : prefix_cmd->prefixname,
+			 false);
+    }
+  else
+    show_default_args_command_1 (cmdlist, "");
+}
+
 
 /* Provide documentation on command or list given by COMMAND.  FROM_TTY
    is ignored.  */
@@ -1541,7 +1687,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);
@@ -1573,6 +1719,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 = false;
+};
+
+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 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 = strchr (text, '=');
+
+  /* If we're past the "=" delimiter, complete the
+     "alias ALIAS = COMMAND [DEFAULT-ARGS...]" as if the user is
+     typing COMMAND DEFAULT-ARGS...  */
+  if (delim != text
+      && delim != nullptr
+      && isspace (delim[-1])
+      && (isspace (delim[1]) || delim[1] == '\0'))
+    {
+      std::string new_text = std::string (delim + 1);
+
+      tracker.advance_custom_word_point_by (delim + 1 - text);
+      complete_nested_command_line (tracker, new_text.c_str ());
+      return;
+    }
+
+  /* We're not yet past the "=" delimiter.  Complete a command, as
+     the user might type an alias following a prefix command.  */
+  complete_nested_command_line (tracker, text);
+}
+
 /* Subroutine of alias_command to simplify it.
    Return the first N elements of ARGV flattened back to a string
    with a space separating each element.
@@ -1607,7 +1818,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;
@@ -1625,7 +1836,7 @@  valid_command_p (const char *command)
 static void
 alias_usage_error (void)
 {
-  error (_("Usage: alias [-a] [--] ALIAS = COMMAND"));
+  error (_("Usage: alias [-a] [--] ALIAS = COMMAND [DEFAULT-ARGS...]"));
 }
 
 /* Make an alias of an existing command.  */
@@ -1633,8 +1844,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;
 
@@ -1645,24 +1861,18 @@  alias_command (const char *args, int from_tty)
   std::string args2 (args, equals - args);
 
   gdb_argv built_alias_argv (args2.c_str ());
-  gdb_argv command_argv (equals + 1);
+
+  const char *default_args = equals + 1;
+  struct cmd_list_element *c_command_prefix;
+
+  lookup_cmd_for_default_args (&default_args, &c_command_prefix);
+  std::string command_argv_str (equals + 1,
+				default_args == nullptr
+				? strlen (equals + 1)
+				: default_args - equals - 1);
+  gdb_argv command_argv (command_argv_str.c_str ());
 
   char **alias_argv = built_alias_argv.get ();
-  while (alias_argv[0] != NULL)
-    {
-      if (strcmp (alias_argv[0], "-a") == 0)
-	{
-	  ++alias_argv;
-	  abbrev_flag = 1;
-	}
-      else if (strcmp (alias_argv[0], "--") == 0)
-	{
-	  ++alias_argv;
-	  break;
-	}
-      else
-	break;
-    }
 
   if (alias_argv[0] == NULL || command_argv[0] == NULL
       || *alias_argv[0] == '\0' || *command_argv[0] == '\0')
@@ -1718,6 +1928,8 @@  alias_command (const char *args, int from_tty)
   }
 
 
+  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
 
@@ -1730,8 +1942,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
     {
@@ -1751,19 +1963,29 @@  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 != nullptr);
+  gdb_assert (alias_cmd->default_args.empty ());
+  if (default_args != nullptr)
+    {
+      default_args = skip_spaces (default_args);
+
+      alias_cmd->default_args = default_args;
     }
 }
 
@@ -1938,7 +2160,7 @@  setting_cmd (const char *fnname, struct cmd_list_element *showlist,
     error (_("First argument of %s must be a string."), fnname);
 
   const char *a0 = (const char *) value_contents (argv[0]);
-  cmd_list_element *cmd = lookup_cmd (&a0, showlist, "", -1, 0);
+  cmd_list_element *cmd = lookup_cmd (&a0, showlist, "", NULL, -1, 0);
 
   if (cmd == nullptr || cmd_type (cmd) != show_cmd)
     error (_("First argument of %s must be a "
@@ -2128,7 +2350,7 @@  well documented as user commands."),
 	   &cmdlist);
   add_cmd ("obscure", class_obscure, _("Obscure features."), &cmdlist);
   add_cmd ("aliases", class_alias,
-	   _("Aliases of other commands."), &cmdlist);
+	   _("User-defined aliases of other commands."), &cmdlist);
   add_cmd ("user-defined", class_user, _("\
 User-defined commands.\n\
 The commands in this class are those defined by the user.\n\
@@ -2297,6 +2519,26 @@  You can supply a command number to start with, or a `+' to start after\n\
 the previous command number shown."),
 	   &showlist);
 
+  c = add_cmd ("default-args", class_support, set_default_args_command, _("\
+Set or clear default args of an alias.\n\
+Usage: set default-args ALIAS [DEFAULT-ARGS...]\n\
+Set or clear the default arguments automatically prepended\n\
+to the list of arguments the user explicitly provides when the user-defined\n\
+ALIAS is run.\n\
+The different aliases of a command do not share their default arguments:\n\
+Each alias of a command can be configured with different values.\n\
+Without DEFAULT-ARGS..., clears ALIAS default arguments."),
+	   &setlist);
+  set_cmd_completer_handle_brkchars (c, default_args_cmd_completer);
+
+  c = add_cmd ("default-args", class_support, show_default_args_command, _("\
+Show the default args of an alias, or of all aliases.\n\
+Usage: show default-args [ALIAS]\n\
+Show the default args of ALIAS.  Without ALIAS, show the default args\n\
+of all aliases."),
+	   &showlist);
+  set_cmd_completer_handle_brkchars (c, default_args_cmd_completer);
+
   add_cmd ("version", no_set_class, show_version,
 	   _("Show what version of GDB this is."), &showlist);
 
@@ -2448,19 +2690,39 @@  When 'on', each command is displayed as it is executed."),
 			   NULL,
 			   &setlist, &showlist);
 
-  c = add_com ("alias", class_support, alias_command, _("\
+  const auto alias_opts = make_alias_options_def_group (nullptr);
+
+  static std::string alias_help
+    = gdb::option::build_help (_("\
 Define a new command that is an alias of an existing command.\n\
-Usage: alias [-a] [--] ALIAS = COMMAND\n\
+Usage: alias [-a] [--] ALIAS = COMMAND [DEFAULT-ARGS...]\n\
 ALIAS is the name of the alias command to create.\n\
 COMMAND is the command being aliased to.\n\
 If \"-a\" is specified, the command is an abbreviation,\n\
 and will not be used in command completion.\n\
 \n\
+Options:\n\
+%OPTIONS%\n\
+You can optionally provide DEFAULT-ARGS to define at the same time\n\
+ALIAS and its default args.  This is the equivalent of:\n\
+  alias ALIAS = COMMAND\n\
+  set default-args ALIAS DEFAULT-ARGS...\n\
+\n\
 Examples:\n\
 Make \"spe\" an alias of \"set print elements\":\n\
-  alias spe = set print elements\n\
+  alias spe set print elements\n\
 Make \"elms\" an alias of \"elements\" in the \"set print\" command:\n\
-  alias -a set print elms = set print elements"));
+  alias -a set print elms set print elements\n\
+Make \"btf\" an alias of \"backtrace -full -past-entry -past-main\" :\n\
+  alias btf = backtrace -full -past-entry -past-main\n\
+Make \"wLapPeu\" an alias of 2 nested \"with\":\n\
+  alias wLapPeu = with language pascal -- with print elements unlimited --"),
+			       alias_opts);
+
+  c = add_com ("alias", class_support, alias_command,
+	       alias_help.c_str ());
+
+  set_cmd_completer_handle_brkchars (c, alias_command_completer);
 
   const char *source_help_text = xstrprintf (_("\
 Read commands from a file named FILE.\n\
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index a4ef93c62b..1568c4a96c 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -337,7 +337,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);
 }
@@ -1046,6 +1046,32 @@  fput_command_name_styled (struct cmd_list_element *c, struct ui_file *stream)
    If one or more names are printed, POSTFIX is printed after the last name.
 */
 
+/* Print the definition of alias C using title style for alias
+   and aliased command.  */
+
+static void
+fput_alias_definition_styled (struct cmd_list_element *c, struct ui_file *stream)
+{
+  gdb_assert (c->cmd_pointer != nullptr);
+  fputs_filtered ("  alias ", stream);
+  fput_command_name_styled (c, stream);
+  fprintf_filtered (stream, " = ");
+  fput_command_name_styled (c->cmd_pointer, stream);
+  fprintf_filtered (stream, " %s\n", c->default_args.c_str ());
+}
+
+/* Print the definition of the aliases of CMD that have default args.  */
+
+static void
+fput_aliases_definition_styled (struct cmd_list_element *cmd, struct ui_file *stream)
+{
+  if (cmd->aliases != nullptr)
+    for (cmd_list_element *iter = cmd->aliases; iter; iter = iter->alias_chain)
+      if (!iter->default_args.empty ())
+	fput_alias_definition_styled (iter, stream);
+}
+
+
 static void
 fput_command_names_styled (struct cmd_list_element *c,
 			   bool always_fput_c_name, const char *postfix,
@@ -1081,12 +1107,21 @@  print_doc_of_command (struct cmd_list_element *c, const char *prefix,
   if (verbose)
     fputs_filtered ("\n", stream);
 
-  fput_command_names_styled (c, true, " -- ", stream);
+  fput_command_names_styled (c, true,
+			     verbose ? "" : " -- ", stream);
   if (verbose)
-    fputs_highlighted (c->doc, highlight, stream);
+    {
+      fputs_filtered ("\n", stream);
+      fput_aliases_definition_styled (c, stream);
+      fputs_highlighted (c->doc, highlight, stream);
+      fputs_filtered ("\n", stream);
+    }
   else
-    print_doc_line (stream, c->doc, false);
-  fputs_filtered ("\n", stream);
+    {
+      print_doc_line (stream, c->doc, false);
+      fputs_filtered ("\n", stream);
+      fput_aliases_definition_styled (c, stream);
+    }
 }
 
 /* Recursively walk the commandlist structures, and print out the
@@ -1183,7 +1218,7 @@  help_cmd (const char *command, struct ui_file *stream)
     }
 
   const char *orig_command = command;
-  c = lookup_cmd (&command, cmdlist, "", 0, 0);
+  c = lookup_cmd (&command, cmdlist, "", NULL, 0, 0);
 
   if (c == 0)
     return;
@@ -1205,9 +1240,43 @@  help_cmd (const char *command, struct ui_file *stream)
   /* If the user asked 'help somecommand' and there is no alias,
      the false indicates to not output the (single) command name.  */
   fput_command_names_styled (c, false, "\n", stream);
+  fput_aliases_definition_styled (c, stream);
   fputs_filtered (c->doc, stream);
   fputs_filtered ("\n", stream);
 
+  if (c->func != nullptr)
+    {
+      /* Print the default args of the command if it has some.  Use the
+	 lookup_cmd_composition result to find if help was requested for an
+	 alias.  In this case, rather print the alias default_args.  */
+
+      const char *name;
+      std::string default_args;
+
+      gdb_assert (c_cmd != nullptr);
+      gdb_assert (c_cmd == c);
+
+      if (alias == nullptr)
+	{
+	  name = c->name;
+	  default_args = c->default_args;
+	}
+      else
+	{
+	  name = alias->name;
+	  default_args = alias->default_args;
+	}
+      if (!default_args.empty ())
+	{
+	  fputs_filtered ("default-args ", stream);
+	  fprintf_styled (stream, title_style.style (),
+			  "%s%s",
+			  prefix_cmd == nullptr ? "" : prefix_cmd->prefixname,
+			  name);
+	  fprintf_filtered (stream, " = %s\n", default_args.c_str ());
+	}
+    }
+
   if (c->prefixlist == 0 && c->func != NULL)
     return;
   fprintf_filtered (stream, "\n");
@@ -1341,7 +1410,7 @@  help_all (struct ui_file *stream)
 	      fprintf_filtered (stream, "\nUnclassified commands\n\n");
 	      seen_unclassified = 1;
 	    }
-	  print_help_for_command (c, 1, stream);
+	  print_help_for_command (c, true, stream);
 	}
     }
 
@@ -1399,6 +1468,9 @@  print_help_for_command (struct cmd_list_element *c,
   fput_command_names_styled (c, true, " -- ", stream);
   print_doc_line (stream, c->doc, false);
   fputs_filtered ("\n", stream);
+  if (!c->default_args.empty ())
+    fput_alias_definition_styled (c, stream);
+  fput_aliases_definition_styled (c, stream);
 
   if (recurse
       && c->prefixlist != 0
@@ -1582,8 +1654,12 @@  valid_user_defined_cmd_name_p (const char *name)
    the list in which there are ambiguous choices (and *TEXT will be set to
    the ambiguous text string).
 
+   if DEFAULT_ARGS is not null, *DEFAULT_ARGS is set to the found command 
+   default args (possibly empty).
+
    If the located command was an abbreviation, this routine returns the base
-   command of the abbreviation.
+   command of the abbreviation.  Note that *DEFAULT_ARGS will contain the
+   default args defined for the alias.
 
    It does no error reporting whatsoever; control will always return
    to the superior routine.
@@ -1610,11 +1686,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, std::string *default_args,
+	      int ignore_help_classes)
 {
   char *command;
   int len, nfound;
   struct cmd_list_element *found, *c;
+  bool found_alias = false;
   const char *line = *text;
 
   while (**text == ' ' || **text == '\t')
@@ -1646,10 +1724,12 @@  lookup_cmd_1 (const char **text, struct cmd_list_element *clist,
 
   if (nfound > 1)
     {
-      if (result_list != NULL)
+      if (result_list != nullptr)
 	/* Will be modified in calling routine
 	   if we know what the prefix command is.  */
 	*result_list = 0;
+      if (default_args != nullptr)
+	*default_args = std::string ();
       return CMD_LIST_AMBIGUOUS;	/* Ambiguous.  */
     }
 
@@ -1665,22 +1745,30 @@  lookup_cmd_1 (const char **text, struct cmd_list_element *clist,
        are warning about the alias, we may also warn about the command
        itself and we will adjust the appropriate DEPRECATED_WARN_USER
        flags.  */
-      
+
       if (found->deprecated_warn_user)
 	deprecated_cmd_warning (line);
+
+      /* Return the default_args of the alias, not the default_args
+	 of the command it is pointing to.  */
+      if (default_args != nullptr)
+	*default_args = found->default_args;
       found = found->cmd_pointer;
+      found_alias = true;
     }
   /* If we found a prefix command, keep looking.  */
 
   if (found->prefixlist)
     {
       c = lookup_cmd_1 (text, *found->prefixlist, result_list,
-			ignore_help_classes);
+			default_args, ignore_help_classes);
       if (!c)
 	{
 	  /* Didn't find anything; this is as far as we got.  */
-	  if (result_list != NULL)
+	  if (result_list != nullptr)
 	    *result_list = clist;
+	  if (!found_alias && default_args != nullptr)
+	    *default_args = found->default_args;
 	  return found;
 	}
       else if (c == CMD_LIST_AMBIGUOUS)
@@ -1688,13 +1776,16 @@  lookup_cmd_1 (const char **text, struct cmd_list_element *clist,
 	  /* We've gotten this far properly, but the next step is
 	     ambiguous.  We need to set the result list to the best
 	     we've found (if an inferior hasn't already set it).  */
-	  if (result_list != NULL)
+	  if (result_list != nullptr)
 	    if (!*result_list)
 	      /* This used to say *result_list = *found->prefixlist.
 	         If that was correct, need to modify the documentation
 	         at the top of this function to clarify what is
 	         supposed to be going on.  */
 	      *result_list = found;
+	  /* For ambiguous commands, do not return any default_args args.  */
+	  if (default_args != nullptr)
+	    *default_args = std::string ();
 	  return c;
 	}
       else
@@ -1705,8 +1796,10 @@  lookup_cmd_1 (const char **text, struct cmd_list_element *clist,
     }
   else
     {
-      if (result_list != NULL)
+      if (result_list != nullptr)
 	*result_list = clist;
+      if (!found_alias && default_args != nullptr)
+	*default_args = found->default_args;
       return found;
     }
 }
@@ -1726,8 +1819,13 @@  undef_cmd_error (const char *cmdtype, const char *q)
 
 /* Look up the contents of *LINE as a command in the command list LIST.
    LIST is a chain of struct cmd_list_element's.
-   If it is found, return the struct cmd_list_element for that command
-   and update *LINE to point after the command name, at the first argument.
+   If it is found, return the struct cmd_list_element for that command,
+   update *LINE to point after the command name, at the first argument
+   and update *DEFAULT_ARGS (if DEFAULT_ARGS is not null) to the default
+   args to prepend to the user provided args when running the command.
+   Note that if the found cmd_list_element is found via an alias,
+   the default args of the alias are returned.
+
    If not found, call error if ALLOW_UNKNOWN is zero
    otherwise (or if error returns) return zero.
    Call error if specified command is ambiguous,
@@ -1741,6 +1839,7 @@  undef_cmd_error (const char *cmdtype, const char *q)
 struct cmd_list_element *
 lookup_cmd (const char **line, struct cmd_list_element *list,
 	    const char *cmdtype,
+	    std::string *default_args,
 	    int allow_unknown, int ignore_help_classes)
 {
   struct cmd_list_element *last_list = 0;
@@ -1752,7 +1851,7 @@  lookup_cmd (const char **line, struct cmd_list_element *list,
   if (!*line)
     error (_("Lack of needed %scommand"), cmdtype);
 
-  c = lookup_cmd_1 (line, list, &last_list, ignore_help_classes);
+  c = lookup_cmd_1 (line, list, &last_list, default_args, ignore_help_classes);
 
   if (!c)
     {
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index f4719bfac4..f855ee5724 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -183,6 +183,10 @@  struct cmd_list_element
     /* Hook for another command to be executed after this command.  */
     struct cmd_list_element *hook_post = nullptr;
 
+    /* Default arguments to automatically prepend to the user
+       provided arguments when running this command or alias.  */
+    std::string default_args;
+
     /* Nonzero identifies a prefix command.  For them, the address
        of the variable containing the list of subcommands.  */
     struct cmd_list_element **prefixlist = nullptr;
diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index c9d2378906..e912613785 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -974,7 +974,7 @@  process_next_line (const char *p, struct command_line **command,
       /* Resolve command abbreviations (e.g. 'ws' for 'while-stepping').  */
       const char *cmd_name = p;
       struct cmd_list_element *cmd
-	= lookup_cmd_1 (&cmd_name, cmdlist, NULL, 1);
+	= lookup_cmd_1 (&cmd_name, cmdlist, NULL, NULL, 1);
       cmd_name = skip_spaces (cmd_name);
       bool inline_cmd = *cmd_name != '\0';
 
@@ -1331,7 +1331,7 @@  validate_comname (const char **comname)
       std::string prefix (*comname, last_word - 1);
       const char *tem = prefix.c_str ();
 
-      c = lookup_cmd (&tem, cmdlist, "", 0, 1);
+      c = lookup_cmd (&tem, cmdlist, "", NULL, 0, 1);
       if (c->prefixlist == NULL)
 	error (_("\"%s\" is not a prefix command."), prefix.c_str ());
 
@@ -1387,7 +1387,7 @@  do_define_command (const char *comname, int from_tty,
 
   /* Look it up, and verify that we got an exact match.  */
   tem = comname;
-  c = lookup_cmd (&tem, *list, "", -1, 1);
+  c = lookup_cmd (&tem, *list, "", NULL, -1, 1);
   if (c && strcmp (comname, c->name) != 0)
     c = 0;
 
@@ -1432,7 +1432,7 @@  do_define_command (const char *comname, int from_tty,
     {
       /* Look up cmd it hooks, and verify that we got an exact match.  */
       tem = comname + hook_name_size;
-      hookc = lookup_cmd (&tem, *list, "", -1, 0);
+      hookc = lookup_cmd (&tem, *list, "", NULL, -1, 0);
       if (hookc && strcmp (comname + hook_name_size, hookc->name) != 0)
 	hookc = 0;
       if (!hookc && commands == nullptr)
@@ -1518,7 +1518,7 @@  document_command (const char *comname, int from_tty)
   list = validate_comname (&comname);
 
   tem = comname;
-  c = lookup_cmd (&tem, *list, "", 0, 1);
+  c = lookup_cmd (&tem, *list, "", NULL, 0, 1);
 
   if (c->theclass != class_user)
     error (_("Command \"%s\" is built-in."), comfull);
@@ -1567,7 +1567,7 @@  define_prefix_command (const char *comname, int from_tty)
 
   /* Look it up, and verify that we got an exact match.  */
   tem = comname;
-  c = lookup_cmd (&tem, *list, "", -1, 1);
+  c = lookup_cmd (&tem, *list, "", NULL, -1, 1);
   if (c != nullptr && strcmp (comname, c->name) != 0)
     c = nullptr;
 
diff --git a/gdb/command.h b/gdb/command.h
index 04a380cba4..39d48a6c2f 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -291,11 +291,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 *,
+					    std::string *,
 					    int, int);
 
 extern struct cmd_list_element *lookup_cmd_1 (const char **,
 					      struct cmd_list_element *,
 					      struct cmd_list_element **,
+					      std::string *,
 					      int);
 
 extern struct cmd_list_element *deprecate_cmd (struct cmd_list_element *,
diff --git a/gdb/completer.c b/gdb/completer.c
index d03dc77c65..cd077097dc 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -1385,7 +1385,7 @@  complete_line_internal_1 (completion_tracker &tracker,
     }
   else
     {
-      c = lookup_cmd_1 (&p, cmdlist, &result_list, ignore_help_classes);
+      c = lookup_cmd_1 (&p, cmdlist, &result_list, NULL, ignore_help_classes);
     }
 
   /* Move p up to the next interesting thing.  */
diff --git a/gdb/guile/scm-cmd.c b/gdb/guile/scm-cmd.c
index ec1adffa25..8fd2772df4 100644
--- a/gdb/guile/scm-cmd.c
+++ b/gdb/guile/scm-cmd.c
@@ -512,7 +512,7 @@  gdbscm_parse_command_name (const char *name,
   prefix_text[i + 1] = '\0';
 
   prefix_text2 = prefix_text;
-  elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, 1);
+  elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, NULL, 1);
   if (elt == NULL || elt == CMD_LIST_AMBIGUOUS)
     {
       msg = xstrprintf (_("could not find command prefix '%s'"), prefix_text);
diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c
index b72766523a..62e2108740 100644
--- a/gdb/guile/scm-param.c
+++ b/gdb/guile/scm-param.c
@@ -466,13 +466,13 @@  add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
   /* Lookup created parameter, and register Scheme object against the
      parameter context.  Perform this task against both lists.  */
   tmp_name = cmd_name;
-  param = lookup_cmd (&tmp_name, *show_list, "", 0, 1);
+  param = lookup_cmd (&tmp_name, *show_list, "", NULL, 0, 1);
   gdb_assert (param != NULL);
   set_cmd_context (param, self);
   *set_cmd = param;
 
   tmp_name = cmd_name;
-  param = lookup_cmd (&tmp_name, *set_list, "", 0, 1);
+  param = lookup_cmd (&tmp_name, *set_list, "", NULL, 0, 1);
   gdb_assert (param != NULL);
   set_cmd_context (param, self);
   *show_cmd = param;
@@ -969,7 +969,7 @@  pascm_parameter_defined_p (const char *name, struct cmd_list_element *list)
 {
   struct cmd_list_element *c;
 
-  c = lookup_cmd_1 (&name, list, NULL, 1);
+  c = lookup_cmd_1 (&name, list, NULL, NULL, 1);
 
   /* If the name is ambiguous that's ok, it's a new parameter still.  */
   return c != NULL && c != CMD_LIST_AMBIGUOUS;
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 32905a7b59..40e46db396 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -3178,7 +3178,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_run, 0, &cmdlist);
 
@@ -3191,7 +3191,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);
 
@@ -3210,7 +3210,7 @@  working directory."),
 				   set_cwd_command,
 				   show_cwd_command,
 				   &setlist, &showlist);
-  c = lookup_cmd (&cmd_name, setlist, "", -1, 1);
+  c = lookup_cmd (&cmd_name, setlist, "", NULL, -1, 1);
   gdb_assert (c != NULL);
   set_cmd_completer (c, filename_completer);
 
diff --git a/gdb/python/py-auto-load.c b/gdb/python/py-auto-load.c
index 2084fc43ff..56db946463 100644
--- a/gdb/python/py-auto-load.c
+++ b/gdb/python/py-auto-load.c
@@ -84,12 +84,12 @@  Show the debugger's behaviour regarding auto-loaded Python scripts, "
 			   NULL, NULL, show_auto_load_python_scripts,
 			   &setlist, &showlist);
   cmd_name = "auto-load-scripts";
-  cmd = lookup_cmd (&cmd_name, setlist, "", -1, 1);
+  cmd = lookup_cmd (&cmd_name, setlist, "", NULL, -1, 1);
   deprecate_cmd (cmd, "set auto-load python-scripts");
 
   /* It is needed because lookup_cmd updates the CMD_NAME pointer.  */
   cmd_name = "auto-load-scripts";
-  cmd = lookup_cmd (&cmd_name, showlist, "", -1, 1);
+  cmd = lookup_cmd (&cmd_name, showlist, "", NULL, -1, 1);
   deprecate_cmd (cmd, "show auto-load python-scripts");
 
   add_cmd ("python-scripts", class_info, info_auto_load_python_scripts,
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index 3c1c566b0a..760208f52b 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -390,7 +390,7 @@  gdbpy_parse_command_name (const char *name,
   std::string prefix_text (name, i + 1);
 
   prefix_text2 = prefix_text.c_str ();
-  elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, 1);
+  elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, NULL, 1);
   if (elt == NULL || elt == CMD_LIST_AMBIGUOUS)
     {
       PyErr_Format (PyExc_RuntimeError, _("Could not find command prefix %s."),
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index 7b183cfa55..fb39187b18 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -569,12 +569,12 @@  add_setshow_generic (int parmclass, enum command_class cmdclass,
   /* Lookup created parameter, and register Python object against the
      parameter context.  Perform this task against both lists.  */
   tmp_name = cmd_name;
-  param = lookup_cmd (&tmp_name, *show_list, "", 0, 1);
+  param = lookup_cmd (&tmp_name, *show_list, "", NULL, 0, 1);
   if (param)
     set_cmd_context (param, self);
 
   tmp_name = cmd_name;
-  param = lookup_cmd (&tmp_name, *set_list, "", 0, 1);
+  param = lookup_cmd (&tmp_name, *set_list, "", NULL, 0, 1);
   if (param)
     set_cmd_context (param, self);
 }
diff --git a/gdb/remote.c b/gdb/remote.c
index 812ab8bc1b..ae9b177f2b 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -14425,10 +14425,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 c62eb57695..da9b805b47 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -579,6 +579,8 @@  execute_command (const char *p, int from_tty)
     {
       const char *cmd = p;
       const char *arg;
+      std::string default_args;
+      std::string default_args_and_arg;
       int was_sync = current_ui->prompt_state == PROMPT_BLOCKED;
 
       line = p;
@@ -586,15 +588,26 @@  execute_command (const char *p, int from_tty)
       /* If trace-commands is set then this will print this command.  */
       print_command_trace ("%s", p);
 
-      c = lookup_cmd (&cmd, cmdlist, "", 0, 1);
+      c = lookup_cmd (&cmd, cmdlist, "", &default_args, 0, 1);
       p = cmd;
 
       scoped_restore save_repeat_args
 	= make_scoped_restore (&repeat_arguments, nullptr);
       const char *args_pointer = p;
 
-      /* Pass null arg rather than an empty one.  */
-      arg = *p ? p : 0;
+      if (!default_args.empty ())
+	{
+	  if (*p != '\0')
+	    default_args_and_arg = default_args + ' ' + p;
+	  else
+	    default_args_and_arg = default_args;
+	  arg = default_args_and_arg.c_str ();
+	}
+      else
+	{
+	  /* Pass null arg rather than an empty one.  */
+	  arg = *p == '\0' ? nullptr : p;
+	}
 
       /* FIXME: cagney/2002-02-02: The c->type test is pretty dodgy
          while the is_complete_command(cfunc) test is just plain
@@ -1957,7 +1970,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)
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index f4a208f616..00b7059be5 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -651,7 +651,7 @@  validate_actionline (const char *line, struct breakpoint *b)
   if (*p == '#')		/* comment line */
     return;
 
-  c = lookup_cmd (&p, cmdlist, "", -1, 1);
+  c = lookup_cmd (&p, cmdlist, "", NULL, -1, 1);
   if (c == 0)
     error (_("`%s' is not a tracepoint action, or is ambiguous."), p);
 
@@ -1303,7 +1303,7 @@  encode_actions_1 (struct command_line *action,
       action_exp = action->line;
       action_exp = skip_spaces (action_exp);
 
-      cmd = lookup_cmd (&action_exp, cmdlist, "", -1, 1);
+      cmd = lookup_cmd (&action_exp, cmdlist, "", NULL, -1, 1);
       if (cmd == 0)
 	error (_("Bad action list item: %s"), action_exp);
 
@@ -2673,7 +2673,7 @@  trace_dump_actions (struct command_line *action,
       if (*action_exp == '#')	/* comment line */
 	continue;
 
-      cmd = lookup_cmd (&action_exp, cmdlist, "", -1, 1);
+      cmd = lookup_cmd (&action_exp, cmdlist, "", NULL, -1, 1);
       if (cmd == 0)
 	error (_("Bad action list item: %s"), action_exp);