public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/4] Add support for command renaming
@ 2021-01-08 10:07 Marco Barisione
  2021-01-08 10:07 ` [PATCH 1/4] gdb: add lookup_cmd_exact to simplify a common pattern Marco Barisione
                   ` (4 more replies)
  0 siblings, 5 replies; 38+ messages in thread
From: Marco Barisione @ 2021-01-08 10:07 UTC (permalink / raw)
  To: gdb-patches

This patch series adds support for command renaming to GDB.  The main
use case is to extend existing commands:
* The original command gets renamed.
* A new command with the same original name gets defined and then calls
  the renamed one.

This feature is accessible via GDB commands ("rename" and
"define -rename-existing-to") and in Python via a new rename_existing_to
argument argument to gdb.Command.__init__.

This is the third attempt at achieving this, the first patch series (see
https://sourceware.org/legacy-ml/gdb-patches/2019-10/msg00985.html) used
a completely different approach where a command, once overwritten, was
preserved by the new command (via a new field in cmd_list_element).
Overwriting commands could access the original implementation via a
Python method on gdb.Command.

The second attempt (see
https://sourceware.org/pipermail/gdb-patches/2020-September/171829.html)
was a revised version of the previous one which worked in a similar way,
but using a slightly different terminology.  It also added a way to
access this feature via a GDB command, not only via Python.

In response to my patches, Pedro Alves recommended a different approach
involving command renaming, which is what I implemented here.

Note that I didn't use "v3" in these patches are the approach is
completely different from previous attempts, so it's not really a new
version of already proposed patches (even if the end goal of this
feature is the same).

Marco Barisione (4):
  gdb: add lookup_cmd_exact to simplify a common pattern
  gdb: prevent prefix commands from being hooks
  gdb: update the docs for add_cmd and do_add_cmd to match reality
  gdb: Add support for renaming commands

 gdb/NEWS                                      |  26 +
 gdb/cli/cli-decode.c                          | 708 +++++++++++++++---
 gdb/cli/cli-decode.h                          |   3 +
 gdb/cli/cli-script.c                          | 368 +++++++--
 gdb/command.h                                 | 103 ++-
 gdb/doc/gdb.texinfo                           |  55 +-
 gdb/doc/python.texi                           |  38 +-
 gdb/python/py-cmd.c                           | 172 ++++-
 gdb/testsuite/gdb.base/command-renaming.exp   | 571 ++++++++++++++
 gdb/testsuite/gdb.base/define-prefix.exp      |  24 +
 .../gdb.python/py-rename-existing.exp         | 364 +++++++++
 .../gdb.python/py-rename-existing.py          |  46 ++
 12 files changed, 2238 insertions(+), 240 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/command-renaming.exp
 create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.exp
 create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.py

-- 
2.28.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 1/4] gdb: add lookup_cmd_exact to simplify a common pattern
  2021-01-08 10:07 [PATCH 0/4] Add support for command renaming Marco Barisione
@ 2021-01-08 10:07 ` Marco Barisione
  2021-01-10  0:06   ` Lancelot SIX
  2021-01-08 10:07 ` [PATCH 2/4] gdb: prevent prefix commands from being hooks Marco Barisione
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 38+ messages in thread
From: Marco Barisione @ 2021-01-08 10:07 UTC (permalink / raw)
  To: gdb-patches

In code dealing with commands, there's a pattern repeated a few times of
calling lookup_cmd with some speficic arguments and then using strcmp
on the returned command to check for an exact match.
As a later patch would add a few more similar lines of code, this patch
adds a new lookup_cmd_exact function which simplify this use case.

gdb/ChangeLog:

	* cli/cli-decode.c (lookup_cmd_exact): Add.
	* cli/cli-script.c (do_define_command): Use lookup_cmd_exact.
	(define_prefix_command): Ditto.
	* command.h: Add lookup_cmd_exact.
---
 gdb/cli/cli-decode.c | 15 +++++++++++++++
 gdb/cli/cli-script.c | 23 ++++++-----------------
 gdb/command.h        | 19 +++++++++++++++++++
 3 files changed, 40 insertions(+), 17 deletions(-)

diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 99bd4c6d2cd..87785107e3d 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -1875,6 +1875,21 @@ lookup_cmd (const char **line, struct cmd_list_element *list,
   return 0;
 }
 
+/* See command.h.  */
+
+struct cmd_list_element *
+lookup_cmd_exact (const char *name,
+		  struct cmd_list_element *list,
+		  bool ignore_help_classes)
+{
+  const char *tem = name;
+  struct cmd_list_element *cmd = lookup_cmd (&tem, list, "", NULL, -1,
+					     ignore_help_classes);
+  if (cmd && strcmp (name, cmd->name) != 0)
+    cmd = nullptr;
+  return cmd;
+}
+
 /* We are here presumably because an alias or command in TEXT is
    deprecated and a warning message should be generated.  This
    function decodes TEXT and potentially generates a warning message
diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index 9d0dd7796e0..0544f3efb1b 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -1391,7 +1391,7 @@ do_define_command (const char *comname, int from_tty,
       CMD_POST_HOOK
     };
   struct cmd_list_element *c, *newc, *hookc = 0, **list;
-  const char *tem, *comfull;
+  const char *comfull;
   int  hook_type      = CMD_NO_HOOK;
   int  hook_name_size = 0;
    
@@ -1403,11 +1403,7 @@ do_define_command (const char *comname, int from_tty,
   comfull = comname;
   list = validate_comname (&comname);
 
-  /* Look it up, and verify that we got an exact match.  */
-  tem = comname;
-  c = lookup_cmd (&tem, *list, "", NULL, -1, 1);
-  if (c && strcmp (comname, c->name) != 0)
-    c = 0;
+  c = lookup_cmd_exact (comname, *list);
 
   if (c && commands == nullptr)
     {
@@ -1448,11 +1444,9 @@ do_define_command (const char *comname, int from_tty,
 
   if (hook_type != CMD_NO_HOOK)
     {
-      /* Look up cmd it hooks, and verify that we got an exact match.  */
-      tem = comname + hook_name_size;
-      hookc = lookup_cmd (&tem, *list, "", NULL, -1, 0);
-      if (hookc && strcmp (comname + hook_name_size, hookc->name) != 0)
-	hookc = 0;
+      /* Look up cmd it hooks.  */
+      hookc = lookup_cmd_exact (comname + hook_name_size, *list,
+				/* ignore_help_classes = */ false);
       if (!hookc && commands == nullptr)
 	{
 	  warning (_("Your new `%s' command does not "
@@ -1593,17 +1587,12 @@ static void
 define_prefix_command (const char *comname, int from_tty)
 {
   struct cmd_list_element *c, **list;
-  const char *tem;
   const char *comfull;
 
   comfull = comname;
   list = validate_comname (&comname);
 
-  /* Look it up, and verify that we got an exact match.  */
-  tem = comname;
-  c = lookup_cmd (&tem, *list, "", NULL, -1, 1);
-  if (c != nullptr && strcmp (comname, c->name) != 0)
-    c = nullptr;
+  c = lookup_cmd_exact (comname, *list);
 
   if (c != nullptr && c->theclass != class_user)
     error (_("Command \"%s\" is built-in."), comfull);
diff --git a/gdb/command.h b/gdb/command.h
index 79e5017ff7a..827a19637a2 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -326,6 +326,25 @@ extern struct cmd_list_element *lookup_cmd_1
 	 struct cmd_list_element **result_list, std::string *default_args,
 	 int ignore_help_classes, bool lookup_for_completion_p = false);
 
+/* Look up the command called NAME in the command list LIST.
+
+   Unlike LOOKUP_CMD, partial matches are ignored and only exact matches
+   on NAME are considered.
+
+   LIST is a chain of struct cmd_list_element's.
+
+   If IGNORE_HELP_CLASSES is true (the default), 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 null).
+
+   If found, return the struct cmd_list_element for that command,
+   otherwise return NULLPTR.  */
+
+extern struct cmd_list_element *lookup_cmd_exact
+			(const char *name,
+			 struct cmd_list_element *list,
+			 bool ignore_help_classes = true);
+
 extern struct cmd_list_element *deprecate_cmd (struct cmd_list_element *,
 					       const char * );
 
-- 
2.28.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 2/4] gdb: prevent prefix commands from being hooks
  2021-01-08 10:07 [PATCH 0/4] Add support for command renaming Marco Barisione
  2021-01-08 10:07 ` [PATCH 1/4] gdb: add lookup_cmd_exact to simplify a common pattern Marco Barisione
@ 2021-01-08 10:07 ` Marco Barisione
  2021-01-08 10:07 ` [PATCH 3/4] gdb: update the docs for add_cmd and do_add_cmd to match reality Marco Barisione
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 38+ messages in thread
From: Marco Barisione @ 2021-01-08 10:07 UTC (permalink / raw)
  To: gdb-patches

Currently it's possible for hooks to be prefix commands:

  define-prefix hook-run

  define hook-run
    echo Will run!\n
  end

  define hook-run subcommand
    echo Subcommand\n
  end

Then:

  (gdb) run
  Will run!
  [...]
  (gdb) hook-run subcommand
  Subcommand

This doesn't seem very useful or worse, it can be confusing.  Moreover,
this creates some obscure corner cases in a later patch adding command
renaming.

Because of this, this patch prevents hooks to become prefixes and
prefixes to become hooks.

An alternative approach would have been to prevent prefixes starting
with "hook-" or "hookpost-".  The end result would have been similar
but not identical as GDB allows commands starting with "hook-" or
"hookpost-" which are not hooks (for instance, "define
hook-this-doesnt-exist").  To be more consistent with this use case,
prefixes for commands which are named like hooks but are not hooks are
allowed.

gdb/ChangeLog:

	* cli/cli-script.c (do_define_command): Prevent prefix commands
	from becoming hooks.
	(define_prefix_command): Prevent hooks from becoming prefix
	commands.

gdb/testsuite/ChangeLog:

	* gdb.base/define-prefix.exp: Test that prefix commands cannot
	become hooks and vice versa.
---
 gdb/cli/cli-script.c                     | 25 ++++++++++++++++++------
 gdb/testsuite/gdb.base/define-prefix.exp | 24 +++++++++++++++++++++++
 2 files changed, 43 insertions(+), 6 deletions(-)

diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index 0544f3efb1b..e197852bc71 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -1455,6 +1455,11 @@ do_define_command (const char *comname, int from_tty,
 	  if (!query (_("Proceed? ")))
 	    error (_("Not confirmed."));
 	}
+
+      if (c != nullptr && c->prefixlist != nullptr)
+        error (_("Command \"%s\" cannot become a hook as it's already a "
+		 "prefix command."),
+	       comfull);
     }
 
   comname = xstrdup (comname);
@@ -1594,13 +1599,21 @@ define_prefix_command (const char *comname, int from_tty)
 
   c = lookup_cmd_exact (comname, *list);
 
-  if (c != nullptr && c->theclass != class_user)
-    error (_("Command \"%s\" is built-in."), comfull);
-
-  if (c != nullptr && c->prefixlist != nullptr)
+  if (c != nullptr)
     {
-      /* c is already a user defined prefix command.  */
-      return;
+      if (c->theclass != class_user)
+        error (_("Command \"%s\" is built-in."), comfull);
+
+      if (c->prefixlist != nullptr)
+        {
+          /* C is already a user defined prefix command.  */
+          return;
+        }
+
+      if (c->hookee_pre != nullptr || c->hookee_post != nullptr)
+        error (_("Command \"%s\" cannot be a prefix command as it's "
+		 "already a hook."),
+	       comfull);
     }
 
   /* If the command does not exist at all, create it.  */
diff --git a/gdb/testsuite/gdb.base/define-prefix.exp b/gdb/testsuite/gdb.base/define-prefix.exp
index 71369caaed6..a8ce5765ec7 100644
--- a/gdb/testsuite/gdb.base/define-prefix.exp
+++ b/gdb/testsuite/gdb.base/define-prefix.exp
@@ -162,3 +162,27 @@ gdb_test "define-prefix something-not-existing something-else" \
 gdb_test "define-prefix abc-prefix something-not-existing something-else" \
     "Undefined abc-prefix command: \"something-not-existing\".*"
 
+####################
+# Check error behaviour when interacting with hooks.
+
+# Define a command and hooks into it.
+gdb_define_cmd "hookee1" {}
+gdb_define_cmd "hook-hookee1" {}
+gdb_define_cmd "hookpost-hookee1" {}
+
+# Check that making the hooks into prefix commands is not allowed.
+gdb_test_exact "define-prefix hook-hookee1" \
+  "Command \"hook-hookee1\" cannot be a prefix command as it's already a hook."
+gdb_test_exact "define-prefix hookpost-hookee1" \
+  "Command \"hookpost-hookee1\" cannot be a prefix command as it's already a hook."
+
+# Define a command and prefixes which look like hooks into it (but they
+# are not).
+gdb_define_cmd "hookee2" {}
+gdb_test_no_output "define-prefix hook-hookee2" {}
+gdb_test_no_output "define-prefix hookpost-hookee2" {}
+
+gdb_test_exact "define hook-hookee2" \
+  "Command \"hook-hookee2\" cannot become a hook as it's already a prefix command."
+gdb_test_exact "define hookpost-hookee2" \
+  "Command \"hookpost-hookee2\" cannot become a hook as it's already a prefix command."
-- 
2.28.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 3/4] gdb: update the docs for add_cmd and do_add_cmd to match reality
  2021-01-08 10:07 [PATCH 0/4] Add support for command renaming Marco Barisione
  2021-01-08 10:07 ` [PATCH 1/4] gdb: add lookup_cmd_exact to simplify a common pattern Marco Barisione
  2021-01-08 10:07 ` [PATCH 2/4] gdb: prevent prefix commands from being hooks Marco Barisione
@ 2021-01-08 10:07 ` Marco Barisione
  2021-01-08 10:07 ` [PATCH 4/4] gdb: Add support for renaming commands Marco Barisione
  2021-01-25 11:26 ` [PATCH v2 0/5] Add support for command renaming Marco Barisione
  4 siblings, 0 replies; 38+ messages in thread
From: Marco Barisione @ 2021-01-08 10:07 UTC (permalink / raw)
  To: gdb-patches

It looks like the docs for add_cmd ended up being the docs for
do_add_cmd.  Moreover, they were outdated.

gdb/ChangeLog:

	* cli/cli-decode.c (do_add_cmd): Remove outdated documentation
	and refer to the documentation for add_cmd.
	(add_cmd): Add comment referring to the docs in command.h.
	* command.h (add_cmd): Add updated documentation.
---
 gdb/cli/cli-decode.c | 22 +++++-----------------
 gdb/command.h        | 40 +++++++++++++++++++++++++++++++++-------
 2 files changed, 38 insertions(+), 24 deletions(-)

diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 87785107e3d..a25e3f420b3 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -160,23 +160,7 @@ set_cmd_completer_handle_brkchars (struct cmd_list_element *cmd,
   cmd->completer_handle_brkchars = func;
 }
 
-/* Add element named NAME.
-   Space for NAME and DOC must be allocated by the caller.
-   CLASS is the top level category into which commands are broken down
-   for "help" purposes.
-   FUN should be the function to execute the command;
-   it will get a character string as argument, with leading
-   and trailing blanks already eliminated.
-
-   DOC is a documentation string for the command.
-   Its first line should be a complete sentence.
-   It should start with ? for a command that is an abbreviation
-   or with * for a command that most users don't need to know about.
-
-   Add this command to command list *LIST.
-
-   Returns a pointer to the added command (not necessarily the head 
-   of *LIST).  */
+/* Like ADD_CMD, but the command function fields are not modified.  */
 
 static struct cmd_list_element *
 do_add_cmd (const char *name, enum command_class theclass,
@@ -227,6 +211,8 @@ do_add_cmd (const char *name, enum command_class theclass,
   return c;
 }
 
+/* See command.h.  */
+
 struct cmd_list_element *
 add_cmd (const char *name, enum command_class theclass,
 	 const char *doc, struct cmd_list_element **list)
@@ -237,6 +223,8 @@ add_cmd (const char *name, enum command_class theclass,
   return result;
 }
 
+/* See command.h.  */
+
 struct cmd_list_element *
 add_cmd (const char *name, enum command_class theclass,
 	 cmd_const_cfunc_ftype *fun,
diff --git a/gdb/command.h b/gdb/command.h
index 827a19637a2..df40cbf7119 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -155,18 +155,44 @@ extern bool valid_user_defined_cmd_name_p (const char *name);
 
 extern bool valid_cmd_char_p (int c);
 
-/* Const-correct variant of the above.  */
+/* Add a command named NAME in command list *LIST.
 
-extern struct cmd_list_element *add_cmd (const char *, enum command_class,
+   NAME and DOC are not duplicated. If they are not static string, they
+   must have been allocated with xmalloc or xstrdup and the
+   NAME_ALLOCATED/DOC_ALLOCATED fields must be set to 1 on the returned
+   command.
+
+   THECLASS is the top level category into which commands are broken down
+   for "help" purposes.
+
+   FUN should be the function to execute the command; it will get two
+   arguments, a character string (with leading and trailing blanks already
+   eliminated) containing the command arguments, and an integer indicating
+   whether input comes from a TTY or not.
+
+   DOC is a documentation string for the command.
+   Its first line should be a complete sentence.
+   It should start with ? for a command that is an abbreviation
+   or with * for a command that most users don't need to know about.
+
+   If NAME already existed in *LIST, all its hooks and aliases are moved
+   to the new command.
+
+   Return a pointer to the added command (not necessarily the head of
+   *LIST).  */
+
+extern struct cmd_list_element *add_cmd (const char *name,
+					 enum command_class theclass,
 					 cmd_const_cfunc_ftype *fun,
-					 const char *,
-					 struct cmd_list_element **);
+					 const char *doc,
+					 struct cmd_list_element **list);
 
 /* Like add_cmd, but no command function is specified.  */
 
-extern struct cmd_list_element *add_cmd (const char *, enum command_class,
-					 const char *,
-					 struct cmd_list_element **);
+extern struct cmd_list_element *add_cmd (const char *name,
+					 enum command_class theclass,
+					 const char *doc,
+					 struct cmd_list_element **list);
 
 extern struct cmd_list_element *add_cmd_suppress_notification
 			(const char *name, enum command_class theclass,
-- 
2.28.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 4/4] gdb: Add support for renaming commands
  2021-01-08 10:07 [PATCH 0/4] Add support for command renaming Marco Barisione
                   ` (2 preceding siblings ...)
  2021-01-08 10:07 ` [PATCH 3/4] gdb: update the docs for add_cmd and do_add_cmd to match reality Marco Barisione
@ 2021-01-08 10:07 ` Marco Barisione
  2021-01-08 10:30   ` Eli Zaretskii
  2021-01-25 11:26 ` [PATCH v2 0/5] Add support for command renaming Marco Barisione
  4 siblings, 1 reply; 38+ messages in thread
From: Marco Barisione @ 2021-01-08 10:07 UTC (permalink / raw)
  To: gdb-patches

This patch adds:
* A "rename" command.
* A "-rename-existing-to" option for the "define" command.
* A "rename_existing_to" optional argument to gdb.Command.__init__
  which matches the behaviour of "define -rename-existing-to".

The goal of this is to allow users to build on top of existing commands
without losing their original implementation.
Something similar could be achieved through hooks but they are limited:
* Hooks cannot prevent the command from being executed without printing
  any error.
* Hooks don't get passed the arguments passed to the command.
* Post hooks can't know if the command failed.
* Hooks cannot be defined in Python.

gdb/ChangeLog:

	* NEWS: Add items for the new rename command, the new
	-rename-existing-to option for define and the new
	rename_existing_to argument for gdb.Command.__init__.
	* cli/cli-decode.c (delete_cmd): Rename to disconnect_cmd.
	(disconnect_cmd): Disconnect a command from other commands and
	lists rather than deleting the command.
	(delete_cmd_by_name): Add.
	(update_prefix_links): Add.
	(update_cmd): Add.
	(do_add_cmd): Add ability to rename existing commands rather
	than redefining them.
	(add_cmd): Ditto.
	(add_prefix_cmd): Ditto.
	(add_alias_cmd): Update to use disconnect_cmd.
	(break_cmd_relationships): Add.
	(rename_hook): Add.
	(user_defined_command): Move from cli/cli-script.c.
	(rename_cmd): Add.
	* cli/cli-decode.h (struct cmd_list_element): Call the destroyer
	function from the destructor and always free the prefixname.
	* cli/cli-script.c (do_define_command): Add ability to rename
	existing commands rather than redefining them.
	(user_defined_command): Move to cli/cli-decode.c
	(check_command_redefinition): Add.
	(enum cmd_hook_type): Move from do_define_command to the global
	scope.
	(get_hook_type): Add.
	(HOOK_STRING): Make a variable in get_hook_type.
	(HOOK_LEN): Ditto.
	(HOOK_POST_STRING): Ditto.
	(HOOK_POST_LEN): Ditto.
	(struct define_cmd_opts): Add.
	(make_define_cmd_options_def_group): Add.
	(define_command): Add -rename-existing-to option.
	(do_rename_command): Add.
	(rename_command): Add.
	(_initialize_cli_script): Add option parsing for the define
	command and add the rename command.
	* command.h (add_cmd): Add ability to rename existing commands
	rather than redefining them.
	(add_prefix_cmd): Ditto.
	(rename_cmd): Add.
	(user_defined_command): Add declaration of previously static
	function.
	* python/py-cmd.c (cmdpy_init): Add rename_existing_to argument.

gdb/doc/ChangeLog:

	* gdb.texinfo: Document the new rename command and the new
	-rename-existing-to option for define.
	* python.texi: Document the new rename_existing_to argument for
	gdb.Command.__init__.

gdb/testsuite/ChangeLog:

	* gdb.base/command-renaming.exp: New test.
	* gdb.python/py-rename-existing.exp: New test.
	* gdb.python/py-rename-existing.py: New test.
---
 gdb/NEWS                                      |  26 +
 gdb/cli/cli-decode.c                          | 671 +++++++++++++++---
 gdb/cli/cli-decode.h                          |   3 +
 gdb/cli/cli-script.c                          | 324 +++++++--
 gdb/command.h                                 |  60 +-
 gdb/doc/gdb.texinfo                           |  55 +-
 gdb/doc/python.texi                           |  38 +-
 gdb/python/py-cmd.c                           | 172 ++++-
 gdb/testsuite/gdb.base/command-renaming.exp   | 571 +++++++++++++++
 .../gdb.python/py-rename-existing.exp         | 364 ++++++++++
 .../gdb.python/py-rename-existing.py          |  46 ++
 11 files changed, 2127 insertions(+), 203 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/command-renaming.exp
 create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.exp
 create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.py

diff --git a/gdb/NEWS b/gdb/NEWS
index 66702862efb..99014c1331e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -35,6 +35,11 @@
 
 * New commands
 
+rename OLD_NAME NEW_NAME
+  Rename the command called OLD_NAME to NEW_NAME.  All subcommands, hooks
+  and aliases associated to OLD_NAME are moved to refer to NEW_NAME.
+  If a command name contains spaces, it must be enclosed in double quotes.
+
 set debug event-loop
 show debug event-loop
   Control the display of debug output about GDB's event loop.
@@ -49,6 +54,18 @@ maintenance flush dcache
 
 * Changed commands
 
+define [-rename-existing-to NEW_NAME_FOR_EXISTING_COMMAND] COMMAND_NAME
+  Added a -rename-existing-to option which causes the existing command
+  called COMMAND_NAME to be renamed to NEW_NAME_FOR_EXISTING_COMMAND
+  instead of being redefined and overwritten.  This allows user-defined
+  commands to modify existing commands and extend them rather than
+  replacing them completely.
+  The behaviour is similar to using the "rename" command followed by
+  "define", but "define -rename-existing-to" keeps aliases, hooks and
+  subcommands associated to the newly defined COMMAND_NAME command.
+  If NEW_NAME_FOR_EXISTING_COMMAND contains spaces, it must be enclosed in
+  double quotes, unlike COMMAND_NAME which must not be enclosed in quotes.
+
 break [PROBE_MODIFIER] [LOCATION] [thread THREADNUM]
       [-force-condition] [if CONDITION]
   This command would previously refuse setting a breakpoint if the
@@ -75,6 +92,15 @@ maintenance flush-symbol-cache
   'maintenance flush register-cache' and 'maintenance flush
   symbol-cache' respectively.
 
+* Python API
+
+  ** Added a new rename_existing_to optional parameter to gdb.Command.
+     If specified, the existing command with the same name as the new
+     command gets renamed to the value of rename_existing_to instead of
+     being redefined and overwritten.  This allows Python code to modify
+     existing commands and extend them rather than replacing them
+     completely.  See also "define -rename-existing-to".
+
 *** Changes in GDB 10
 
 * There are new feature names for ARC targets: "org.gnu.gdb.arc.core"
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index a25e3f420b3..11794a656c1 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -30,12 +30,18 @@
 
 static void undef_cmd_error (const char *, const char *);
 
-static struct cmd_list_element *delete_cmd (const char *name,
-					    struct cmd_list_element **list,
-					    struct cmd_list_element **prehook,
-					    struct cmd_list_element **prehookee,
-					    struct cmd_list_element **posthook,
-					    struct cmd_list_element **posthookee);
+static cmd_list_element *disconnect_cmd (const char *name,
+					 struct cmd_list_element **list,
+					 struct cmd_list_element ***prefixlist,
+					 struct cmd_list_element **aliases,
+					 struct cmd_list_element **prehook,
+					 struct cmd_list_element **prehookee,
+					 struct cmd_list_element **posthook,
+					 struct cmd_list_element **posthookee);
+
+static bool delete_cmd_by_name (const char *name,
+				struct cmd_list_element **list,
+				struct cmd_list_element ***prefixlist);
 
 static struct cmd_list_element *find_cmd (const char *command,
 					  int len,
@@ -160,40 +166,110 @@ set_cmd_completer_handle_brkchars (struct cmd_list_element *cmd,
   cmd->completer_handle_brkchars = func;
 }
 
-/* Like ADD_CMD, but the command function fields are not modified.  */
+/* Update the prefix-related fields associated with command C, which must
+   be part of the comand list *LIST, and, if not null, adds PREFIXLIST
+   as subcommands of C.
 
-static struct cmd_list_element *
-do_add_cmd (const char *name, enum command_class theclass,
-	    const char *doc, struct cmd_list_element **list)
+   In particular, this function always updates the relationship of C with
+   the command it's a subcommand of (if any) and, if PREFIXLIST is not
+   null:
+   * Makes PREFIXLIST the list of subcommands of C (which makes C a prefix
+     command);
+   * Updates the subcommands in PREFIXLIST accordingly.
+   * Updates C->prefixname.  */
+
+static void
+update_prefix_links (struct cmd_list_element *c,
+		     struct cmd_list_element **list,
+		     struct cmd_list_element **prefixlist)
 {
-  struct cmd_list_element *c = new struct cmd_list_element (name, theclass,
-							    doc);
-  struct cmd_list_element *p, *iter;
-
-  /* Turn each alias of the old command into an alias of the new
-     command.  */
-  c->aliases = delete_cmd (name, list, &c->hook_pre, &c->hookee_pre,
-			   &c->hook_post, &c->hookee_post);
-  for (iter = c->aliases; iter; iter = iter->alias_chain)
-    iter->cmd_pointer = c;
-  if (c->hook_pre)
-    c->hook_pre->hookee_pre = c;
-  if (c->hookee_pre)
-    c->hookee_pre->hook_pre = c;
-  if (c->hook_post)
-    c->hook_post->hookee_post = c;
-  if (c->hookee_post)
-    c->hookee_post->hook_post = c;
-
-  if (*list == NULL || strcmp ((*list)->name, name) >= 0)
+  /* Update the subcommands to point to C as their prefix.  */
+  gdb_assert (c->prefixlist == nullptr);
+  c->prefixlist = prefixlist;
+  if (c->prefixlist)
+    {
+      for (struct cmd_list_element *iter = *(c->prefixlist);
+	   iter != nullptr;
+	   iter = iter->next)
+	iter->prefix = c;
+    }
+
+  /* Find the prefix cmd of C, and assign it to C->prefix.
+     See also add_prefix_cmd and update_prefix_field_of_prefixed_commands.  */
+  gdb_assert (c->prefix == nullptr);
+  struct cmd_list_element *prefixcmd = lookup_cmd_for_prefixlist (list,
+								  cmdlist);
+  c->prefix = prefixcmd;
+
+  /* Update the prefix name.  */
+  gdb_assert (c->prefixname == nullptr);
+  if (c->prefixlist != nullptr)
+    {
+      /* C's prefixname is its prefix's name plus its name and terminated
+	 with a space.  */
+      std::string prefixname;
+      if (c->prefix != nullptr)
+	prefixname = c->prefix->prefixname;
+      prefixname += c->name;
+      prefixname += " ";
+
+      c->prefixname = xstrdup (prefixname.c_str ());
+    }
+
+  /* When doing a rename of a command, the command itself gets moved, but
+     not its subcommands.  This means that their prefixname fields don't
+     need updating.  */
+}
+
+/* Update command C, by adding it to *LIST and updating it from the other
+   arguments.
+
+   C must not already be in any command list, or have any subcommands,
+   aliases or hooks associated.
+
+   If NEW_NAME is not null, then the command name is updated to the
+   specified name.
+
+   If PREFIXLIST is not null, then the *PREFIXLIST commands become
+   subcommands of C.
+
+   If ALIASES is not null, then the specified commands become aliases of
+   C.
+
+   The various HOOKs, if not null, specify commands hooking into C or
+   commands hooked into C.  */
+
+static void
+update_cmd (struct cmd_list_element *c,
+	    struct cmd_list_element **list,
+	    const char *new_name = nullptr,
+	    struct cmd_list_element **prefixlist = nullptr,
+	    struct cmd_list_element *aliases = nullptr,
+	    struct cmd_list_element *hook_pre = nullptr,
+	    struct cmd_list_element *hookee_pre = nullptr,
+	    struct cmd_list_element *hook_post = nullptr,
+	    struct cmd_list_element *hookee_post = nullptr)
+{
+  /* Update the name, if specified.  */
+  if (new_name != nullptr)
+    {
+      if (c->name_allocated)
+	xfree ((char *)c->name);
+
+      c->name = xstrdup (new_name);
+      c->name_allocated = true;
+    }
+
+  /* Add C to the command list LIST in the appropriate (sorted) position.  */
+  if (*list == NULL || strcmp ((*list)->name, c->name) >= 0)
     {
       c->next = *list;
       *list = c;
     }
   else
     {
-      p = *list;
-      while (p->next && strcmp (p->next->name, name) <= 0)
+      struct cmd_list_element *p = *list;
+      while (p->next && strcmp (p->next->name, c->name) <= 0)
 	{
 	  p = p->next;
 	}
@@ -201,12 +277,102 @@ do_add_cmd (const char *name, enum command_class theclass,
       p->next = c;
     }
 
-  /* Search the prefix cmd of C, and assigns it to C->prefix.
-     See also add_prefix_cmd and update_prefix_field_of_prefixed_commands.  */
-  struct cmd_list_element *prefixcmd = lookup_cmd_for_prefixlist (list,
-								  cmdlist);
-  c->prefix = prefixcmd;
+  /* Update prefix information.  */
+  update_prefix_links (c, list, prefixlist);
 
+  /* Update the aliases.  */
+  gdb_assert (c->aliases == nullptr);
+  c->aliases = aliases;
+  for (struct cmd_list_element *iter = c->aliases;
+       iter != nullptr;
+       iter = iter->alias_chain)
+    iter->cmd_pointer = c;
+
+  /* Updates hooks and hookees.  */
+  gdb_assert (c->hook_pre == nullptr);
+  c->hook_pre = hook_pre;
+  if (c->hook_pre)
+    c->hook_pre->hookee_pre = c;
+
+  gdb_assert (c->hookee_pre == nullptr);
+  c->hookee_pre = hookee_pre;
+  if (c->hookee_pre)
+    c->hookee_pre->hook_pre = c;
+
+  gdb_assert (c->hook_post == nullptr);
+  c->hook_post = hook_post;
+  if (c->hook_post)
+    c->hook_post->hookee_post = c;
+
+  gdb_assert (c->hookee_post == nullptr);
+  c->hookee_post = hookee_post;
+  if (c->hookee_post)
+    c->hookee_post->hook_post = c;
+}
+
+/* Like ADD_CMD, but the command function fields are not modified.  */
+
+static struct cmd_list_element *
+do_add_cmd (const char *name, enum command_class theclass,
+	    const char *doc, struct cmd_list_element **list,
+	    const char *rename_existing_to = nullptr,
+	    struct cmd_list_element **rename_existing_to_list = nullptr)
+{
+  /* If a command called RENAME_EXISTING_TO already exists, delete it.  */
+  struct cmd_list_element **deleted_cmd_prefixlist = nullptr;
+  if (rename_existing_to != nullptr)
+    {
+      gdb_assert (rename_existing_to_list != nullptr);
+      delete_cmd_by_name (rename_existing_to, rename_existing_to_list,
+			  &deleted_cmd_prefixlist);
+    }
+
+  /* Create the new command.  */
+  struct cmd_list_element *c = new struct cmd_list_element (name, theclass,
+							    doc);
+
+  /* Remove the old NAME command from LIST and disconnect all the
+     subcommands, aliases, hooks as well.  */
+  struct cmd_list_element **prefixlist;
+  struct cmd_list_element *aliases;
+  struct cmd_list_element *hook_pre, *hookee_pre, *hook_post, *hookee_post;
+  struct cmd_list_element *existing_cmd
+    = disconnect_cmd (name, list,
+		      &prefixlist,
+		      &aliases,
+		      &hook_pre, &hookee_pre,
+		      &hook_post, &hookee_post);
+
+  /* Add the new command to LIST turning aliases and hooks of the old
+     command into aliases/hooks of the new command.  */
+  update_cmd (c, list,
+	      /* new_name = */ nullptr,
+	      prefixlist,
+	      aliases,
+	      hook_pre, hookee_pre, hook_post, hookee_post);
+
+  /* Finally, rename the existing command or, if renaming is not
+     requested, delete it.  */
+  if (rename_existing_to != nullptr)
+    {
+      gdb_assert (existing_cmd != nullptr);
+      update_cmd (existing_cmd, rename_existing_to_list,
+		  rename_existing_to,
+		  deleted_cmd_prefixlist);
+      if (prefixlist)
+	{
+	  /* The existing command was a prefix command so it must remain
+	     a prefix command (with no subcommands).  This is needed as
+	     the implementation of the command could try to access its
+	     subcommands.  See define_prefix_command for details on how
+	     prefix lists work. */
+	  gdb_assert (existing_cmd->prefixlist == nullptr);
+	  existing_cmd->prefixlist = new struct cmd_list_element*;
+	  *(existing_cmd->prefixlist) = nullptr;
+	}
+    }
+  else
+    delete existing_cmd;
 
   return c;
 }
@@ -215,9 +381,13 @@ do_add_cmd (const char *name, enum command_class theclass,
 
 struct cmd_list_element *
 add_cmd (const char *name, enum command_class theclass,
-	 const char *doc, struct cmd_list_element **list)
+	 const char *doc, struct cmd_list_element **list,
+	 const char *rename_existing_to,
+	 struct cmd_list_element **rename_existing_to_list)
 {
-  cmd_list_element *result = do_add_cmd (name, theclass, doc, list);
+  cmd_list_element *result = do_add_cmd (name, theclass, doc, list,
+					 rename_existing_to,
+					 rename_existing_to_list);
   result->func = NULL;
   result->function.const_cfunc = NULL;
   return result;
@@ -228,9 +398,13 @@ add_cmd (const char *name, enum command_class theclass,
 struct cmd_list_element *
 add_cmd (const char *name, enum command_class theclass,
 	 cmd_const_cfunc_ftype *fun,
-	 const char *doc, struct cmd_list_element **list)
+	 const char *doc, struct cmd_list_element **list,
+	 const char *rename_existing_to,
+	 struct cmd_list_element **rename_existing_to_list)
 {
-  cmd_list_element *result = do_add_cmd (name, theclass, doc, list);
+  cmd_list_element *result = do_add_cmd (name, theclass, doc, list,
+					 rename_existing_to,
+					 rename_existing_to_list);
   set_cmd_cfunc (result, fun);
   return result;
 }
@@ -283,14 +457,19 @@ add_alias_cmd (const char *name, cmd_list_element *old,
 {
   if (old == 0)
     {
+      struct cmd_list_element **prefixlist;
+      struct cmd_list_element *aliases;
       struct cmd_list_element *prehook, *prehookee, *posthook, *posthookee;
-      struct cmd_list_element *aliases = delete_cmd (name, list,
-						     &prehook, &prehookee,
-						     &posthook, &posthookee);
-
+      struct cmd_list_element *old_cmd
+	= disconnect_cmd (name, list,
+			  &prefixlist,
+			  &aliases,
+			  &prehook, &prehookee,
+			  &posthook, &posthookee);
       /* If this happens, it means a programmer error somewhere.  */
-      gdb_assert (!aliases && !prehook && !prehookee
-		  && !posthook && ! posthookee);
+      gdb_assert (!old_cmd);
+      /* As OLD_CMD is null, NAME didn't exist, so the prefix list,
+       * aliases and hooks are all null as well.  */
       return 0;
     }
 
@@ -372,12 +551,16 @@ add_prefix_cmd (const char *name, enum command_class theclass,
 		cmd_const_cfunc_ftype *fun,
 		const char *doc, struct cmd_list_element **prefixlist,
 		const char *prefixname, int allow_unknown,
-		struct cmd_list_element **list)
+		struct cmd_list_element **list,
+		const char *rename_existing_to,
+		struct cmd_list_element **rename_existing_to_list)
 {
-  struct cmd_list_element *c = add_cmd (name, theclass, fun, doc, list);
+  struct cmd_list_element *c = add_cmd (name, theclass, fun, doc, list,
+					rename_existing_to,
+					rename_existing_to_list);
 
   c->prefixlist = prefixlist;
-  c->prefixname = prefixname;
+  c->prefixname = xstrdup (prefixname);
   c->allow_unknown = allow_unknown;
 
   /* Now that prefix command C is defined, we need to set the prefix field
@@ -895,76 +1078,352 @@ add_setshow_zuinteger_cmd (const char *name, enum command_class theclass,
 			NULL, NULL);
 }
 
-/* Remove the command named NAME from the command list.  Return the
-   list commands which were aliased to the deleted command.  If the
-   command had no aliases, return NULL.  The various *HOOKs are set to
-   the pre- and post-hook commands for the deleted command.  If the
-   command does not have a hook, the corresponding out parameter is
-   set to NULL.  */
-
-static struct cmd_list_element *
-delete_cmd (const char *name, struct cmd_list_element **list,
-	    struct cmd_list_element **prehook,
-	    struct cmd_list_element **prehookee,
-	    struct cmd_list_element **posthook,
-	    struct cmd_list_element **posthookee)
+/* Break the relationships between C and other commands.
+
+   This is done by removing subcommands, aliases and hooks from C and
+   making the other commands not point to C any more.
+
+   *PREFIXLIST is set to point to the list of subcommands for the removed
+   command.
+
+   *ALIASES is set to the list of commands which were aliased to C.  If
+   the command had no aliases, it's set to NULLPTR.
+
+   *ALIASES is set to the removed aliases.  If the command has no aliases,
+   it's set to NULLPTR.
+
+   The various *HOOKs are set to the pre- and post-hook commands for C.
+   If the command does not have a hook, the corresponding out parameter
+   is set to NULLPTR.  */
+
+static void
+break_cmd_relationships (struct cmd_list_element *c,
+			 struct cmd_list_element ***prefixlist,
+			 struct cmd_list_element **aliases,
+			 struct cmd_list_element **hook_pre,
+			 struct cmd_list_element **hookee_pre,
+			 struct cmd_list_element **hook_post,
+			 struct cmd_list_element **hookee_post)
 {
-  struct cmd_list_element *iter;
-  struct cmd_list_element **previous_chain_ptr;
-  struct cmd_list_element *aliases = NULL;
+  /* Make the subcommands not point to this command any more. */
+  if (c->prefixlist)
+    {
+      for (struct cmd_list_element *iter = *(c->prefixlist);
+	   iter != nullptr;
+	   iter = iter->next)
+	iter->prefix = nullptr;
+    }
+
+  /* Remove the subcommands.  */
+  *prefixlist = c->prefixlist;
+  c->prefixlist = nullptr;
+  /* And unset the prefix name (as only prefix commans have it).  */
+  xfree ((char *)c->prefixname);
+  c->prefixname = nullptr;
+
+  /* Make the aliases not point to this command any more. */
+  for (struct cmd_list_element *iter = c->aliases;
+       iter != nullptr;
+       iter = iter->alias_chain)
+    iter->cmd_pointer = nullptr;
+
+  /* Remove the aliases. */
+  *aliases = c->aliases;
+  c->aliases = nullptr;
+
+  /* If this command was an alias, remove it from the list of aliases.  */
+  if (c->cmd_pointer)
+    {
+      struct cmd_list_element **prevp = &c->cmd_pointer->aliases;
+      struct cmd_list_element *a = *prevp;
 
-  *prehook = NULL;
-  *prehookee = NULL;
-  *posthook = NULL;
-  *posthookee = NULL;
-  previous_chain_ptr = list;
+      while (a != c)
+	{
+	  prevp = &a->alias_chain;
+	  a = *prevp;
+	}
+      *prevp = c->alias_chain;
+    }
+
+  /* Save the previous hook lists in the output parameters.  */
+  *hook_pre = c->hook_pre;
+  *hookee_pre = c->hookee_pre;
+  *hook_post = c->hook_post;
+  *hookee_post = c->hookee_post;
+
+  /* Break the hook relationships with other commands.  */
+  if (c->hook_pre)
+    c->hook_pre->hookee_pre = nullptr;
+  if (c->hookee_pre)
+    c->hookee_pre->hook_pre = nullptr;
+  if (c->hook_post)
+    c->hook_post->hookee_post = nullptr;
+  if (c->hookee_post)
+    c->hookee_post->hook_post = nullptr;
+
+  /* And finally remove the hooks.  */
+  c->hook_pre = nullptr;
+  c->hookee_pre = nullptr;
+  c->hook_post = nullptr;
+  c->hookee_post = nullptr;
+}
+
+/* Disconnect the command named NAME from the command list *LIST, from
+   its subcommands, and all associated aliases and hooks.
+
+   *PREFIXLIST is set to point to the list of subcommands for the removed
+   command.
+
+   *ALIASES is set to the list of commands which were aliased to the
+   removed command.  If the command has no aliases, it's set to NULLPTR.
+
+   The various *HOOKs are set to the pre- and post-hook commands for the
+   removed command.  If the command does not have a hook, the
+   corresponding out parameter is set to NULLPTR.
+
+   Return the diconnected command if it was found.  Otherwise, return
+   NULLPTR (and the output parameters are all set to NULLPTR as well).  */
+
+static cmd_list_element *
+disconnect_cmd (const char *name, struct cmd_list_element **list,
+		struct cmd_list_element ***prefixlist,
+		struct cmd_list_element **aliases,
+		struct cmd_list_element **hook_pre,
+		struct cmd_list_element **hookee_pre,
+		struct cmd_list_element **hook_post,
+		struct cmd_list_element **hookee_post)
+{
+  struct cmd_list_element **previous_chain_ptr = list;
 
-  for (iter = *previous_chain_ptr; iter; iter = *previous_chain_ptr)
+  for (struct cmd_list_element *iter = *previous_chain_ptr;
+       iter != nullptr;
+       iter = *previous_chain_ptr)
     {
       if (strcmp (iter->name, name) == 0)
 	{
-	  if (iter->destroyer)
-	    iter->destroyer (iter, iter->context);
-	  if (iter->hookee_pre)
-	    iter->hookee_pre->hook_pre = 0;
-	  *prehook = iter->hook_pre;
-	  *prehookee = iter->hookee_pre;
-	  if (iter->hookee_post)
-	    iter->hookee_post->hook_post = 0;
-	  *posthook = iter->hook_post;
-	  *posthookee = iter->hookee_post;
-
-	  /* Update the link.  */
+	  /* Found the only command with the specified name.  */
+	  break_cmd_relationships (iter,
+				   prefixlist,
+				   aliases,
+				   hook_pre, hookee_pre,
+				   hook_post, hookee_post);
+	  /* Update the link in the command list, that is remove the
+	     command from its command list.  */
 	  *previous_chain_ptr = iter->next;
+	  /* And finally unlink from the prefix command.  */
+	  iter->prefix = nullptr;
 
-	  aliases = iter->aliases;
-
-	  /* If this command was an alias, remove it from the list of
-	     aliases.  */
-	  if (iter->cmd_pointer)
-	    {
-	      struct cmd_list_element **prevp = &iter->cmd_pointer->aliases;
-	      struct cmd_list_element *a = *prevp;
-
-	      while (a != iter)
-		{
-		  prevp = &a->alias_chain;
-		  a = *prevp;
-		}
-	      *prevp = iter->alias_chain;
-	    }
-
-	  delete iter;
-
-	  /* We won't see another command with the same name.  */
-	  break;
+	  return iter;
 	}
       else
 	previous_chain_ptr = &iter->next;
     }
 
-  return aliases;
+  /* Command not found. */
+
+  *prefixlist = nullptr;
+  *aliases = nullptr;
+  *hook_pre = nullptr;
+  *hookee_pre = nullptr;
+  *hook_post = nullptr;
+  *hookee_post = nullptr;
+
+  return nullptr;
 }
+
+/* Delete command C and all associated aliases and hooks.
+
+   Subcommands are not modified.  If C had subcommands, it's the caller's
+   responsibility to either assign the subcommands to another command or
+   to delete them.
+
+   If PREFIXLIST is not null, *PREFIXLIST is set to point to the list of
+   subcommands previously associated with C.
+   If PREFIXLIST is null, then C must be an alias or hook which cannot
+   have subcommands.  */
+
+static void
+delete_cmd (struct cmd_list_element *c,
+	    struct cmd_list_element ***prefixlist)
+{
+  /* The PREFIXLIST argument is required for all command types which could
+     have subcommands.  That is, PREFIXLIST can be null only if C is a
+     hook or alias.  */
+  gdb_assert (prefixlist != nullptr
+	      || (c->cmd_pointer == nullptr
+		  || c->hookee_pre == nullptr
+		  || c->hookee_post == nullptr));
+
+  /* Find the list for C.  */
+  struct cmd_list_element **list;
+  if (c->prefix != nullptr)
+    list = c->prefix->prefixlist;
+  else
+    list = &cmdlist;
+
+  /* Delete C itself.  */
+  struct cmd_list_element **prefixlist_tmp;
+  struct cmd_list_element *aliases;
+  struct cmd_list_element *hook_pre, *hookee_pre, *hook_post, *hookee_post;
+  struct cmd_list_element *actual_removed_cmd
+    = disconnect_cmd (c->name, list,
+		      &prefixlist_tmp,
+		      &aliases,
+		      &hook_pre, &hookee_pre,
+		      &hook_post, &hookee_post);
+  gdb_assert (actual_removed_cmd == c);
+
+  if (prefixlist != nullptr)
+    *prefixlist = prefixlist_tmp;
+  else
+    /* We asserted earlier that, if PREFIXLIST is null, then C is either
+       a hook or an alias, so prefixlist_tmp cannot be null.  */
+    gdb_assert (prefixlist_tmp == nullptr);
+
+  /* Delete all aliases.  */
+  for (struct cmd_list_element *alias_iter = aliases;
+       alias_iter != nullptr;
+       alias_iter = alias_iter->alias_chain)
+    delete_cmd (alias_iter, nullptr);
+
+  /* Delete hooks.  */
+  if (hook_pre)
+    delete_cmd (hook_pre, nullptr);
+  if (hook_post)
+    delete_cmd (hook_post, nullptr);
+
+  /* There's nothing to do with hookee_pre and hookee_post.  If they are
+     set then C is a hook and, if C is a hook, deleting it doesn't mean
+     that the hookee must be deleted.  */
+
+  delete c;
+}
+
+/* Delete the command called NAME in *LIST and all associated aliases and
+   hooks.
+
+   See DELETE_CMD for details on PREFIXLIST and whether it can be null.
+
+   Return TRUE if the command was found and deleted, FALSE otherwise.  */
+
+static bool
+delete_cmd_by_name (const char *name,
+		    struct cmd_list_element **list,
+		    struct cmd_list_element ***prefixlist)
+{
+  int nfound = 0;
+  struct cmd_list_element *cmd = find_cmd (name, strlen (name), *list,
+					   /* ignore_help_classes = */ true,
+					   &nfound);
+  if (cmd != nullptr)
+    {
+      delete_cmd (cmd, prefixlist);
+      return true;
+    }
+  else
+    {
+      if (prefixlist)
+	*prefixlist = nullptr;
+      return false;
+    }
+}
+
+/* Rename HOOK to match the command OLD_CMD_NAME in *OLD_LIST which was
+   renamed to NEW_CMD_NAME in NEW_LIST.  */
+
+static void
+rename_hook (struct cmd_list_element *hook,
+	     const char *old_cmd_name, struct cmd_list_element **old_list,
+	     const char *new_cmd_name, struct cmd_list_element **new_list)
+{
+  struct cmd_list_element **prefixlist;
+  struct cmd_list_element *aliases;
+  struct cmd_list_element *hook_pre, *hookee_pre, *hook_post, *hookee_post;
+  struct cmd_list_element *cmd = disconnect_cmd (hook->name, old_list,
+						 &prefixlist,
+						 &aliases,
+						 &hook_pre, &hookee_pre,
+						 &hook_post, &hookee_post);
+
+  /* Rename the hook command keeping the part that comes before the
+     hookee's name (i.e. "hook-" or "hookpost-").  */
+  std::string old_hook_name (hook->name);
+  size_t cmd_name_offset = old_hook_name.rfind (old_cmd_name);
+  gdb_assert (cmd_name_offset != std::string::npos);
+  std::string new_hook_name
+    = old_hook_name.substr (0, cmd_name_offset) + new_cmd_name;
+
+  update_cmd (cmd, new_list,
+	      new_hook_name.c_str (),
+	      prefixlist,
+	      aliases,
+	      hook_pre, hookee_pre, hook_post, hookee_post);
+}
+
+/* See command.h.  */
+
+void
+user_defined_command (const char *ignore, int from_tty)
+{
+}
+
+/* See command.h.  */
+
+struct cmd_list_element *
+rename_cmd (const char *old_name, struct cmd_list_element **old_list,
+	    const char *new_name, struct cmd_list_element **new_list)
+{
+  /* Remove the command from its current list.  */
+  struct cmd_list_element **old_name_prefixlist;
+  struct cmd_list_element *aliases;
+  struct cmd_list_element *hook_pre, *hookee_pre, *hook_post, *hookee_post;
+  struct cmd_list_element *cmd = disconnect_cmd (old_name, old_list,
+						 &old_name_prefixlist,
+						 &aliases,
+						 &hook_pre, &hookee_pre,
+						 &hook_post, &hookee_post);
+  if (cmd == nullptr)
+    {
+      /* No command called OLD_NAME exists in OLD_LIST.  */
+      return nullptr;
+    }
+
+  /* If there was already a NEW_NAME command then it must be deleted.
+     Its subcommands, if present, are moved to the renamed command.  */
+  struct cmd_list_element **new_name_prefixlist;
+  delete_cmd_by_name (new_name, new_list, &new_name_prefixlist);
+
+  /* Re-insert the command but in NEW_LIST.  */
+  update_cmd (cmd, new_list,
+	      new_name,
+	      new_name_prefixlist,
+	      aliases,
+	      hook_pre, hookee_pre, hook_post, hookee_post);
+
+  /* Rename the hooks as well.  */
+  if (hook_pre)
+    rename_hook (hook_pre, old_name, old_list, new_name, new_list);
+  if (hook_post)
+    rename_hook (hook_post, old_name, old_list, new_name, new_list);
+
+  /* If the old command had subcommands, then a new prefix-only command
+     must be created so they can be preserved.  */
+  if (old_name_prefixlist != nullptr && *old_name_prefixlist != nullptr)
+    {
+      struct cmd_list_element *prefix_for_old_name
+	  = add_cmd (xstrdup (old_name), class_user, user_defined_command,
+		     xstrdup ("User-defined."), old_list);
+      /* The new prefix is a prefix only so passing invalid subcommands
+	 must lead to an error.  */
+      prefix_for_old_name->allow_unknown = false;
+
+      update_prefix_links (prefix_for_old_name, old_list,
+			   old_name_prefixlist);
+    }
+
+  return cmd;
+}
+
 \f
 /* Shorthands to the commands above.  */
 
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index e6907a95a84..9a8cbca49b3 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -63,10 +63,13 @@ struct cmd_list_element
 
     ~cmd_list_element ()
     {
+      if (destroyer)
+	destroyer (this, context);
       if (doc && doc_allocated)
 	xfree ((char *) doc);
       if (name_allocated)
 	xfree ((char *) name);
+      xfree ((char *) prefixname);
     }
 
     DISABLE_COPY_AND_ASSIGN (cmd_list_element);
diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index e197852bc71..74345275ecf 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -30,6 +30,7 @@
 #include "cli/cli-decode.h"
 #include "cli/cli-script.h"
 #include "cli/cli-style.h"
+#include "cli/cli-utils.h"
 
 #include "extension.h"
 #include "interps.h"
@@ -49,7 +50,8 @@ recurse_read_control_structure
      gdb::function_view<void (const char *)> validator);
 
 static void do_define_command (const char *comname, int from_tty,
-			       const counted_command_line *commands);
+			       const counted_command_line *commands,
+			       const char *rename_existing_to = nullptr);
 
 static void do_document_command (const char *comname, int from_tty,
                                  const counted_command_line *commands);
@@ -1368,84 +1370,157 @@ validate_comname (const char **comname)
   return list;
 }
 
-/* This is just a placeholder in the command data structures.  */
+/* Ask the user if they really want to redefine the command called
+   CMD->name, thus overwriting CMD.
+
+   Throws an error if the users says no.  */
+
 static void
-user_defined_command (const char *ignore, int from_tty)
+check_command_redefinition (struct cmd_list_element *cmd)
 {
+  int q;
+
+  if (cmd->theclass == class_user || cmd->theclass == class_alias)
+    {
+      /* If C is a prefix command that was previously defined,
+	 tell the user its subcommands will be kept, and ask
+	 if ok to redefine the command.  */
+      if (cmd->prefixlist != nullptr)
+	q = (cmd->user_commands.get () == nullptr
+	     || query (_("Keeping subcommands of prefix command \"%s\".\n"
+			 "Redefine command \"%s\"? "), cmd->name, cmd->name));
+      else
+	q = query (_("Redefine command \"%s\"? "), cmd->name);
+    }
+  else
+    q = query (_("Really redefine built-in command \"%s\"? "), cmd->name);
+  if (!q)
+    error (_("Command \"%s\" not redefined."), cmd->name);
+}
+
+/* Whether a command is a hook into another command and which type of hook
+   it is.  */
+
+enum cmd_hook_type
+{
+  /* Not a hook.  */
+  CMD_NO_HOOK = 0,
+  /* Hook executed before the associated command.  */
+  CMD_PRE_HOOK,
+  /* Hook executed after the associated command.  */
+  CMD_POST_HOOK
+};
+
+/* Given a command name NAME, returns an element of CMD_HOOK_TYPE
+   identifying whether NAME is a hook name and which type of hook it is.
+
+   If HOOKEE_NAME is not null, it's set to point to the position in NAME
+   identifying the name of the command hooked by NAME.  */
+
+static cmd_hook_type
+get_hook_type (const char *name, const char **hookee_name = nullptr)
+{
+  static const char hook_pre_prefix[] = "hook-";
+  static const char hook_post_prefix[] = "hookpost-";
+  static const size_t hook_pre_prefix_len = sizeof hook_pre_prefix - 1;
+  static const size_t hook_post_prefix_len = sizeof hook_post_prefix - 1;
+
+  cmd_hook_type type;
+  const char *hookee_name_tmp;
+
+  if (strncmp (name, hook_pre_prefix, hook_pre_prefix_len) == 0)
+    {
+      hookee_name_tmp = name + hook_pre_prefix_len;
+      type = CMD_PRE_HOOK;
+    }
+  else if (strncmp (name, hook_post_prefix, hook_post_prefix_len) == 0)
+    {
+      hookee_name_tmp = name + hook_post_prefix_len;
+      type = CMD_POST_HOOK;
+    }
+  else
+    {
+      hookee_name_tmp = nullptr;
+      type = CMD_NO_HOOK;
+    }
+
+  if (hookee_name != nullptr)
+    *hookee_name = hookee_name_tmp;
+
+  return type;
 }
 
 /* Define a user-defined command.  If COMMANDS is NULL, then this is a
    top-level call and the commands will be read using
    read_command_lines.  Otherwise, it is a "define" command in an
    existing command and the commands are provided.  In the
-   non-top-level case, various prompts and warnings are disabled.  */
+   non-top-level case, various prompts and warnings are disabled.
+
+   If RENAME_EXISTING_TO is non-NULL, then the existing command called
+   COMNAME will be renamed to RENAME_EXISTING_TO instead of being
+   overwritten.  */
 
 static void
 do_define_command (const char *comname, int from_tty,
-		   const counted_command_line *commands)
+		   const counted_command_line *commands,
+		   const char *rename_existing_to)
 {
-  enum cmd_hook_type
-    {
-      CMD_NO_HOOK = 0,
-      CMD_PRE_HOOK,
-      CMD_POST_HOOK
-    };
   struct cmd_list_element *c, *newc, *hookc = 0, **list;
-  const char *comfull;
-  int  hook_type      = CMD_NO_HOOK;
-  int  hook_name_size = 0;
-   
-#define	HOOK_STRING	"hook-"
-#define	HOOK_LEN 5
-#define HOOK_POST_STRING "hookpost-"
-#define HOOK_POST_LEN    9
+  struct cmd_list_element *rename_existing_to_cmd = nullptr;
+  struct cmd_list_element **rename_existing_to_list;
+  const char *comfull, *rename_existing_to_full;
+  int hook_type;
+  const char *hookee_name = nullptr;
 
   comfull = comname;
   list = validate_comname (&comname);
 
+  rename_existing_to_full = rename_existing_to;
+  if (rename_existing_to != nullptr)
+    rename_existing_to_list = validate_comname (&rename_existing_to);
+  else
+    rename_existing_to_list = nullptr; /* Not renaming.  */
+
+  if (rename_existing_to_full &&
+      strcmp (rename_existing_to_full, comfull) == 0)
+    error (_("Cannot rename command \"%s\" as the new name is identical "
+	     "to the existing one."),
+	   comfull);
+
   c = lookup_cmd_exact (comname, *list);
+  if (rename_existing_to_list)
+      rename_existing_to_cmd = lookup_cmd_exact (rename_existing_to,
+                                                 *rename_existing_to_list);
 
-  if (c && commands == nullptr)
+  if (commands == nullptr)
     {
-      int q;
+      /* Check whether the user is trying to redefine an existing command.  */
+      if (rename_existing_to_cmd != nullptr)
+	check_command_redefinition (rename_existing_to_cmd);
+      else if (c != nullptr && rename_existing_to == nullptr)
+	check_command_redefinition (c);
+    }
 
-      if (c->theclass == class_user || c->theclass == class_alias)
-	{
-	  /* if C is a prefix command that was previously defined,
-	     tell the user its subcommands will be kept, and ask
-	     if ok to redefine the command.  */
-	  if (c->prefixlist != nullptr)
-	    q = (c->user_commands.get () == nullptr
-		 || query (_("Keeping subcommands of prefix command \"%s\".\n"
-			     "Redefine command \"%s\"? "), c->name, c->name));
-	  else
-	    q = query (_("Redefine command \"%s\"? "), c->name);
-	}
-      else
-	q = query (_("Really redefine built-in command \"%s\"? "), c->name);
-      if (!q)
-	error (_("Command \"%s\" not redefined."), c->name);
+  /* Checks on the validity of renames.  */
+  if (rename_existing_to)
+    {
+      if (c == nullptr)
+	error (_("Command \"%s\" does not exist, so it cannot "
+		 "be renamed to \"%s\"."),
+		 comfull, rename_existing_to_full);
+
+      if (get_hook_type (rename_existing_to) != CMD_NO_HOOK)
+	error (_("Cannot define hooks by renaming commands."));
     }
 
   /* If this new command is a hook, then mark the command which it
      is hooking.  Note that we allow hooking `help' commands, so that
      we can hook the `stop' pseudo-command.  */
-
-  if (!strncmp (comname, HOOK_STRING, HOOK_LEN))
-    {
-       hook_type      = CMD_PRE_HOOK;
-       hook_name_size = HOOK_LEN;
-    }
-  else if (!strncmp (comname, HOOK_POST_STRING, HOOK_POST_LEN))
-    {
-      hook_type      = CMD_POST_HOOK;
-      hook_name_size = HOOK_POST_LEN;
-    }
-
+  hook_type = get_hook_type (comname, &hookee_name);
   if (hook_type != CMD_NO_HOOK)
     {
       /* Look up cmd it hooks.  */
-      hookc = lookup_cmd_exact (comname + hook_name_size, *list,
+      hookc = lookup_cmd_exact (hookee_name, *list,
 				/* ignore_help_classes = */ false);
       if (!hookc && commands == nullptr)
 	{
@@ -1462,6 +1537,8 @@ do_define_command (const char *comname, int from_tty,
 	       comfull);
     }
 
+  /* ADD_CMD takes ownership of the command name, but not of
+     RENAME_EXISTING_TO.  */
   comname = xstrdup (comname);
 
   counted_command_line cmds;
@@ -1481,7 +1558,8 @@ do_define_command (const char *comname, int from_tty,
 
     newc = add_cmd (comname, class_user, user_defined_command,
 		    (c != nullptr && c->theclass == class_user)
-		    ? c->doc : xstrdup ("User-defined."), list);
+		    ? c->doc : xstrdup ("User-defined."), list,
+                    rename_existing_to, rename_existing_to_list);
     newc->user_commands = std::move (cmds);
 
     /* If we define or re-define a command that was previously defined
@@ -1518,10 +1596,118 @@ do_define_command (const char *comname, int from_tty,
     }
 }
 
+/* The options for the "define" command.  */
+
+struct define_cmd_opts
+{
+  /* For "-rename-existing-to".  */
+  char *rename_existing_to = nullptr;
+
+  ~define_cmd_opts ()
+  {
+    xfree (rename_existing_to);
+  }
+};
+
+static const gdb::option::option_def define_cmd_option_defs[] = {
+
+  gdb::option::string_option_def<define_cmd_opts> {
+    "rename-existing-to",
+    [] (define_cmd_opts *opts) { return &opts->rename_existing_to; },
+    nullptr,
+    N_("Rename the existing command called NAME to the specified name.\n\
+This is useful to allow the new implementation of a command to invoke\n\
+the original implementation.  Existing subcommands, hooks and aliases\n\
+for the original command are moved to the newly defined one."),
+  },
+
+};
+
+/* Create an option_def_group for the "define" command's options, with
+   OPTS as context.  */
+
+static inline gdb::option::option_def_group
+make_define_cmd_options_def_group (define_cmd_opts *opts)
+{
+  return {{define_cmd_option_defs}, opts};
+}
+
+/* Implementation of the "define" command.  */
+
+static void
+define_command (const char *arg, int from_tty)
+{
+  define_cmd_opts opts;
+
+  auto grp = make_define_cmd_options_def_group (&opts);
+  gdb::option::process_options
+    (&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp);
+
+  do_define_command (arg, from_tty, nullptr, opts.rename_existing_to);
+}
+
+/* Rename command OLD_NAME to NEW_NAME.  */
+
+static void
+do_rename_command (const char *old_name, const char *new_name, int from_tty)
+{
+  const char *old_name_full = old_name;
+  struct cmd_list_element **old_list = validate_comname (&old_name);
+
+  const char *new_name_full = new_name;
+  struct cmd_list_element **new_list = validate_comname (&new_name);
+
+  if (strcmp (old_name_full, new_name_full) == 0)
+    error (_("Cannot rename command \"%s\" as the new name is identical "
+	     "to the existing one."),
+	   old_name_full);
+
+  if (get_hook_type (old_name) != CMD_NO_HOOK)
+    error (_("Cannot rename hooks."));
+
+  if (get_hook_type (new_name) != CMD_NO_HOOK)
+    error (_("Cannot define hooks by renaming commands."));
+
+  /* Are we trying to redefine a command already called NEW_NAME_FULL?  */
+  struct cmd_list_element *existing_cmd_with_new_name
+    = lookup_cmd_exact (new_name, *new_list);
+  if (existing_cmd_with_new_name != nullptr)
+    check_command_redefinition (existing_cmd_with_new_name);
+
+  struct cmd_list_element *cmd = rename_cmd (old_name, old_list,
+					     new_name, new_list);
+  if (cmd == nullptr)
+    error (_("Command \"%s\" does not exist, so it cannot be renamed "
+	     "to \"%s\"."),
+	   old_name_full, new_name_full);
+}
+
+/* Implementation of the "rename" command.  */
+
 static void
-define_command (const char *comname, int from_tty)
+rename_command (const char *arg, int from_tty)
 {
-  do_define_command (comname, from_tty, nullptr);
+  if (arg == nullptr)
+    arg = "";
+
+  std::string old_name = extract_string_maybe_quoted (&arg);
+  std::string new_name = extract_string_maybe_quoted (&arg);
+
+  arg = skip_spaces (arg);
+
+  if (*arg != '\0')
+    {
+      error (_("Too many arguments: %s\n"
+	       "If you are trying to use a command name with spaces, "
+	       "use quotes."),
+	     arg);
+    }
+  else if (old_name.empty ())
+    error (_("Arguments required (name of command to rename and new name)."));
+  else if (new_name.empty ())
+    error (_("Argument required (new name for \"%s\")."), old_name.c_str ());
+
+  do_rename_command (old_name.c_str (), new_name.c_str (), from_tty);
 }
 
 /* Document a user-defined command.  If COMMANDS is NULL, then this is a
@@ -1709,7 +1895,7 @@ _initialize_cli_script ()
 {
   struct cmd_list_element *c;
 
-  /* "document", "define" and "define-prefix" use command_completer,
+  /* "document", "define", "define-prefix" and "rename" use command_completer,
      as this helps the user to either type the command name and/or
      its prefixes.  */
   document_cmd_element = add_com ("document", class_support, document_command,
@@ -1718,15 +1904,27 @@ Document a user-defined command.\n\
 Give command name as argument.  Give documentation on following lines.\n\
 End with a line of just \"end\"."));
   set_cmd_completer (document_cmd_element, command_completer);
-  define_cmd_element = add_com ("define", class_support, define_command, _("\
+
+  const auto define_opts = make_define_cmd_options_def_group (nullptr);
+  static const std::string define_help
+    = gdb::option::build_help (_("\
 Define a new command name.  Command name is argument.\n\
+Usage: define [-rename-existing-to NEW_NAME_FOR_EXISTING_COMMAND] NAME\n\
+\n\
+Options:\n\
+%OPTIONS%\n\
+\n\
 Definition appears on following lines, one command per line.\n\
 End with a line of just \"end\".\n\
 Use the \"document\" command to give documentation for the new command.\n\
 Commands defined in this way may accept an unlimited number of arguments\n\
 accessed via $arg0 .. $argN.  $argc tells how many arguments have\n\
-been passed."));
+been passed."),
+			       define_opts);
+  define_cmd_element = add_com ("define", class_support, define_command,
+				define_help.c_str ());
   set_cmd_completer (define_cmd_element, command_completer);
+
   c = add_com ("define-prefix", class_support, define_prefix_command,
 	   _("\
 Define or mark a command as a user-defined prefix command.\n\
@@ -1735,6 +1933,20 @@ other user defined commands.\n\
 If the command already exists, it is changed to a prefix command."));
   set_cmd_completer (c, command_completer);
 
+  c = add_com ("rename", class_support, rename_command, _("\
+Rename a command.\n\
+Usage: rename OLD_NAME NEW_NAME\n\
+\n\
+If a command called NEW_NAME exists, it's overwritten.  Any subcommands\n\
+of NEW_NAME are not modified.\n\
+\n\
+If OLD_NAME has subcommands, they are preserved and OLD_NAME becomes a\n\
+prefix-only command, see the \"define-prefix\" command.\n\
+\n\
+Aliases and hooks are preserved and, after the rename, will point to\n\
+NEW_NAME."));
+  set_cmd_completer (c, command_completer);
+
   while_cmd_element = add_com ("while", class_support, while_command, _("\
 Execute nested commands WHILE the conditional expression is non zero.\n\
 The conditional expression must follow the word `while' and must in turn be\n\
diff --git a/gdb/command.h b/gdb/command.h
index df40cbf7119..c4740d1c1a6 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -175,24 +175,37 @@ extern bool valid_cmd_char_p (int c);
    It should start with ? for a command that is an abbreviation
    or with * for a command that most users don't need to know about.
 
+   If RENAME_EXISTING_TO is not null, then the existing command called
+   NAME in *LIST will be renamed to RENAME_EXISTING_TO in
+   *RENAME_EXISTING_TO_LIST.  If a command called RENAME_EXISTING_TO
+   already exists in *RENAME_EXISTING_TO_LIST, it gets deleted first.
+   Ownership of RENAME_EXISTING_TO is not taken by this function (i.e.
+   the caller is responsible to free it if needed).
+
    If NAME already existed in *LIST, all its hooks and aliases are moved
    to the new command.
 
    Return a pointer to the added command (not necessarily the head of
    *LIST).  */
 
-extern struct cmd_list_element *add_cmd (const char *name,
-					 enum command_class theclass,
-					 cmd_const_cfunc_ftype *fun,
-					 const char *doc,
-					 struct cmd_list_element **list);
+extern struct cmd_list_element *add_cmd
+		(const char *name,
+		 enum command_class theclass,
+		 cmd_const_cfunc_ftype *fun,
+		 const char *doc,
+		 struct cmd_list_element **list,
+		 const char *rename_existing_to=nullptr,
+		 struct cmd_list_element **rename_existing_to_list=nullptr);
 
 /* Like add_cmd, but no command function is specified.  */
 
-extern struct cmd_list_element *add_cmd (const char *name,
-					 enum command_class theclass,
-					 const char *doc,
-					 struct cmd_list_element **list);
+extern struct cmd_list_element *add_cmd
+		(const char *name,
+		 enum command_class theclass,
+		 const char *doc,
+		 struct cmd_list_element **list,
+		 const char *rename_existing_to=nullptr,
+		 struct cmd_list_element **rename_existing_to_list=nullptr);
 
 extern struct cmd_list_element *add_cmd_suppress_notification
 			(const char *name, enum command_class theclass,
@@ -210,12 +223,17 @@ extern struct cmd_list_element *add_alias_cmd (const char *,
 					       struct cmd_list_element **);
 
 
-extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class,
-						cmd_const_cfunc_ftype *fun,
-						const char *,
-						struct cmd_list_element **,
-						const char *, int,
-						struct cmd_list_element **);
+extern struct cmd_list_element *add_prefix_cmd
+		(const char *name,
+		 enum command_class theclass,
+		 cmd_const_cfunc_ftype *fun,
+		 const char *doc,
+		 struct cmd_list_element **prefixlist,
+		 const char *prefixname,
+		 int allow_unknown,
+		 struct cmd_list_element **list,
+		 const char *rename_existing_to=nullptr,
+		 struct cmd_list_element **rename_existing_to_list=nullptr);
 
 /* Like add_prefix_cmd, but sets the callback to a function that
    simply calls help_list.  */
@@ -253,6 +271,15 @@ typedef void cmd_const_sfunc_ftype (const char *args, int from_tty,
 extern void set_cmd_sfunc (struct cmd_list_element *cmd,
 			   cmd_const_sfunc_ftype *sfunc);
 
+/* Rename the command called OLD_NAME in *OLD_LIST to NEW_NAME in
+   *NEW_LIST.
+
+   Return the renamed command or, if no such command exists, NULLPTR.  */
+extern struct cmd_list_element *rename_cmd (const char *old_name,
+					    struct cmd_list_element **old_list,
+					    const char *new_name,
+					    struct cmd_list_element **new_list);
+
 /* A completion routine.  Add possible completions to tracker.
 
    TEXT is the text beyond what was matched for the command itself
@@ -560,6 +587,9 @@ extern void
 				       struct cmd_list_element **set_list,
 				       struct cmd_list_element **show_list);
 
+/* Placeholder in the command data structures for user-defined commands.  */
+extern void user_defined_command (const char *ignore, int from_tty);
+
 /* Do a "show" command for each thing on a command list.  */
 
 extern void cmd_show_list (struct cmd_list_element *, int);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 69fa6b709bf..da498ea2c8c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -27021,18 +27021,41 @@ define adder
 end
 @end smallexample
 
+It's possible to replace existing commands and build on top of them, by
+using the @code{-rename-existing-to} option:
+
+@smallexample
+define -rename-existing-to original-run run
+  echo Will now run a program!\n
+  # Invoke the original "run" command which was renamed to
+  # "original-run".
+  original-run
+end
+@end smallexample
+
 @table @code
 
 @kindex define
-@item define @var{commandname}
-Define a command named @var{commandname}.  If there is already a command
-by that name, you are asked to confirm that you want to redefine it.
+@item define @r{[}-rename-existing-to @var{new-name}@r{]} @var{commandname}
+Define a command named @var{commandname}.
 The argument @var{commandname} may be a bare command name consisting of letters,
 numbers, dashes, dots, and underscores.  It may also start with any
 predefined or user-defined prefix command.
 For example, @samp{define target my-target} creates
 a user-defined @samp{target my-target} command.
 
+By default, if there is already a command by that name, you are asked to
+confirm that you want to redefine it.
+If @code{-rename-existing-to} is specified, the existing command called
+@var{commandname} is renamed to @var{new-name} first.  All subcommands,
+aliases (@pxref{Aliases}) and hooks (@pxref{Hooks}) for @var{commandname}
+will refer to the newly defined @var{commandname}, not to the renamed
+command.  This is different from using the @code{rename} command
+(@pxref{rename command}) and @code{define} separately as, in that case,
+subcommands, aliases and hooks refer to the renamed command.
+If @var{new-name} contains spaces, it must be enclosed in double quotes,
+unlike @var{commandname} which should not be enclosed in quotes.
+
 The definition of the command is made up of other @value{GDBN} command lines,
 which are given following the @code{define} command.  The end of these
 commands is marked by a line containing @code{end}.
@@ -27091,6 +27114,32 @@ command def
 (gdb)
 @end example
 
+@kindex rename
+@cindex rename command
+@anchor{rename command}
+@item rename @var{old-name} @var{new-name}
+Rename the command called @var{old-name} to @var{new-name}.
+All subcommands, aliases (@pxref{Aliases}) and hooks (@pxref{Hooks}) for
+@var{old-name} will refer to @var{new-name} after the rename.
+If a command name contains spaces, it must be enclosed in double quotes.
+
+Example:
+@example
+(gdb) define-prefix abc
+(gdb) define abc def
+Type commands for definition of "abc def".
+End with a line saying just "end".
+>echo this is the command\n
+>end
+(gdb) abc def
+this is the command
+(gdb) rename "abc def" renamed-command
+(gdb) abc def
+Undefined abc command: "def".  Try "help abc".
+(gdb) renamed-command
+this is the command
+@end example
+
 @kindex dont-repeat
 @cindex don't repeat command
 @item dont-repeat
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 0f776f54768..2e3e1bcbcae 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -3657,7 +3657,7 @@ You can implement new @value{GDBN} CLI commands in Python.  A CLI
 command is implemented using an instance of the @code{gdb.Command}
 class, most commonly using a subclass.
 
-@defun Command.__init__ (name, @var{command_class} @r{[}, @var{completer_class} @r{[}, @var{prefix}@r{]]})
+@defun Command.__init__ (name, @var{command_class} @r{[}, @var{completer_class} @r{[}, @var{prefix} @r{[}, @var{rename_existing_to}@r{]]]})
 The object initializer for @code{Command} registers the new command
 with @value{GDBN}.  This initializer is normally invoked from the
 subclass' own @code{__init__} method.
@@ -3671,7 +3671,9 @@ There is no support for multi-line commands.
 
 @var{command_class} should be one of the @samp{COMMAND_} constants
 defined below.  This argument tells @value{GDBN} how to categorize the
-new command in the help system.
+new command in the help system.  This argument is required unless
+@var{rename_existing_to} is specified, in which case it defaults to the
+value used by the existing command being renamed.
 
 @var{completer_class} is an optional argument.  If given, it should be
 one of the @samp{COMPLETE_} constants defined below.  This argument
@@ -3682,7 +3684,19 @@ error will occur when completion is attempted.
 
 @var{prefix} is an optional argument.  If @code{True}, then the new
 command is a prefix command; sub-commands of this command may be
-registered.
+registered.  If @var{rename_existing_to} is set, then @var{prefix} must
+not be set as whether the command is a prefix command or not is inherited
+from the existing command being renamed.
+
+@var{rename_existing_to} is an optional argument.  If set, then the
+existing command called @var{name} is renamed to @var{rename_existing_to}.
+Existing subcommands, aliases and hooks are preserved and will refer to
+the newly defined command.  Additionally, then @var{prefix} must be unset
+as whether the new command is a prefix or not will depend on whether the
+original command was a prefix command or not.
+When renaming an existing command, @var{command_class} and
+@var{completer_class}, if unspecified, are copied from the existing
+command.
 
 The help text for the new command is taken from the Python
 documentation string for the command's class, if there is one.  If no
@@ -3931,6 +3945,24 @@ registration of the command with @value{GDBN}.  Depending on how the
 Python code is read into @value{GDBN}, you may need to import the
 @code{gdb} module explicitly.
 
+The following code snippet shows how a CLI command building on top of an
+existing command can be implemented:
+
+@smallexample
+class MyQuit (gdb.Command):
+  """Print a message before executing the "quit" command."""
+
+  def __init__ (self):
+    super (MyQuit, self).__init__ ("quit", gdb.COMMAND_RUNNING,
+                                  rename_existing_to="original-quit")
+
+  def invoke (self, arg, from_tty):
+    print ("Will now quit!")
+    gdb.execute ("original-quit %s" % arg, from_tty=from_tty)
+
+MyQuit ()
+@end smallexample
+
 @node Parameters In Python
 @subsubsection Parameters In Python
 
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index f4d3dcc3121..7ad69348518 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -434,15 +434,22 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 {
   cmdpy_object *obj = (cmdpy_object *) self;
   const char *name;
-  int cmdtype;
-  int completetype = -1;
+  long cmdtype;
+  long completetype = -1;
   char *docstring = NULL;
   struct cmd_list_element **cmd_list;
   char *cmd_name, *pfx_name;
   static const char *keywords[] = { "name", "command_class", "completer_class",
-				    "prefix", NULL };
-  PyObject *is_prefix = NULL;
+				    "prefix", "rename_existing_to",
+				    NULL };
+  PyObject *cmdtype_obj = NULL;
+  PyObject *completetype_obj = NULL;
+  PyObject *is_prefix_obj = NULL;
   int cmp;
+  const char *rename_existing_to = NULL;
+  char *rename_existing_to_cmd_name = NULL;
+  struct cmd_list_element *renamed_cmd = NULL;
+  struct cmd_list_element **rename_existing_to_cmd_list = nullptr;
 
   if (obj->command)
     {
@@ -453,11 +460,102 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
       return -1;
     }
 
-  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "si|iO",
-					keywords, &name, &cmdtype,
-					&completetype, &is_prefix))
+  /* The second argument (command_class) is marked as optional because
+     it should not be specified if RENAME_EXISTING_TO is specified.
+     Otherwise it's required.  */
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!Os",
+					keywords,
+					&name,
+					&PyInt_Type, &cmdtype_obj,
+					&PyInt_Type, &completetype_obj,
+					&is_prefix_obj,
+					&rename_existing_to))
     return -1;
 
+  cmd_name = gdbpy_parse_command_name (name, &cmd_list, &cmdlist);
+  if (! cmd_name)
+    return -1;
+
+  /* Deal with rename_existing_to before other arguments as it affects how
+     some of them are treated.  */
+  if (rename_existing_to != NULL)
+    {
+      renamed_cmd = lookup_cmd_exact (cmd_name, *cmd_list);
+      if (renamed_cmd == nullptr)
+	{
+	  PyErr_Format (PyExc_RuntimeError,
+			_("Command \"%s\" does not exist, so it cannot "
+			  "be renamed to \"%s\"."),
+			name, rename_existing_to);
+	  xfree (cmd_name);
+	  return -1;
+	}
+
+      rename_existing_to_cmd_name =
+	gdbpy_parse_command_name (rename_existing_to,
+				  &rename_existing_to_cmd_list,
+				  &cmdlist);
+      if (! rename_existing_to_cmd_name)
+	{
+	  xfree (cmd_name);
+	  return -1;
+	}
+    }
+
+  if (cmdtype_obj == NULL)
+    {
+      /* COMMAND_CLASS is optional only when rename_existing_to is
+	 specified.  This error matches the phrasing of the error the
+	 interpreter would raise if COMMAND_CLASS was not marked as
+	 optional in gdb_PyArg_ParseTupleAndKeywords.  */
+      if (renamed_cmd == NULL)
+	{
+	  PyErr_Format (PyExc_TypeError,
+			_("function missing required argument "
+			  "'command_class' (pos 2)"));
+	  xfree (cmd_name);
+	  xfree (rename_existing_to_cmd_name);
+	  return -1;
+	}
+
+      cmdtype = renamed_cmd->theclass;
+    }
+  else
+    {
+      cmdtype = PyInt_AsLong (cmdtype_obj);
+      if (PyErr_Occurred ())
+	{
+	  xfree (cmd_name);
+	  xfree (rename_existing_to_cmd_name);
+	  return -1;
+	}
+      if (cmdtype != no_class && cmdtype != class_run
+	  && cmdtype != class_vars && cmdtype != class_stack
+	  && cmdtype != class_files && cmdtype != class_support
+	  && cmdtype != class_info && cmdtype != class_breakpoint
+	  && cmdtype != class_trace && cmdtype != class_obscure
+	  && cmdtype != class_maintenance && cmdtype != class_user
+	  && cmdtype != class_tui)
+	{
+	  PyErr_Format (PyExc_RuntimeError,
+			_("Invalid command class argument."));
+	  xfree (cmd_name);
+	  xfree (rename_existing_to_cmd_name);
+	  return -1;
+	}
+    }
+
+  if (completetype_obj != NULL)
+    {
+      completetype = PyInt_AsLong (completetype_obj);
+      if (PyErr_Occurred ())
+	{
+	  xfree (cmd_name);
+	  xfree (rename_existing_to_cmd_name);
+	  return -1;
+	}
+    }
+
   if (cmdtype != no_class && cmdtype != class_run
       && cmdtype != class_vars && cmdtype != class_stack
       && cmdtype != class_files && cmdtype != class_support
@@ -467,6 +565,8 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
       && cmdtype != class_tui)
     {
       PyErr_Format (PyExc_RuntimeError, _("Invalid command class argument."));
+      xfree (cmd_name);
+      xfree (rename_existing_to_cmd_name);
       return -1;
     }
 
@@ -474,17 +574,27 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
     {
       PyErr_Format (PyExc_RuntimeError,
 		    _("Invalid completion type argument."));
+      xfree (cmd_name);
+      xfree (rename_existing_to_cmd_name);
       return -1;
     }
 
-  cmd_name = gdbpy_parse_command_name (name, &cmd_list, &cmdlist);
-  if (! cmd_name)
-    return -1;
-
   pfx_name = NULL;
-  if (is_prefix != NULL)
+  if (is_prefix_obj != NULL)
     {
-      cmp = PyObject_IsTrue (is_prefix);
+      if (renamed_cmd != NULL)
+	{
+	  PyErr_Format
+	    (PyExc_TypeError,
+	     _("Cannot specify argument 'prefix' with 'rename_existing_to'.  "
+	       "Whether the command is a prefix command depends on the "
+	       "renamed command."));
+	  xfree (cmd_name);
+	  xfree (rename_existing_to_cmd_name);
+	  return -1;
+	}
+
+      cmp = PyObject_IsTrue (is_prefix_obj);
       if (cmp == 1)
 	{
 	  int i, out;
@@ -511,9 +621,11 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
       else if (cmp < 0)
 	{
 	  xfree (cmd_name);
+	  xfree (rename_existing_to_cmd_name);
 	  return -1;
 	}
     }
+
   if (PyObject_HasAttr (self, gdbpy_doc_cst))
     {
       gdbpy_ref<> ds_obj (PyObject_GetAttr (self, gdbpy_doc_cst));
@@ -524,6 +636,7 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 	  if (docstring == NULL)
 	    {
 	      xfree (cmd_name);
+	      xfree (rename_existing_to_cmd_name);
 	      xfree (pfx_name);
 	      return -1;
 	    }
@@ -547,11 +660,15 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 	  allow_unknown = PyObject_HasAttr (self, invoke_cst);
 	  cmd = add_prefix_cmd (cmd_name, (enum command_class) cmdtype,
 				NULL, docstring, &obj->sub_list,
-				pfx_name, allow_unknown, cmd_list);
+				pfx_name, allow_unknown, cmd_list,
+				rename_existing_to_cmd_name,
+				rename_existing_to_cmd_list);
 	}
       else
 	cmd = add_cmd (cmd_name, (enum command_class) cmdtype,
-		       docstring, cmd_list);
+		       docstring, cmd_list,
+		       rename_existing_to_cmd_name,
+		       rename_existing_to_cmd_list);
 
       /* There appears to be no API to set this.  */
       cmd->func = cmdpy_function;
@@ -561,15 +678,30 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 
       obj->command = cmd;
       set_cmd_context (cmd, self_ref.release ());
-      set_cmd_completer (cmd, ((completetype == -1) ? cmdpy_completer
-			       : completers[completetype].completer));
-      if (completetype == -1)
-	set_cmd_completer_handle_brkchars (cmd,
-					   cmdpy_completer_handle_brkchars);
+
+      if (completetype_obj == NULL && renamed_cmd != NULL)
+	{
+	    /* No explicit completer was passed and the existing command
+	       was renamed, so use the command completer information from
+	       the original command.  */
+	    set_cmd_completer (cmd, renamed_cmd->completer);
+	    set_cmd_completer_handle_brkchars
+	      (cmd, renamed_cmd->completer_handle_brkchars);
+	}
+      else
+	{
+	  set_cmd_completer (cmd, ((completetype == -1) ? cmdpy_completer
+				   : completers[completetype].completer));
+	  if (completetype == -1)
+	    set_cmd_completer_handle_brkchars (cmd,
+					       cmdpy_completer_handle_brkchars);
+
+	}
     }
   catch (const gdb_exception &except)
     {
       xfree (cmd_name);
+      xfree (rename_existing_to_cmd_name);
       xfree (docstring);
       xfree (pfx_name);
       gdbpy_convert_exception (except);
diff --git a/gdb/testsuite/gdb.base/command-renaming.exp b/gdb/testsuite/gdb.base/command-renaming.exp
new file mode 100644
index 00000000000..238a735dfb0
--- /dev/null
+++ b/gdb/testsuite/gdb.base/command-renaming.exp
@@ -0,0 +1,571 @@
+# Copyright (C) 2009-2021 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests renaming of commands.
+
+standard_testfile
+
+clean_restart
+
+proc prepare_gdb {} {
+  gdb_exit
+  gdb_start
+}
+
+# Test basic usage of the "rename" command.
+#
+proc test_rename_ok {} {
+  prepare_gdb
+
+  with_test_prefix "basic" {
+    gdb_test_no_output "rename run my-run"
+    # The old "run" command doesn't exist any more.
+    gdb_test_exact "run" \
+      "Undefined command: \"run\".  Try \"help\"."
+    # The new one works.
+    gdb_test "my-run" ".*No executable file specified.*"
+    # The "r" alias still works.
+    gdb_test "r" ".*No executable file specified.*"
+  }
+
+  with_test_prefix "hooks" {
+    gdb_define_cmd "original" {
+      "echo Original\\n"
+    }
+    gdb_test_no_output "alias alias-original = original"
+    gdb_define_cmd "hook-original" {
+      "echo Before original\\n"
+    }
+    gdb_define_cmd "hookpost-original" {
+      "echo After original\\n"
+    }
+
+    gdb_test_no_output "rename original renamed"
+    # The old "original" command doesn't exist any more.
+    gdb_test_exact "original" \
+      "Undefined command: \"original\".  Try \"help\"."
+    # The new one works, including hooks.
+    gdb_test_exact "renamed" \
+      "Before original\nOriginal\nAfter original"
+    # The "alias-original" alias still works, including hooks.
+    gdb_test_exact "alias-original" \
+      "Before original\nOriginal\nAfter original"
+  }
+}
+
+# Test that "rename" deals correctly with error conditions.
+
+proc test_rename_errors {} {
+  prepare_gdb
+
+  with_test_prefix "non-existing command" {
+    gdb_test_exact "rename nope renamed" \
+      "Command \"nope\" does not exist, so it cannot be renamed to \"renamed\"."
+  }
+
+  with_test_prefix "same old and new name" {
+    gdb_test_exact "rename run run" \
+      "Cannot rename command \"run\" as the new name is identical to the existing one."
+  }
+
+  with_test_prefix "rename to hook" {
+    gdb_define_cmd "my-cmd" {
+      "echo Command\\n"
+    }
+
+    with_test_prefix "pre" {
+      gdb_test_exact "rename my-cmd hook-echo" \
+	"Cannot define hooks by renaming commands."
+    }
+
+    with_test_prefix "post" {
+      gdb_test_exact "rename my-cmd hookpost-echo" \
+	"Cannot define hooks by renaming commands."
+    }
+  }
+}
+
+# Test that "rename" works when the prefix of the renamed command changes.
+
+proc test_rename_across_prefixes {} {
+  prepare_gdb
+
+  with_test_prefix "rename with prefixes" {
+    gdb_test_no_output "define-prefix prefix1"
+    gdb_test_no_output "define-prefix prefix2"
+    gdb_test_no_output "define-prefix prefix2 sub-prefix"
+
+    gdb_define_cmd "prefix1 foo" {
+      "echo Foo\\n"
+    }
+
+    gdb_test_no_output "rename 'prefix1 foo' 'prefix2 sub-prefix renamed-foo'"
+
+    gdb_test_exact "prefix1 foo" \
+      "Undefined prefix1 command: \"foo\".  Try \"help prefix1\"."
+    gdb_test_exact "prefix2 sub-prefix renamed-foo" "Foo"
+  }
+}
+
+# Test that renaming of prefix commands works.
+
+proc test_rename_prefixes {} {
+  prepare_gdb
+  gdb_test_no_output "set confirm off"
+
+  with_test_prefix "rename of prefixes" {
+    # Define two prefixes, each one with a subcommand.
+    foreach letter {"A" "B"} {
+      gdb_test_no_output "define-prefix prefix-${letter}"
+      gdb_define_cmd "prefix-${letter}" [subst {
+	"echo Original prefix ${letter}\\\\n"
+      }]
+      gdb_define_cmd "prefix-${letter} sub-${letter}" [subst {
+	"echo Subcommand ${letter} of prefix ${letter}\\\\n"
+      }]
+    }
+
+    gdb_test_no_output "rename prefix-A prefix-B"
+
+    # The subcommands still work as before, the rename didn't affect them.
+    gdb_test_exact "prefix-A sub-A" "Subcommand A of prefix A"
+    gdb_test_exact "prefix-B sub-B" "Subcommand B of prefix B"
+    # "prefix-B"'s implementation is what used to be "prefix-A"'s
+    # implementation.
+    gdb_test_exact "prefix-B" "Original prefix A"
+    # "prefix-A" is just a prefix command without any code associated with
+    # it, and it exists only for the sake of providing subcommands.
+    gdb_test "prefix-A" \
+      ".*\"prefix-A\" must be followed by the name of a subcommand.*"
+  }
+}
+
+# Test that "rename" works when it leads to a command being overwritten by
+# the renamed one.
+#
+# PREFIX, if not empty, if the prefix for the renamed and overwritten
+# commands.
+
+proc test_rename_redefine {prefix} {
+  global gdb_prompt
+
+  prepare_gdb
+
+  with_test_prefix "overwriting existing command by renaming, prefix=\"${prefix}\"" {
+    if { $prefix == "" } {
+      set foo_cmd "foo"
+      set bar_cmd "bar"
+    } else {
+      gdb_test_no_output "define-prefix ${prefix}"
+      set foo_cmd "${prefix} foo"
+      set bar_cmd "${prefix} bar"
+    }
+
+    # Define "foo".
+    gdb_define_cmd $foo_cmd {
+      "echo Original foo\\n"
+    }
+
+    # Define an alias to the original "foo" with the same prefix.
+    gdb_test_no_output "alias ${prefix} alias-to-original-foo-same-prefix = ${foo_cmd}"
+    # And another one but without a prefix.
+    gdb_test_no_output "alias alias-to-original-foo-no-prefix = ${foo_cmd}"
+
+    # Define hooks for the original "foo".
+    gdb_define_cmd "${prefix} hook-foo" {
+      "echo Pre-hook for original foo\\n"
+    }
+    gdb_define_cmd "${prefix} hookpost-foo" {
+      "echo Post-hook for original foo\\n"
+    }
+
+    # Define "bar".
+    gdb_define_cmd $bar_cmd {
+      "echo Original bar\\n"
+    }
+
+    # Rename "bar" to "foo", which deletes the old "foo".
+    send_gdb "rename '${bar_cmd}' '${foo_cmd}'\n"
+    gdb_expect {
+      -re "Redefine command \"foo\".*y or n. $"\
+	      {send_gdb "y\n"
+	       gdb_expect {
+		 -re "$gdb_prompt $"\
+		   {pass "rename user command"}
+		 timeout {fail "(timeout) rename user command"}
+	       }
+	     }
+      -re "$gdb_prompt $"\
+	      {fail "redefine user command"}
+      timeout {fail "(timeout) rename user command"}
+    }
+
+    # "foo" should be the new command (which used to be called "bar",
+    # hence the message).
+    gdb_test_exact $foo_cmd "Original bar"
+
+    # The aliases to the original "foo" should have been deleted.
+    gdb_test "${prefix} alias-to-original-foo-same-prefix" \
+      ".*Undefined.* command:.*"
+    gdb_test "alias-to-original-foo-no-prefix" \
+      ".*Undefined command:.*"
+
+    # The hooks for the original "foo" should have been deleted as well.
+    gdb_test "${prefix} hook-foo" \
+      ".*Undefined.* command:.*"
+    gdb_test "${prefix} hookpost-foo" \
+      ".*Undefined.* command:.*"
+  }
+}
+
+# Test that, when a command with hooks is renamed, the hooks get updated
+# as well.
+
+proc test_rename_hooks_updated {} {
+  prepare_gdb
+
+  with_test_prefix "hooks renamed if command renamed" {
+    # Define some hooks for "delete".
+    gdb_define_cmd "hook-delete" {
+      "echo Pre-hook\\n"
+    }
+    gdb_define_cmd "hookpost-delete" {
+      "echo Post-hook\\n"
+    }
+    # Define aliases for the hooks.
+    gdb_test_no_output "alias hd-pre = hook-delete"
+    gdb_test_no_output "alias hd-post = hookpost-delete"
+    # Rename "delete" to "my-prefix delete-cmd"
+    gdb_test_no_output "define-prefix my-prefix"
+    gdb_test_no_output "rename 'delete' 'my-prefix delete-cmd'"
+
+    # The renamed command must still work.
+    gdb_test_exact "my-prefix delete-cmd 9999" \
+      "Pre-hook\nNo breakpoint number 9999.\nPost-hook"
+
+    # Hooks must have been renamed as well.
+    gdb_test_exact "my-prefix hook-delete-cmd" "Pre-hook"
+    gdb_test_exact "my-prefix hookpost-delete-cmd" "Post-hook"
+    # And the aliases to the hooks must still work.
+    gdb_test_exact "hd-pre" "Pre-hook"
+    gdb_test_exact "hd-post" "Post-hook"
+    # The old names for the hooks don't work any more (as they were
+    # renamed).
+    gdb_test "hook-delete" "Undefined command.*"
+    gdb_test "hookpost-delete" "Undefined command.*"
+    # The help for the hooks and their aliases must show the correct
+    # updated names.
+    gdb_test "help my-prefix hook-delete-cmd" \
+      "my-prefix hook-delete-cmd, hd-pre.*"
+    gdb_test "help hd-pre" "my-prefix hook-delete-cmd, hd-pre.*"
+    gdb_test "help my-prefix hookpost-delete-cmd" \
+      "my-prefix hookpost-delete-cmd, hd-post.*"
+    gdb_test "help hd-post" "my-prefix hookpost-delete-cmd, hd-post.*"
+  }
+}
+
+# Helper function for defining a command called "new" and then redefining
+# it with "define -rename-existing-to old new".
+#
+# If CREATE_ALIAS is 1, then an "alias-to-new" alias to the original
+# command is created.
+#
+# If CREATE_PRE_HOOK is 1, then a hook for the original command is
+# created.
+#
+# CREATE_POST_HOOK behaves likes CREATE_PRE_HOOK except that it creates
+# a post-hook.
+
+proc define_with_rename {create_alias create_pre_hook create_post_hook} {
+  prepare_gdb
+
+  gdb_define_cmd "new" {
+    "echo Original\\n"
+  }
+
+  if {$create_alias} then {
+    gdb_test_no_output "alias alias-to-new = new"
+  }
+
+  if {$create_pre_hook} then {
+    gdb_define_cmd "hook-new" {
+      "echo Pre-hook\\n"
+    }
+  }
+
+  if {$create_post_hook} then {
+    gdb_define_cmd "hookpost-new" {
+      "echo Post-hook\\n"
+    }
+  }
+
+  gdb_define_cmd "-rename-existing-to old new" {
+    "echo Redefined: before\\n"
+    "old"
+    "echo Redefined: after\\n"
+  }
+}
+
+
+# Test that "define -rename-existing-to" works with and without aliases
+# and hooks.
+
+proc test_define_renaming_ok {} {
+  with_test_prefix "basic" {
+    define_with_rename 0 0 0
+    gdb_test_exact "new" \
+      "Redefined: before\nOriginal\nRedefined: after"
+    gdb_test_exact "old" "Original"
+  }
+
+  with_test_prefix "alias" {
+    define_with_rename 1 0 0
+    gdb_test_exact "new" \
+      "Redefined: before\nOriginal\nRedefined: after"
+    gdb_test_exact "alias-to-new" \
+      "Redefined: before\nOriginal\nRedefined: after"
+    gdb_test_exact "old" "Original"
+  }
+
+  with_test_prefix "pre-hook" {
+    define_with_rename 0 1 0
+    gdb_test_exact "new" \
+      "Pre-hook\nRedefined: before\nOriginal\nRedefined: after"
+    gdb_test_exact "old" "Original"
+  }
+
+  with_test_prefix "post-hook" {
+    define_with_rename 0 0 1
+    gdb_test_exact "new" \
+      "Redefined: before\nOriginal\nRedefined: after\nPost-hook"
+    gdb_test_exact "old" "Original"
+  }
+
+  with_test_prefix "alias, pre-hook and post-hook" {
+    define_with_rename 1 1 1
+    gdb_test_exact "new" \
+      "Pre-hook\nRedefined: before\nOriginal\nRedefined: after\nPost-hook"
+    gdb_test_exact "alias-to-new" \
+      "Pre-hook\nRedefined: before\nOriginal\nRedefined: after\nPost-hook"
+    gdb_test_exact "old" "Original"
+  }
+}
+
+# Test that renaming with "define -rename-existing-to" works for builtin
+# commands.
+
+proc test_define_renaming_builtin {} {
+  prepare_gdb
+  gdb_test_no_output "set confirm off"
+
+  with_test_prefix "rename builtin" {
+    gdb_define_cmd "-rename-existing-to original-run run" {
+      "echo Redefined run\\n"
+    }
+
+    gdb_test_exact "run" "Redefined run"
+
+    gdb_test "original-run" ".*No executable file specified.*"
+  }
+}
+
+# Test that "define -rename-existing-to" deals correctly with error
+# conditions.
+
+proc test_define_renaming_errors {} {
+  prepare_gdb
+
+  with_test_prefix "non-existing command" {
+    gdb_test_exact "define -rename-existing-to renamed nope" \
+      "Command \"nope\" does not exist, so it cannot be renamed to \"renamed\"."
+  }
+
+  with_test_prefix "same old and new name" {
+    gdb_test_exact "define -rename-existing-to run run" \
+      "Cannot rename command \"run\" as the new name is identical to the existing one."
+  }
+
+  with_test_prefix "rename to hook" {
+    gdb_define_cmd "my-cmd" {
+      "echo Command\\n"
+    }
+
+    with_test_prefix "pre" {
+      gdb_test_exact "define -rename-existing-to hook-echo my-cmd" \
+	"Cannot define hooks by renaming commands."
+    }
+
+    with_test_prefix "post" {
+      gdb_test_exact "define -rename-existing-to hookpost-echo my-cmd" \
+	"Cannot define hooks by renaming commands."
+    }
+  }
+}
+
+# Test that using "define -rename-existing-to" to rename to a different
+# prefix works.
+
+proc test_define_renaming_across_prefixes {} {
+  prepare_gdb
+
+  with_test_prefix "define -rename-existing-to prefixes" {
+    gdb_test_no_output "define-prefix prefix1"
+    gdb_test_no_output "define-prefix prefix2"
+    gdb_test_no_output "define-prefix prefix2 sub-prefix"
+
+    gdb_define_cmd "prefix1 foo" {
+      "echo First\\n"
+    }
+
+    gdb_define_cmd "-rename-existing-to 'prefix2 sub-prefix renamed-foo' prefix1 foo" {
+      "echo Second\\n"
+    }
+
+    gdb_test_exact "prefix2 sub-prefix renamed-foo" "First"
+    gdb_test_exact "prefix1 foo" "Second"
+  }
+}
+
+# Test that renaming prefixes with "define -rename-existing-to" works.
+
+proc test_define_renaming_prefixes {} {
+  prepare_gdb
+
+  with_test_prefix "define -rename-existing-to of prefixes" {
+    # Define a prefix with an implementation.
+    gdb_test_no_output "define-prefix prefix"
+    gdb_define_cmd "prefix" {
+      "echo Original prefix\\n"
+    }
+    # Define a subcommand.
+    gdb_define_cmd "prefix subcommand" {
+      "echo Subcommand\\n"
+    }
+
+    # Define a new implementation of "prefix" renaming the existing to
+    # "renamed-prefix".
+    gdb_define_cmd "-rename-existing-to renamed-prefix prefix" {
+      "echo New prefix\\n"
+    }
+
+    # "renamed-prefix" still works as a command.
+    gdb_test_exact "renamed-prefix" "Original prefix"
+    # The subcommand remains a subcommand of "prefix", not of its renamed
+    # implementation, so "renamed-prefix subcommand" doesn't exist (and
+    # never existed).
+    # This means that that "renamed-prefix subcommand" invokes the
+    # "renamed-prefix" command with "subcommand" as argument (i.e.
+    # subcommand is not a command), so the original implementation of
+    # what was "prefix" is invoked.
+    gdb_test_exact "renamed-prefix subcommand" "Original prefix"
+    # The newly defined prefix implementation works.
+    gdb_test_exact "prefix" "New prefix"
+    # And the subcommand works as well.
+    gdb_test_exact "prefix subcommand" "Subcommand"
+  }
+}
+
+# Test that "define -rename-existing-to" works when it leads to a command
+# being overwritten by the renamed one.
+#
+# PREFIX, if not empty, if the prefix for the renamed and overwritten
+# commands.
+
+proc test_define_renaming_redefine {prefix} {
+  global gdb_prompt
+
+  prepare_gdb
+
+  with_test_prefix "overwriting existing command with define -rename-existing-to, prefix=\"${prefix}\"" {
+    if { $prefix == "" } {
+      set foo_cmd "foo"
+      set bar_cmd "bar"
+    } else {
+      gdb_test_no_output "define-prefix ${prefix}"
+      set foo_cmd "${prefix} foo"
+      set bar_cmd "${prefix} bar"
+    }
+
+    # Define "foo".
+    gdb_define_cmd $foo_cmd {
+      "echo Original foo\\n"
+    }
+
+    # Define an alias to the original "foo" with the same prefix.
+    gdb_test_no_output "alias ${prefix} alias-to-original-foo-same-prefix = ${foo_cmd}"
+    # And another one but without a prefix.
+    gdb_test_no_output "alias alias-to-original-foo-no-prefix = ${foo_cmd}"
+
+    # Define "bar".
+    gdb_define_cmd $bar_cmd {
+      "echo Original bar\\n"
+    }
+
+    # Redefine "bar" while renaming the original "bar" to "foo".
+    send_gdb "define -rename-existing-to '${foo_cmd}' ${bar_cmd}\n"
+    gdb_expect {
+      -re "Redefine command \"foo\".*y or n. $"\
+	      {send_gdb "y\n"
+	       gdb_expect {
+		 -re "Type commands for definition of \"${bar_cmd}\".\r\nEnd with a line saying just \"end\".\r\n>$"\
+			 {send_gdb "echo New bar\\n\n${foo_cmd}\nend\n"
+			  gdb_expect {
+			    -re "$gdb_prompt $"\
+				    {pass "redefine user command"}
+			    timeout {fail "(timeout) redefine user command"}
+			  }
+			 }
+		 timeout {fail "(timeout) redefine user command"}
+	       }
+	      }
+      -re "$gdb_prompt $"\
+	      {fail "redefine user command"}
+      timeout {fail "(timeout) redefine user command"}
+    }
+
+    # "bar" should be the new command which calls the old orginal "bar".
+    gdb_test_exact $bar_cmd "New bar\nOriginal bar"
+
+    # The aliases to the original foo should have been deleted.
+    gdb_test "${prefix} alias-to-original-foo-same-prefix" \
+      ".*Undefined.* command:.*"
+    gdb_test "alias-to-original-foo-no-prefix" \
+      ".*Undefined command:.*"
+  }
+}
+
+with_test_prefix "command renaming" {
+  # Tests for "rename".
+  with_test_prefix "rename" {
+    test_rename_ok
+    test_rename_errors
+    test_rename_across_prefixes
+    test_rename_prefixes
+    test_rename_redefine ""
+    test_rename_redefine "my-prefix"
+    test_rename_hooks_updated
+  }
+
+  # Tests for the "define -rename-existing-to".
+  with_test_prefix "define -rename-existing-to" {
+    test_define_renaming_ok
+    test_define_renaming_builtin
+    test_define_renaming_errors
+    test_define_renaming_across_prefixes
+    test_define_renaming_prefixes
+    test_define_renaming_redefine ""
+    test_define_renaming_redefine "my-prefix"
+  }
+}
diff --git a/gdb/testsuite/gdb.python/py-rename-existing.exp b/gdb/testsuite/gdb.python/py-rename-existing.exp
new file mode 100644
index 00000000000..189633b068a
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-rename-existing.exp
@@ -0,0 +1,364 @@
+# Copyright (C) 2009-2021 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests renaming of existing
+# commands.
+
+load_lib gdb-python.exp
+load_lib completion-support.exp
+
+standard_testfile
+
+# Prepare for testing.
+#
+# This quits GDB (if running), starts a new one, and loads any required
+# external scripts.
+
+proc prepare_gdb {} {
+  global srcdir subdir testfile
+
+  gdb_exit
+  gdb_start
+  gdb_reinitialize_dir $srcdir/$subdir
+
+  # Skip all tests if Python scripting is not enabled.
+  if { [skip_python_tests] } { continue }
+
+  gdb_test_no_output "set confirm off"
+
+  # Load the code which adds commands.
+  set remote_python_file \
+    [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+  gdb_test_no_output "source ${remote_python_file}" "load python file"
+}
+
+# Add a command called CMD through the Python API renaming the existing
+# command of the same name to RENAME_EXISTING_TO.
+#
+# When invoked, the command prints messages and call the renamed command.
+# If MSG is not empty then it will be printed when the command is invoked.
+# Otherwise, the command name is printed.
+#
+# EXTRA_ARGS is passed as it to gdb.Command.__init__ after the other
+# arguments.
+
+proc add_py_cmd {cmd rename_existing_to {msg ""} {kwargs ""}} {
+  gdb_test_no_output \
+    "python TestCommand ('${cmd}', '${rename_existing_to}', '${msg}', ${kwargs})"
+}
+
+# test_sequence_exact CMD LIST
+# Like gdb_test_exact but, for convenience, it accepts a list of lines
+# instead of a single line.
+
+proc test_sequence_exact { cmd lines } {
+  set expected_string [join $lines "\n"]
+  gdb_test_exact $cmd $expected_string
+}
+
+# Test that renaming existing command works and that, after a rename,
+# aliases execute the new command.
+
+proc test_rename_simple {} {
+  with_test_prefix "rename command" {
+    prepare_gdb
+
+    gdb_define_cmd "my-cmd" {
+      "echo my-cmd original\\n"
+    }
+    gdb_test_no_output "alias my-cmd-alias = my-cmd"
+
+    add_py_cmd "my-cmd" "original-my-cmd"
+
+    set expected_list {
+      "py-before: my-cmd"
+      "my-cmd original"
+      "py-after: my-cmd"
+    }
+
+    test_sequence_exact "my-cmd" $expected_list
+    test_sequence_exact "my-cmd-alias" $expected_list
+  }
+}
+
+# Test that renaming a command multiple times works.
+
+proc test_rename_multiple {} {
+  with_test_prefix "rename command multiple times" {
+    prepare_gdb
+
+    gdb_define_cmd "my-cmd" {
+      "echo my-cmd original\\n"
+    }
+
+    add_py_cmd "my-cmd" "original-my-cmd" "first my-cmd"
+    add_py_cmd "my-cmd" "first-redefined-my-cmd" "second my-cmd"
+
+    test_sequence_exact "my-cmd" {
+      "py-before: second my-cmd"
+      "py-before: first my-cmd"
+      "my-cmd original"
+      "py-after: first my-cmd"
+      "py-after: second my-cmd"
+    }
+  }
+}
+
+# Test that trying to rename a non-existing command raises a RuntimeError.
+
+proc test_rename_non_existing {} {
+  with_test_prefix "rename non-existing" {
+    prepare_gdb
+
+    gdb_test \
+      "python TestCommand ('this_doesnt_exist', 'this_doesnt_matter')" \
+      ".*RuntimeError: Command \"this_doesnt_exist\" does not exist,\
+      so it cannot be renamed to \"this_doesnt_matter\"\..*"
+  }
+}
+
+# Test renaming of a command with a prefix.
+
+proc test_same_prefix {} {
+  with_test_prefix "rename keeping the same prefix" {
+    prepare_gdb
+
+    add_py_cmd "info args" "info renamed-args"
+
+    test_sequence_exact "info args" {
+      "py-before: info args"
+      "No frame selected."
+      "py-after: info args"
+    }
+    gdb_test_exact "info renamed-args" "No frame selected."
+  }
+}
+
+# Test renaming of a command without a prefix to a command with a prefix.
+
+proc test_rename_adding_prefix {} {
+  with_test_prefix "rename adding a prefix" {
+    prepare_gdb
+
+    gdb_define_cmd "my-cmd" {
+      "echo my-cmd original\\n"
+    }
+
+    gdb_test_no_output "define-prefix my-prefix"
+
+    add_py_cmd "my-cmd" "my-prefix my-cmd"
+
+    test_sequence_exact "my-cmd" {
+      "py-before: my-cmd"
+      "my-cmd original"
+      "py-after: my-cmd"
+    }
+    gdb_test_exact "my-prefix my-cmd" "my-cmd original"
+  }
+}
+
+# Test renaming of a command with a prefix to a command without prefix.
+
+proc test_rename_removing_prefix {} {
+  with_test_prefix "rename removing a prefix" {
+    prepare_gdb
+
+    add_py_cmd "info args" "renamed-info-args"
+
+    test_sequence_exact "info args" {
+      "py-before: info args"
+      "No frame selected."
+      "py-after: info args"
+    }
+    gdb_test_exact "renamed-info-args" "No frame selected."
+  }
+}
+
+# Test renaming of a command with prefix to a different prefix.
+
+proc test_rename_changing_prefix {} {
+  with_test_prefix "rename changing prefix" {
+    prepare_gdb
+
+    gdb_test_no_output "define-prefix my-prefix"
+
+    add_py_cmd "info args" "my-prefix args"
+
+    test_sequence_exact "info args" {
+      "py-before: info args"
+      "No frame selected."
+      "py-after: info args"
+    }
+    gdb_test_exact "my-prefix args" "No frame selected."
+  }
+}
+
+# Test that replacing a prefix command replaces its implementation but
+# doesn't affect its subcommands.
+
+proc test_replace_prefix {} {
+  with_test_prefix "replace a prefix command" {
+    prepare_gdb
+
+    # Replace the "show" command (which has subcommands).
+    add_py_cmd "show" "original-show"
+
+    # "original-show" won't print anything as it doesn't have subcommands.
+    test_sequence_exact "show" {
+      "py-before: show"
+      "py-after: show"
+    }
+
+    gdb_test_no_output "set pagination off"
+    # Subcommands still work.
+    gdb_test_exact "show pagination" "State of pagination is off."
+  }
+}
+
+# Test that prefix cannot be passed to gdb.Command.__init__ if
+# rename_existing_to is used.
+
+proc test_prefix_arg_not_allowed {} {
+  with_test_prefix "prefix argument not allowed with rename_existing_to" {
+    prepare_gdb
+
+    with_test_prefix "non-prefix command" {
+      gdb_test \
+	"python gdb.Command ('run', rename_existing_to='renamed-run', prefix=True)" \
+	".*Cannot specify argument 'prefix' with 'rename_existing_to'.*"
+      gdb_test \
+	"python gdb.Command ('run', rename_existing_to='renamed-run', prefix=False)" \
+	".*Cannot specify argument 'prefix' with 'rename_existing_to'.*"
+    }
+
+    with_test_prefix "prefix command" {
+      gdb_test \
+	"python gdb.Command ('show', rename_existing_to='renamed-show', prefix=True)" \
+	".*Cannot specify argument 'prefix' with 'rename_existing_to'.*"
+      gdb_test \
+	"python gdb.Command ('show', rename_existing_to='renamed-run', prefix=False)" \
+	".*Cannot specify argument 'prefix' with 'rename_existing_to'.*"
+    }
+  }
+}
+
+# Test that dont-repeat from a renamed command invoked by the new
+# implementation of the command prevents the repetition of the new
+# command.  This is needed to make the new implementation work
+# consistently with the original one.
+
+proc test_repeat {} {
+  with_test_prefix "repeat" {
+    prepare_gdb
+
+    # Define a command which repeats and one which doesn't.
+    gdb_define_cmd "repeat-cmd" {
+      "echo This command repeats\\n"
+    }
+    gdb_define_cmd "no-repeat-cmd" {
+      "echo This command does NOT repeat\\n"
+      "dont-repeat\\n"
+    }
+    # Then redefine them.
+    add_py_cmd "repeat-cmd" "renamed-repeat-cmd"
+    add_py_cmd "no-repeat-cmd" "renamed-no-repeat-cmd"
+
+    # The repeating command must still repeat.
+    set expected_repeat_list {
+      "py-before: repeat-cmd"
+      "This command repeats"
+      "py-after: repeat-cmd"
+    }
+    test_sequence_exact "repeat-cmd" $expected_repeat_list
+    with_test_prefix "repeating command" {
+      # Using an empty string works but is slow, probably because it
+      # waits for a timeout. A string with just whitespace does the same
+      # but fast.
+      test_sequence_exact " " $expected_repeat_list
+    }
+
+    # The non-repeating command must not repeat.
+    test_sequence_exact "no-repeat-cmd" {
+      "py-before: no-repeat-cmd"
+      "This command does NOT repeat"
+      "py-after: no-repeat-cmd"
+    }
+    with_test_prefix "non-repeating command" {
+      gdb_test_exact " " ""
+    }
+  }
+}
+
+# Test that the completer, if not specified, is copied from the original
+# command. If specified, the specified one is used.
+
+proc test_complete {} {
+  with_test_prefix "command completion" {
+    prepare_gdb
+
+    # Add a command whose arguments get completed as commands.
+    gdb_test_no_output \
+      "python gdb.Command ('my-cmd', gdb.COMMAND_NONE, gdb.COMPLETE_COMMAND)"
+
+    # Redefine the command.  As no completer was specified, it must
+    # inherit the one from the original command.
+    add_py_cmd "my-cmd" "renamed-my-cmd1"
+    with_test_prefix "completer inherited" {
+      test_gdb_complete_unique "my-cmd dele" "my-cmd delete"
+    }
+
+    # Redefine the command again.  As a completer was specified, the
+    # command must use the specified completer, not the original one.
+    add_py_cmd "my-cmd" "renamed-my-cmd2" "" "completer_class=gdb.COMPLETE_NONE"
+    with_test_prefix "completer specified" {
+      test_gdb_complete_none "my-cmd dele"
+  }
+  }
+}
+
+# Test the behaviour of command classes with renamed commands.
+
+proc test_command_class {} {
+  with_test_prefix "command class" {
+    prepare_gdb
+
+    # Rename the "run" command. The command class, as it's not specified,
+    # is inherited from the original command.
+    add_py_cmd "run" "renamed-run1"
+    gdb_test "help running" \
+      ".*renamed-run1 -- Start debugged program\..*run, r -- .*"
+
+    # Rename it again but, this time, put the new command in a different
+    # command class.
+    add_py_cmd "run" "renamed-run2" "" "command_class=gdb.COMMAND_STATUS"
+    gdb_test "help status" \
+      ".*run, r -- .*"
+  }
+}
+
+with_test_prefix "renaming commands via Python" {
+  test_rename_simple
+  test_rename_multiple
+  test_rename_non_existing
+  test_same_prefix
+  test_rename_adding_prefix
+  test_rename_removing_prefix
+  test_rename_changing_prefix
+  test_replace_prefix
+  test_prefix_arg_not_allowed
+  test_repeat
+  test_complete
+  test_command_class
+}
diff --git a/gdb/testsuite/gdb.python/py-rename-existing.py b/gdb/testsuite/gdb.python/py-rename-existing.py
new file mode 100644
index 00000000000..7db0f505c7a
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-rename-existing.py
@@ -0,0 +1,46 @@
+# Copyright (C) 2008-2021 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests renaming of existing
+# commands.
+
+import gdb
+
+class TestCommand (gdb.Command):
+
+    def __init__ (self, cmd_name, rename_existing_to, msg=None, **kwargs):
+        if not msg:
+            msg = cmd_name
+
+        self._cmd_name = cmd_name
+        self._rename_existing_to = rename_existing_to
+        self._msg = msg
+
+        gdb.Command.__init__ (
+            self, cmd_name,
+            rename_existing_to=rename_existing_to,
+            **kwargs)
+
+    def invoke (self, args, from_tty):
+        print ('py-before: %s' % self._msg)
+
+        # Invoke the original command.
+        try:
+            gdb.execute ('%s %s' % (self._rename_existing_to, args),
+                         from_tty=from_tty)
+        except gdb.error as exc:
+            print (exc)
+
+        print ('py-after: %s' % self._msg)
-- 
2.28.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 4/4] gdb: Add support for renaming commands
  2021-01-08 10:07 ` [PATCH 4/4] gdb: Add support for renaming commands Marco Barisione
@ 2021-01-08 10:30   ` Eli Zaretskii
  0 siblings, 0 replies; 38+ messages in thread
From: Eli Zaretskii @ 2021-01-08 10:30 UTC (permalink / raw)
  To: Marco Barisione; +Cc: gdb-patches

> Date: Fri,  8 Jan 2021 10:07:06 +0000
> From: Marco Barisione via Gdb-patches <gdb-patches@sourceware.org>
> 
> gdb/ChangeLog:
> 
> 	* NEWS: Add items for the new rename command, the new
> 	-rename-existing-to option for define and the new
> 	rename_existing_to argument for gdb.Command.__init__.
> 	* cli/cli-decode.c (delete_cmd): Rename to disconnect_cmd.
> 	(disconnect_cmd): Disconnect a command from other commands and
> 	lists rather than deleting the command.
> 	(delete_cmd_by_name): Add.
> 	(update_prefix_links): Add.
> 	(update_cmd): Add.
> 	(do_add_cmd): Add ability to rename existing commands rather
> 	than redefining them.
> 	(add_cmd): Ditto.
> 	(add_prefix_cmd): Ditto.
> 	(add_alias_cmd): Update to use disconnect_cmd.
> 	(break_cmd_relationships): Add.
> 	(rename_hook): Add.
> 	(user_defined_command): Move from cli/cli-script.c.
> 	(rename_cmd): Add.
> 	* cli/cli-decode.h (struct cmd_list_element): Call the destroyer
> 	function from the destructor and always free the prefixname.
> 	* cli/cli-script.c (do_define_command): Add ability to rename
> 	existing commands rather than redefining them.
> 	(user_defined_command): Move to cli/cli-decode.c
> 	(check_command_redefinition): Add.
> 	(enum cmd_hook_type): Move from do_define_command to the global
> 	scope.
> 	(get_hook_type): Add.
> 	(HOOK_STRING): Make a variable in get_hook_type.
> 	(HOOK_LEN): Ditto.
> 	(HOOK_POST_STRING): Ditto.
> 	(HOOK_POST_LEN): Ditto.
> 	(struct define_cmd_opts): Add.
> 	(make_define_cmd_options_def_group): Add.
> 	(define_command): Add -rename-existing-to option.
> 	(do_rename_command): Add.
> 	(rename_command): Add.
> 	(_initialize_cli_script): Add option parsing for the define
> 	command and add the rename command.
> 	* command.h (add_cmd): Add ability to rename existing commands
> 	rather than redefining them.
> 	(add_prefix_cmd): Ditto.
> 	(rename_cmd): Add.
> 	(user_defined_command): Add declaration of previously static
> 	function.
> 	* python/py-cmd.c (cmdpy_init): Add rename_existing_to argument.
> 
> gdb/doc/ChangeLog:
> 
> 	* gdb.texinfo: Document the new rename command and the new
> 	-rename-existing-to option for define.
> 	* python.texi: Document the new rename_existing_to argument for
> 	gdb.Command.__init__.
> 
> gdb/testsuite/ChangeLog:
> 
> 	* gdb.base/command-renaming.exp: New test.
> 	* gdb.python/py-rename-existing.exp: New test.
> 	* gdb.python/py-rename-existing.py: New test.

Thanks, the documentation parts are OK.

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 1/4] gdb: add lookup_cmd_exact to simplify a common pattern
  2021-01-08 10:07 ` [PATCH 1/4] gdb: add lookup_cmd_exact to simplify a common pattern Marco Barisione
@ 2021-01-10  0:06   ` Lancelot SIX
  2021-01-17 10:47     ` Marco Barisione
  0 siblings, 1 reply; 38+ messages in thread
From: Lancelot SIX @ 2021-01-10  0:06 UTC (permalink / raw)
  To: gdb-patches

Hi

I just have a few style-related remarks above.

On Fri, Jan 08, 2021 at 10:07:03AM +0000, Marco Barisione via Gdb-patches wrote:
> In code dealing with commands, there's a pattern repeated a few times of
> calling lookup_cmd with some speficic arguments and then using strcmp
> on the returned command to check for an exact match.
> As a later patch would add a few more similar lines of code, this patch
> adds a new lookup_cmd_exact function which simplify this use case.
> 
> gdb/ChangeLog:
> 
> 	* cli/cli-decode.c (lookup_cmd_exact): Add.
> 	* cli/cli-script.c (do_define_command): Use lookup_cmd_exact.
> 	(define_prefix_command): Ditto.
> 	* command.h: Add lookup_cmd_exact.
> ---
>  gdb/cli/cli-decode.c | 15 +++++++++++++++
>  gdb/cli/cli-script.c | 23 ++++++-----------------
>  gdb/command.h        | 19 +++++++++++++++++++
>  3 files changed, 40 insertions(+), 17 deletions(-)
> 
> diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
> index 99bd4c6d2cd..87785107e3d 100644
> --- a/gdb/cli/cli-decode.c
> +++ b/gdb/cli/cli-decode.c
> @@ -1875,6 +1875,21 @@ lookup_cmd (const char **line, struct cmd_list_element *list,
>    return 0;
>  }
>  
> +/* See command.h.  */
> +
> +struct cmd_list_element *
> +lookup_cmd_exact (const char *name,
> +		  struct cmd_list_element *list,
> +		  bool ignore_help_classes)
> +{
> +  const char *tem = name;
> +  struct cmd_list_element *cmd = lookup_cmd (&tem, list, "", NULL, -1,

Probably s/NULL/nullptr/ ?

> +					     ignore_help_classes);
> +  if (cmd && strcmp (name, cmd->name) != 0)

I think gdb prefers explicit comparison to check for null pointers:

https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards#Comparison_With_NULL_And_Zero

+  if (cmd != nullptr && strcmp (name, cmd->name) != 0)

BR
Lancelot

> +    cmd = nullptr;
> +  return cmd;
> +}
> +
>  /* We are here presumably because an alias or command in TEXT is
>     deprecated and a warning message should be generated.  This
>     function decodes TEXT and potentially generates a warning message
> diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
> index 9d0dd7796e0..0544f3efb1b 100644
> --- a/gdb/cli/cli-script.c
> +++ b/gdb/cli/cli-script.c
> @@ -1391,7 +1391,7 @@ do_define_command (const char *comname, int from_tty,
>        CMD_POST_HOOK
>      };
>    struct cmd_list_element *c, *newc, *hookc = 0, **list;
> -  const char *tem, *comfull;
> +  const char *comfull;
>    int  hook_type      = CMD_NO_HOOK;
>    int  hook_name_size = 0;
>     
> @@ -1403,11 +1403,7 @@ do_define_command (const char *comname, int from_tty,
>    comfull = comname;
>    list = validate_comname (&comname);
>  
> -  /* Look it up, and verify that we got an exact match.  */
> -  tem = comname;
> -  c = lookup_cmd (&tem, *list, "", NULL, -1, 1);
> -  if (c && strcmp (comname, c->name) != 0)
> -    c = 0;
> +  c = lookup_cmd_exact (comname, *list);
>  
>    if (c && commands == nullptr)
>      {
> @@ -1448,11 +1444,9 @@ do_define_command (const char *comname, int from_tty,
>  
>    if (hook_type != CMD_NO_HOOK)
>      {
> -      /* Look up cmd it hooks, and verify that we got an exact match.  */
> -      tem = comname + hook_name_size;
> -      hookc = lookup_cmd (&tem, *list, "", NULL, -1, 0);
> -      if (hookc && strcmp (comname + hook_name_size, hookc->name) != 0)
> -	hookc = 0;
> +      /* Look up cmd it hooks.  */
> +      hookc = lookup_cmd_exact (comname + hook_name_size, *list,
> +				/* ignore_help_classes = */ false);
>        if (!hookc && commands == nullptr)
>  	{
>  	  warning (_("Your new `%s' command does not "
> @@ -1593,17 +1587,12 @@ static void
>  define_prefix_command (const char *comname, int from_tty)
>  {
>    struct cmd_list_element *c, **list;
> -  const char *tem;
>    const char *comfull;
>  
>    comfull = comname;
>    list = validate_comname (&comname);
>  
> -  /* Look it up, and verify that we got an exact match.  */
> -  tem = comname;
> -  c = lookup_cmd (&tem, *list, "", NULL, -1, 1);
> -  if (c != nullptr && strcmp (comname, c->name) != 0)
> -    c = nullptr;
> +  c = lookup_cmd_exact (comname, *list);
>  
>    if (c != nullptr && c->theclass != class_user)
>      error (_("Command \"%s\" is built-in."), comfull);
> diff --git a/gdb/command.h b/gdb/command.h
> index 79e5017ff7a..827a19637a2 100644
> --- a/gdb/command.h
> +++ b/gdb/command.h
> @@ -326,6 +326,25 @@ extern struct cmd_list_element *lookup_cmd_1
>  	 struct cmd_list_element **result_list, std::string *default_args,
>  	 int ignore_help_classes, bool lookup_for_completion_p = false);
>  
> +/* Look up the command called NAME in the command list LIST.
> +
> +   Unlike LOOKUP_CMD, partial matches are ignored and only exact matches
> +   on NAME are considered.
> +
> +   LIST is a chain of struct cmd_list_element's.
> +
> +   If IGNORE_HELP_CLASSES is true (the default), 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 null).
> +
> +   If found, return the struct cmd_list_element for that command,
> +   otherwise return NULLPTR.  */
> +
> +extern struct cmd_list_element *lookup_cmd_exact
> +			(const char *name,
> +			 struct cmd_list_element *list,
> +			 bool ignore_help_classes = true);
> +
>  extern struct cmd_list_element *deprecate_cmd (struct cmd_list_element *,
>  					       const char * );
>  
> -- 
> 2.28.0
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 1/4] gdb: add lookup_cmd_exact to simplify a common pattern
  2021-01-10  0:06   ` Lancelot SIX
@ 2021-01-17 10:47     ` Marco Barisione
  2021-01-17 19:02       ` Lancelot SIX
  0 siblings, 1 reply; 38+ messages in thread
From: Marco Barisione @ 2021-01-17 10:47 UTC (permalink / raw)
  To: Lancelot SIX; +Cc: gdb-patches

On 10 Jan 2021, at 00:06, Lancelot SIX <lsix@lancelotsix.com> wrote:
> 
> Hi
> 
> I just have a few style-related remarks above.
> 
> On Fri, Jan 08, 2021 at 10:07:03AM +0000, Marco Barisione via Gdb-patches wrote:
>> +/* See command.h.  */
>> +
>> +struct cmd_list_element *
>> +lookup_cmd_exact (const char *name,
>> +		  struct cmd_list_element *list,
>> +		  bool ignore_help_classes)
>> +{
>> +  const char *tem = name;
>> +  struct cmd_list_element *cmd = lookup_cmd (&tem, list, "", NULL, -1,
> 
> Probably s/NULL/nullptr/ ?
> 
>> +					     ignore_help_classes);
>> +  if (cmd && strcmp (name, cmd->name) != 0)
> 
> I think gdb prefers explicit comparison to check for null pointers:
> 
> https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards#Comparison_With_NULL_And_Zero
> 
> +  if (cmd != nullptr && strcmp (name, cmd->name) != 0)

I tried (but I noticed I didn’t succeed!) to use nullptr and compare
with nullptr in new code or code which already looked like that.
Otherwise, I tried to stick to the style used in nearby code.

That is, if there's code like this:
    foo *bar = NULL;
When adding a new variable on the next line, I would use NULL for
consistency.

What is the approach used in GDB?
1. Add new code using the current style even if inconsistent with nearby
   code.
2. Stick to the style of nearby code.
3. Also update nearby code.


-- 
Marco Barisione


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 1/4] gdb: add lookup_cmd_exact to simplify a common pattern
  2021-01-17 10:47     ` Marco Barisione
@ 2021-01-17 19:02       ` Lancelot SIX
  2021-01-25 11:33         ` Luis Machado
  0 siblings, 1 reply; 38+ messages in thread
From: Lancelot SIX @ 2021-01-17 19:02 UTC (permalink / raw)
  To: Marco Barisione; +Cc: gdb-patches

Le Sun, Jan 17, 2021 at 10:47:38AM +0000, Marco Barisione a écrit :
> On 10 Jan 2021, at 00:06, Lancelot SIX <lsix@lancelotsix.com> wrote:
> > 
> > Hi
> > 
> > I just have a few style-related remarks above.
> > 
> > On Fri, Jan 08, 2021 at 10:07:03AM +0000, Marco Barisione via Gdb-patches wrote:
> >> +/* See command.h.  */
> >> +
> >> +struct cmd_list_element *
> >> +lookup_cmd_exact (const char *name,
> >> +		  struct cmd_list_element *list,
> >> +		  bool ignore_help_classes)
> >> +{
> >> +  const char *tem = name;
> >> +  struct cmd_list_element *cmd = lookup_cmd (&tem, list, "", NULL, -1,
> > 
> > Probably s/NULL/nullptr/ ?
> > 
> >> +					     ignore_help_classes);
> >> +  if (cmd && strcmp (name, cmd->name) != 0)
> > 
> > I think gdb prefers explicit comparison to check for null pointers:
> > 
> > https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards#Comparison_With_NULL_And_Zero
> > 
> > +  if (cmd != nullptr && strcmp (name, cmd->name) != 0)
> 
> I tried (but I noticed I didn’t succeed!) to use nullptr and compare
> with nullptr in new code or code which already looked like that.
> Otherwise, I tried to stick to the style used in nearby code.
> 
> That is, if there's code like this:
>     foo *bar = NULL;
> When adding a new variable on the next line, I would use NULL for
> consistency.
> 
> What is the approach used in GDB?
> 1. Add new code using the current style even if inconsistent with nearby
>    code.
> 2. Stick to the style of nearby code.
> 3. Also update nearby code.
> 
> 
> -- 
> Marco Barisione
> 

My personal approach would be to migrate to nullptr slowly but surely,
but this is quite personal.  I’ll let a more experienced gdb maintainer
state what is the prefered way of doing things for the project.

Independently of that matter, cmd should be compared to something in
this expression. I guess something like

+  if (cmd != NULL && strcmp (name, cmd->name) != 0)

should be ok to maintain consistency.

Lancelot.

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v2 0/5] Add support for command renaming
  2021-01-08 10:07 [PATCH 0/4] Add support for command renaming Marco Barisione
                   ` (3 preceding siblings ...)
  2021-01-08 10:07 ` [PATCH 4/4] gdb: Add support for renaming commands Marco Barisione
@ 2021-01-25 11:26 ` Marco Barisione
  2021-01-25 11:26   ` [PATCH v2 1/5] gdb: add lookup_cmd_exact to simplify a common pattern Marco Barisione
                     ` (5 more replies)
  4 siblings, 6 replies; 38+ messages in thread
From: Marco Barisione @ 2021-01-25 11:26 UTC (permalink / raw)
  To: gdb-patches

This patch series adds support for command renaming to GDB.  See the
first email of this thread for details on the rationale and design.

This second version fixes a few style issues, a bug in my code, and the
way the prefix name for prefix commands is set.  In particular:
* Patch 1: add lookup_cmd_exact to simplify a common pattern
  Same as before except for style fixes.
* Patch 2: prevent prefix commands from being hooks
  Same as before.
* Patch 3: update the docs for add_cmd and do_add_cmd to match reality
  Same as before.
* Patch 4: generate the prefix name for prefix commands on demand
  New patch which changes how the prefix name is set/generated.
  This simplifies a lot of existing code and the code from the next
  patch.  Moreover, it fixes what I think could lead to a use after
  free (based on reading code, I didn't actually reproduce any crash).
* Patch 5: Add support for renaming commands
  Updated version of what was before patch 4.
  This contains a few style fixes and makes delete_cmd_by_name use
  the lookup_cmd_exact function rather than find_cmd.

Note that patches 1 to 4 are needed for the new feature but could be
merged separately as they are improvements to the existing code.

Marco Barisione (5):
  gdb: add lookup_cmd_exact to simplify a common pattern
  gdb: prevent prefix commands from being hooks
  gdb: update the docs for add_cmd and do_add_cmd to match reality
  gdb: generate the prefix name for prefix commands on demand
  gdb: Add support for renaming commands

 gdb/NEWS                                      |  26 +
 gdb/ada-lang.c                                |   8 +-
 gdb/arm-tdep.c                                |   4 +-
 gdb/auto-load.c                               |   9 +-
 gdb/breakpoint.c                              |  20 +-
 gdb/bsd-kvm.c                                 |   2 +-
 gdb/btrace.c                                  |   9 +-
 gdb/cli/cli-cmds.c                            |  12 +-
 gdb/cli/cli-decode.c                          | 731 ++++++++++++++----
 gdb/cli/cli-decode.h                          |  29 +-
 gdb/cli/cli-dump.c                            |  16 +-
 gdb/cli/cli-logging.c                         |   4 +-
 gdb/cli/cli-script.c                          | 375 +++++++--
 gdb/cli/cli-setshow.c                         |  11 +-
 gdb/cli/cli-style.c                           |  11 +-
 gdb/cli/cli-style.h                           |   3 -
 gdb/command.h                                 | 110 ++-
 gdb/compile/compile.c                         |   2 +-
 gdb/cp-support.c                              |   1 -
 gdb/dcache.c                                  |   5 +-
 gdb/doc/gdb.texinfo                           |  55 +-
 gdb/doc/python.texi                           |  38 +-
 gdb/dwarf2/index-cache.c                      |   4 +-
 gdb/dwarf2/read.c                             |   4 +-
 gdb/f-lang.c                                  |   4 +-
 gdb/frame.c                                   |   4 +-
 gdb/gnu-nat.c                                 |  12 +-
 gdb/go32-nat.c                                |   2 +-
 gdb/guile/guile.c                             |   9 +-
 gdb/guile/scm-cmd.c                           |   2 +-
 gdb/i386-tdep.c                               |   4 +-
 gdb/infcmd.c                                  |   8 +-
 gdb/language.c                                |   4 +-
 gdb/macrocmd.c                                |   2 +-
 gdb/maint-test-options.c                      |   3 +-
 gdb/maint-test-settings.c                     |   2 -
 gdb/maint.c                                   |  18 +-
 gdb/memattr.c                                 |   4 +-
 gdb/mips-tdep.c                               |   6 +-
 gdb/printcmd.c                                |   2 +-
 gdb/probe.c                                   |   3 +-
 gdb/python/py-cmd.c                           | 228 ++++--
 gdb/python/python-internal.h                  |   1 +
 gdb/python/python.c                           |   6 +-
 gdb/ravenscar-thread.c                        |   4 +-
 gdb/record-btrace.c                           |  16 +-
 gdb/record-full.c                             |   6 +-
 gdb/record.c                                  |  10 +-
 gdb/remote.c                                  |   6 +-
 gdb/riscv-tdep.c                              |   8 +-
 gdb/rs6000-tdep.c                             |   4 +-
 gdb/ser-tcp.c                                 |   4 +-
 gdb/serial.c                                  |   4 +-
 gdb/sh-tdep.c                                 |   4 +-
 gdb/skip.c                                    |   2 +-
 gdb/sparc64-tdep.c                            |   2 +-
 gdb/stack.c                                   |   8 +-
 gdb/symfile.c                                 |   2 +-
 gdb/symtab.c                                  |   3 +-
 gdb/target-descriptions.c                     |   6 +-
 gdb/target.c                                  |   2 +-
 gdb/testsuite/gdb.base/command-renaming.exp   | 571 ++++++++++++++
 gdb/testsuite/gdb.base/define-prefix.exp      |  24 +
 .../gdb.python/py-rename-existing.exp         | 364 +++++++++
 .../gdb.python/py-rename-existing.py          |  46 ++
 gdb/thread.c                                  |   4 +-
 gdb/top.c                                     |  10 +-
 gdb/tracepoint.c                              |   2 +-
 gdb/tui/tui-layout.c                          |   2 +-
 gdb/tui/tui-win.c                             |   8 +-
 gdb/typeprint.c                               |   6 +-
 gdb/unittests/command-def-selftests.c         |   4 +-
 gdb/utils.c                                   |   4 -
 gdb/valprint.c                                |  10 +-
 gdb/value.c                                   |   2 +-
 gdb/windows-tdep.c                            |   2 +-
 76 files changed, 2434 insertions(+), 529 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/command-renaming.exp
 create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.exp
 create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.py

-- 
2.28.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v2 1/5] gdb: add lookup_cmd_exact to simplify a common pattern
  2021-01-25 11:26 ` [PATCH v2 0/5] Add support for command renaming Marco Barisione
@ 2021-01-25 11:26   ` Marco Barisione
  2021-03-08 18:58     ` Simon Marchi
  2021-01-25 11:26   ` [PATCH v2 2/5] gdb: prevent prefix commands from being hooks Marco Barisione
                     ` (4 subsequent siblings)
  5 siblings, 1 reply; 38+ messages in thread
From: Marco Barisione @ 2021-01-25 11:26 UTC (permalink / raw)
  To: gdb-patches

In code dealing with commands, there's a pattern repeated a few times of
calling lookup_cmd with some speficic arguments and then using strcmp
on the returned command to check for an exact match.
As a later patch would add a few more similar lines of code, this patch
adds a new lookup_cmd_exact function which simplify this use case.

gdb/ChangeLog:

	* cli/cli-decode.c (lookup_cmd_exact): Add.
	* cli/cli-script.c (do_define_command): Use lookup_cmd_exact.
	(define_prefix_command): Ditto.
	* command.h: Add lookup_cmd_exact.
---
 gdb/cli/cli-decode.c | 15 +++++++++++++++
 gdb/cli/cli-script.c | 23 ++++++-----------------
 gdb/command.h        | 19 +++++++++++++++++++
 3 files changed, 40 insertions(+), 17 deletions(-)

diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 99bd4c6d2cd..f48a9add4d4 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -1875,6 +1875,21 @@ lookup_cmd (const char **line, struct cmd_list_element *list,
   return 0;
 }
 
+/* See command.h.  */
+
+struct cmd_list_element *
+lookup_cmd_exact (const char *name,
+		  struct cmd_list_element *list,
+		  bool ignore_help_classes)
+{
+  const char *tem = name;
+  struct cmd_list_element *cmd = lookup_cmd (&tem, list, "", NULL, -1,
+					     ignore_help_classes);
+  if (cmd != nullptr && strcmp (name, cmd->name) != 0)
+    cmd = nullptr;
+  return cmd;
+}
+
 /* We are here presumably because an alias or command in TEXT is
    deprecated and a warning message should be generated.  This
    function decodes TEXT and potentially generates a warning message
diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index 9d0dd7796e0..0544f3efb1b 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -1391,7 +1391,7 @@ do_define_command (const char *comname, int from_tty,
       CMD_POST_HOOK
     };
   struct cmd_list_element *c, *newc, *hookc = 0, **list;
-  const char *tem, *comfull;
+  const char *comfull;
   int  hook_type      = CMD_NO_HOOK;
   int  hook_name_size = 0;
    
@@ -1403,11 +1403,7 @@ do_define_command (const char *comname, int from_tty,
   comfull = comname;
   list = validate_comname (&comname);
 
-  /* Look it up, and verify that we got an exact match.  */
-  tem = comname;
-  c = lookup_cmd (&tem, *list, "", NULL, -1, 1);
-  if (c && strcmp (comname, c->name) != 0)
-    c = 0;
+  c = lookup_cmd_exact (comname, *list);
 
   if (c && commands == nullptr)
     {
@@ -1448,11 +1444,9 @@ do_define_command (const char *comname, int from_tty,
 
   if (hook_type != CMD_NO_HOOK)
     {
-      /* Look up cmd it hooks, and verify that we got an exact match.  */
-      tem = comname + hook_name_size;
-      hookc = lookup_cmd (&tem, *list, "", NULL, -1, 0);
-      if (hookc && strcmp (comname + hook_name_size, hookc->name) != 0)
-	hookc = 0;
+      /* Look up cmd it hooks.  */
+      hookc = lookup_cmd_exact (comname + hook_name_size, *list,
+				/* ignore_help_classes = */ false);
       if (!hookc && commands == nullptr)
 	{
 	  warning (_("Your new `%s' command does not "
@@ -1593,17 +1587,12 @@ static void
 define_prefix_command (const char *comname, int from_tty)
 {
   struct cmd_list_element *c, **list;
-  const char *tem;
   const char *comfull;
 
   comfull = comname;
   list = validate_comname (&comname);
 
-  /* Look it up, and verify that we got an exact match.  */
-  tem = comname;
-  c = lookup_cmd (&tem, *list, "", NULL, -1, 1);
-  if (c != nullptr && strcmp (comname, c->name) != 0)
-    c = nullptr;
+  c = lookup_cmd_exact (comname, *list);
 
   if (c != nullptr && c->theclass != class_user)
     error (_("Command \"%s\" is built-in."), comfull);
diff --git a/gdb/command.h b/gdb/command.h
index 79e5017ff7a..827a19637a2 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -326,6 +326,25 @@ extern struct cmd_list_element *lookup_cmd_1
 	 struct cmd_list_element **result_list, std::string *default_args,
 	 int ignore_help_classes, bool lookup_for_completion_p = false);
 
+/* Look up the command called NAME in the command list LIST.
+
+   Unlike LOOKUP_CMD, partial matches are ignored and only exact matches
+   on NAME are considered.
+
+   LIST is a chain of struct cmd_list_element's.
+
+   If IGNORE_HELP_CLASSES is true (the default), 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 null).
+
+   If found, return the struct cmd_list_element for that command,
+   otherwise return NULLPTR.  */
+
+extern struct cmd_list_element *lookup_cmd_exact
+			(const char *name,
+			 struct cmd_list_element *list,
+			 bool ignore_help_classes = true);
+
 extern struct cmd_list_element *deprecate_cmd (struct cmd_list_element *,
 					       const char * );
 
-- 
2.28.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v2 2/5] gdb: prevent prefix commands from being hooks
  2021-01-25 11:26 ` [PATCH v2 0/5] Add support for command renaming Marco Barisione
  2021-01-25 11:26   ` [PATCH v2 1/5] gdb: add lookup_cmd_exact to simplify a common pattern Marco Barisione
@ 2021-01-25 11:26   ` Marco Barisione
  2021-03-08 21:32     ` Simon Marchi
  2021-01-25 11:26   ` [PATCH v2 3/5] gdb: update the docs for add_cmd and do_add_cmd to match reality Marco Barisione
                     ` (3 subsequent siblings)
  5 siblings, 1 reply; 38+ messages in thread
From: Marco Barisione @ 2021-01-25 11:26 UTC (permalink / raw)
  To: gdb-patches

Currently it's possible for hooks to be prefix commands:

  define-prefix hook-run

  define hook-run
    echo Will run!\n
  end

  define hook-run subcommand
    echo Subcommand\n
  end

Then:

  (gdb) run
  Will run!
  [...]
  (gdb) hook-run subcommand
  Subcommand

This doesn't seem very useful or worse, it can be confusing.  Moreover,
this creates some obscure corner cases in a later patch adding command
renaming.

Because of this, this patch prevents hooks to become prefixes and
prefixes to become hooks.

An alternative approach would have been to prevent prefixes starting
with "hook-" or "hookpost-".  The end result would have been similar
but not identical as GDB allows commands starting with "hook-" or
"hookpost-" which are not hooks (for instance, "define
hook-this-doesnt-exist").  To be more consistent with this use case,
prefixes for commands which are named like hooks but are not hooks are
allowed.

gdb/ChangeLog:

	* cli/cli-script.c (do_define_command): Prevent prefix commands
	from becoming hooks.
	(define_prefix_command): Prevent hooks from becoming prefix
	commands.

gdb/testsuite/ChangeLog:

	* gdb.base/define-prefix.exp: Test that prefix commands cannot
	become hooks and vice versa.
---
 gdb/cli/cli-script.c                     | 25 ++++++++++++++++++------
 gdb/testsuite/gdb.base/define-prefix.exp | 24 +++++++++++++++++++++++
 2 files changed, 43 insertions(+), 6 deletions(-)

diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index 0544f3efb1b..e197852bc71 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -1455,6 +1455,11 @@ do_define_command (const char *comname, int from_tty,
 	  if (!query (_("Proceed? ")))
 	    error (_("Not confirmed."));
 	}
+
+      if (c != nullptr && c->prefixlist != nullptr)
+        error (_("Command \"%s\" cannot become a hook as it's already a "
+		 "prefix command."),
+	       comfull);
     }
 
   comname = xstrdup (comname);
@@ -1594,13 +1599,21 @@ define_prefix_command (const char *comname, int from_tty)
 
   c = lookup_cmd_exact (comname, *list);
 
-  if (c != nullptr && c->theclass != class_user)
-    error (_("Command \"%s\" is built-in."), comfull);
-
-  if (c != nullptr && c->prefixlist != nullptr)
+  if (c != nullptr)
     {
-      /* c is already a user defined prefix command.  */
-      return;
+      if (c->theclass != class_user)
+        error (_("Command \"%s\" is built-in."), comfull);
+
+      if (c->prefixlist != nullptr)
+        {
+          /* C is already a user defined prefix command.  */
+          return;
+        }
+
+      if (c->hookee_pre != nullptr || c->hookee_post != nullptr)
+        error (_("Command \"%s\" cannot be a prefix command as it's "
+		 "already a hook."),
+	       comfull);
     }
 
   /* If the command does not exist at all, create it.  */
diff --git a/gdb/testsuite/gdb.base/define-prefix.exp b/gdb/testsuite/gdb.base/define-prefix.exp
index 71369caaed6..a8ce5765ec7 100644
--- a/gdb/testsuite/gdb.base/define-prefix.exp
+++ b/gdb/testsuite/gdb.base/define-prefix.exp
@@ -162,3 +162,27 @@ gdb_test "define-prefix something-not-existing something-else" \
 gdb_test "define-prefix abc-prefix something-not-existing something-else" \
     "Undefined abc-prefix command: \"something-not-existing\".*"
 
+####################
+# Check error behaviour when interacting with hooks.
+
+# Define a command and hooks into it.
+gdb_define_cmd "hookee1" {}
+gdb_define_cmd "hook-hookee1" {}
+gdb_define_cmd "hookpost-hookee1" {}
+
+# Check that making the hooks into prefix commands is not allowed.
+gdb_test_exact "define-prefix hook-hookee1" \
+  "Command \"hook-hookee1\" cannot be a prefix command as it's already a hook."
+gdb_test_exact "define-prefix hookpost-hookee1" \
+  "Command \"hookpost-hookee1\" cannot be a prefix command as it's already a hook."
+
+# Define a command and prefixes which look like hooks into it (but they
+# are not).
+gdb_define_cmd "hookee2" {}
+gdb_test_no_output "define-prefix hook-hookee2" {}
+gdb_test_no_output "define-prefix hookpost-hookee2" {}
+
+gdb_test_exact "define hook-hookee2" \
+  "Command \"hook-hookee2\" cannot become a hook as it's already a prefix command."
+gdb_test_exact "define hookpost-hookee2" \
+  "Command \"hookpost-hookee2\" cannot become a hook as it's already a prefix command."
-- 
2.28.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v2 3/5] gdb: update the docs for add_cmd and do_add_cmd to match reality
  2021-01-25 11:26 ` [PATCH v2 0/5] Add support for command renaming Marco Barisione
  2021-01-25 11:26   ` [PATCH v2 1/5] gdb: add lookup_cmd_exact to simplify a common pattern Marco Barisione
  2021-01-25 11:26   ` [PATCH v2 2/5] gdb: prevent prefix commands from being hooks Marco Barisione
@ 2021-01-25 11:26   ` Marco Barisione
  2021-03-08 22:52     ` Simon Marchi
  2021-01-25 11:26   ` [PATCH v2 4/5] gdb: generate the prefix name for prefix commands on demand Marco Barisione
                     ` (2 subsequent siblings)
  5 siblings, 1 reply; 38+ messages in thread
From: Marco Barisione @ 2021-01-25 11:26 UTC (permalink / raw)
  To: gdb-patches

It looks like the docs for add_cmd ended up being the docs for
do_add_cmd.  Moreover, they were outdated.

gdb/ChangeLog:

	* cli/cli-decode.c (do_add_cmd): Remove outdated documentation
	and refer to the documentation for add_cmd.
	(add_cmd): Add comment referring to the docs in command.h.
	* command.h (add_cmd): Add updated documentation.
---
 gdb/cli/cli-decode.c | 22 +++++-----------------
 gdb/command.h        | 40 +++++++++++++++++++++++++++++++++-------
 2 files changed, 38 insertions(+), 24 deletions(-)

diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index f48a9add4d4..6e076c4519f 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -160,23 +160,7 @@ set_cmd_completer_handle_brkchars (struct cmd_list_element *cmd,
   cmd->completer_handle_brkchars = func;
 }
 
-/* Add element named NAME.
-   Space for NAME and DOC must be allocated by the caller.
-   CLASS is the top level category into which commands are broken down
-   for "help" purposes.
-   FUN should be the function to execute the command;
-   it will get a character string as argument, with leading
-   and trailing blanks already eliminated.
-
-   DOC is a documentation string for the command.
-   Its first line should be a complete sentence.
-   It should start with ? for a command that is an abbreviation
-   or with * for a command that most users don't need to know about.
-
-   Add this command to command list *LIST.
-
-   Returns a pointer to the added command (not necessarily the head 
-   of *LIST).  */
+/* Like ADD_CMD, but the command function fields are not modified.  */
 
 static struct cmd_list_element *
 do_add_cmd (const char *name, enum command_class theclass,
@@ -227,6 +211,8 @@ do_add_cmd (const char *name, enum command_class theclass,
   return c;
 }
 
+/* See command.h.  */
+
 struct cmd_list_element *
 add_cmd (const char *name, enum command_class theclass,
 	 const char *doc, struct cmd_list_element **list)
@@ -237,6 +223,8 @@ add_cmd (const char *name, enum command_class theclass,
   return result;
 }
 
+/* See command.h.  */
+
 struct cmd_list_element *
 add_cmd (const char *name, enum command_class theclass,
 	 cmd_const_cfunc_ftype *fun,
diff --git a/gdb/command.h b/gdb/command.h
index 827a19637a2..df40cbf7119 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -155,18 +155,44 @@ extern bool valid_user_defined_cmd_name_p (const char *name);
 
 extern bool valid_cmd_char_p (int c);
 
-/* Const-correct variant of the above.  */
+/* Add a command named NAME in command list *LIST.
 
-extern struct cmd_list_element *add_cmd (const char *, enum command_class,
+   NAME and DOC are not duplicated. If they are not static string, they
+   must have been allocated with xmalloc or xstrdup and the
+   NAME_ALLOCATED/DOC_ALLOCATED fields must be set to 1 on the returned
+   command.
+
+   THECLASS is the top level category into which commands are broken down
+   for "help" purposes.
+
+   FUN should be the function to execute the command; it will get two
+   arguments, a character string (with leading and trailing blanks already
+   eliminated) containing the command arguments, and an integer indicating
+   whether input comes from a TTY or not.
+
+   DOC is a documentation string for the command.
+   Its first line should be a complete sentence.
+   It should start with ? for a command that is an abbreviation
+   or with * for a command that most users don't need to know about.
+
+   If NAME already existed in *LIST, all its hooks and aliases are moved
+   to the new command.
+
+   Return a pointer to the added command (not necessarily the head of
+   *LIST).  */
+
+extern struct cmd_list_element *add_cmd (const char *name,
+					 enum command_class theclass,
 					 cmd_const_cfunc_ftype *fun,
-					 const char *,
-					 struct cmd_list_element **);
+					 const char *doc,
+					 struct cmd_list_element **list);
 
 /* Like add_cmd, but no command function is specified.  */
 
-extern struct cmd_list_element *add_cmd (const char *, enum command_class,
-					 const char *,
-					 struct cmd_list_element **);
+extern struct cmd_list_element *add_cmd (const char *name,
+					 enum command_class theclass,
+					 const char *doc,
+					 struct cmd_list_element **list);
 
 extern struct cmd_list_element *add_cmd_suppress_notification
 			(const char *name, enum command_class theclass,
-- 
2.28.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v2 4/5] gdb: generate the prefix name for prefix commands on demand
  2021-01-25 11:26 ` [PATCH v2 0/5] Add support for command renaming Marco Barisione
                     ` (2 preceding siblings ...)
  2021-01-25 11:26   ` [PATCH v2 3/5] gdb: update the docs for add_cmd and do_add_cmd to match reality Marco Barisione
@ 2021-01-25 11:26   ` Marco Barisione
  2021-03-08 23:25     ` Simon Marchi
  2021-01-25 11:26   ` [PATCH v2 5/5] gdb: Add support for renaming commands Marco Barisione
  2021-02-08 17:53   ` [PING] [PATCH v2 0/5] Add support for command renaming Marco Barisione
  5 siblings, 1 reply; 38+ messages in thread
From: Marco Barisione @ 2021-01-25 11:26 UTC (permalink / raw)
  To: gdb-patches

Previously, the prefixname field of struct cmd_list_element was manually
set for prefix commands.  This seems verbose and error prone as it
required every single call to functions adding prefix commands to
specify the prefix name while the same information can be easily
generated.

Historically, this was not possible as the prefix field was null for
many commands, but this was fixed in commit
3f4d92ebdf7f848b5ccc9e8d8e8514c64fde1183 by Philippe Waroquiers, so
we can rely on the prefix field being set when generating the prefix
name.

This commit also fixes a free after in this scenario:
* A command gets created via Python (using the gdb.Command class).
  The prefix name member is dynamically allocated.
* An alias to the new command is created. The alias's prefixname is set
  to point to the prefixname for the original command with a direct
  assignment.
* A new command with the same name as the Python command is created.
* The object for the original Python command gets freed and its
  prefixname gets freed as well.
* The alias is update to point to the new command, but its prefixname
  is not updated so it keeps pointing to the freed one.

gdb/ChangeLog:

	* command.h (add_prefix_cmd): Remove the prefixname argument as
	it can now be generated automatically.
	(add_basic_prefix_cmd): Ditto.
	(add_show_prefix_cmd): Ditto.
	(add_prefix_cmd_suppress_notification): Ditto.
	(add_abbrev_prefix_cmd): Ditto.
	* cli/cli-decode.h (struct cmd_list_element): Replace the
	prefixname member variable with a method which generates the
	prefix name at runtime.
	* python/py-cmd.c (cmdpy_destroyer): Remove code to free the
	prefixname member as it's now a method.
	(cmdpy_function): Determine if the command is a prefix by
	looking at prefixlist, not prefixname.
	(cmdpy_init): Remove code to generate the prefix name.
	* cli/cli-decode.c (add_alias_cmd): Do not set prefixname on
	aliases.
	(do_prefix_cmd): Do not pass the prefixname argument to
	help_list.
	(add_prefix_cmd): Remove the prefixname argument.
	(add_basic_prefix_cmd): Ditto.
	(add_show_prefix_cmd): Ditto.
	(add_abbrev_prefix_cmd): Ditto.
	(fput_command_name_styled): Use the prefixname method.
	(apropos_cmd): Ditto.
	(help_cmd): Ditto.
	(lookup_cmd): Ditto.
	(deprecated_cmd_warning): Ditto.
	* cli/cli-script.c (do_define_command): Do not set the prefix
	name.
	(define_prefix_command): Ditto.
	(show_user_1): Uupdate to use the prefixname method.
	* cli/cli-setshow.c (cmd_show_list): Ditto.
	* top.c (execute_command): Ditto.
	* unittests/command-def-selftests.c (check_doc): Ditto.
	(traverse_command_structure): Ditto.
	* ada-lang.c (_initialize_ada_language): Do not pass the prefix
	name as it's not needed any more.
	* arm-tdep.c (_initialize_arm_tdep): Ditto.
	* auto-load.c: Ditto.
	* breakpoint.c: Ditto.
	* bsd-kvm.c: Ditto.
	* btrace.c: Ditto.
	* cli/cli-cmds.c: Ditto.
	* cli/cli-dump.c (_initialize_cli_dump): Ditto.
	* cli/cli-logging.c (_initialize_cli_logging): Ditto.
	* cli/cli-style.c (cli_style_option::add_setshow_commands): Ditto.
	* cli/cli-style.h (class cli_style_option): Ditto.
	* compile/compile.c: Ditto.
	* cp-support.c (_initialize_cp_support): Ditto.
	* dcache.c: Ditto.
	* dwarf2/index-cache.c (_initialize_index_cache): Ditto.
	* dwarf2/read.c: Ditto.
	* f-lang.c (_initialize_f_language): Ditto.
	* frame.c: Ditto.
	* gnu-nat.c (add_thread_commands): Ditto.
	* go32-nat.c (DJGPP): Ditto.
	* guile/guile.c: Ditto.
	* guile/scm-cmd.c (gdbscm_register_command_x): Ditto.
	* i386-tdep.c: Ditto.
	* infcmd.c: Ditto.
	* language.c (_initialize_language): Ditto.
	* macrocmd.c (_initialize_macrocmd): Ditto.
	* maint-test-options.c: Ditto.
	* maint-test-settings.c: Ditto.
	* maint.c: Ditto.
	* memattr.c: Ditto.
	* mips-tdep.c (_initialize_mips_tdep): Ditto.
	* printcmd.c: Ditto.
	* probe.c: Ditto.
	* python/python.c: Ditto.
	* ravenscar-thread.c (_initialize_ravenscar): Ditto.
	* record-btrace.c (_initialize_record_btrace): Ditto.
	* record-full.c (_initialize_record_full): Ditto.
	* record.c: Ditto.
	* remote.c: Ditto.
	* riscv-tdep.c (_initialize_riscv_tdep): Ditto.
	* rs6000-tdep.c (_initialize_rs6000_tdep): Ditto.
	* ser-tcp.c: Ditto.
	* serial.c: Ditto.
	* sh-tdep.c (_initialize_sh_tdep): Ditto.
	* skip.c: Ditto.
	* sparc64-tdep.c (_initialize_sparc64_adi_tdep): Ditto.
	* stack.c: Ditto.
	* symfile.c: Ditto.
	* symtab.c: Ditto.
	* target-descriptions.c: Ditto.
	* target.c: Ditto.
	* thread.c: Ditto.
	* tracepoint.c: Ditto.
	* tui/tui-layout.c: Ditto.
	* tui/tui-win.c (tui_get_cmd_list): Ditto.
	(_initialize_tui_win): Ditto.
	* typeprint.c: Ditto.
	* utils.c (add_internal_problem_command): Ditto.
	* valprint.c: Ditto.
	* value.c: Ditto.
	* windows-tdep.c (init_w32_command_list): Ditto.
---
 gdb/ada-lang.c                        |  8 ++---
 gdb/arm-tdep.c                        |  4 +--
 gdb/auto-load.c                       |  9 ++---
 gdb/breakpoint.c                      | 20 +++++------
 gdb/bsd-kvm.c                         |  2 +-
 gdb/btrace.c                          |  9 ++---
 gdb/cli/cli-cmds.c                    | 12 +++----
 gdb/cli/cli-decode.c                  | 51 +++++++++++++--------------
 gdb/cli/cli-decode.h                  | 27 ++++++++++----
 gdb/cli/cli-dump.c                    | 16 ++++-----
 gdb/cli/cli-logging.c                 |  4 +--
 gdb/cli/cli-script.c                  |  7 ++--
 gdb/cli/cli-setshow.c                 | 11 +++---
 gdb/cli/cli-style.c                   | 11 +++---
 gdb/cli/cli-style.h                   |  3 --
 gdb/command.h                         | 10 +++---
 gdb/compile/compile.c                 |  2 +-
 gdb/cp-support.c                      |  1 -
 gdb/dcache.c                          |  5 ++-
 gdb/dwarf2/index-cache.c              |  4 +--
 gdb/dwarf2/read.c                     |  4 +--
 gdb/f-lang.c                          |  4 +--
 gdb/frame.c                           |  4 +--
 gdb/gnu-nat.c                         | 12 +++----
 gdb/go32-nat.c                        |  2 +-
 gdb/guile/guile.c                     |  9 ++---
 gdb/guile/scm-cmd.c                   |  2 +-
 gdb/i386-tdep.c                       |  4 +--
 gdb/infcmd.c                          |  8 ++---
 gdb/language.c                        |  4 +--
 gdb/macrocmd.c                        |  2 +-
 gdb/maint-test-options.c              |  3 +-
 gdb/maint-test-settings.c             |  2 --
 gdb/maint.c                           | 18 +++++-----
 gdb/memattr.c                         |  4 +--
 gdb/mips-tdep.c                       |  6 ++--
 gdb/printcmd.c                        |  2 +-
 gdb/probe.c                           |  3 +-
 gdb/python/py-cmd.c                   | 51 +++++++--------------------
 gdb/python/python.c                   |  6 ++--
 gdb/ravenscar-thread.c                |  4 +--
 gdb/record-btrace.c                   | 16 ++++-----
 gdb/record-full.c                     |  6 ++--
 gdb/record.c                          | 10 +++---
 gdb/remote.c                          |  6 ++--
 gdb/riscv-tdep.c                      |  8 ++---
 gdb/rs6000-tdep.c                     |  4 +--
 gdb/ser-tcp.c                         |  4 +--
 gdb/serial.c                          |  4 +--
 gdb/sh-tdep.c                         |  4 +--
 gdb/skip.c                            |  2 +-
 gdb/sparc64-tdep.c                    |  2 +-
 gdb/stack.c                           |  8 ++---
 gdb/symfile.c                         |  2 +-
 gdb/symtab.c                          |  3 +-
 gdb/target-descriptions.c             |  6 ++--
 gdb/target.c                          |  2 +-
 gdb/thread.c                          |  4 +--
 gdb/top.c                             | 10 ++++--
 gdb/tracepoint.c                      |  2 +-
 gdb/tui/tui-layout.c                  |  2 +-
 gdb/tui/tui-win.c                     |  8 ++---
 gdb/typeprint.c                       |  6 ++--
 gdb/unittests/command-def-selftests.c |  4 +--
 gdb/utils.c                           |  4 ---
 gdb/valprint.c                        | 10 +++---
 gdb/value.c                           |  2 +-
 gdb/windows-tdep.c                    |  2 +-
 68 files changed, 231 insertions(+), 280 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index e2befe1d82e..0d8320a44ac 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -14325,11 +14325,11 @@ _initialize_ada_language ()
 
   add_basic_prefix_cmd ("ada", no_class,
 			_("Prefix command for changing Ada-specific settings."),
-			&set_ada_list, "set ada ", 0, &setlist);
+			&set_ada_list, 0, &setlist);
 
   add_show_prefix_cmd ("ada", no_class,
 		       _("Generic command for showing Ada-specific settings."),
-		       &show_ada_list, "show ada ", 0, &showlist);
+		       &show_ada_list, 0, &showlist);
 
   add_setshow_boolean_cmd ("trust-PAD-over-XVS", class_obscure,
 			   &trust_pad_over_xvs, _("\
@@ -14408,12 +14408,12 @@ the regular expression are listed."));
 
   add_basic_prefix_cmd ("ada", class_maintenance,
 			_("Set Ada maintenance-related variables."),
-			&maint_set_ada_cmdlist, "maintenance set ada ",
+			&maint_set_ada_cmdlist,
 			0/*allow-unknown*/, &maintenance_set_cmdlist);
 
   add_show_prefix_cmd ("ada", class_maintenance,
 		       _("Show Ada maintenance-related variables."),
-		       &maint_show_ada_cmdlist, "maintenance show ada ",
+		       &maint_show_ada_cmdlist,
 		       0/*allow-unknown*/, &maintenance_show_cmdlist);
 
   add_setshow_boolean_cmd
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 4e2ab32b5ee..f334203c0ed 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -9563,11 +9563,11 @@ _initialize_arm_tdep ()
   /* Add root prefix command for all "set arm"/"show arm" commands.  */
   add_basic_prefix_cmd ("arm", no_class,
 			_("Various ARM-specific commands."),
-			&setarmcmdlist, "set arm ", 0, &setlist);
+			&setarmcmdlist, 0, &setlist);
 
   add_show_prefix_cmd ("arm", no_class,
 		       _("Various ARM-specific commands."),
-		       &showarmcmdlist, "show arm ", 0, &showlist);
+		       &showarmcmdlist, 0, &showlist);
 
 
   arm_disassembler_options = xstrdup ("reg-names-std");
diff --git a/gdb/auto-load.c b/gdb/auto-load.c
index 1dfcf21eeeb..4e6af9fbb2f 100644
--- a/gdb/auto-load.c
+++ b/gdb/auto-load.c
@@ -1427,8 +1427,7 @@ auto_load_set_cmdlist_get (void)
 Auto-loading specific settings.\n\
 Configure various auto-load-specific variables such as\n\
 automatic loading of Python scripts."),
-		    &retval, "set auto-load ",
-		    1/*allow-unknown*/, &setlist);
+		    &retval, 1/*allow-unknown*/, &setlist);
 
   return &retval;
 }
@@ -1445,8 +1444,7 @@ auto_load_show_cmdlist_get (void)
 Show auto-loading specific settings.\n\
 Show configuration of various auto-load-specific variables such as\n\
 automatic loading of Python scripts."),
-			 &retval, "show auto-load ",
-			 0/*allow-unknown*/, &showlist);
+			 &retval, 0/*allow-unknown*/, &showlist);
 
   return &retval;
 }
@@ -1488,8 +1486,7 @@ auto_load_info_cmdlist_get (void)
 Print current status of auto-loaded files.\n\
 Print whether various files like Python scripts or .gdbinit files have been\n\
 found and/or loaded."),
-		    &retval, "info auto-load ",
-		    0/*allow-unknown*/, &infolist);
+		    &retval, 0/*allow-unknown*/, &infolist);
 
   return &retval;
 }
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 70a4d0e1690..0cfafcc4d3f 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -15758,7 +15758,7 @@ Give breakpoint numbers (separated by spaces) as arguments.\n\
 With no subcommand, breakpoints are enabled until you command otherwise.\n\
 This is used to cancel the effect of the \"disable\" command.\n\
 With a subcommand you can enable temporarily."),
-		  &enablelist, "enable ", 1, &cmdlist);
+		  &enablelist, 1, &cmdlist);
 
   add_com_alias ("en", "enable", class_breakpoint, 1);
 
@@ -15768,7 +15768,7 @@ Usage: enable breakpoints [BREAKPOINTNUM]...\n\
 Give breakpoint numbers (separated by spaces) as arguments.\n\
 This is used to cancel the effect of the \"disable\" command.\n\
 May be abbreviated to simply \"enable\"."),
-		   &enablebreaklist, "enable breakpoints ", 1, &enablelist);
+		   &enablebreaklist, 1, &enablelist);
 
   add_cmd ("once", no_class, enable_once_command, _("\
 Enable some breakpoints for one hit.\n\
@@ -15814,7 +15814,7 @@ Usage: disable [BREAKPOINTNUM]...\n\
 Arguments are breakpoint numbers with spaces in between.\n\
 To disable all breakpoints, give no argument.\n\
 A disabled breakpoint is not forgotten, but has no effect until re-enabled."),
-		  &disablelist, "disable ", 1, &cmdlist);
+		  &disablelist, 1, &cmdlist);
   add_com_alias ("dis", "disable", class_breakpoint, 1);
   add_com_alias ("disa", "disable", class_breakpoint, 1);
 
@@ -15834,7 +15834,7 @@ Arguments are breakpoint numbers with spaces in between.\n\
 To delete all breakpoints, give no argument.\n\
 \n\
 Also a prefix command for deletion of other GDB objects."),
-		  &deletelist, "delete ", 1, &cmdlist);
+		  &deletelist, 1, &cmdlist);
   add_com_alias ("d", "delete", class_breakpoint, 1);
   add_com_alias ("del", "delete", class_breakpoint, 1);
 
@@ -15870,7 +15870,7 @@ BREAK_ARGS_HELP ("break")));
     {
       add_abbrev_prefix_cmd ("stop", class_breakpoint, stop_command, _("\
 Break in function/address or break at a line in the current file."),
-			     &stoplist, "stop ", 1, &cmdlist);
+			     &stoplist, 1, &cmdlist);
       add_cmd ("in", class_breakpoint, stopin_command,
 	       _("Break in function or address."), &stoplist);
       add_cmd ("at", class_breakpoint, stopat_command,
@@ -15933,12 +15933,12 @@ breakpoint set."),
 
   add_basic_prefix_cmd ("catch", class_breakpoint, _("\
 Set catchpoints to catch events."),
-			&catch_cmdlist, "catch ",
+			&catch_cmdlist,
 			0/*allow-unknown*/, &cmdlist);
 
   add_basic_prefix_cmd ("tcatch", class_breakpoint, _("\
 Set temporary catchpoints to catch events."),
-			&tcatch_cmdlist, "tcatch ",
+			&tcatch_cmdlist,
 			0/*allow-unknown*/, &cmdlist);
 
   add_catch_command ("fork", _("Catch calls to fork."),
@@ -16110,7 +16110,7 @@ if TPNUM is omitted, passcount refers to the last tracepoint defined."));
 
   add_basic_prefix_cmd ("save", class_breakpoint,
 			_("Save breakpoint definitions as a script."),
-			&save_cmdlist, "save ",
+			&save_cmdlist,
 			0/*allow-unknown*/, &cmdlist);
 
   c = add_cmd ("breakpoints", class_breakpoint, save_breakpoints_command, _("\
@@ -16134,13 +16134,13 @@ Use the 'source' command in another debug session to restore them."),
 Breakpoint specific settings.\n\
 Configure various breakpoint-specific variables such as\n\
 pending breakpoint behavior."),
-			&breakpoint_set_cmdlist, "set breakpoint ",
+			&breakpoint_set_cmdlist,
 			0/*allow-unknown*/, &setlist);
   add_show_prefix_cmd ("breakpoint", class_maintenance, _("\
 Breakpoint specific settings.\n\
 Configure various breakpoint-specific variables such as\n\
 pending breakpoint behavior."),
-		       &breakpoint_show_cmdlist, "show breakpoint ",
+		       &breakpoint_show_cmdlist,
 		       0/*allow-unknown*/, &showlist);
 
   add_setshow_auto_boolean_cmd ("pending", no_class,
diff --git a/gdb/bsd-kvm.c b/gdb/bsd-kvm.c
index cd25e19a544..a555372ae3d 100644
--- a/gdb/bsd-kvm.c
+++ b/gdb/bsd-kvm.c
@@ -388,7 +388,7 @@ bsd_kvm_add_target (int (*supply_pcb)(struct regcache *, struct pcb *))
   
   add_prefix_cmd ("kvm", class_obscure, bsd_kvm_cmd, _("\
 Generic command for manipulating the kernel memory interface."),
-		  &bsd_kvm_cmdlist, "kvm ", 0, &cmdlist);
+		  &bsd_kvm_cmdlist, 0, &cmdlist);
 
 #ifndef HAVE_STRUCT_THREAD_TD_PCB
   add_cmd ("proc", class_obscure, bsd_kvm_proc_cmd,
diff --git a/gdb/btrace.c b/gdb/btrace.c
index c697f37f46c..b6b9bfcc46d 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -3438,29 +3438,26 @@ _initialize_btrace ()
 
   add_basic_prefix_cmd ("btrace", class_maintenance,
 			_("Branch tracing maintenance commands."),
-			&maint_btrace_cmdlist, "maintenance btrace ",
-			0, &maintenancelist);
+			&maint_btrace_cmdlist, 0, &maintenancelist);
 
   add_basic_prefix_cmd ("btrace", class_maintenance, _("\
 Set branch tracing specific variables."),
-			&maint_btrace_set_cmdlist, "maintenance set btrace ",
+			&maint_btrace_set_cmdlist,
 			0, &maintenance_set_cmdlist);
 
   add_basic_prefix_cmd ("pt", class_maintenance, _("\
 Set Intel Processor Trace specific variables."),
 			&maint_btrace_pt_set_cmdlist,
-			"maintenance set btrace pt ",
 			0, &maint_btrace_set_cmdlist);
 
   add_show_prefix_cmd ("btrace", class_maintenance, _("\
 Show branch tracing specific variables."),
-		       &maint_btrace_show_cmdlist, "maintenance show btrace ",
+		       &maint_btrace_show_cmdlist,
 		       0, &maintenance_show_cmdlist);
 
   add_show_prefix_cmd ("pt", class_maintenance, _("\
 Show Intel Processor Trace specific variables."),
 		       &maint_btrace_pt_show_cmdlist,
-		       "maintenance show btrace pt ",
 		       0, &maint_btrace_show_cmdlist);
 
   add_setshow_boolean_cmd ("skip-pad", class_maintenance,
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 68ef92eca4c..c971d7b7587 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -2343,10 +2343,10 @@ Show verbosity."), NULL,
 
   add_basic_prefix_cmd ("history", class_support, _("\
 Generic command for setting command history parameters."),
-			&sethistlist, "set history ", 0, &setlist);
+			&sethistlist, 0, &setlist);
   add_show_prefix_cmd ("history", class_support, _("\
 Generic command for showing command history parameters."),
-		       &showhistlist, "show history ", 0, &showlist);
+		       &showhistlist, 0, &showlist);
 
   add_setshow_boolean_cmd ("expansion", no_class, &history_expansion_p, _("\
 Set history expansion on command input."), _("\
@@ -2358,7 +2358,7 @@ Without an argument, history expansion is enabled."),
 
   add_prefix_cmd ("info", class_info, info_command, _("\
 Generic command for showing things about the program being debugged."),
-		  &infolist, "info ", 0, &cmdlist);
+		  &infolist, 0, &cmdlist);
   add_com_alias ("i", "info", class_info, 1);
   add_com_alias ("inf", "info", class_info, 1);
 
@@ -2367,7 +2367,7 @@ Generic command for showing things about the program being debugged."),
 
   c = add_show_prefix_cmd ("show", class_info, _("\
 Generic command for showing things about the debugger."),
-			   &showlist, "show ", 0, &cmdlist);
+			   &showlist, 0, &cmdlist);
   /* Another way to get at the same thing.  */
   add_alias_cmd ("set", c, class_info, 0, &infolist);
 
@@ -2440,11 +2440,11 @@ the previous command number shown."),
 
   add_basic_prefix_cmd ("debug", no_class,
 			_("Generic command for setting gdb debugging flags."),
-			&setdebuglist, "set debug ", 0, &setlist);
+			&setdebuglist, 0, &setlist);
 
   add_show_prefix_cmd ("debug", no_class,
 		       _("Generic command for showing gdb debugging flags."),
-		       &showdebuglist, "show debug ", 0, &showlist);
+		       &showdebuglist, 0, &showlist);
 
   c = add_com ("shell", class_support, shell_command, _("\
 Execute the rest of the line as a shell command.\n\
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 6e076c4519f..aea7302213a 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -306,7 +306,6 @@ add_alias_cmd (const char *name, cmd_list_element *old,
   c->func = old->func;
   c->function = old->function;
   c->prefixlist = old->prefixlist;
-  c->prefixname = old->prefixname;
   c->allow_unknown = old->allow_unknown;
   c->abbrev_flag = abbrev_flag;
   c->cmd_pointer = old;
@@ -371,13 +370,11 @@ struct cmd_list_element *
 add_prefix_cmd (const char *name, enum command_class theclass,
 		cmd_const_cfunc_ftype *fun,
 		const char *doc, struct cmd_list_element **prefixlist,
-		const char *prefixname, int allow_unknown,
-		struct cmd_list_element **list)
+		int allow_unknown, struct cmd_list_element **list)
 {
   struct cmd_list_element *c = add_cmd (name, theclass, fun, doc, list);
 
   c->prefixlist = prefixlist;
-  c->prefixname = prefixname;
   c->allow_unknown = allow_unknown;
 
   /* Now that prefix command C is defined, we need to set the prefix field
@@ -397,7 +394,8 @@ do_prefix_cmd (const char *args, int from_tty, struct cmd_list_element *c)
   while (c->cmd_pointer != nullptr)
     c = c->cmd_pointer;
 
-  help_list (*c->prefixlist, c->prefixname, all_commands, gdb_stdout);
+  help_list (*c->prefixlist, c->prefixname ().c_str (),
+	     all_commands, gdb_stdout);
 }
 
 /* See command.h.  */
@@ -405,11 +403,10 @@ do_prefix_cmd (const char *args, int from_tty, struct cmd_list_element *c)
 struct cmd_list_element *
 add_basic_prefix_cmd (const char *name, enum command_class theclass,
 		      const char *doc, struct cmd_list_element **prefixlist,
-		      const char *prefixname, int allow_unknown,
-		      struct cmd_list_element **list)
+		      int allow_unknown, struct cmd_list_element **list)
 {
   struct cmd_list_element *cmd = add_prefix_cmd (name, theclass, nullptr,
-						 doc, prefixlist, prefixname,
+						 doc, prefixlist,
 						 allow_unknown, list);
   set_cmd_sfunc (cmd, do_prefix_cmd);
   return cmd;
@@ -429,11 +426,10 @@ do_show_prefix_cmd (const char *args, int from_tty, struct cmd_list_element *c)
 struct cmd_list_element *
 add_show_prefix_cmd (const char *name, enum command_class theclass,
 		     const char *doc, struct cmd_list_element **prefixlist,
-		     const char *prefixname, int allow_unknown,
-		     struct cmd_list_element **list)
+		     int allow_unknown, struct cmd_list_element **list)
 {
   struct cmd_list_element *cmd = add_prefix_cmd (name, theclass, nullptr,
-						 doc, prefixlist, prefixname,
+						 doc, prefixlist,
 						 allow_unknown, list);
   set_cmd_sfunc (cmd, do_show_prefix_cmd);
   return cmd;
@@ -447,13 +443,12 @@ add_prefix_cmd_suppress_notification
 	       (const char *name, enum command_class theclass,
 		cmd_const_cfunc_ftype *fun,
 		const char *doc, struct cmd_list_element **prefixlist,
-		const char *prefixname, int allow_unknown,
-		struct cmd_list_element **list,
+		int allow_unknown, struct cmd_list_element **list,
 		int *suppress_notification)
 {
   struct cmd_list_element *element
     = add_prefix_cmd (name, theclass, fun, doc, prefixlist,
-		      prefixname, allow_unknown, list);
+		      allow_unknown, list);
   element->suppress_notification = suppress_notification;
   return element;
 }
@@ -464,13 +459,11 @@ struct cmd_list_element *
 add_abbrev_prefix_cmd (const char *name, enum command_class theclass,
 		       cmd_const_cfunc_ftype *fun, const char *doc,
 		       struct cmd_list_element **prefixlist,
-		       const char *prefixname,
 		       int allow_unknown, struct cmd_list_element **list)
 {
   struct cmd_list_element *c = add_cmd (name, theclass, fun, doc, list);
 
   c->prefixlist = prefixlist;
-  c->prefixname = prefixname;
   c->allow_unknown = allow_unknown;
   c->abbrev_flag = 1;
   return c;
@@ -1022,10 +1015,11 @@ add_com_suppress_notification (const char *name, enum command_class theclass,
 static void
 fput_command_name_styled (struct cmd_list_element *c, struct ui_file *stream)
 {
-  const char *prefixname
-    = c->prefix == nullptr ? "" : c->prefix->prefixname;
+  std::string prefixname
+    = c->prefix == nullptr ? "" : c->prefix->prefixname ();
 
-  fprintf_styled (stream, title_style.style (), "%s%s", prefixname, c->name);
+  fprintf_styled (stream, title_style.style (), "%s%s",
+		  prefixname.c_str (), c->name);
 }
 
 /* Print the definition of alias C using title style for alias
@@ -1180,7 +1174,8 @@ apropos_cmd (struct ui_file *stream,
 	{
 	  /* Recursively call ourselves on the subcommand list,
 	     passing the right prefix in.  */
-	  apropos_cmd (stream, *c->prefixlist, verbose, regex, c->prefixname);
+	  apropos_cmd (stream, *c->prefixlist, verbose, regex,
+		       c->prefixname ().c_str ());
 	}
     }
 }
@@ -1246,7 +1241,8 @@ help_cmd (const char *command, struct ui_file *stream)
 
   /* If this is a prefix command, print it's subcommands.  */
   if (c->prefixlist)
-    help_list (*c->prefixlist, c->prefixname, all_commands, stream);
+    help_list (*c->prefixlist, c->prefixname ().c_str (),
+	       all_commands, stream);
 
   /* If this is a class name, print all of the commands in the class.  */
   if (c->func == NULL)
@@ -1800,7 +1796,8 @@ lookup_cmd (const char **line, struct cmd_list_element *list,
 	 values.  */
       int local_allow_unknown = (last_list ? last_list->allow_unknown :
 				 allow_unknown);
-      const char *local_cmdtype = last_list ? last_list->prefixname : cmdtype;
+      std::string local_cmdtype
+	= last_list ? last_list->prefixname () : cmdtype;
       struct cmd_list_element *local_list =
 	(last_list ? *(last_list->prefixlist) : list);
 
@@ -1840,8 +1837,8 @@ lookup_cmd (const char **line, struct cmd_list_element *list,
 		    break;
 		  }
 	      }
-	  error (_("Ambiguous %scommand \"%s\": %s."), local_cmdtype,
-		 *line, ambbuf);
+	  error (_("Ambiguous %scommand \"%s\": %s."),
+		 local_cmdtype.c_str (), *line, ambbuf);
 	}
     }
   else
@@ -1855,7 +1852,7 @@ lookup_cmd (const char **line, struct cmd_list_element *list,
 	(*line)++;
 
       if (c->prefixlist && **line && !c->allow_unknown)
-	undef_cmd_error (c->prefixname, *line);
+	undef_cmd_error (c->prefixname ().c_str (), *line);
 
       /* Seems to be what he wants.  Return it.  */
       return c;
@@ -1928,7 +1925,7 @@ deprecated_cmd_warning (const char *text, struct cmd_list_element *list)
   /* Join command prefix (if any) and the command name.  */
   std::string tmp_cmd_str;
   if (cmd->prefix != nullptr)
-    tmp_cmd_str += std::string (cmd->prefix->prefixname);
+    tmp_cmd_str += cmd->prefix->prefixname ();
   tmp_cmd_str += std::string (cmd->name);
 
   /* Display the appropriate first line, this warns that the thing the user
@@ -1938,7 +1935,7 @@ deprecated_cmd_warning (const char *text, struct cmd_list_element *list)
       /* Join the alias prefix (if any) and the alias name.  */
       std::string tmp_alias_str;
       if (alias->prefix != nullptr)
-	tmp_alias_str += std::string (alias->prefix->prefixname);
+	tmp_alias_str += alias->prefix->prefixname ();
       tmp_alias_str += std::string (alias->name);
 
       if (cmd->cmd_deprecated)
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index e6907a95a84..ddcb2ea9578 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -71,6 +71,26 @@ struct cmd_list_element
 
     DISABLE_COPY_AND_ASSIGN (cmd_list_element);
 
+    /* For prefix commands, retun a string containing prefix commands to
+       get here: this one plus any others needed to get to it.  Ends in a
+       space.  It is used before the word "command" in describing the
+       commands reached through this prefix.
+
+       For non-prefix commands, an empty string is returned.  */
+    std::string prefixname ()
+      {
+	if (prefixlist == nullptr)
+	  /* Not a prefix command.  */
+	  return "";
+
+	std::string prefixname;
+	if (prefix != nullptr)
+	  prefixname = prefix->prefixname ();
+	prefixname += name;
+	prefixname += " ";
+	return prefixname;
+      }
+
 
     /* Points to next command in this list.  */
     struct cmd_list_element *next = nullptr;
@@ -186,13 +206,6 @@ struct cmd_list_element
        of the variable containing the list of subcommands.  */
     struct cmd_list_element **prefixlist = nullptr;
 
-    /* For prefix commands only:
-       String containing prefix commands to get here: this one
-       plus any others needed to get to it.  Should end in a space.
-       It is used before the word "command" in describing the
-       commands reached through this prefix.  */
-    const char *prefixname = nullptr;
-
     /* The prefix command of this command.  */
     struct cmd_list_element *prefix = nullptr;
 
diff --git a/gdb/cli/cli-dump.c b/gdb/cli/cli-dump.c
index ee3ce03be03..edd63fbd0d4 100644
--- a/gdb/cli/cli-dump.c
+++ b/gdb/cli/cli-dump.c
@@ -569,12 +569,12 @@ _initialize_cli_dump ()
 
   add_basic_prefix_cmd ("dump", class_vars,
 			_("Dump target code/data to a local file."),
-			&dump_cmdlist, "dump ",
+			&dump_cmdlist,
 			0/*allow-unknown*/,
 			&cmdlist);
   add_basic_prefix_cmd ("append", class_vars,
 			_("Append target code/data to a local file."),
-			&append_cmdlist, "append ",
+			&append_cmdlist,
 			0/*allow-unknown*/,
 			&cmdlist);
 
@@ -590,37 +590,37 @@ the specified FILE in raw target ordered bytes.");
 
   add_basic_prefix_cmd ("srec", all_commands,
 			_("Write target code/data to an srec file."),
-			&srec_cmdlist, "dump srec ", 
+			&srec_cmdlist,
 			0 /*allow-unknown*/, 
 			&dump_cmdlist);
 
   add_basic_prefix_cmd ("ihex", all_commands,
 			_("Write target code/data to an intel hex file."),
-			&ihex_cmdlist, "dump ihex ", 
+			&ihex_cmdlist,
 			0 /*allow-unknown*/, 
 			&dump_cmdlist);
 
   add_basic_prefix_cmd ("verilog", all_commands,
 			_("Write target code/data to a verilog hex file."),
-			&verilog_cmdlist, "dump verilog ",
+			&verilog_cmdlist,
 			0 /*allow-unknown*/,
 			&dump_cmdlist);
 
   add_basic_prefix_cmd ("tekhex", all_commands,
 			_("Write target code/data to a tekhex file."),
-			&tekhex_cmdlist, "dump tekhex ", 
+			&tekhex_cmdlist,
 			0 /*allow-unknown*/, 
 			&dump_cmdlist);
 
   add_basic_prefix_cmd ("binary", all_commands,
 			_("Write target code/data to a raw binary file."),
-			&binary_dump_cmdlist, "dump binary ", 
+			&binary_dump_cmdlist,
 			0 /*allow-unknown*/, 
 			&dump_cmdlist);
 
   add_basic_prefix_cmd ("binary", all_commands,
 			_("Append target code/data to a raw binary file."),
-			&binary_append_cmdlist, "append binary ", 
+			&binary_append_cmdlist,
 			0 /*allow-unknown*/, 
 			&append_cmdlist);
 
diff --git a/gdb/cli/cli-logging.c b/gdb/cli/cli-logging.c
index 9cde7b9e903..dfedd7599a2 100644
--- a/gdb/cli/cli-logging.c
+++ b/gdb/cli/cli-logging.c
@@ -173,10 +173,10 @@ _initialize_cli_logging ()
 
   add_basic_prefix_cmd ("logging", class_support,
 			_("Set logging options."), &set_logging_cmdlist,
-			"set logging ", 0, &setlist);
+			0, &setlist);
   add_show_prefix_cmd ("logging", class_support,
 		       _("Show logging options."), &show_logging_cmdlist,
-		       "show logging ", 0, &showlist);
+		       0, &showlist);
   add_setshow_boolean_cmd ("overwrite", class_support, &logging_overwrite, _("\
 Set whether logging overwrites or appends to the log file."), _("\
 Show whether logging overwrites or appends to the log file."), _("\
diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index e197852bc71..5841f545882 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -1477,7 +1477,6 @@ do_define_command (const char *comname, int from_tty,
   {
     struct cmd_list_element **c_prefixlist
       = c == nullptr ? nullptr : c->prefixlist;
-    const char *c_prefixname = c == nullptr ? nullptr : c->prefixname;
 
     newc = add_cmd (comname, class_user, user_defined_command,
 		    (c != nullptr && c->theclass == class_user)
@@ -1489,7 +1488,6 @@ do_define_command (const char *comname, int from_tty,
     if (c_prefixlist != nullptr)
       {
 	newc->prefixlist = c_prefixlist;
-	newc->prefixname = c_prefixname;
 	/* allow_unknown: see explanation in equivalent logic in
 	   define_prefix_command ().  */
 	newc->allow_unknown = newc->user_commands.get () != nullptr;
@@ -1628,7 +1626,6 @@ define_prefix_command (const char *comname, int from_tty)
      command.  */
   c->prefixlist = new struct cmd_list_element*;
   *(c->prefixlist) = nullptr;
-  c->prefixname = xstrprintf ("%s ", comfull);
   /* If the prefix command C is not a command, then it must be followed
      by known subcommands.  Otherwise, if C is also a normal command,
      it can be followed by C args that must not cause a 'subcommand'
@@ -1694,11 +1691,11 @@ show_user_1 (struct cmd_list_element *c, const char *prefix, const char *name,
 
   if (c->prefixlist != NULL)
     {
-      const char *prefixname = c->prefixname;
+      const std::string prefixname = c->prefixname ();
 
       for (c = *c->prefixlist; c != NULL; c = c->next)
 	if (c->theclass == class_user || c->prefixlist != NULL)
-	  show_user_1 (c, prefixname, c->name, gdb_stdout);
+	  show_user_1 (c, prefixname.c_str (), c->name, gdb_stdout);
     }
 
 }
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 1a8c6e2f62a..cc1e9b6cb24 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -747,7 +747,8 @@ cmd_show_list (struct cmd_list_element *list, int from_tty)
       if (list->prefixlist && list->cmd_pointer == nullptr)
 	{
 	  ui_out_emit_tuple optionlist_emitter (uiout, "optionlist");
-	  const char *new_prefix = strstr (list->prefixname, "show ") + 5;
+	  std::string prefixname = list->prefixname ();
+	  const char *new_prefix = strstr (prefixname.c_str (), "show ") + 5;
 
 	  if (uiout->is_mi_like_p ())
 	    uiout->field_string ("prefix", new_prefix);
@@ -759,10 +760,10 @@ cmd_show_list (struct cmd_list_element *list, int from_tty)
 
 	  {
 	    /* If we find a prefix, output it (with "show " skipped).  */
-	    const char *prefixname
-	      = (list->prefix == nullptr ? ""
-		 : strstr (list->prefix->prefixname, "show ") + 5);
-	    uiout->text (prefixname);
+	    std::string prefixname = list->prefixname ();
+	    prefixname = (list->prefix == nullptr ? ""
+			  : strstr (prefixname.c_str (), "show ") + 5);
+	    uiout->text (prefixname.c_str ());
 	  }
 	  uiout->field_string ("name", list->name);
 	  uiout->text (":  ");
diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c
index 8b4b6b24cda..25a1d888f86 100644
--- a/gdb/cli/cli-style.c
+++ b/gdb/cli/cli-style.c
@@ -225,13 +225,10 @@ cli_style_option::add_setshow_commands (enum command_class theclass,
 					struct cmd_list_element **show_list,
 					bool skip_intensity)
 {
-  m_set_prefix = std::string ("set style ") + m_name + " ";
-  m_show_prefix = std::string ("show style ") + m_name + " ";
-
   add_basic_prefix_cmd (m_name, no_class, prefix_doc, &m_set_list,
-			m_set_prefix.c_str (), 0, set_list);
+			0, set_list);
   add_show_prefix_cmd (m_name, no_class, prefix_doc, &m_show_list,
-		       m_show_prefix.c_str (), 0, show_list);
+		       0, show_list);
 
   add_setshow_enum_cmd ("foreground", theclass, cli_colors,
 			&m_foreground,
@@ -297,11 +294,11 @@ _initialize_cli_style ()
   add_basic_prefix_cmd ("style", no_class, _("\
 Style-specific settings.\n\
 Configure various style-related variables, such as colors"),
-		  &style_set_list, "set style ", 0, &setlist);
+		  &style_set_list, 0, &setlist);
   add_show_prefix_cmd ("style", no_class, _("\
 Style-specific settings.\n\
 Configure various style-related variables, such as colors"),
-		  &style_show_list, "show style ", 0, &showlist);
+		  &style_show_list, 0, &showlist);
 
   add_setshow_boolean_cmd ("enabled", no_class, &cli_styling, _("\
 Set whether CLI styling is enabled."), _("\
diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h
index 187e1d07ce7..78bc2cd6f1e 100644
--- a/gdb/cli/cli-style.h
+++ b/gdb/cli/cli-style.h
@@ -72,9 +72,6 @@ class cli_style_option
   /* The intensity.  */
   const char *m_intensity;
 
-  /* Storage for prefixes needed when registering the commands.  */
-  std::string m_show_prefix;
-  std::string m_set_prefix;
   /* Storage for command lists needed when registering
      subcommands.  */
   struct cmd_list_element *m_set_list = nullptr;
diff --git a/gdb/command.h b/gdb/command.h
index df40cbf7119..ca791cff809 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -214,7 +214,7 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class
 						cmd_const_cfunc_ftype *fun,
 						const char *,
 						struct cmd_list_element **,
-						const char *, int,
+						int,
 						struct cmd_list_element **);
 
 /* Like add_prefix_cmd, but sets the callback to a function that
@@ -222,20 +222,20 @@ extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class
 
 extern struct cmd_list_element *add_basic_prefix_cmd
   (const char *, enum command_class, const char *, struct cmd_list_element **,
-   const char *, int, struct cmd_list_element **);
+   int, struct cmd_list_element **);
 
 /* Like add_prefix_cmd, but useful for "show" prefixes.  This sets the
    callback to a function that simply calls cmd_show_list.  */
 
 extern struct cmd_list_element *add_show_prefix_cmd
   (const char *, enum command_class, const char *, struct cmd_list_element **,
-   const char *, int, struct cmd_list_element **);
+   int, struct cmd_list_element **);
 
 extern struct cmd_list_element *add_prefix_cmd_suppress_notification
 			(const char *name, enum command_class theclass,
 			 cmd_const_cfunc_ftype *fun,
 			 const char *doc, struct cmd_list_element **prefixlist,
-			 const char *prefixname, int allow_unknown,
+			 int allow_unknown,
 			 struct cmd_list_element **list,
 			 int *suppress_notification);
 
@@ -244,7 +244,7 @@ extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
 						       cmd_const_cfunc_ftype *fun,
 						       const char *,
 						       struct cmd_list_element
-						       **, const char *, int,
+						       **, int,
 						       struct cmd_list_element
 						       **);
 
diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c
index 4e72adc943d..04b40ea22d7 100644
--- a/gdb/compile/compile.c
+++ b/gdb/compile/compile.c
@@ -924,7 +924,7 @@ _initialize_compile ()
   compile_cmd_element = add_prefix_cmd ("compile", class_obscure,
 					compile_command, _("\
 Command to compile source code and inject it into the inferior."),
-		  &compile_command_list, "compile ", 1, &cmdlist);
+		  &compile_command_list, 1, &cmdlist);
   add_com_alias ("expression", "compile", class_obscure, 0);
 
   const auto compile_opts = make_compile_options_def_group (nullptr);
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 1abc3e22a72..657541d6886 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -2216,7 +2216,6 @@ _initialize_cp_support ()
   add_basic_prefix_cmd ("cplus", class_maintenance,
 			_("C++ maintenance commands."),
 			&maint_cplus_cmd_list,
-			"maintenance cplus ",
 			0, &maintenancelist);
   add_alias_cmd ("cp", "cplus",
 		 class_maintenance, 1,
diff --git a/gdb/dcache.c b/gdb/dcache.c
index 201505bf9e9..50b3854fe69 100644
--- a/gdb/dcache.c
+++ b/gdb/dcache.c
@@ -696,11 +696,10 @@ the contents of the given line."));
 
   add_basic_prefix_cmd ("dcache", class_obscure, _("\
 Use this command to set number of lines in dcache and line-size."),
-			&dcache_set_list, "set dcache ", /*allow_unknown*/0,
-			&setlist);
+			&dcache_set_list, /*allow_unknown*/0, &setlist);
   add_show_prefix_cmd ("dcache", class_obscure, _("\
 Show dcachesettings."),
-		       &dcache_show_list, "show dcache ", /*allow_unknown*/0,
+		       &dcache_show_list, /*allow_unknown*/0,
 		       &showlist);
 
   add_setshow_zuinteger_cmd ("line-size", class_obscure,
diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c
index ed8fd8e64e6..c3decf16ead 100644
--- a/gdb/dwarf2/index-cache.c
+++ b/gdb/dwarf2/index-cache.c
@@ -335,12 +335,12 @@ _initialize_index_cache ()
   add_basic_prefix_cmd ("index-cache", class_files,
 			_("Set index-cache options."),
 			&set_index_cache_prefix_list,
-			"set index-cache ", false, &setlist);
+			false, &setlist);
 
   /* show index-cache */
   add_prefix_cmd ("index-cache", class_files, show_index_cache_command,
 		  _("Show index-cache options."), &show_index_cache_prefix_list,
-		  "show index-cache ", false, &showlist);
+		  false, &showlist);
 
   /* set index-cache on */
   add_cmd ("on", class_files, set_index_cache_on_command,
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 309ff8331e7..c463441a481 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -25319,13 +25319,13 @@ _initialize_dwarf2_read ()
   add_basic_prefix_cmd ("dwarf", class_maintenance, _("\
 Set DWARF specific variables.\n\
 Configure DWARF variables such as the cache size."),
-			&set_dwarf_cmdlist, "maintenance set dwarf ",
+			&set_dwarf_cmdlist,
 			0/*allow-unknown*/, &maintenance_set_cmdlist);
 
   add_show_prefix_cmd ("dwarf", class_maintenance, _("\
 Show DWARF specific variables.\n\
 Show DWARF variables such as the cache size."),
-		       &show_dwarf_cmdlist, "maintenance show dwarf ",
+		       &show_dwarf_cmdlist,
 		       0/*allow-unknown*/, &maintenance_show_cmdlist);
 
   add_setshow_zinteger_cmd ("max-cache-age", class_obscure,
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 832910969ef..e0e31ef666b 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -1296,11 +1296,11 @@ _initialize_f_language ()
 
   add_basic_prefix_cmd ("fortran", no_class,
 			_("Prefix command for changing Fortran-specific settings."),
-			&set_fortran_list, "set fortran ", 0, &setlist);
+			&set_fortran_list, 0, &setlist);
 
   add_show_prefix_cmd ("fortran", no_class,
 		       _("Generic command for showing Fortran-specific settings."),
-		       &show_fortran_list, "show fortran ", 0, &showlist);
+		       &show_fortran_list, 0, &showlist);
 
   add_setshow_boolean_cmd ("repack-array-slices", class_vars,
 			   &repack_array_slices, _("\
diff --git a/gdb/frame.c b/gdb/frame.c
index 4578b1acab3..1c145a638ad 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -3185,12 +3185,12 @@ _initialize_frame ()
   add_basic_prefix_cmd ("backtrace", class_maintenance, _("\
 Set backtrace specific variables.\n\
 Configure backtrace variables such as the backtrace limit"),
-			&set_backtrace_cmdlist, "set backtrace ",
+			&set_backtrace_cmdlist,
 			0/*allow-unknown*/, &setlist);
   add_show_prefix_cmd ("backtrace", class_maintenance, _("\
 Show backtrace specific variables.\n\
 Show backtrace variables such as the backtrace limit."),
-		       &show_backtrace_cmdlist, "show backtrace ",
+		       &show_backtrace_cmdlist,
 		       0/*allow-unknown*/, &showlist);
 
   add_setshow_uinteger_cmd ("limit", class_obscure,
diff --git a/gdb/gnu-nat.c b/gdb/gnu-nat.c
index 409d141909b..3bc5a1c5bd6 100644
--- a/gdb/gnu-nat.c
+++ b/gdb/gnu-nat.c
@@ -3237,10 +3237,10 @@ Show whether exceptions in the inferior process will be trapped."),
 
   add_prefix_cmd ("task", no_class, set_task_cmd,
 		  _("Command prefix for setting task attributes."),
-		  &set_task_cmd_list, "set task ", 0, &setlist);
+		  &set_task_cmd_list, 0, &setlist);
   add_prefix_cmd ("task", no_class, show_task_cmd,
 		  _("Command prefix for showing task attributes."),
-		  &show_task_cmd_list, "show task ", 0, &showlist);
+		  &show_task_cmd_list, 0, &showlist);
 
   add_cmd ("pause", class_run, set_task_pause_cmd, _("\
 Set whether the task is suspended while gdb has control.\n\
@@ -3409,17 +3409,17 @@ add_thread_commands (void)
 {
   add_prefix_cmd ("thread", no_class, set_thread_cmd,
 		  _("Command prefix for setting thread properties."),
-		  &set_thread_cmd_list, "set thread ", 0, &setlist);
+		  &set_thread_cmd_list, 0, &setlist);
   add_prefix_cmd ("default", no_class, show_thread_cmd,
 		  _("Command prefix for setting default thread properties."),
-		  &set_thread_default_cmd_list, "set thread default ", 0,
+		  &set_thread_default_cmd_list, 0,
 		  &set_thread_cmd_list);
   add_prefix_cmd ("thread", no_class, set_thread_default_cmd,
 		  _("Command prefix for showing thread properties."),
-		  &show_thread_cmd_list, "show thread ", 0, &showlist);
+		  &show_thread_cmd_list, 0, &showlist);
   add_prefix_cmd ("default", no_class, show_thread_default_cmd,
 		  _("Command prefix for showing default thread properties."),
-		  &show_thread_default_cmd_list, "show thread default ", 0,
+		  &show_thread_default_cmd_list, 0,
 		  &show_thread_cmd_list);
 
   add_cmd ("pause", class_run, set_thread_pause_cmd, _("\
diff --git a/gdb/go32-nat.c b/gdb/go32-nat.c
index b18a62e9490..7f630e1259c 100644
--- a/gdb/go32-nat.c
+++ b/gdb/go32-nat.c
@@ -2099,7 +2099,7 @@ _initialize_go32_nat ()
 
   add_basic_prefix_cmd ("dos", class_info, _("\
 Print information specific to DJGPP (aka MS-DOS) debugging."),
-			&info_dos_cmdlist, "info dos ", 0, &infolist);
+			&info_dos_cmdlist, 0, &infolist);
 
   add_cmd ("sysinfo", class_info, go32_sysinfo, _("\
 Display information about the target system, including CPU, OS, DPMI, etc."),
diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c
index 68c4532b390..eab00d96c95 100644
--- a/gdb/guile/guile.c
+++ b/gdb/guile/guile.c
@@ -736,20 +736,17 @@ This command is only a placeholder.")
 
   add_basic_prefix_cmd ("guile", class_obscure,
 			_("Prefix command for Guile preference settings."),
-			&set_guile_list, "set guile ", 0,
-			&setlist);
+			&set_guile_list, 0, &setlist);
   add_alias_cmd ("gu", "guile", class_obscure, 1, &setlist);
 
   add_show_prefix_cmd ("guile", class_obscure,
 		       _("Prefix command for Guile preference settings."),
-		       &show_guile_list, "show guile ", 0,
-		       &showlist);
+		       &show_guile_list, 0, &showlist);
   add_alias_cmd ("gu", "guile", class_obscure, 1, &showlist);
 
   add_basic_prefix_cmd ("guile", class_obscure,
 			_("Prefix command for Guile info displays."),
-			&info_guile_list, "info guile ", 0,
-			&infolist);
+			&info_guile_list, 0, &infolist);
   add_info_alias ("gu", "guile", 1);
 
   /* The name "print-stack" is carried over from Python.
diff --git a/gdb/guile/scm-cmd.c b/gdb/guile/scm-cmd.c
index 1708d662b87..1f35d881a2d 100644
--- a/gdb/guile/scm-cmd.c
+++ b/gdb/guile/scm-cmd.c
@@ -766,7 +766,7 @@ gdbscm_register_command_x (SCM self)
 
 	  cmd = add_prefix_cmd (c_smob->cmd_name, c_smob->cmd_class,
 				NULL, c_smob->doc, &c_smob->sub_list,
-				c_smob->name, allow_unknown, cmd_list);
+				allow_unknown, cmd_list);
 	}
       else
 	{
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 6386716c3b8..af3081882e9 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -9083,14 +9083,14 @@ is \"default\"."),
 
   add_basic_prefix_cmd ("mpx", class_support, _("\
 Set Intel Memory Protection Extensions specific variables."),
-			&mpx_set_cmdlist, "set mpx ",
+			&mpx_set_cmdlist,
 			0 /* allow-unknown */, &setlist);
 
   /* Add "mpx" prefix for the show commands.  */
 
   add_show_prefix_cmd ("mpx", class_support, _("\
 Show Intel Memory Protection Extensions specific variables."),
-		       &mpx_show_cmdlist, "show mpx ",
+		       &mpx_show_cmdlist,
 		       0 /* allow-unknown */, &showlist);
 
   /* Add "bound" command for the show mpx commands list.  */
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 6f0ed952de6..04ce9801186 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -3075,7 +3075,7 @@ environment to be given to the program."), &showlist);
 
   add_basic_prefix_cmd ("unset", no_class,
 			_("Complement to certain \"set\" commands."),
-			&unsetlist, "unset ", 0, &cmdlist);
+			&unsetlist, 0, &cmdlist);
 
   c = add_cmd ("environment", class_run, unset_environment_command, _("\
 Cancel environment variable VAR for the program.\n\
@@ -3112,7 +3112,7 @@ needed."),
 
   add_prefix_cmd ("kill", class_run, kill_command,
 		  _("Kill execution of program being debugged."),
-		  &killlist, "kill ", 0, &cmdlist);
+		  &killlist, 0, &cmdlist);
 
   add_com ("attach", class_run, attach_command, _("\
 Attach to a process or file outside of GDB.\n\
@@ -3131,7 +3131,7 @@ to specify the program, and to load its symbol table."));
 Detach a process or file previously attached.\n\
 If a process, it is no longer traced, and it continues its execution.  If\n\
 you were debugging a file, the file is closed and gdb no longer accesses it."),
-		  &detachlist, "detach ", 0, &cmdlist);
+		  &detachlist, 0, &cmdlist);
 
   add_com ("disconnect", class_run, disconnect_command, _("\
 Disconnect from a target.\n\
@@ -3286,7 +3286,7 @@ in the named register groups."));
 		  _("\
 Show additional information about a process.\n\
 Specify any process id, or use the program being debugged by default."),
-		  &info_proc_cmdlist, "info proc ",
+		  &info_proc_cmdlist,
 		  1/*allow-unknown*/, &infolist);
 
   add_cmd ("mappings", class_info, info_proc_cmd_mappings, _("\
diff --git a/gdb/language.c b/gdb/language.c
index 6b4be71c3f2..86781a6dbc1 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -1186,13 +1186,13 @@ _initialize_language ()
 
   add_basic_prefix_cmd ("check", no_class,
 			_("Set the status of the type/range checker."),
-			&setchecklist, "set check ", 0, &setlist);
+			&setchecklist, 0, &setlist);
   add_alias_cmd ("c", "check", no_class, 1, &setlist);
   add_alias_cmd ("ch", "check", no_class, 1, &setlist);
 
   add_show_prefix_cmd ("check", no_class,
 		       _("Show the status of the type/range checker."),
-		       &showchecklist, "show check ", 0, &showlist);
+		       &showchecklist, 0, &showlist);
   add_alias_cmd ("c", "check", no_class, 1, &showlist);
   add_alias_cmd ("ch", "check", no_class, 1, &showlist);
 
diff --git a/gdb/macrocmd.c b/gdb/macrocmd.c
index d76792675b1..4e4a68d5c1d 100644
--- a/gdb/macrocmd.c
+++ b/gdb/macrocmd.c
@@ -455,7 +455,7 @@ _initialize_macrocmd ()
      the various commands for working with preprocessor macros.  */
   add_basic_prefix_cmd ("macro", class_info,
 			_("Prefix for commands dealing with C preprocessor macros."),
-			&macrolist, "macro ", 0, &cmdlist);
+			&macrolist, 0, &cmdlist);
 
   add_cmd ("expand", no_class, macro_expand_command, _("\
 Fully expand any C/C++ preprocessor macro invocations in EXPRESSION.\n\
diff --git a/gdb/maint-test-options.c b/gdb/maint-test-options.c
index 16ecd1dd7e5..b773e759bb7 100644
--- a/gdb/maint-test-options.c
+++ b/gdb/maint-test-options.c
@@ -422,8 +422,7 @@ _initialize_maint_test_options ()
 			_("\
 Generic command for testing the options infrastructure."),
 			&maintenance_test_options_list,
-			"maintenance test-options ", 0,
-			&maintenancelist);
+			0, &maintenancelist);
 
   const auto def_group = make_test_options_options_def_group (nullptr);
 
diff --git a/gdb/maint-test-settings.c b/gdb/maint-test-settings.c
index ca28fcf88aa..0ce88905518 100644
--- a/gdb/maint-test-settings.c
+++ b/gdb/maint-test-settings.c
@@ -89,7 +89,6 @@ _initialize_maint_test_settings ()
 			_("\
 Set GDB internal variables used for set/show command infrastructure testing."),
 			&maintenance_set_test_settings_list,
-			"maintenance set test-settings ",
 			0/*allow-unknown*/,
 			&maintenance_set_cmdlist);
 
@@ -97,7 +96,6 @@ Set GDB internal variables used for set/show command infrastructure testing."),
 		       _("\
 Show GDB internal variables used for set/show command infrastructure testing."),
 		       &maintenance_show_test_settings_list,
-		       "maintenance show test-settings ",
 		       0/*allow-unknown*/,
 		       &maintenance_show_cmdlist);
 
diff --git a/gdb/maint.c b/gdb/maint.c
index d718d707134..90c3d2ef5e2 100644
--- a/gdb/maint.c
+++ b/gdb/maint.c
@@ -1055,14 +1055,14 @@ _initialize_maint_cmds ()
 Commands for use by GDB maintainers.\n\
 Includes commands to dump specific internal GDB structures in\n\
 a human readable form, to cause GDB to deliberately dump core, etc."),
-			&maintenancelist, "maintenance ", 0,
+			&maintenancelist, 0,
 			&cmdlist);
 
   add_com_alias ("mt", "maintenance", class_maintenance, 1);
 
   add_basic_prefix_cmd ("info", class_maintenance, _("\
 Commands for showing internal info about the program being debugged."),
-			&maintenanceinfolist, "maintenance info ", 0,
+			&maintenanceinfolist, 0,
 			&maintenancelist);
   add_alias_cmd ("i", "info", class_maintenance, 1, &maintenancelist);
 
@@ -1080,25 +1080,25 @@ lists all sections from all object files, including shared libraries."),
 
   add_basic_prefix_cmd ("print", class_maintenance,
 			_("Maintenance command for printing GDB internal state."),
-			&maintenanceprintlist, "maintenance print ", 0,
+			&maintenanceprintlist, 0,
 			&maintenancelist);
 
   add_basic_prefix_cmd ("flush", class_maintenance,
 			_("Maintenance command for flushing GDB internal caches."),
-			&maintenanceflushlist, "maintenance flush ", 0,
+			&maintenanceflushlist, 0,
 			&maintenancelist);
 
   add_basic_prefix_cmd ("set", class_maintenance, _("\
 Set GDB internal variables used by the GDB maintainer.\n\
 Configure variables internal to GDB that aid in GDB's maintenance"),
-			&maintenance_set_cmdlist, "maintenance set ",
+			&maintenance_set_cmdlist,
 			0/*allow-unknown*/,
 			&maintenancelist);
 
   add_show_prefix_cmd ("show", class_maintenance, _("\
 Show GDB internal variables used by the GDB maintainer.\n\
 Configure variables internal to GDB that aid in GDB's maintenance"),
-		       &maintenance_show_cmdlist, "maintenance show ",
+		       &maintenance_show_cmdlist,
 		       0/*allow-unknown*/,
 		       &maintenancelist);
 
@@ -1144,12 +1144,12 @@ This command has been moved to \"demangle\"."),
 
   add_prefix_cmd ("per-command", class_maintenance, set_per_command_cmd, _("\
 Per-command statistics settings."),
-		    &per_command_setlist, "maintenance set per-command ",
+		    &per_command_setlist,
 		    1/*allow-unknown*/, &maintenance_set_cmdlist);
 
   add_show_prefix_cmd ("per-command", class_maintenance, _("\
 Show per-command statistics settings."),
-		       &per_command_showlist, "maintenance show per-command ",
+		       &per_command_showlist,
 		       0/*allow-unknown*/, &maintenance_show_cmdlist);
 
   add_setshow_boolean_cmd ("time", class_maintenance,
@@ -1217,7 +1217,7 @@ Takes an optional file parameter."),
 
   add_basic_prefix_cmd ("check", class_maintenance, _("\
 Commands for checking internal gdb state."),
-			&maintenancechecklist, "maintenance check ", 0,
+			&maintenancechecklist, 0,
 			&maintenancelist);
 
   add_cmd ("translate-address", class_maintenance,
diff --git a/gdb/memattr.c b/gdb/memattr.c
index 17de5839767..e1c3931c262 100644
--- a/gdb/memattr.c
+++ b/gdb/memattr.c
@@ -625,11 +625,11 @@ Do \"info mem\" to see current list of IDs."), &deletelist);
 
   add_basic_prefix_cmd ("mem", class_vars, _("\
 Memory regions settings."),
-			&mem_set_cmdlist, "set mem ",
+			&mem_set_cmdlist,
 			0/* allow-unknown */, &setlist);
   add_show_prefix_cmd ("mem", class_vars, _("\
 Memory regions settings."),
-		       &mem_show_cmdlist, "show mem  ",
+		       &mem_show_cmdlist,
 		       0/* allow-unknown */, &showlist);
 
   add_setshow_boolean_cmd ("inaccessible-by-default", no_class,
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index 61545aed86c..2fe83324738 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -8945,11 +8945,11 @@ _initialize_mips_tdep ()
   /* Add root prefix command for all "set mips"/"show mips" commands.  */
   add_basic_prefix_cmd ("mips", no_class,
 			_("Various MIPS specific commands."),
-			&setmipscmdlist, "set mips ", 0, &setlist);
+			&setmipscmdlist, 0, &setlist);
 
   add_show_prefix_cmd ("mips", no_class,
 		       _("Various MIPS specific commands."),
-		       &showmipscmdlist, "show mips ", 0, &showlist);
+		       &showmipscmdlist, 0, &showlist);
 
   /* Allow the user to override the ABI.  */
   add_setshow_enum_cmd ("abi", class_obscure, mips_abi_strings,
@@ -8990,7 +8990,7 @@ and is updated automatically from ELF file flags if available."),
 
   add_basic_prefix_cmd ("mipsfpu", class_support,
 			_("Set use of MIPS floating-point coprocessor."),
-			&mipsfpulist, "set mipsfpu ", 0, &setlist);
+			&mipsfpulist, 0, &setlist);
   add_cmd ("single", class_support, set_mipsfpu_single_command,
 	   _("Select single-precision MIPS floating-point coprocessor."),
 	   &mipsfpulist);
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index 2e56d28e9f4..b8431a7ccec 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -2882,7 +2882,7 @@ Use \"set variable\" for variables with names identical to set subcommands.\n\
 \n\
 With a subcommand, this command modifies parts of the gdb environment.\n\
 You can see these environment settings with the \"show\" command."),
-		  &setlist, "set ", 1, &cmdlist);
+		  &setlist, 1, &cmdlist);
   if (dbx_commands)
     add_com ("assign", class_vars, set_command, _("\
 Evaluate expression EXP and assign result to variable VAR.\n\
diff --git a/gdb/probe.c b/gdb/probe.c
index 0c4f22cf9f0..9eccf82f25d 100644
--- a/gdb/probe.c
+++ b/gdb/probe.c
@@ -788,8 +788,7 @@ If you specify TYPE, there may be additional arguments needed by the\n\
 subcommand.\n\
 If you do not specify any argument, or specify `all', then the command\n\
 will show information about all types of probes."),
-		    &info_probes_cmdlist, "info probes ",
-		    0/*allow-unknown*/, &infolist);
+		    &info_probes_cmdlist, 0/*allow-unknown*/, &infolist);
 
   return &info_probes_cmdlist;
 }
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index f4d3dcc3121..9833bf863b0 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -95,9 +95,6 @@ cmdpy_destroyer (struct cmd_list_element *self, void *context)
   /* Release our hold on the command object.  */
   gdbpy_ref<cmdpy_object> cmd ((cmdpy_object *) context);
   cmd->command = NULL;
-
-  /* We may have allocated the prefix name.  */
-  xfree ((char *) self->prefixname);
 }
 
 /* Called by gdb to invoke the command.  */
@@ -114,7 +111,7 @@ cmdpy_function (struct cmd_list_element *command,
     error (_("Invalid invocation of Python command object."));
   if (! PyObject_HasAttr ((PyObject *) obj, invoke_cst))
     {
-      if (obj->command->prefixname)
+      if (obj->command->prefixlist != nullptr)
 	{
 	  /* A prefix command does not need an invoke method.  */
 	  return;
@@ -438,11 +435,11 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
   int completetype = -1;
   char *docstring = NULL;
   struct cmd_list_element **cmd_list;
-  char *cmd_name, *pfx_name;
+  char *cmd_name;
   static const char *keywords[] = { "name", "command_class", "completer_class",
 				    "prefix", NULL };
-  PyObject *is_prefix = NULL;
-  int cmp;
+  PyObject *is_prefix_obj = NULL;
+  bool is_prefix = false;
 
   if (obj->command)
     {
@@ -455,7 +452,7 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 
   if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "si|iO",
 					keywords, &name, &cmdtype,
-					&completetype, &is_prefix))
+					&completetype, &is_prefix_obj))
     return -1;
 
   if (cmdtype != no_class && cmdtype != class_run
@@ -481,39 +478,17 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
   if (! cmd_name)
     return -1;
 
-  pfx_name = NULL;
-  if (is_prefix != NULL)
+  if (is_prefix_obj != NULL)
     {
-      cmp = PyObject_IsTrue (is_prefix);
-      if (cmp == 1)
-	{
-	  int i, out;
-	
-	  /* Make a normalized form of the command name.  */
-	  pfx_name = (char *) xmalloc (strlen (name) + 2);
-	
-	  i = 0;
-	  out = 0;
-	  while (name[i])
-	    {
-	      /* Skip whitespace.  */
-	      while (name[i] == ' ' || name[i] == '\t')
-		++i;
-	      /* Copy non-whitespace characters.  */
-	      while (name[i] && name[i] != ' ' && name[i] != '\t')
-		pfx_name[out++] = name[i++];
-	      /* Add a single space after each word -- including the final
-		 word.  */
-	      pfx_name[out++] = ' ';
-	    }
-	  pfx_name[out] = '\0';
-	}
-      else if (cmp < 0)
+      int cmp = PyObject_IsTrue (is_prefix_obj);
+       if (cmp < 0)
 	{
 	  xfree (cmd_name);
 	  return -1;
 	}
+       is_prefix = cmp > 0;
     }
+
   if (PyObject_HasAttr (self, gdbpy_doc_cst))
     {
       gdbpy_ref<> ds_obj (PyObject_GetAttr (self, gdbpy_doc_cst));
@@ -524,7 +499,6 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 	  if (docstring == NULL)
 	    {
 	      xfree (cmd_name);
-	      xfree (pfx_name);
 	      return -1;
 	    }
 	}
@@ -538,7 +512,7 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
     {
       struct cmd_list_element *cmd;
 
-      if (pfx_name)
+      if (is_prefix)
 	{
 	  int allow_unknown;
 
@@ -547,7 +521,7 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 	  allow_unknown = PyObject_HasAttr (self, invoke_cst);
 	  cmd = add_prefix_cmd (cmd_name, (enum command_class) cmdtype,
 				NULL, docstring, &obj->sub_list,
-				pfx_name, allow_unknown, cmd_list);
+				allow_unknown, cmd_list);
 	}
       else
 	cmd = add_cmd (cmd_name, (enum command_class) cmdtype,
@@ -571,7 +545,6 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
     {
       xfree (cmd_name);
       xfree (docstring);
-      xfree (pfx_name);
       gdbpy_convert_exception (except);
       return -1;
     }
diff --git a/gdb/python/python.c b/gdb/python/python.c
index fd760f6a98b..bc164d2b005 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1865,13 +1865,11 @@ This command is only a placeholder.")
   /* Add set/show python print-stack.  */
   add_basic_prefix_cmd ("python", no_class,
 			_("Prefix command for python preference settings."),
-			&user_show_python_list, "show python ", 0,
-			&showlist);
+			&user_show_python_list, 0, &showlist);
 
   add_show_prefix_cmd ("python", no_class,
 		       _("Prefix command for python preference settings."),
-		       &user_set_python_list, "set python ", 0,
-		       &setlist);
+		       &user_set_python_list, 0, &setlist);
 
   add_setshow_enum_cmd ("print-stack", no_class, python_excp_enums,
 			&gdbpy_should_print_stack, _("\
diff --git a/gdb/ravenscar-thread.c b/gdb/ravenscar-thread.c
index 2cc7bbc0ca3..8c4bb64c8b4 100644
--- a/gdb/ravenscar-thread.c
+++ b/gdb/ravenscar-thread.c
@@ -718,11 +718,11 @@ _initialize_ravenscar ()
 
   add_basic_prefix_cmd ("ravenscar", no_class,
 			_("Prefix command for changing Ravenscar-specific settings."),
-			&set_ravenscar_list, "set ravenscar ", 0, &setlist);
+			&set_ravenscar_list, 0, &setlist);
 
   add_show_prefix_cmd ("ravenscar", no_class,
 		       _("Prefix command for showing Ravenscar-specific settings."),
-		       &show_ravenscar_list, "show ravenscar ", 0, &showlist);
+		       &show_ravenscar_list, 0, &showlist);
 
   add_setshow_boolean_cmd ("task-switching", class_obscure,
 			   &ravenscar_task_support, _("\
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 81686ee867b..0b4270b288e 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -3107,7 +3107,7 @@ _initialize_record_btrace ()
 {
   add_prefix_cmd ("btrace", class_obscure, cmd_record_btrace_start,
 		  _("Start branch trace recording."), &record_btrace_cmdlist,
-		  "record btrace ", 0, &record_cmdlist);
+		  0, &record_cmdlist);
   add_alias_cmd ("b", "btrace", class_obscure, 1, &record_cmdlist);
 
   add_cmd ("bts", class_obscure, cmd_record_btrace_bts_start,
@@ -3127,11 +3127,11 @@ This format may not be available on all processors."),
 
   add_basic_prefix_cmd ("btrace", class_support,
 			_("Set record options."), &set_record_btrace_cmdlist,
-			"set record btrace ", 0, &set_record_cmdlist);
+			0, &set_record_cmdlist);
 
   add_show_prefix_cmd ("btrace", class_support,
 		       _("Show record options."), &show_record_btrace_cmdlist,
-		       "show record btrace ", 0, &show_record_cmdlist);
+		       0, &show_record_cmdlist);
 
   add_setshow_enum_cmd ("replay-memory-access", no_class,
 			replay_memory_access_types, &replay_memory_access, _("\
@@ -3159,7 +3159,7 @@ When GDB does not support that cpu, this option can be used to enable\n\
 workarounds for a similar cpu that GDB supports.\n\n\
 When set to \"none\", errata workarounds are disabled."),
 		  &set_record_btrace_cpu_cmdlist,
-		  "set record btrace cpu ", 1,
+		  1,
 		  &set_record_btrace_cmdlist);
 
   add_cmd ("auto", class_support, cmd_set_record_btrace_cpu_auto, _("\
@@ -3177,13 +3177,13 @@ Show the cpu to be used for trace decode."),
   add_basic_prefix_cmd ("bts", class_support,
 			_("Set record btrace bts options."),
 			&set_record_btrace_bts_cmdlist,
-			"set record btrace bts ", 0,
+			0,
 			&set_record_btrace_cmdlist);
 
   add_show_prefix_cmd ("bts", class_support,
 		       _("Show record btrace bts options."),
 		       &show_record_btrace_bts_cmdlist,
-		       "show record btrace bts ", 0,
+		       0,
 		       &show_record_btrace_cmdlist);
 
   add_setshow_uinteger_cmd ("buffer-size", no_class,
@@ -3203,13 +3203,13 @@ The trace buffer size may not be changed while recording."), NULL,
   add_basic_prefix_cmd ("pt", class_support,
 			_("Set record btrace pt options."),
 			&set_record_btrace_pt_cmdlist,
-			"set record btrace pt ", 0,
+			0,
 			&set_record_btrace_cmdlist);
 
   add_show_prefix_cmd ("pt", class_support,
 		       _("Show record btrace pt options."),
 		       &show_record_btrace_pt_cmdlist,
-		       "show record btrace pt ", 0,
+		       0,
 		       &show_record_btrace_cmdlist);
 
   add_setshow_uinteger_cmd ("buffer-size", no_class,
diff --git a/gdb/record-full.c b/gdb/record-full.c
index 22eaaa4bb1b..2f839f4da60 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -2790,7 +2790,7 @@ _initialize_record_full ()
 
   add_prefix_cmd ("full", class_obscure, cmd_record_full_start,
 		  _("Start full execution recording."), &record_full_cmdlist,
-		  "record full ", 0, &record_cmdlist);
+		  0, &record_cmdlist);
 
   c = add_cmd ("restore", class_obscure, cmd_record_full_restore,
 	       _("Restore the execution log from a file.\n\
@@ -2806,11 +2806,11 @@ Argument is filename.  File must be created with 'record save'."),
 
   add_basic_prefix_cmd ("full", class_support,
 			_("Set record options."), &set_record_full_cmdlist,
-			"set record full ", 0, &set_record_cmdlist);
+			0, &set_record_cmdlist);
 
   add_show_prefix_cmd ("full", class_support,
 		       _("Show record options."), &show_record_full_cmdlist,
-		       "show record full ", 0, &show_record_cmdlist);
+		       0, &show_record_cmdlist);
 
   /* Record instructions number limit command.  */
   add_setshow_boolean_cmd ("stop-at-limit", no_class,
diff --git a/gdb/record.c b/gdb/record.c
index cd541b56f43..3b0dfb0f19e 100644
--- a/gdb/record.c
+++ b/gdb/record.c
@@ -787,21 +787,21 @@ A size of \"unlimited\" means unlimited lines.  The default is 10."),
 
   c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
 		      _("Start recording."),
-		      &record_cmdlist, "record ", 0, &cmdlist);
+		      &record_cmdlist, 0, &cmdlist);
   set_cmd_completer (c, filename_completer);
 
   add_com_alias ("rec", "record", class_obscure, 1);
   add_basic_prefix_cmd ("record", class_support,
 			_("Set record options."), &set_record_cmdlist,
-			"set record ", 0, &setlist);
+			0, &setlist);
   add_alias_cmd ("rec", "record", class_obscure, 1, &setlist);
   add_show_prefix_cmd ("record", class_support,
 		       _("Show record options."), &show_record_cmdlist,
-		       "show record ", 0, &showlist);
+		       0, &showlist);
   add_alias_cmd ("rec", "record", class_obscure, 1, &showlist);
   add_prefix_cmd ("record", class_support, info_record_command,
 		  _("Info record options."), &info_record_cmdlist,
-		  "info record ", 0, &infolist);
+		  0, &infolist);
   add_alias_cmd ("rec", "record", class_obscure, 1, &infolist);
 
   c = add_cmd ("save", class_obscure, cmd_record_save,
@@ -825,7 +825,7 @@ Default filename is 'gdb_record.PROCESS_ID'."),
   add_prefix_cmd ("goto", class_obscure, cmd_record_goto, _("\
 Restore the program to its state at instruction number N.\n\
 Argument is instruction number, as shown by 'info record'."),
-		  &record_goto_cmdlist, "record goto ", 1, &record_cmdlist);
+		  &record_goto_cmdlist, 1, &record_cmdlist);
 
   add_cmd ("begin", class_obscure, cmd_record_goto_begin,
 	   _("Go to the beginning of the execution log."),
diff --git a/gdb/remote.c b/gdb/remote.c
index bc995edc538..9b19c5f74e9 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -14499,13 +14499,13 @@ _initialize_remote ()
 Remote protocol specific variables.\n\
 Configure various remote-protocol specific variables such as\n\
 the packets being used."),
-			&remote_set_cmdlist, "set remote ",
+			&remote_set_cmdlist,
 			0 /* allow-unknown */, &setlist);
   add_prefix_cmd ("remote", class_maintenance, show_remote_cmd, _("\
 Remote protocol specific variables.\n\
 Configure various remote-protocol specific variables such as\n\
 the packets being used."),
-		  &remote_show_cmdlist, "show remote ",
+		  &remote_show_cmdlist,
 		  0 /* allow-unknown */, &showlist);
 
   add_cmd ("compare-sections", class_obscure, compare_sections_command, _("\
@@ -14924,7 +14924,7 @@ packets."),
   add_basic_prefix_cmd ("remote", class_files, _("\
 Manipulate files on the remote system.\n\
 Transfer files to and from the remote target system."),
-			&remote_cmdlist, "remote ",
+			&remote_cmdlist,
 			0 /* allow-unknown */, &cmdlist);
 
   add_cmd ("put", class_files, remote_put_command,
diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
index 460746a9bfe..548c9543704 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -3847,12 +3847,12 @@ _initialize_riscv_tdep ()
      riscv" commands.  */
   add_basic_prefix_cmd ("riscv", no_class,
 			_("RISC-V specific debug commands."),
-			&setdebugriscvcmdlist, "set debug riscv ", 0,
+			&setdebugriscvcmdlist, 0,
 			&setdebuglist);
 
   add_show_prefix_cmd ("riscv", no_class,
 		       _("RISC-V specific debug commands."),
-		       &showdebugriscvcmdlist, "show debug riscv ", 0,
+		       &showdebugriscvcmdlist, 0,
 		       &showdebuglist);
 
   add_setshow_zuinteger_cmd ("breakpoints", class_maintenance,
@@ -3898,11 +3898,11 @@ initialisation process."),
   /* Add root prefix command for all "set riscv" and "show riscv" commands.  */
   add_basic_prefix_cmd ("riscv", no_class,
 			_("RISC-V specific commands."),
-			&setriscvcmdlist, "set riscv ", 0, &setlist);
+			&setriscvcmdlist, 0, &setlist);
 
   add_show_prefix_cmd ("riscv", no_class,
 		       _("RISC-V specific commands."),
-		       &showriscvcmdlist, "show riscv ", 0, &showlist);
+		       &showriscvcmdlist, 0, &showlist);
 
 
   use_compressed_breakpoints = AUTO_BOOLEAN_AUTO;
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index b09f63137dc..d64de7925c8 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -7328,11 +7328,11 @@ _initialize_rs6000_tdep ()
      commands.  */
   add_basic_prefix_cmd ("powerpc", no_class,
 			_("Various PowerPC-specific commands."),
-			&setpowerpccmdlist, "set powerpc ", 0, &setlist);
+			&setpowerpccmdlist, 0, &setlist);
 
   add_show_prefix_cmd ("powerpc", no_class,
 		       _("Various PowerPC-specific commands."),
-		       &showpowerpccmdlist, "show powerpc ", 0, &showlist);
+		       &showpowerpccmdlist, 0, &showlist);
 
   /* Add a command to allow the user to force the ABI.  */
   add_setshow_auto_boolean_cmd ("soft-float", class_support,
diff --git a/gdb/ser-tcp.c b/gdb/ser-tcp.c
index e79c9e6573d..2519caa505c 100644
--- a/gdb/ser-tcp.c
+++ b/gdb/ser-tcp.c
@@ -469,12 +469,12 @@ _initialize_ser_tcp ()
   add_basic_prefix_cmd ("tcp", class_maintenance, _("\
 TCP protocol specific variables.\n\
 Configure variables specific to remote TCP connections."),
-			&tcp_set_cmdlist, "set tcp ",
+			&tcp_set_cmdlist,
 			0 /* allow-unknown */, &setlist);
   add_show_prefix_cmd ("tcp", class_maintenance, _("\
 TCP protocol specific variables.\n\
 Configure variables specific to remote TCP connections."),
-		       &tcp_show_cmdlist, "show tcp ",
+		       &tcp_show_cmdlist,
 		       0 /* allow-unknown */, &showlist);
 
   add_setshow_boolean_cmd ("auto-retry", class_obscure,
diff --git a/gdb/serial.c b/gdb/serial.c
index 1854721d7ed..719cd66c08c 100644
--- a/gdb/serial.c
+++ b/gdb/serial.c
@@ -671,13 +671,13 @@ Use <CR>~. or <CR>~^D to break out."));
 
   add_basic_prefix_cmd ("serial", class_maintenance, _("\
 Set default serial/parallel port configuration."),
-			&serial_set_cmdlist, "set serial ",
+			&serial_set_cmdlist,
 			0/*allow-unknown*/,
 			&setlist);
 
   add_show_prefix_cmd ("serial", class_maintenance, _("\
 Show default serial/parallel port configuration."),
-		       &serial_show_cmdlist, "show serial ",
+		       &serial_show_cmdlist,
 		       0/*allow-unknown*/,
 		       &showlist);
 
diff --git a/gdb/sh-tdep.c b/gdb/sh-tdep.c
index ecd73c17217..3f5688dd467 100644
--- a/gdb/sh-tdep.c
+++ b/gdb/sh-tdep.c
@@ -2415,9 +2415,9 @@ _initialize_sh_tdep ()
   gdbarch_register (bfd_arch_sh, sh_gdbarch_init, NULL);
 
   add_basic_prefix_cmd ("sh", no_class, "SH specific commands.",
-			&setshcmdlist, "set sh ", 0, &setlist);
+			&setshcmdlist, 0, &setlist);
   add_show_prefix_cmd ("sh", no_class, "SH specific commands.",
-		       &showshcmdlist, "show sh ", 0, &showlist);
+		       &showshcmdlist, 0, &showlist);
   
   add_setshow_enum_cmd ("calling-convention", class_vars, sh_cc_enum,
 			&sh_active_calling_convention,
diff --git a/gdb/skip.c b/gdb/skip.c
index eeb5715546e..4d24088f247 100644
--- a/gdb/skip.c
+++ b/gdb/skip.c
@@ -676,7 +676,7 @@ FILE-SPEC is one of:\n\
 FUNCTION-SPEC is one of:\n\
        -fu|-function FUNCTION-NAME\n\
        -rfu|-rfunction FUNCTION-NAME-REGULAR-EXPRESSION"),
-		  &skiplist, "skip ", 1, &cmdlist);
+		  &skiplist, 1, &cmdlist);
 
   c = add_cmd ("file", class_breakpoint, skip_file_command, _("\
 Ignore a file while stepping.\n\
diff --git a/gdb/sparc64-tdep.c b/gdb/sparc64-tdep.c
index c4d5ab21b2f..aebd5b93bb7 100644
--- a/gdb/sparc64-tdep.c
+++ b/gdb/sparc64-tdep.c
@@ -532,7 +532,7 @@ _initialize_sparc64_adi_tdep ()
 {
   add_basic_prefix_cmd ("adi", class_support,
 			_("ADI version related commands."),
-			&sparc64adilist, "adi ", 0, &cmdlist);
+			&sparc64adilist, 0, &cmdlist);
   add_cmd ("examine", class_support, adi_examine_command,
 	   _("Examine ADI versions."), &sparc64adilist);
   add_alias_cmd ("x", "examine", no_class, 1, &sparc64adilist);
diff --git a/gdb/stack.c b/gdb/stack.c
index bce6a2f42f8..7856a5a8176 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -3343,7 +3343,7 @@ This is useful in command scripts."));
 Select and print a stack frame.\n\
 With no argument, print the selected stack frame.  (See also \"info frame\").\n\
 A single numerical argument specifies the frame to select."),
-		  &frame_cmd_list, "frame ", 1, &cmdlist);
+		  &frame_cmd_list, 1, &cmdlist);
   add_com_alias ("f", "frame", class_stack, 1);
 
 #define FRAME_APPLY_OPTION_HELP "\
@@ -3367,7 +3367,7 @@ With a negative COUNT argument, applies the command on outermost -COUNT frames.\
 
   cmd = add_prefix_cmd ("apply", class_stack, frame_apply_command,
 			frame_apply_cmd_help.c_str (),
-			&frame_apply_cmd_list, "frame apply ", 1,
+			&frame_apply_cmd_list, 1,
 			&frame_cmd_list);
   set_cmd_completer_handle_brkchars (cmd, frame_apply_cmd_completer);
 
@@ -3440,7 +3440,7 @@ Usage: frame level LEVEL"),
 		      &select_frame_cmd.base_command, _("\
 Select a stack frame without printing anything.\n\
 A single numerical argument specifies the frame to select."),
-		      &select_frame_cmd_list, "select-frame ", 1, &cmdlist,
+		      &select_frame_cmd_list, 1, &cmdlist,
 		      &cli_suppress_notification.user_selected_context);
 
   add_cmd_suppress_notification ("address", class_stack,
@@ -3515,7 +3515,7 @@ With a negative COUNT, print outermost -COUNT frames."),
 With no arguments, displays information about the currently selected stack\n\
 frame.  Alternatively a frame specification may be provided (See \"frame\")\n\
 the information is then printed about the specified frame."),
-		  &info_frame_cmd_list, "info frame ", 1, &infolist);
+		  &info_frame_cmd_list, 1, &infolist);
   add_info_alias ("f", "frame", 1);
 
   add_cmd ("address", class_stack, &info_frame_cmd.address,
diff --git a/gdb/symfile.c b/gdb/symfile.c
index ec5d34ac783..30c20615f77 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -3872,7 +3872,7 @@ on its own."), &cmdlist);
 
   add_basic_prefix_cmd ("overlay", class_support,
 			_("Commands for debugging overlays."), &overlaylist,
-			"overlay ", 0, &cmdlist);
+			0, &cmdlist);
 
   add_com_alias ("ovly", "overlay", class_support, 1);
   add_com_alias ("ov", "overlay", class_support, 1);
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 7ffb52a9435..028c3c5c7a4 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -6832,8 +6832,7 @@ Options:\n\
 
   add_basic_prefix_cmd ("module", class_info, _("\
 Print information about modules."),
-			&info_module_cmdlist, "info module ",
-			0, &infolist);
+			&info_module_cmdlist, 0, &infolist);
 
   c = add_cmd ("functions", class_info, info_module_functions_command, _("\
 Display functions arranged by modules.\n\
diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c
index 248edd51bd1..c46464a46a7 100644
--- a/gdb/target-descriptions.c
+++ b/gdb/target-descriptions.c
@@ -1970,15 +1970,15 @@ _initialize_target_descriptions ()
 
   add_basic_prefix_cmd ("tdesc", class_maintenance, _("\
 Set target description specific variables."),
-			&tdesc_set_cmdlist, "set tdesc ",
+			&tdesc_set_cmdlist,
 			0 /* allow-unknown */, &setlist);
   add_show_prefix_cmd ("tdesc", class_maintenance, _("\
 Show target description specific variables."),
-		       &tdesc_show_cmdlist, "show tdesc ",
+		       &tdesc_show_cmdlist,
 		       0 /* allow-unknown */, &showlist);
   add_basic_prefix_cmd ("tdesc", class_maintenance, _("\
 Unset target description specific variables."),
-			&tdesc_unset_cmdlist, "unset tdesc ",
+			&tdesc_unset_cmdlist,
 			0 /* allow-unknown */, &unsetlist);
 
   add_setshow_filename_cmd ("filename", class_obscure,
diff --git a/gdb/target.c b/gdb/target.c
index 3a03a0ad530..4937b3b3f69 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -255,7 +255,7 @@ The first argument is the type or protocol of the target machine.\n\
 Remaining arguments are interpreted by the target protocol.  For more\n\
 information on the arguments for a particular protocol, type\n\
 `help target ' followed by the protocol name."),
-			  &targetlist, "target ", 0, &cmdlist);
+			  &targetlist, 0, &cmdlist);
   c = add_cmd (t.shortname, no_class, t.doc, &targetlist);
   set_cmd_context (c, (void *) &t);
   set_cmd_sfunc (c, open_target);
diff --git a/gdb/thread.c b/gdb/thread.c
index 899c2116470..92c1209ae20 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -2132,7 +2132,7 @@ Options:\n\
   add_prefix_cmd ("thread", class_run, thread_command, _("\
 Use this command to switch between threads.\n\
 The new thread ID must be currently known."),
-		  &thread_cmd_list, "thread ", 1, &cmdlist);
+		  &thread_cmd_list, 1, &cmdlist);
 
 #define THREAD_APPLY_OPTION_HELP "\
 Prints per-inferior thread number and target system's thread id\n\
@@ -2155,7 +2155,7 @@ THREAD_APPLY_OPTION_HELP),
 
   c = add_prefix_cmd ("apply", class_run, thread_apply_command,
 		      thread_apply_help.c_str (),
-		      &thread_apply_list, "thread apply ", 1,
+		      &thread_apply_list, 1,
 		      &thread_cmd_list);
   set_cmd_completer_handle_brkchars (c, thread_apply_command_completer);
 
diff --git a/gdb/top.c b/gdb/top.c
index 08c47422514..685f9cbb271 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -645,10 +645,14 @@ execute_command (const char *p, int from_tty)
 	   that can be followed by its args), report the list of
 	   subcommands.  */
 	{
+	  std::string prefixname = c->prefixname ();
+          std::string prefixname_no_space
+	    = prefixname.substr (0, prefixname.length () - 1);
 	  printf_unfiltered
-	    ("\"%.*s\" must be followed by the name of a subcommand.\n",
-	     (int) strlen (c->prefixname) - 1, c->prefixname);
-	  help_list (*c->prefixlist, c->prefixname, all_commands, gdb_stdout);
+	    ("\"%s\" must be followed by the name of a subcommand.\n",
+	     prefixname_no_space.c_str ());
+	  help_list (*c->prefixlist, prefixname.c_str (), all_commands,
+		     gdb_stdout);
 	}
       else if (c->type == set_cmd)
 	do_set_command (arg, from_tty, c);
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index 0cbd2c68224..edae56362f8 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -4027,7 +4027,7 @@ List target static tracepoints markers."));
   add_prefix_cmd ("tfind", class_trace, tfind_command, _("\
 Select a trace frame.\n\
 No argument means forward by one frame; '-' means backward by one frame."),
-		  &tfindlist, "tfind ", 1, &cmdlist);
+		  &tfindlist, 1, &cmdlist);
 
   add_cmd ("outside", class_trace, tfind_outside_command, _("\
 Select a trace frame whose PC is outside the given range (exclusive).\n\
diff --git a/gdb/tui/tui-layout.c b/gdb/tui/tui-layout.c
index d6e299b00f1..a6aaf743496 100644
--- a/gdb/tui/tui-layout.c
+++ b/gdb/tui/tui-layout.c
@@ -1034,7 +1034,7 @@ _initialize_tui_layout ()
   add_basic_prefix_cmd ("layout", class_tui, _("\
 Change the layout of windows.\n\
 Usage: layout prev | next | LAYOUT-NAME"),
-			&layout_list, "layout ", 0, &cmdlist);
+			&layout_list, 0, &cmdlist);
 
   add_cmd ("next", class_tui, tui_next_layout_command,
 	   _("Apply the next TUI layout."),
diff --git a/gdb/tui/tui-win.c b/gdb/tui/tui-win.c
index c73d129d1c7..7f33b94902a 100644
--- a/gdb/tui/tui-win.c
+++ b/gdb/tui/tui-win.c
@@ -306,7 +306,7 @@ tui_get_cmd_list (void)
   if (tuilist == 0)
     add_basic_prefix_cmd ("tui", class_tui,
 			  _("Text User Interface commands."),
-			  &tuilist, "tui ", 0, &cmdlist);
+			  &tuilist, 0, &cmdlist);
   return &tuilist;
 }
 
@@ -996,12 +996,10 @@ _initialize_tui_win ()
      They will appear in the help list in the reverse of this order.  */
   add_basic_prefix_cmd ("tui", class_tui,
 			_("TUI configuration variables."),
-			&tui_setlist, "set tui ",
-			0 /* allow-unknown */, &setlist);
+			&tui_setlist, 0 /* allow-unknown */, &setlist);
   add_show_prefix_cmd ("tui", class_tui,
 		       _("TUI configuration variables."),
-		       &tui_showlist, "show tui ",
-		       0 /* allow-unknown */, &showlist);
+		       &tui_showlist, 0 /* allow-unknown */, &showlist);
 
   add_com ("refresh", class_tui, tui_refresh_all_command,
 	   _("Refresh the terminal display."));
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index 1af1d173a61..e34020cdf8e 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -794,12 +794,10 @@ Only one level of typedefs is unrolled.  See also \"ptype\"."));
 
   add_show_prefix_cmd ("type", no_class,
 		       _("Generic command for showing type-printing settings."),
-		       &showprinttypelist, "show print type ", 0,
-		       &showprintlist);
+		       &showprinttypelist, 0, &showprintlist);
   add_basic_prefix_cmd ("type", no_class,
 			_("Generic command for setting how types print."),
-			&setprinttypelist, "set print type ", 0,
-			&setprintlist);
+			&setprinttypelist, 0, &setprintlist);
 
   add_setshow_boolean_cmd ("methods", no_class, &print_methods,
 			   _("\
diff --git a/gdb/unittests/command-def-selftests.c b/gdb/unittests/command-def-selftests.c
index e6aadf3124f..040e22b9390 100644
--- a/gdb/unittests/command-def-selftests.c
+++ b/gdb/unittests/command-def-selftests.c
@@ -87,7 +87,7 @@ check_doc (struct cmd_list_element *commandlist, const char *prefix)
 	{
 	  /* Recursively call ourselves on the subcommand list,
 	     passing the right prefix in.  */
-	  check_doc (*c->prefixlist, c->prefixname);
+	  check_doc (*c->prefixlist, c->prefixname ().c_str ());
 	}
     }
 }
@@ -159,7 +159,7 @@ traverse_command_structure (struct cmd_list_element **list,
 	{
 	  /* Recursively call ourselves on the subcommand list,
 	     passing the right prefix in.  */
-	  traverse_command_structure (c->prefixlist, c->prefixname);
+	  traverse_command_structure (c->prefixlist, c->prefixname ().c_str ());
 	}
       if (prefixcmd != c->prefix
 	  || (prefixcmd == nullptr && *list != cmdlist))
diff --git a/gdb/utils.c b/gdb/utils.c
index b9f8997ec80..bb9472e0c9d 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -506,14 +506,10 @@ add_internal_problem_command (struct internal_problem *problem)
 
   add_basic_prefix_cmd (problem->name, class_maintenance, set_doc,
 			set_cmd_list,
-			concat ("maintenance set ", problem->name, " ",
-				(char *) NULL),
 			0/*allow-unknown*/, &maintenance_set_cmdlist);
 
   add_show_prefix_cmd (problem->name, class_maintenance, show_doc,
 		       show_cmd_list,
-		       concat ("maintenance show ", problem->name, " ",
-			       (char *) NULL),
 		       0/*allow-unknown*/, &maintenance_show_cmdlist);
 
   if (problem->user_settable_should_quit)
diff --git a/gdb/valprint.c b/gdb/valprint.c
index 2c3541e6686..e89d4cc2b72 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -3122,28 +3122,26 @@ _initialize_valprint ()
 
   add_basic_prefix_cmd ("print", no_class,
 			_("Generic command for setting how things print."),
-			&setprintlist, "set print ", 0, &setlist);
+			&setprintlist, 0, &setlist);
   add_alias_cmd ("p", "print", no_class, 1, &setlist);
   /* Prefer set print to set prompt.  */
   add_alias_cmd ("pr", "print", no_class, 1, &setlist);
 
   add_show_prefix_cmd ("print", no_class,
 		       _("Generic command for showing print settings."),
-		       &showprintlist, "show print ", 0, &showlist);
+		       &showprintlist, 0, &showlist);
   add_alias_cmd ("p", "print", no_class, 1, &showlist);
   add_alias_cmd ("pr", "print", no_class, 1, &showlist);
 
   cmd = add_basic_prefix_cmd ("raw", no_class,
 			      _("\
 Generic command for setting what things to print in \"raw\" mode."),
-			      &setprintrawlist, "set print raw ", 0,
-			      &setprintlist);
+			      &setprintrawlist, 0, &setprintlist);
   deprecate_cmd (cmd, nullptr);
 
   cmd = add_show_prefix_cmd ("raw", no_class,
 			     _("Generic command for showing \"print raw\" settings."),
-			     &showprintrawlist, "show print raw ", 0,
-			     &showprintlist);
+			     &showprintrawlist, 0, &showprintlist);
   deprecate_cmd (cmd, nullptr);
 
   gdb::option::add_setshow_cmds_for_options
diff --git a/gdb/value.c b/gdb/value.c
index 6f4ee545311..24173c66e1c 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -4246,7 +4246,7 @@ VARIABLE is already initialized."));
 
   add_prefix_cmd ("function", no_class, function_command, _("\
 Placeholder command for showing help on convenience functions."),
-		  &functionlist, "function ", 0, &cmdlist);
+		  &functionlist, 0, &cmdlist);
 
   add_internal_function ("_isvoid", _("\
 Check whether an expression is void.\n\
diff --git a/gdb/windows-tdep.c b/gdb/windows-tdep.c
index a06b44822d4..a722028d83f 100644
--- a/gdb/windows-tdep.c
+++ b/gdb/windows-tdep.c
@@ -616,7 +616,7 @@ init_w32_command_list (void)
       add_basic_prefix_cmd
 	("w32", class_info,
 	 _("Print information specific to Win32 debugging."),
-	 &info_w32_cmdlist, "info w32 ", 0, &infolist);
+	 &info_w32_cmdlist, 0, &infolist);
       w32_prefix_command_valid = 1;
     }
 }
-- 
2.28.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v2 5/5] gdb: Add support for renaming commands
  2021-01-25 11:26 ` [PATCH v2 0/5] Add support for command renaming Marco Barisione
                     ` (3 preceding siblings ...)
  2021-01-25 11:26   ` [PATCH v2 4/5] gdb: generate the prefix name for prefix commands on demand Marco Barisione
@ 2021-01-25 11:26   ` Marco Barisione
  2021-03-23 18:45     ` Simon Marchi
  2021-02-08 17:53   ` [PING] [PATCH v2 0/5] Add support for command renaming Marco Barisione
  5 siblings, 1 reply; 38+ messages in thread
From: Marco Barisione @ 2021-01-25 11:26 UTC (permalink / raw)
  To: gdb-patches

This patch adds:
* A "rename" command.
* A "-rename-existing-to" option for the "define" command.
* A "rename_existing_to" optional argument to gdb.Command.__init__
  which matches the behaviour of "define -rename-existing-to".

The goal of this is to allow users to build on top of existing commands
without losing their original implementation.
Something similar could be achieved through hooks but they are limited:
* Hooks cannot prevent the command from being executed without printing
  any error.
* Hooks don't get passed the arguments passed to the command.
* Post hooks can't know if the command failed.
* Hooks cannot be defined in Python.

gdb/ChangeLog:

	* NEWS: Add items for the new rename command, the new
	-rename-existing-to option for define and the new
	rename_existing_to argument for gdb.Command.__init__.
	* cli/cli-decode.c (delete_cmd): Rename to disconnect_cmd.
	(disconnect_cmd): Disconnect a command from other commands and
	lists rather than deleting the command.
	(delete_cmd_by_name): Add.
	(update_prefix_links): Add.
	(update_cmd): Add.
	(do_add_cmd): Add ability to rename existing commands rather
	than redefining them.
	(add_cmd): Ditto.
	(add_prefix_cmd): Ditto.
	(add_alias_cmd): Update to use disconnect_cmd.
	(break_cmd_relationships): Add.
	(rename_hook): Add.
	(user_defined_command): Move from cli/cli-script.c.
	(rename_cmd): Add.
	* cli/cli-decode.h (struct cmd_list_element): Call the destroyer
	function from the destructor.
	* cli/cli-script.c (do_define_command): Add ability to rename
	existing commands rather than redefining them.
	(user_defined_command): Move to cli/cli-decode.c
	(check_command_redefinition): Add.
	(enum cmd_hook_type): Move from do_define_command to the global
	scope.
	(get_hook_type): Add.
	(HOOK_STRING): Make a variable in get_hook_type.
	(HOOK_LEN): Ditto.
	(HOOK_POST_STRING): Ditto.
	(HOOK_POST_LEN): Ditto.
	(struct define_cmd_opts): Add.
	(make_define_cmd_options_def_group): Add.
	(define_command): Add -rename-existing-to option.
	(do_rename_command): Add.
	(rename_command): Add.
	(_initialize_cli_script): Add option parsing for the define
	command and add the rename command.
	* command.h (add_cmd): Add ability to rename existing commands
	rather than redefining them.
	(add_prefix_cmd): Ditto.
	(rename_cmd): Add.
	(user_defined_command): Add declaration of previously static
	function.
	* python/py-cmd.c (cmdpy_init): Add rename_existing_to argument.
	* python/python-internal.h (PyInt_Type): Add for compatibility
	between Python 2 and 3.

gdb/doc/ChangeLog:

	* gdb.texinfo: Document the new rename command and the new
	-rename-existing-to option for define.
	* python.texi: Document the new rename_existing_to argument for
	gdb.Command.__init__.

gdb/testsuite/ChangeLog:

	* gdb.base/command-renaming.exp: New test.
	* gdb.python/py-rename-existing.exp: New test.
	* gdb.python/py-rename-existing.py: New test.
---
 gdb/NEWS                                      |  26 +
 gdb/cli/cli-decode.c                          | 645 +++++++++++++++---
 gdb/cli/cli-decode.h                          |   2 +
 gdb/cli/cli-script.c                          | 324 +++++++--
 gdb/command.h                                 |  59 +-
 gdb/doc/gdb.texinfo                           |  55 +-
 gdb/doc/python.texi                           |  38 +-
 gdb/python/py-cmd.c                           | 183 ++++-
 gdb/python/python-internal.h                  |   1 +
 gdb/testsuite/gdb.base/command-renaming.exp   | 571 ++++++++++++++++
 .../gdb.python/py-rename-existing.exp         | 364 ++++++++++
 .../gdb.python/py-rename-existing.py          |  46 ++
 12 files changed, 2097 insertions(+), 217 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/command-renaming.exp
 create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.exp
 create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.py

diff --git a/gdb/NEWS b/gdb/NEWS
index d2ed28857b0..94b2b52de75 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -47,6 +47,11 @@ maintenance flush register-cache
 maintenance flush dcache
   A new command to flush the dcache.
 
+rename OLD_NAME NEW_NAME
+  Rename the command called OLD_NAME to NEW_NAME.  All subcommands, hooks
+  and aliases associated to OLD_NAME are moved to refer to NEW_NAME.
+  If a command name contains spaces, it must be enclosed in double quotes.
+
 * Changed commands
 
 break [PROBE_MODIFIER] [LOCATION] [thread THREADNUM]
@@ -80,6 +85,27 @@ set style version background COLOR
 set style version intensity VALUE
   Control the styling of GDB's version number text.
 
+define [-rename-existing-to NEW_NAME_FOR_EXISTING_COMMAND] COMMAND_NAME
+  Added a -rename-existing-to option which causes the existing command
+  called COMMAND_NAME to be renamed to NEW_NAME_FOR_EXISTING_COMMAND
+  instead of being redefined and overwritten.  This allows user-defined
+  commands to modify existing commands and extend them rather than
+  replacing them completely.
+  The behaviour is similar to using the "rename" command followed by
+  "define", but "define -rename-existing-to" keeps aliases, hooks and
+  subcommands associated to the newly defined COMMAND_NAME command.
+  If NEW_NAME_FOR_EXISTING_COMMAND contains spaces, it must be enclosed in
+  double quotes, unlike COMMAND_NAME which must not be enclosed in quotes.
+
+* Python API
+
+  ** Added a new rename_existing_to optional parameter to gdb.Command.
+     If specified, the existing command with the same name as the new
+     command gets renamed to the value of rename_existing_to instead of
+     being redefined and overwritten.  This allows Python code to modify
+     existing commands and extend them rather than replacing them
+     completely.  See also "define -rename-existing-to".
+
 *** Changes in GDB 10
 
 * There are new feature names for ARC targets: "org.gnu.gdb.arc.core"
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index aea7302213a..5069032eb72 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -30,12 +30,18 @@
 
 static void undef_cmd_error (const char *, const char *);
 
-static struct cmd_list_element *delete_cmd (const char *name,
-					    struct cmd_list_element **list,
-					    struct cmd_list_element **prehook,
-					    struct cmd_list_element **prehookee,
-					    struct cmd_list_element **posthook,
-					    struct cmd_list_element **posthookee);
+static cmd_list_element *disconnect_cmd (const char *name,
+					 struct cmd_list_element **list,
+					 struct cmd_list_element ***prefixlist,
+					 struct cmd_list_element **aliases,
+					 struct cmd_list_element **prehook,
+					 struct cmd_list_element **prehookee,
+					 struct cmd_list_element **posthook,
+					 struct cmd_list_element **posthookee);
+
+static bool delete_cmd_by_name (const char *name,
+				struct cmd_list_element **list,
+				struct cmd_list_element ***prefixlist);
 
 static struct cmd_list_element *find_cmd (const char *command,
 					  int len,
@@ -160,53 +166,191 @@ set_cmd_completer_handle_brkchars (struct cmd_list_element *cmd,
   cmd->completer_handle_brkchars = func;
 }
 
-/* Like ADD_CMD, but the command function fields are not modified.  */
+/* Update the prefix-related fields associated with command C, which must
+   be part of the comand list *LIST, and, if not null, adds PREFIXLIST
+   as subcommands of C.
 
-static struct cmd_list_element *
-do_add_cmd (const char *name, enum command_class theclass,
-	    const char *doc, struct cmd_list_element **list)
+   In particular, this function always updates the relationship of C with
+   the command it's a subcommand of (if any) and, if PREFIXLIST is not
+   null:
+   * Makes PREFIXLIST the list of subcommands of C (which makes C a prefix
+     command);
+   * Updates the subcommands in PREFIXLIST accordingly.  */
+
+static void
+update_prefix_links (struct cmd_list_element *c,
+		     struct cmd_list_element **list,
+		     struct cmd_list_element **prefixlist)
+{
+  /* Update the subcommands to point to C as their prefix.  */
+  gdb_assert (c->prefixlist == nullptr);
+  c->prefixlist = prefixlist;
+  if (c->prefixlist != nullptr)
+    {
+      for (struct cmd_list_element *iter = *(c->prefixlist);
+	   iter != nullptr;
+	   iter = iter->next)
+	iter->prefix = c;
+    }
+
+  /* Find the prefix cmd of C, and assign it to C->prefix.
+     See also add_prefix_cmd and update_prefix_field_of_prefixed_commands.  */
+  gdb_assert (c->prefix == nullptr);
+  struct cmd_list_element *prefixcmd = lookup_cmd_for_prefixlist (list,
+								  cmdlist);
+  c->prefix = prefixcmd;
+}
+
+/* Update command C, by adding it to *LIST and updating it from the other
+   arguments.
+
+   C must not already be in any command list, or have any subcommands,
+   aliases or hooks associated.
+
+   If NEW_NAME is not null, then the command name is updated to the
+   specified name.
+
+   If PREFIXLIST is not null, then the *PREFIXLIST commands become
+   subcommands of C.
+
+   If ALIASES is not null, then the specified commands become aliases of
+   C.
+
+   The various HOOKs, if not null, specify commands hooking into C or
+   commands hooked into C.  */
+
+static void
+update_cmd (struct cmd_list_element *c,
+	    struct cmd_list_element **list,
+	    const char *new_name = nullptr,
+	    struct cmd_list_element **prefixlist = nullptr,
+	    struct cmd_list_element *aliases = nullptr,
+	    struct cmd_list_element *hook_pre = nullptr,
+	    struct cmd_list_element *hookee_pre = nullptr,
+	    struct cmd_list_element *hook_post = nullptr,
+	    struct cmd_list_element *hookee_post = nullptr)
 {
-  struct cmd_list_element *c = new struct cmd_list_element (name, theclass,
-							    doc);
-  struct cmd_list_element *p, *iter;
-
-  /* Turn each alias of the old command into an alias of the new
-     command.  */
-  c->aliases = delete_cmd (name, list, &c->hook_pre, &c->hookee_pre,
-			   &c->hook_post, &c->hookee_post);
-  for (iter = c->aliases; iter; iter = iter->alias_chain)
+  /* Update the name, if specified.  */
+  if (new_name != nullptr)
+    {
+      if (c->name_allocated)
+	xfree ((char *)c->name);
+
+      c->name = xstrdup (new_name);
+      c->name_allocated = true;
+    }
+
+  /* Add C to the command list LIST in the appropriate (sorted) position.  */
+  if (*list == NULL || strcmp ((*list)->name, c->name) >= 0)
+    {
+      c->next = *list;
+      *list = c;
+    }
+  else
+    {
+      struct cmd_list_element *p = *list;
+      while (p->next != nullptr && strcmp (p->next->name, c->name) <= 0)
+	p = p->next;
+      c->next = p->next;
+      p->next = c;
+    }
+
+  /* Update prefix information.  */
+  update_prefix_links (c, list, prefixlist);
+
+  /* Update the aliases.  */
+  gdb_assert (c->aliases == nullptr);
+  c->aliases = aliases;
+  for (struct cmd_list_element *iter = c->aliases;
+       iter != nullptr;
+       iter = iter->alias_chain)
     iter->cmd_pointer = c;
-  if (c->hook_pre)
+
+  /* Updates hooks and hookees.  */
+  gdb_assert (c->hook_pre == nullptr);
+  c->hook_pre = hook_pre;
+  if (c->hook_pre != nullptr)
     c->hook_pre->hookee_pre = c;
-  if (c->hookee_pre)
+
+  gdb_assert (c->hookee_pre == nullptr);
+  c->hookee_pre = hookee_pre;
+  if (c->hookee_pre != nullptr)
     c->hookee_pre->hook_pre = c;
-  if (c->hook_post)
+
+  gdb_assert (c->hook_post == nullptr);
+  c->hook_post = hook_post;
+  if (c->hook_post != nullptr)
     c->hook_post->hookee_post = c;
-  if (c->hookee_post)
+
+  gdb_assert (c->hookee_post == nullptr);
+  c->hookee_post = hookee_post;
+  if (c->hookee_post != nullptr)
     c->hookee_post->hook_post = c;
+}
 
-  if (*list == NULL || strcmp ((*list)->name, name) >= 0)
+/* Like ADD_CMD, but the command function fields are not modified.  */
+
+static struct cmd_list_element *
+do_add_cmd (const char *name, enum command_class theclass,
+	    const char *doc, struct cmd_list_element **list,
+	    const char *rename_existing_to = nullptr,
+	    struct cmd_list_element **rename_existing_to_list = nullptr)
+{
+  /* If a command called RENAME_EXISTING_TO already exists, delete it.  */
+  struct cmd_list_element **deleted_cmd_prefixlist = nullptr;
+  if (rename_existing_to != nullptr)
     {
-      c->next = *list;
-      *list = c;
+      gdb_assert (rename_existing_to_list != nullptr);
+      delete_cmd_by_name (rename_existing_to, rename_existing_to_list,
+			  &deleted_cmd_prefixlist);
     }
-  else
+
+  /* Create the new command.  */
+  struct cmd_list_element *c = new struct cmd_list_element (name, theclass,
+							    doc);
+
+  /* Remove the old NAME command from LIST and disconnect all the
+     subcommands, aliases, hooks as well.  */
+  struct cmd_list_element **prefixlist;
+  struct cmd_list_element *aliases;
+  struct cmd_list_element *hook_pre, *hookee_pre, *hook_post, *hookee_post;
+  struct cmd_list_element *existing_cmd
+    = disconnect_cmd (name, list,
+		      &prefixlist,
+		      &aliases,
+		      &hook_pre, &hookee_pre,
+		      &hook_post, &hookee_post);
+
+  /* Add the new command to LIST turning aliases and hooks of the old
+     command into aliases/hooks of the new command.  */
+  update_cmd (c, list,
+	      /* new_name = */ nullptr,
+	      prefixlist,
+	      aliases,
+	      hook_pre, hookee_pre, hook_post, hookee_post);
+
+  /* Finally, rename the existing command or, if renaming is not
+     requested, delete it.  */
+  if (rename_existing_to != nullptr)
     {
-      p = *list;
-      while (p->next && strcmp (p->next->name, name) <= 0)
+      gdb_assert (existing_cmd != nullptr);
+      update_cmd (existing_cmd, rename_existing_to_list,
+		  rename_existing_to,
+		  deleted_cmd_prefixlist);
+      if (prefixlist)
 	{
-	  p = p->next;
+	  /* The existing command was a prefix command so it must remain
+	     a prefix command (with no subcommands).  This is needed as
+	     the implementation of the command could try to access its
+	     subcommands.  See define_prefix_command for details on how
+	     prefix lists work. */
+	  gdb_assert (existing_cmd->prefixlist == nullptr);
+	  existing_cmd->prefixlist = new struct cmd_list_element*;
+	  *(existing_cmd->prefixlist) = nullptr;
 	}
-      c->next = p->next;
-      p->next = c;
     }
-
-  /* Search the prefix cmd of C, and assigns it to C->prefix.
-     See also add_prefix_cmd and update_prefix_field_of_prefixed_commands.  */
-  struct cmd_list_element *prefixcmd = lookup_cmd_for_prefixlist (list,
-								  cmdlist);
-  c->prefix = prefixcmd;
-
+  else
+    delete existing_cmd;
 
   return c;
 }
@@ -215,9 +359,13 @@ do_add_cmd (const char *name, enum command_class theclass,
 
 struct cmd_list_element *
 add_cmd (const char *name, enum command_class theclass,
-	 const char *doc, struct cmd_list_element **list)
+	 const char *doc, struct cmd_list_element **list,
+	 const char *rename_existing_to,
+	 struct cmd_list_element **rename_existing_to_list)
 {
-  cmd_list_element *result = do_add_cmd (name, theclass, doc, list);
+  cmd_list_element *result = do_add_cmd (name, theclass, doc, list,
+					 rename_existing_to,
+					 rename_existing_to_list);
   result->func = NULL;
   result->function.const_cfunc = NULL;
   return result;
@@ -228,9 +376,13 @@ add_cmd (const char *name, enum command_class theclass,
 struct cmd_list_element *
 add_cmd (const char *name, enum command_class theclass,
 	 cmd_const_cfunc_ftype *fun,
-	 const char *doc, struct cmd_list_element **list)
+	 const char *doc, struct cmd_list_element **list,
+	 const char *rename_existing_to,
+	 struct cmd_list_element **rename_existing_to_list)
 {
-  cmd_list_element *result = do_add_cmd (name, theclass, doc, list);
+  cmd_list_element *result = do_add_cmd (name, theclass, doc, list,
+					 rename_existing_to,
+					 rename_existing_to_list);
   set_cmd_cfunc (result, fun);
   return result;
 }
@@ -283,14 +435,19 @@ add_alias_cmd (const char *name, cmd_list_element *old,
 {
   if (old == 0)
     {
+      struct cmd_list_element **prefixlist;
+      struct cmd_list_element *aliases;
       struct cmd_list_element *prehook, *prehookee, *posthook, *posthookee;
-      struct cmd_list_element *aliases = delete_cmd (name, list,
-						     &prehook, &prehookee,
-						     &posthook, &posthookee);
-
+      struct cmd_list_element *old_cmd
+	= disconnect_cmd (name, list,
+			  &prefixlist,
+			  &aliases,
+			  &prehook, &prehookee,
+			  &posthook, &posthookee);
       /* If this happens, it means a programmer error somewhere.  */
-      gdb_assert (!aliases && !prehook && !prehookee
-		  && !posthook && ! posthookee);
+      gdb_assert (!old_cmd);
+      /* As OLD_CMD is null, NAME didn't exist, so the prefix list,
+       * aliases and hooks are all null as well.  */
       return 0;
     }
 
@@ -370,9 +527,13 @@ struct cmd_list_element *
 add_prefix_cmd (const char *name, enum command_class theclass,
 		cmd_const_cfunc_ftype *fun,
 		const char *doc, struct cmd_list_element **prefixlist,
-		int allow_unknown, struct cmd_list_element **list)
+		int allow_unknown, struct cmd_list_element **list,
+		const char *rename_existing_to,
+		struct cmd_list_element **rename_existing_to_list)
 {
-  struct cmd_list_element *c = add_cmd (name, theclass, fun, doc, list);
+  struct cmd_list_element *c = add_cmd (name, theclass, fun, doc, list,
+					rename_existing_to,
+					rename_existing_to_list);
 
   c->prefixlist = prefixlist;
   c->allow_unknown = allow_unknown;
@@ -888,76 +1049,346 @@ add_setshow_zuinteger_cmd (const char *name, enum command_class theclass,
 			NULL, NULL);
 }
 
-/* Remove the command named NAME from the command list.  Return the
-   list commands which were aliased to the deleted command.  If the
-   command had no aliases, return NULL.  The various *HOOKs are set to
-   the pre- and post-hook commands for the deleted command.  If the
-   command does not have a hook, the corresponding out parameter is
-   set to NULL.  */
-
-static struct cmd_list_element *
-delete_cmd (const char *name, struct cmd_list_element **list,
-	    struct cmd_list_element **prehook,
-	    struct cmd_list_element **prehookee,
-	    struct cmd_list_element **posthook,
-	    struct cmd_list_element **posthookee)
+/* Break the relationships between C and other commands.
+
+   This is done by removing subcommands, aliases and hooks from C and
+   making the other commands not point to C any more.
+
+   *PREFIXLIST is set to point to the list of subcommands for the removed
+   command.
+
+   *ALIASES is set to the list of commands which were aliased to C.  If
+   the command had no aliases, it's set to NULLPTR.
+
+   *ALIASES is set to the removed aliases.  If the command has no aliases,
+   it's set to NULLPTR.
+
+   The various *HOOKs are set to the pre- and post-hook commands for C.
+   If the command does not have a hook, the corresponding out parameter
+   is set to NULLPTR.  */
+
+static void
+break_cmd_relationships (struct cmd_list_element *c,
+			 struct cmd_list_element ***prefixlist,
+			 struct cmd_list_element **aliases,
+			 struct cmd_list_element **hook_pre,
+			 struct cmd_list_element **hookee_pre,
+			 struct cmd_list_element **hook_post,
+			 struct cmd_list_element **hookee_post)
 {
-  struct cmd_list_element *iter;
-  struct cmd_list_element **previous_chain_ptr;
-  struct cmd_list_element *aliases = NULL;
+  /* Make the subcommands not point to this command any more. */
+  if (c->prefixlist != nullptr)
+    {
+      for (struct cmd_list_element *iter = *(c->prefixlist);
+	   iter != nullptr;
+	   iter = iter->next)
+	iter->prefix = nullptr;
+    }
+
+  /* Remove the subcommands.  */
+  *prefixlist = c->prefixlist;
+  c->prefixlist = nullptr;
+
+  /* Make the aliases not point to this command any more. */
+  for (struct cmd_list_element *iter = c->aliases;
+       iter != nullptr;
+       iter = iter->alias_chain)
+    iter->cmd_pointer = nullptr;
+
+  /* Remove the aliases. */
+  *aliases = c->aliases;
+  c->aliases = nullptr;
+
+  /* If this command was an alias, remove it from the list of aliases.  */
+  if (c->cmd_pointer != nullptr)
+    {
+      struct cmd_list_element **prevp = &c->cmd_pointer->aliases;
+      struct cmd_list_element *a = *prevp;
 
-  *prehook = NULL;
-  *prehookee = NULL;
-  *posthook = NULL;
-  *posthookee = NULL;
-  previous_chain_ptr = list;
+      while (a != c)
+	{
+	  prevp = &a->alias_chain;
+	  a = *prevp;
+	}
+      *prevp = c->alias_chain;
+    }
+
+  /* Save the previous hook lists in the output parameters.  */
+  *hook_pre = c->hook_pre;
+  *hookee_pre = c->hookee_pre;
+  *hook_post = c->hook_post;
+  *hookee_post = c->hookee_post;
+
+  /* Break the hook relationships with other commands.  */
+  if (c->hook_pre)
+    c->hook_pre->hookee_pre = nullptr;
+  if (c->hookee_pre)
+    c->hookee_pre->hook_pre = nullptr;
+  if (c->hook_post)
+    c->hook_post->hookee_post = nullptr;
+  if (c->hookee_post)
+    c->hookee_post->hook_post = nullptr;
+
+  /* And finally remove the hooks.  */
+  c->hook_pre = nullptr;
+  c->hookee_pre = nullptr;
+  c->hook_post = nullptr;
+  c->hookee_post = nullptr;
+}
+
+/* Disconnect the command named NAME from the command list *LIST, from
+   its subcommands, and all associated aliases and hooks.
+
+   *PREFIXLIST is set to point to the list of subcommands for the removed
+   command.
+
+   *ALIASES is set to the list of commands which were aliased to the
+   removed command.  If the command has no aliases, it's set to NULLPTR.
+
+   The various *HOOKs are set to the pre- and post-hook commands for the
+   removed command.  If the command does not have a hook, the
+   corresponding out parameter is set to NULLPTR.
+
+   Return the diconnected command if it was found.  Otherwise, return
+   NULLPTR (and the output parameters are all set to NULLPTR as well).  */
+
+static cmd_list_element *
+disconnect_cmd (const char *name, struct cmd_list_element **list,
+		struct cmd_list_element ***prefixlist,
+		struct cmd_list_element **aliases,
+		struct cmd_list_element **hook_pre,
+		struct cmd_list_element **hookee_pre,
+		struct cmd_list_element **hook_post,
+		struct cmd_list_element **hookee_post)
+{
+  struct cmd_list_element **previous_chain_ptr = list;
 
-  for (iter = *previous_chain_ptr; iter; iter = *previous_chain_ptr)
+  for (struct cmd_list_element *iter = *previous_chain_ptr;
+       iter != nullptr;
+       iter = *previous_chain_ptr)
     {
       if (strcmp (iter->name, name) == 0)
 	{
-	  if (iter->destroyer)
-	    iter->destroyer (iter, iter->context);
-	  if (iter->hookee_pre)
-	    iter->hookee_pre->hook_pre = 0;
-	  *prehook = iter->hook_pre;
-	  *prehookee = iter->hookee_pre;
-	  if (iter->hookee_post)
-	    iter->hookee_post->hook_post = 0;
-	  *posthook = iter->hook_post;
-	  *posthookee = iter->hookee_post;
-
-	  /* Update the link.  */
+	  /* Found the only command with the specified name.  */
+	  break_cmd_relationships (iter,
+				   prefixlist,
+				   aliases,
+				   hook_pre, hookee_pre,
+				   hook_post, hookee_post);
+	  /* Update the link in the command list, that is remove the
+	     command from its command list.  */
 	  *previous_chain_ptr = iter->next;
+	  /* And finally unlink from the prefix command.  */
+	  iter->prefix = nullptr;
 
-	  aliases = iter->aliases;
-
-	  /* If this command was an alias, remove it from the list of
-	     aliases.  */
-	  if (iter->cmd_pointer)
-	    {
-	      struct cmd_list_element **prevp = &iter->cmd_pointer->aliases;
-	      struct cmd_list_element *a = *prevp;
-
-	      while (a != iter)
-		{
-		  prevp = &a->alias_chain;
-		  a = *prevp;
-		}
-	      *prevp = iter->alias_chain;
-	    }
-
-	  delete iter;
-
-	  /* We won't see another command with the same name.  */
-	  break;
+	  return iter;
 	}
       else
 	previous_chain_ptr = &iter->next;
     }
 
-  return aliases;
+  /* Command not found. */
+
+  *prefixlist = nullptr;
+  *aliases = nullptr;
+  *hook_pre = nullptr;
+  *hookee_pre = nullptr;
+  *hook_post = nullptr;
+  *hookee_post = nullptr;
+
+  return nullptr;
 }
+
+/* Delete command C and all associated aliases and hooks.
+
+   Subcommands are not modified.  If C had subcommands, it's the caller's
+   responsibility to either assign the subcommands to another command or
+   to delete them.
+
+   If PREFIXLIST is not null, *PREFIXLIST is set to point to the list of
+   subcommands previously associated with C.
+   If PREFIXLIST is null, then C must be an alias or hook which cannot
+   have subcommands.  */
+
+static void
+delete_cmd (struct cmd_list_element *c,
+	    struct cmd_list_element ***prefixlist)
+{
+  /* The PREFIXLIST argument is required for all command types which could
+     have subcommands.  That is, PREFIXLIST can be null only if C is a
+     hook or alias.  */
+  gdb_assert (prefixlist != nullptr
+	      || (c->cmd_pointer == nullptr
+		  || c->hookee_pre == nullptr
+		  || c->hookee_post == nullptr));
+
+  /* Find the list for C.  */
+  struct cmd_list_element **list;
+  if (c->prefix != nullptr)
+    list = c->prefix->prefixlist;
+  else
+    list = &cmdlist;
+
+  /* Delete C itself.  */
+  struct cmd_list_element **prefixlist_tmp;
+  struct cmd_list_element *aliases;
+  struct cmd_list_element *hook_pre, *hookee_pre, *hook_post, *hookee_post;
+  struct cmd_list_element *actual_removed_cmd
+    = disconnect_cmd (c->name, list,
+		      &prefixlist_tmp,
+		      &aliases,
+		      &hook_pre, &hookee_pre,
+		      &hook_post, &hookee_post);
+  gdb_assert (actual_removed_cmd == c);
+
+  if (prefixlist != nullptr)
+    *prefixlist = prefixlist_tmp;
+  else
+    /* We asserted earlier that, if PREFIXLIST is null, then C is either
+       a hook or an alias, so prefixlist_tmp cannot be null.  */
+    gdb_assert (prefixlist_tmp == nullptr);
+
+  /* Delete all aliases.  */
+  for (struct cmd_list_element *alias_iter = aliases;
+       alias_iter != nullptr;
+       alias_iter = alias_iter->alias_chain)
+    delete_cmd (alias_iter, nullptr);
+
+  /* Delete hooks.  */
+  if (hook_pre != nullptr)
+    delete_cmd (hook_pre, nullptr);
+  if (hook_post != nullptr)
+    delete_cmd (hook_post, nullptr);
+
+  /* There's nothing to do with hookee_pre and hookee_post.  If they are
+     set then C is a hook and, if C is a hook, deleting it doesn't mean
+     that the hookee must be deleted.  */
+
+  delete c;
+}
+
+/* Delete the command called NAME in *LIST and all associated aliases and
+   hooks.
+
+   See DELETE_CMD for details on PREFIXLIST and whether it can be null.
+
+   Return TRUE if the command was found and deleted, FALSE otherwise.  */
+
+static bool
+delete_cmd_by_name (const char *name,
+		    struct cmd_list_element **list,
+		    struct cmd_list_element ***prefixlist)
+{
+  struct cmd_list_element *cmd = lookup_cmd_exact (name, *list);
+  if (cmd != nullptr)
+    {
+      delete_cmd (cmd, prefixlist);
+      return true;
+    }
+  else
+    {
+      if (prefixlist != nullptr)
+	*prefixlist = nullptr;
+      return false;
+    }
+}
+
+/* Rename HOOK to match the command OLD_CMD_NAME in *OLD_LIST which was
+   renamed to NEW_CMD_NAME in NEW_LIST.  */
+
+static void
+rename_hook (struct cmd_list_element *hook,
+	     const char *old_cmd_name, struct cmd_list_element **old_list,
+	     const char *new_cmd_name, struct cmd_list_element **new_list)
+{
+  struct cmd_list_element **prefixlist;
+  struct cmd_list_element *aliases;
+  struct cmd_list_element *hook_pre, *hookee_pre, *hook_post, *hookee_post;
+  struct cmd_list_element *cmd = disconnect_cmd (hook->name, old_list,
+						 &prefixlist,
+						 &aliases,
+						 &hook_pre, &hookee_pre,
+						 &hook_post, &hookee_post);
+
+  /* Rename the hook command keeping the part that comes before the
+     hookee's name (i.e. "hook-" or "hookpost-").  */
+  std::string old_hook_name (hook->name);
+  size_t cmd_name_offset = old_hook_name.rfind (old_cmd_name);
+  gdb_assert (cmd_name_offset != std::string::npos);
+  std::string new_hook_name
+    = old_hook_name.substr (0, cmd_name_offset) + new_cmd_name;
+
+  update_cmd (cmd, new_list,
+	      new_hook_name.c_str (),
+	      prefixlist,
+	      aliases,
+	      hook_pre, hookee_pre, hook_post, hookee_post);
+}
+
+/* See command.h.  */
+
+void
+user_defined_command (const char *ignore, int from_tty)
+{
+}
+
+/* See command.h.  */
+
+struct cmd_list_element *
+rename_cmd (const char *old_name, struct cmd_list_element **old_list,
+	    const char *new_name, struct cmd_list_element **new_list)
+{
+  /* Remove the command from its current list.  */
+  struct cmd_list_element **old_name_prefixlist;
+  struct cmd_list_element *aliases;
+  struct cmd_list_element *hook_pre, *hookee_pre, *hook_post, *hookee_post;
+  struct cmd_list_element *cmd = disconnect_cmd (old_name, old_list,
+						 &old_name_prefixlist,
+						 &aliases,
+						 &hook_pre, &hookee_pre,
+						 &hook_post, &hookee_post);
+  if (cmd == nullptr)
+    {
+      /* No command called OLD_NAME exists in OLD_LIST.  */
+      return nullptr;
+    }
+
+  /* If there was already a NEW_NAME command then it must be deleted.
+     Its subcommands, if present, are moved to the renamed command.  */
+  struct cmd_list_element **new_name_prefixlist;
+  delete_cmd_by_name (new_name, new_list, &new_name_prefixlist);
+
+  /* Re-insert the command but in NEW_LIST.  */
+  update_cmd (cmd, new_list,
+	      new_name,
+	      new_name_prefixlist,
+	      aliases,
+	      hook_pre, hookee_pre, hook_post, hookee_post);
+
+  /* Rename the hooks as well.  */
+  if (hook_pre)
+    rename_hook (hook_pre, old_name, old_list, new_name, new_list);
+  if (hook_post)
+    rename_hook (hook_post, old_name, old_list, new_name, new_list);
+
+  /* If the old command had subcommands, then a new prefix-only command
+     must be created so they can be preserved.  */
+  if (old_name_prefixlist != nullptr && *old_name_prefixlist != nullptr)
+    {
+      struct cmd_list_element *prefix_for_old_name
+	  = add_cmd (xstrdup (old_name), class_user, user_defined_command,
+		     xstrdup ("User-defined."), old_list);
+      /* The new prefix is a prefix only so passing invalid subcommands
+	 must lead to an error.  */
+      prefix_for_old_name->allow_unknown = false;
+
+      update_prefix_links (prefix_for_old_name, old_list,
+			   old_name_prefixlist);
+    }
+
+  return cmd;
+}
+
 \f
 /* Shorthands to the commands above.  */
 
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index ddcb2ea9578..2197a16ad36 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -63,6 +63,8 @@ struct cmd_list_element
 
     ~cmd_list_element ()
     {
+      if (destroyer != nullptr)
+	destroyer (this, context);
       if (doc && doc_allocated)
 	xfree ((char *) doc);
       if (name_allocated)
diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index 5841f545882..d7981d8bb2c 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -30,6 +30,7 @@
 #include "cli/cli-decode.h"
 #include "cli/cli-script.h"
 #include "cli/cli-style.h"
+#include "cli/cli-utils.h"
 
 #include "extension.h"
 #include "interps.h"
@@ -49,7 +50,8 @@ recurse_read_control_structure
      gdb::function_view<void (const char *)> validator);
 
 static void do_define_command (const char *comname, int from_tty,
-			       const counted_command_line *commands);
+			       const counted_command_line *commands,
+			       const char *rename_existing_to = nullptr);
 
 static void do_document_command (const char *comname, int from_tty,
                                  const counted_command_line *commands);
@@ -1368,84 +1370,157 @@ validate_comname (const char **comname)
   return list;
 }
 
-/* This is just a placeholder in the command data structures.  */
+/* Ask the user if they really want to redefine the command called
+   CMD->name, thus overwriting CMD.
+
+   Throws an error if the users says no.  */
+
 static void
-user_defined_command (const char *ignore, int from_tty)
+check_command_redefinition (struct cmd_list_element *cmd)
 {
+  int q;
+
+  if (cmd->theclass == class_user || cmd->theclass == class_alias)
+    {
+      /* If C is a prefix command that was previously defined,
+	 tell the user its subcommands will be kept, and ask
+	 if ok to redefine the command.  */
+      if (cmd->prefixlist != nullptr)
+	q = (cmd->user_commands.get () == nullptr
+	     || query (_("Keeping subcommands of prefix command \"%s\".\n"
+			 "Redefine command \"%s\"? "), cmd->name, cmd->name));
+      else
+	q = query (_("Redefine command \"%s\"? "), cmd->name);
+    }
+  else
+    q = query (_("Really redefine built-in command \"%s\"? "), cmd->name);
+  if (!q)
+    error (_("Command \"%s\" not redefined."), cmd->name);
+}
+
+/* Whether a command is a hook into another command and which type of hook
+   it is.  */
+
+enum cmd_hook_type
+{
+  /* Not a hook.  */
+  CMD_NO_HOOK = 0,
+  /* Hook executed before the associated command.  */
+  CMD_PRE_HOOK,
+  /* Hook executed after the associated command.  */
+  CMD_POST_HOOK
+};
+
+/* Given a command name NAME, returns an element of CMD_HOOK_TYPE
+   identifying whether NAME is a hook name and which type of hook it is.
+
+   If HOOKEE_NAME is not null, it's set to point to the position in NAME
+   identifying the name of the command hooked by NAME.  */
+
+static cmd_hook_type
+get_hook_type (const char *name, const char **hookee_name = nullptr)
+{
+  static const char hook_pre_prefix[] = "hook-";
+  static const char hook_post_prefix[] = "hookpost-";
+  static const size_t hook_pre_prefix_len = sizeof hook_pre_prefix - 1;
+  static const size_t hook_post_prefix_len = sizeof hook_post_prefix - 1;
+
+  cmd_hook_type type;
+  const char *hookee_name_tmp;
+
+  if (strncmp (name, hook_pre_prefix, hook_pre_prefix_len) == 0)
+    {
+      hookee_name_tmp = name + hook_pre_prefix_len;
+      type = CMD_PRE_HOOK;
+    }
+  else if (strncmp (name, hook_post_prefix, hook_post_prefix_len) == 0)
+    {
+      hookee_name_tmp = name + hook_post_prefix_len;
+      type = CMD_POST_HOOK;
+    }
+  else
+    {
+      hookee_name_tmp = nullptr;
+      type = CMD_NO_HOOK;
+    }
+
+  if (hookee_name != nullptr)
+    *hookee_name = hookee_name_tmp;
+
+  return type;
 }
 
 /* Define a user-defined command.  If COMMANDS is NULL, then this is a
    top-level call and the commands will be read using
    read_command_lines.  Otherwise, it is a "define" command in an
    existing command and the commands are provided.  In the
-   non-top-level case, various prompts and warnings are disabled.  */
+   non-top-level case, various prompts and warnings are disabled.
+
+   If RENAME_EXISTING_TO is non-NULL, then the existing command called
+   COMNAME will be renamed to RENAME_EXISTING_TO instead of being
+   overwritten.  */
 
 static void
 do_define_command (const char *comname, int from_tty,
-		   const counted_command_line *commands)
+		   const counted_command_line *commands,
+		   const char *rename_existing_to)
 {
-  enum cmd_hook_type
-    {
-      CMD_NO_HOOK = 0,
-      CMD_PRE_HOOK,
-      CMD_POST_HOOK
-    };
   struct cmd_list_element *c, *newc, *hookc = 0, **list;
-  const char *comfull;
-  int  hook_type      = CMD_NO_HOOK;
-  int  hook_name_size = 0;
-   
-#define	HOOK_STRING	"hook-"
-#define	HOOK_LEN 5
-#define HOOK_POST_STRING "hookpost-"
-#define HOOK_POST_LEN    9
+  struct cmd_list_element *rename_existing_to_cmd = nullptr;
+  struct cmd_list_element **rename_existing_to_list;
+  const char *comfull, *rename_existing_to_full;
+  int hook_type;
+  const char *hookee_name = nullptr;
 
   comfull = comname;
   list = validate_comname (&comname);
 
+  rename_existing_to_full = rename_existing_to;
+  if (rename_existing_to != nullptr)
+    rename_existing_to_list = validate_comname (&rename_existing_to);
+  else
+    rename_existing_to_list = nullptr; /* Not renaming.  */
+
+  if (rename_existing_to_full != nullptr &&
+      strcmp (rename_existing_to_full, comfull) == 0)
+    error (_("Cannot rename command \"%s\" as the new name is identical "
+	     "to the existing one."),
+	   comfull);
+
   c = lookup_cmd_exact (comname, *list);
+  if (rename_existing_to_list != nullptr)
+      rename_existing_to_cmd = lookup_cmd_exact (rename_existing_to,
+                                                 *rename_existing_to_list);
 
-  if (c && commands == nullptr)
+  if (commands == nullptr)
     {
-      int q;
+      /* Check whether the user is trying to redefine an existing command.  */
+      if (rename_existing_to_cmd != nullptr)
+	check_command_redefinition (rename_existing_to_cmd);
+      else if (c != nullptr && rename_existing_to == nullptr)
+	check_command_redefinition (c);
+    }
 
-      if (c->theclass == class_user || c->theclass == class_alias)
-	{
-	  /* if C is a prefix command that was previously defined,
-	     tell the user its subcommands will be kept, and ask
-	     if ok to redefine the command.  */
-	  if (c->prefixlist != nullptr)
-	    q = (c->user_commands.get () == nullptr
-		 || query (_("Keeping subcommands of prefix command \"%s\".\n"
-			     "Redefine command \"%s\"? "), c->name, c->name));
-	  else
-	    q = query (_("Redefine command \"%s\"? "), c->name);
-	}
-      else
-	q = query (_("Really redefine built-in command \"%s\"? "), c->name);
-      if (!q)
-	error (_("Command \"%s\" not redefined."), c->name);
+  /* Checks on the validity of renames.  */
+  if (rename_existing_to != nullptr)
+    {
+      if (c == nullptr)
+	error (_("Command \"%s\" does not exist, so it cannot "
+		 "be renamed to \"%s\"."),
+		 comfull, rename_existing_to_full);
+
+      if (get_hook_type (rename_existing_to) != CMD_NO_HOOK)
+	error (_("Cannot define hooks by renaming commands."));
     }
 
   /* If this new command is a hook, then mark the command which it
      is hooking.  Note that we allow hooking `help' commands, so that
      we can hook the `stop' pseudo-command.  */
-
-  if (!strncmp (comname, HOOK_STRING, HOOK_LEN))
-    {
-       hook_type      = CMD_PRE_HOOK;
-       hook_name_size = HOOK_LEN;
-    }
-  else if (!strncmp (comname, HOOK_POST_STRING, HOOK_POST_LEN))
-    {
-      hook_type      = CMD_POST_HOOK;
-      hook_name_size = HOOK_POST_LEN;
-    }
-
+  hook_type = get_hook_type (comname, &hookee_name);
   if (hook_type != CMD_NO_HOOK)
     {
       /* Look up cmd it hooks.  */
-      hookc = lookup_cmd_exact (comname + hook_name_size, *list,
+      hookc = lookup_cmd_exact (hookee_name, *list,
 				/* ignore_help_classes = */ false);
       if (!hookc && commands == nullptr)
 	{
@@ -1462,6 +1537,8 @@ do_define_command (const char *comname, int from_tty,
 	       comfull);
     }
 
+  /* ADD_CMD takes ownership of the command name, but not of
+     RENAME_EXISTING_TO.  */
   comname = xstrdup (comname);
 
   counted_command_line cmds;
@@ -1480,7 +1557,8 @@ do_define_command (const char *comname, int from_tty,
 
     newc = add_cmd (comname, class_user, user_defined_command,
 		    (c != nullptr && c->theclass == class_user)
-		    ? c->doc : xstrdup ("User-defined."), list);
+		    ? c->doc : xstrdup ("User-defined."), list,
+                    rename_existing_to, rename_existing_to_list);
     newc->user_commands = std::move (cmds);
 
     /* If we define or re-define a command that was previously defined
@@ -1516,10 +1594,118 @@ do_define_command (const char *comname, int from_tty,
     }
 }
 
+/* The options for the "define" command.  */
+
+struct define_cmd_opts
+{
+  /* For "-rename-existing-to".  */
+  char *rename_existing_to = nullptr;
+
+  ~define_cmd_opts ()
+  {
+    xfree (rename_existing_to);
+  }
+};
+
+static const gdb::option::option_def define_cmd_option_defs[] = {
+
+  gdb::option::string_option_def<define_cmd_opts> {
+    "rename-existing-to",
+    [] (define_cmd_opts *opts) { return &opts->rename_existing_to; },
+    nullptr,
+    N_("Rename the existing command called NAME to the specified name.\n\
+This is useful to allow the new implementation of a command to invoke\n\
+the original implementation.  Existing subcommands, hooks and aliases\n\
+for the original command are moved to the newly defined one."),
+  },
+
+};
+
+/* Create an option_def_group for the "define" command's options, with
+   OPTS as context.  */
+
+static inline gdb::option::option_def_group
+make_define_cmd_options_def_group (define_cmd_opts *opts)
+{
+  return {{define_cmd_option_defs}, opts};
+}
+
+/* Implementation of the "define" command.  */
+
+static void
+define_command (const char *arg, int from_tty)
+{
+  define_cmd_opts opts;
+
+  auto grp = make_define_cmd_options_def_group (&opts);
+  gdb::option::process_options
+    (&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp);
+
+  do_define_command (arg, from_tty, nullptr, opts.rename_existing_to);
+}
+
+/* Rename command OLD_NAME to NEW_NAME.  */
+
+static void
+do_rename_command (const char *old_name, const char *new_name, int from_tty)
+{
+  const char *old_name_full = old_name;
+  struct cmd_list_element **old_list = validate_comname (&old_name);
+
+  const char *new_name_full = new_name;
+  struct cmd_list_element **new_list = validate_comname (&new_name);
+
+  if (strcmp (old_name_full, new_name_full) == 0)
+    error (_("Cannot rename command \"%s\" as the new name is identical "
+	     "to the existing one."),
+	   old_name_full);
+
+  if (get_hook_type (old_name) != CMD_NO_HOOK)
+    error (_("Cannot rename hooks."));
+
+  if (get_hook_type (new_name) != CMD_NO_HOOK)
+    error (_("Cannot define hooks by renaming commands."));
+
+  /* Are we trying to redefine a command already called NEW_NAME_FULL?  */
+  struct cmd_list_element *existing_cmd_with_new_name
+    = lookup_cmd_exact (new_name, *new_list);
+  if (existing_cmd_with_new_name != nullptr)
+    check_command_redefinition (existing_cmd_with_new_name);
+
+  struct cmd_list_element *cmd = rename_cmd (old_name, old_list,
+					     new_name, new_list);
+  if (cmd == nullptr)
+    error (_("Command \"%s\" does not exist, so it cannot be renamed "
+	     "to \"%s\"."),
+	   old_name_full, new_name_full);
+}
+
+/* Implementation of the "rename" command.  */
+
 static void
-define_command (const char *comname, int from_tty)
+rename_command (const char *arg, int from_tty)
 {
-  do_define_command (comname, from_tty, nullptr);
+  if (arg == nullptr)
+    arg = "";
+
+  std::string old_name = extract_string_maybe_quoted (&arg);
+  std::string new_name = extract_string_maybe_quoted (&arg);
+
+  arg = skip_spaces (arg);
+
+  if (*arg != '\0')
+    {
+      error (_("Too many arguments: %s\n"
+	       "If you are trying to use a command name with spaces, "
+	       "use quotes."),
+	     arg);
+    }
+  else if (old_name.empty ())
+    error (_("Arguments required (name of command to rename and new name)."));
+  else if (new_name.empty ())
+    error (_("Argument required (new name for \"%s\")."), old_name.c_str ());
+
+  do_rename_command (old_name.c_str (), new_name.c_str (), from_tty);
 }
 
 /* Document a user-defined command.  If COMMANDS is NULL, then this is a
@@ -1706,7 +1892,7 @@ _initialize_cli_script ()
 {
   struct cmd_list_element *c;
 
-  /* "document", "define" and "define-prefix" use command_completer,
+  /* "document", "define", "define-prefix" and "rename" use command_completer,
      as this helps the user to either type the command name and/or
      its prefixes.  */
   document_cmd_element = add_com ("document", class_support, document_command,
@@ -1715,15 +1901,27 @@ Document a user-defined command.\n\
 Give command name as argument.  Give documentation on following lines.\n\
 End with a line of just \"end\"."));
   set_cmd_completer (document_cmd_element, command_completer);
-  define_cmd_element = add_com ("define", class_support, define_command, _("\
+
+  const auto define_opts = make_define_cmd_options_def_group (nullptr);
+  static const std::string define_help
+    = gdb::option::build_help (_("\
 Define a new command name.  Command name is argument.\n\
+Usage: define [-rename-existing-to NEW_NAME_FOR_EXISTING_COMMAND] NAME\n\
+\n\
+Options:\n\
+%OPTIONS%\n\
+\n\
 Definition appears on following lines, one command per line.\n\
 End with a line of just \"end\".\n\
 Use the \"document\" command to give documentation for the new command.\n\
 Commands defined in this way may accept an unlimited number of arguments\n\
 accessed via $arg0 .. $argN.  $argc tells how many arguments have\n\
-been passed."));
+been passed."),
+			       define_opts);
+  define_cmd_element = add_com ("define", class_support, define_command,
+				define_help.c_str ());
   set_cmd_completer (define_cmd_element, command_completer);
+
   c = add_com ("define-prefix", class_support, define_prefix_command,
 	   _("\
 Define or mark a command as a user-defined prefix command.\n\
@@ -1732,6 +1930,20 @@ other user defined commands.\n\
 If the command already exists, it is changed to a prefix command."));
   set_cmd_completer (c, command_completer);
 
+  c = add_com ("rename", class_support, rename_command, _("\
+Rename a command.\n\
+Usage: rename OLD_NAME NEW_NAME\n\
+\n\
+If a command called NEW_NAME exists, it's overwritten.  Any subcommands\n\
+of NEW_NAME are not modified.\n\
+\n\
+If OLD_NAME has subcommands, they are preserved and OLD_NAME becomes a\n\
+prefix-only command, see the \"define-prefix\" command.\n\
+\n\
+Aliases and hooks are preserved and, after the rename, will point to\n\
+NEW_NAME."));
+  set_cmd_completer (c, command_completer);
+
   while_cmd_element = add_com ("while", class_support, while_command, _("\
 Execute nested commands WHILE the conditional expression is non zero.\n\
 The conditional expression must follow the word `while' and must in turn be\n\
diff --git a/gdb/command.h b/gdb/command.h
index ca791cff809..b20737a6aee 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -175,24 +175,37 @@ extern bool valid_cmd_char_p (int c);
    It should start with ? for a command that is an abbreviation
    or with * for a command that most users don't need to know about.
 
+   If RENAME_EXISTING_TO is not null, then the existing command called
+   NAME in *LIST will be renamed to RENAME_EXISTING_TO in
+   *RENAME_EXISTING_TO_LIST.  If a command called RENAME_EXISTING_TO
+   already exists in *RENAME_EXISTING_TO_LIST, it gets deleted first.
+   Ownership of RENAME_EXISTING_TO is not taken by this function (i.e.
+   the caller is responsible to free it if needed).
+
    If NAME already existed in *LIST, all its hooks and aliases are moved
    to the new command.
 
    Return a pointer to the added command (not necessarily the head of
    *LIST).  */
 
-extern struct cmd_list_element *add_cmd (const char *name,
-					 enum command_class theclass,
-					 cmd_const_cfunc_ftype *fun,
-					 const char *doc,
-					 struct cmd_list_element **list);
+extern struct cmd_list_element *add_cmd
+		(const char *name,
+		 enum command_class theclass,
+		 cmd_const_cfunc_ftype *fun,
+		 const char *doc,
+		 struct cmd_list_element **list,
+		 const char *rename_existing_to=nullptr,
+		 struct cmd_list_element **rename_existing_to_list=nullptr);
 
 /* Like add_cmd, but no command function is specified.  */
 
-extern struct cmd_list_element *add_cmd (const char *name,
-					 enum command_class theclass,
-					 const char *doc,
-					 struct cmd_list_element **list);
+extern struct cmd_list_element *add_cmd
+		(const char *name,
+		 enum command_class theclass,
+		 const char *doc,
+		 struct cmd_list_element **list,
+		 const char *rename_existing_to=nullptr,
+		 struct cmd_list_element **rename_existing_to_list=nullptr);
 
 extern struct cmd_list_element *add_cmd_suppress_notification
 			(const char *name, enum command_class theclass,
@@ -210,12 +223,16 @@ extern struct cmd_list_element *add_alias_cmd (const char *,
 					       struct cmd_list_element **);
 
 
-extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class,
-						cmd_const_cfunc_ftype *fun,
-						const char *,
-						struct cmd_list_element **,
-						int,
-						struct cmd_list_element **);
+extern struct cmd_list_element *add_prefix_cmd
+		(const char *name,
+		 enum command_class theclass,
+		 cmd_const_cfunc_ftype *fun,
+		 const char *doc,
+		 struct cmd_list_element **prefixlist,
+		 int allow_unknown,
+		 struct cmd_list_element **list,
+		 const char *rename_existing_to=nullptr,
+		 struct cmd_list_element **rename_existing_to_list=nullptr);
 
 /* Like add_prefix_cmd, but sets the callback to a function that
    simply calls help_list.  */
@@ -253,6 +270,15 @@ typedef void cmd_const_sfunc_ftype (const char *args, int from_tty,
 extern void set_cmd_sfunc (struct cmd_list_element *cmd,
 			   cmd_const_sfunc_ftype *sfunc);
 
+/* Rename the command called OLD_NAME in *OLD_LIST to NEW_NAME in
+   *NEW_LIST.
+
+   Return the renamed command or, if no such command exists, NULLPTR.  */
+extern struct cmd_list_element *rename_cmd (const char *old_name,
+					    struct cmd_list_element **old_list,
+					    const char *new_name,
+					    struct cmd_list_element **new_list);
+
 /* A completion routine.  Add possible completions to tracker.
 
    TEXT is the text beyond what was matched for the command itself
@@ -560,6 +586,9 @@ extern void
 				       struct cmd_list_element **set_list,
 				       struct cmd_list_element **show_list);
 
+/* Placeholder in the command data structures for user-defined commands.  */
+extern void user_defined_command (const char *ignore, int from_tty);
+
 /* Do a "show" command for each thing on a command list.  */
 
 extern void cmd_show_list (struct cmd_list_element *, int);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 3e3c38dea3a..78bee27a333 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -26949,18 +26949,41 @@ define adder
 end
 @end smallexample
 
+It's possible to replace existing commands and build on top of them, by
+using the @code{-rename-existing-to} option:
+
+@smallexample
+define -rename-existing-to original-run run
+  echo Will now run a program!\n
+  # Invoke the original "run" command which was renamed to
+  # "original-run".
+  original-run
+end
+@end smallexample
+
 @table @code
 
 @kindex define
-@item define @var{commandname}
-Define a command named @var{commandname}.  If there is already a command
-by that name, you are asked to confirm that you want to redefine it.
+@item define @r{[}-rename-existing-to @var{new-name}@r{]} @var{commandname}
+Define a command named @var{commandname}.
 The argument @var{commandname} may be a bare command name consisting of letters,
 numbers, dashes, dots, and underscores.  It may also start with any
 predefined or user-defined prefix command.
 For example, @samp{define target my-target} creates
 a user-defined @samp{target my-target} command.
 
+By default, if there is already a command by that name, you are asked to
+confirm that you want to redefine it.
+If @code{-rename-existing-to} is specified, the existing command called
+@var{commandname} is renamed to @var{new-name} first.  All subcommands,
+aliases (@pxref{Aliases}) and hooks (@pxref{Hooks}) for @var{commandname}
+will refer to the newly defined @var{commandname}, not to the renamed
+command.  This is different from using the @code{rename} command
+(@pxref{rename command}) and @code{define} separately as, in that case,
+subcommands, aliases and hooks refer to the renamed command.
+If @var{new-name} contains spaces, it must be enclosed in double quotes,
+unlike @var{commandname} which should not be enclosed in quotes.
+
 The definition of the command is made up of other @value{GDBN} command lines,
 which are given following the @code{define} command.  The end of these
 commands is marked by a line containing @code{end}.
@@ -27019,6 +27042,32 @@ command def
 (gdb)
 @end example
 
+@kindex rename
+@cindex rename command
+@anchor{rename command}
+@item rename @var{old-name} @var{new-name}
+Rename the command called @var{old-name} to @var{new-name}.
+All subcommands, aliases (@pxref{Aliases}) and hooks (@pxref{Hooks}) for
+@var{old-name} will refer to @var{new-name} after the rename.
+If a command name contains spaces, it must be enclosed in double quotes.
+
+Example:
+@example
+(gdb) define-prefix abc
+(gdb) define abc def
+Type commands for definition of "abc def".
+End with a line saying just "end".
+>echo this is the command\n
+>end
+(gdb) abc def
+this is the command
+(gdb) rename "abc def" renamed-command
+(gdb) abc def
+Undefined abc command: "def".  Try "help abc".
+(gdb) renamed-command
+this is the command
+@end example
+
 @kindex dont-repeat
 @cindex don't repeat command
 @item dont-repeat
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 35568594f58..22155f595c6 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -3657,7 +3657,7 @@ You can implement new @value{GDBN} CLI commands in Python.  A CLI
 command is implemented using an instance of the @code{gdb.Command}
 class, most commonly using a subclass.
 
-@defun Command.__init__ (name, @var{command_class} @r{[}, @var{completer_class} @r{[}, @var{prefix}@r{]]})
+@defun Command.__init__ (name, @var{command_class} @r{[}, @var{completer_class} @r{[}, @var{prefix} @r{[}, @var{rename_existing_to}@r{]]]})
 The object initializer for @code{Command} registers the new command
 with @value{GDBN}.  This initializer is normally invoked from the
 subclass' own @code{__init__} method.
@@ -3671,7 +3671,9 @@ There is no support for multi-line commands.
 
 @var{command_class} should be one of the @samp{COMMAND_} constants
 defined below.  This argument tells @value{GDBN} how to categorize the
-new command in the help system.
+new command in the help system.  This argument is required unless
+@var{rename_existing_to} is specified, in which case it defaults to the
+value used by the existing command being renamed.
 
 @var{completer_class} is an optional argument.  If given, it should be
 one of the @samp{COMPLETE_} constants defined below.  This argument
@@ -3682,7 +3684,19 @@ error will occur when completion is attempted.
 
 @var{prefix} is an optional argument.  If @code{True}, then the new
 command is a prefix command; sub-commands of this command may be
-registered.
+registered.  If @var{rename_existing_to} is set, then @var{prefix} must
+not be set as whether the command is a prefix command or not is inherited
+from the existing command being renamed.
+
+@var{rename_existing_to} is an optional argument.  If set, then the
+existing command called @var{name} is renamed to @var{rename_existing_to}.
+Existing subcommands, aliases and hooks are preserved and will refer to
+the newly defined command.  Additionally, then @var{prefix} must be unset
+as whether the new command is a prefix or not will depend on whether the
+original command was a prefix command or not.
+When renaming an existing command, @var{command_class} and
+@var{completer_class}, if unspecified, are copied from the existing
+command.
 
 The help text for the new command is taken from the Python
 documentation string for the command's class, if there is one.  If no
@@ -3931,6 +3945,24 @@ registration of the command with @value{GDBN}.  Depending on how the
 Python code is read into @value{GDBN}, you may need to import the
 @code{gdb} module explicitly.
 
+The following code snippet shows how a CLI command building on top of an
+existing command can be implemented:
+
+@smallexample
+class MyQuit (gdb.Command):
+  """Print a message before executing the "quit" command."""
+
+  def __init__ (self):
+    super (MyQuit, self).__init__ ("quit", gdb.COMMAND_RUNNING,
+                                  rename_existing_to="original-quit")
+
+  def invoke (self, arg, from_tty):
+    print ("Will now quit!")
+    gdb.execute ("original-quit %s" % arg, from_tty=from_tty)
+
+MyQuit ()
+@end smallexample
+
 @node Parameters In Python
 @subsubsection Parameters In Python
 
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index 9833bf863b0..e5cb7a881ca 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -431,15 +431,22 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 {
   cmdpy_object *obj = (cmdpy_object *) self;
   const char *name;
-  int cmdtype;
-  int completetype = -1;
+  long cmdtype;
+  long completetype = -1;
   char *docstring = NULL;
   struct cmd_list_element **cmd_list;
   char *cmd_name;
   static const char *keywords[] = { "name", "command_class", "completer_class",
-				    "prefix", NULL };
+				    "prefix", "rename_existing_to",
+				    NULL };
+  PyObject *cmdtype_obj = NULL;
+  PyObject *completetype_obj = NULL;
   PyObject *is_prefix_obj = NULL;
   bool is_prefix = false;
+  const char *rename_existing_to = NULL;
+  char *rename_existing_to_cmd_name = NULL;
+  struct cmd_list_element *renamed_cmd = NULL;
+  struct cmd_list_element **rename_existing_to_cmd_list = nullptr;
 
   if (obj->command)
     {
@@ -450,40 +457,130 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
       return -1;
     }
 
-  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "si|iO",
-					keywords, &name, &cmdtype,
-					&completetype, &is_prefix_obj))
+  /* The second argument (command_class) is marked as optional because
+     it should not be specified if RENAME_EXISTING_TO is specified.
+     Otherwise it's required.  */
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!Os",
+					keywords,
+					&name,
+					&PyInt_Type, &cmdtype_obj,
+					&PyInt_Type, &completetype_obj,
+					&is_prefix_obj,
+					&rename_existing_to))
     return -1;
 
-  if (cmdtype != no_class && cmdtype != class_run
-      && cmdtype != class_vars && cmdtype != class_stack
-      && cmdtype != class_files && cmdtype != class_support
-      && cmdtype != class_info && cmdtype != class_breakpoint
-      && cmdtype != class_trace && cmdtype != class_obscure
-      && cmdtype != class_maintenance && cmdtype != class_user
-      && cmdtype != class_tui)
-    {
-      PyErr_Format (PyExc_RuntimeError, _("Invalid command class argument."));
-      return -1;
-    }
-
-  if (completetype < -1 || completetype >= (int) N_COMPLETERS)
-    {
-      PyErr_Format (PyExc_RuntimeError,
-		    _("Invalid completion type argument."));
-      return -1;
-    }
-
   cmd_name = gdbpy_parse_command_name (name, &cmd_list, &cmdlist);
   if (! cmd_name)
     return -1;
 
+  /* Deal with rename_existing_to before other arguments as it affects how
+     some of them are treated.  */
+  if (rename_existing_to != NULL)
+    {
+      renamed_cmd = lookup_cmd_exact (cmd_name, *cmd_list);
+      if (renamed_cmd == nullptr)
+	{
+	  PyErr_Format (PyExc_RuntimeError,
+			_("Command \"%s\" does not exist, so it cannot "
+			  "be renamed to \"%s\"."),
+			name, rename_existing_to);
+	  xfree (cmd_name);
+	  return -1;
+	}
+
+      rename_existing_to_cmd_name =
+	gdbpy_parse_command_name (rename_existing_to,
+				  &rename_existing_to_cmd_list,
+				  &cmdlist);
+      if (! rename_existing_to_cmd_name)
+	{
+	  xfree (cmd_name);
+	  return -1;
+	}
+    }
+
+  if (cmdtype_obj == NULL)
+    {
+      /* COMMAND_CLASS is optional only when rename_existing_to is
+	 specified.  This error matches the phrasing of the error the
+	 interpreter would raise if COMMAND_CLASS was not marked as
+	 optional in gdb_PyArg_ParseTupleAndKeywords.  */
+      if (renamed_cmd == NULL)
+	{
+	  PyErr_Format (PyExc_TypeError,
+			_("function missing required argument "
+			  "'command_class' (pos 2)"));
+	  xfree (cmd_name);
+	  xfree (rename_existing_to_cmd_name);
+	  return -1;
+	}
+
+      cmdtype = renamed_cmd->theclass;
+    }
+  else
+    {
+      cmdtype = PyInt_AsLong (cmdtype_obj);
+      if (PyErr_Occurred ())
+	{
+	  xfree (cmd_name);
+	  xfree (rename_existing_to_cmd_name);
+	  return -1;
+	}
+      if (cmdtype != no_class && cmdtype != class_run
+	  && cmdtype != class_vars && cmdtype != class_stack
+	  && cmdtype != class_files && cmdtype != class_support
+	  && cmdtype != class_info && cmdtype != class_breakpoint
+	  && cmdtype != class_trace && cmdtype != class_obscure
+	  && cmdtype != class_maintenance && cmdtype != class_user
+	  && cmdtype != class_tui)
+	{
+	  PyErr_Format (PyExc_RuntimeError,
+			_("Invalid command class argument."));
+	  xfree (cmd_name);
+	  xfree (rename_existing_to_cmd_name);
+	  return -1;
+	}
+    }
+
+  if (completetype_obj != NULL)
+    {
+      completetype = PyInt_AsLong (completetype_obj);
+      if (PyErr_Occurred ())
+	{
+	  xfree (cmd_name);
+	  xfree (rename_existing_to_cmd_name);
+	  return -1;
+	}
+    }
+
+  if (completetype < -1 || completetype >= (int) N_COMPLETERS)
+    {
+      PyErr_Format (PyExc_RuntimeError,
+		    _("Invalid completion type argument."));
+      xfree (cmd_name);
+      xfree (rename_existing_to_cmd_name);
+      return -1;
+    }
+
   if (is_prefix_obj != NULL)
     {
+      if (renamed_cmd != NULL)
+	{
+	  PyErr_Format
+	    (PyExc_TypeError,
+	     _("Cannot specify argument 'prefix' with 'rename_existing_to'.  "
+	       "Whether the command is a prefix command depends on the "
+	       "renamed command."));
+	  xfree (cmd_name);
+	  xfree (rename_existing_to_cmd_name);
+	  return -1;
+	}
+
       int cmp = PyObject_IsTrue (is_prefix_obj);
-       if (cmp < 0)
+      if (cmp < 0)
 	{
 	  xfree (cmd_name);
+	  xfree (rename_existing_to_cmd_name);
 	  return -1;
 	}
        is_prefix = cmp > 0;
@@ -499,6 +596,7 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 	  if (docstring == NULL)
 	    {
 	      xfree (cmd_name);
+	      xfree (rename_existing_to_cmd_name);
 	      return -1;
 	    }
 	}
@@ -521,11 +619,15 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 	  allow_unknown = PyObject_HasAttr (self, invoke_cst);
 	  cmd = add_prefix_cmd (cmd_name, (enum command_class) cmdtype,
 				NULL, docstring, &obj->sub_list,
-				allow_unknown, cmd_list);
+				allow_unknown, cmd_list,
+				rename_existing_to_cmd_name,
+				rename_existing_to_cmd_list);
 	}
       else
 	cmd = add_cmd (cmd_name, (enum command_class) cmdtype,
-		       docstring, cmd_list);
+		       docstring, cmd_list,
+		       rename_existing_to_cmd_name,
+		       rename_existing_to_cmd_list);
 
       /* There appears to be no API to set this.  */
       cmd->func = cmdpy_function;
@@ -535,15 +637,30 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 
       obj->command = cmd;
       set_cmd_context (cmd, self_ref.release ());
-      set_cmd_completer (cmd, ((completetype == -1) ? cmdpy_completer
-			       : completers[completetype].completer));
-      if (completetype == -1)
-	set_cmd_completer_handle_brkchars (cmd,
-					   cmdpy_completer_handle_brkchars);
+
+      if (completetype_obj == NULL && renamed_cmd != NULL)
+	{
+	    /* No explicit completer was passed and the existing command
+	       was renamed, so use the command completer information from
+	       the original command.  */
+	    set_cmd_completer (cmd, renamed_cmd->completer);
+	    set_cmd_completer_handle_brkchars
+	      (cmd, renamed_cmd->completer_handle_brkchars);
+	}
+      else
+	{
+	  set_cmd_completer (cmd, ((completetype == -1) ? cmdpy_completer
+				   : completers[completetype].completer));
+	  if (completetype == -1)
+	    set_cmd_completer_handle_brkchars (cmd,
+					       cmdpy_completer_handle_brkchars);
+
+	}
     }
   catch (const gdb_exception &except)
     {
       xfree (cmd_name);
+      xfree (rename_existing_to_cmd_name);
       xfree (docstring);
       gdbpy_convert_exception (except);
       return -1;
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 56702cad53a..e4b4facdda9 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -95,6 +95,7 @@
 #define Py_TPFLAGS_HAVE_ITER 0
 #define Py_TPFLAGS_CHECKTYPES 0
 
+#define PyInt_Type PyLong_Type
 #define PyInt_Check PyLong_Check
 #define PyInt_AsLong PyLong_AsLong
 #define PyInt_AsSsize_t PyLong_AsSsize_t
diff --git a/gdb/testsuite/gdb.base/command-renaming.exp b/gdb/testsuite/gdb.base/command-renaming.exp
new file mode 100644
index 00000000000..238a735dfb0
--- /dev/null
+++ b/gdb/testsuite/gdb.base/command-renaming.exp
@@ -0,0 +1,571 @@
+# Copyright (C) 2009-2021 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests renaming of commands.
+
+standard_testfile
+
+clean_restart
+
+proc prepare_gdb {} {
+  gdb_exit
+  gdb_start
+}
+
+# Test basic usage of the "rename" command.
+#
+proc test_rename_ok {} {
+  prepare_gdb
+
+  with_test_prefix "basic" {
+    gdb_test_no_output "rename run my-run"
+    # The old "run" command doesn't exist any more.
+    gdb_test_exact "run" \
+      "Undefined command: \"run\".  Try \"help\"."
+    # The new one works.
+    gdb_test "my-run" ".*No executable file specified.*"
+    # The "r" alias still works.
+    gdb_test "r" ".*No executable file specified.*"
+  }
+
+  with_test_prefix "hooks" {
+    gdb_define_cmd "original" {
+      "echo Original\\n"
+    }
+    gdb_test_no_output "alias alias-original = original"
+    gdb_define_cmd "hook-original" {
+      "echo Before original\\n"
+    }
+    gdb_define_cmd "hookpost-original" {
+      "echo After original\\n"
+    }
+
+    gdb_test_no_output "rename original renamed"
+    # The old "original" command doesn't exist any more.
+    gdb_test_exact "original" \
+      "Undefined command: \"original\".  Try \"help\"."
+    # The new one works, including hooks.
+    gdb_test_exact "renamed" \
+      "Before original\nOriginal\nAfter original"
+    # The "alias-original" alias still works, including hooks.
+    gdb_test_exact "alias-original" \
+      "Before original\nOriginal\nAfter original"
+  }
+}
+
+# Test that "rename" deals correctly with error conditions.
+
+proc test_rename_errors {} {
+  prepare_gdb
+
+  with_test_prefix "non-existing command" {
+    gdb_test_exact "rename nope renamed" \
+      "Command \"nope\" does not exist, so it cannot be renamed to \"renamed\"."
+  }
+
+  with_test_prefix "same old and new name" {
+    gdb_test_exact "rename run run" \
+      "Cannot rename command \"run\" as the new name is identical to the existing one."
+  }
+
+  with_test_prefix "rename to hook" {
+    gdb_define_cmd "my-cmd" {
+      "echo Command\\n"
+    }
+
+    with_test_prefix "pre" {
+      gdb_test_exact "rename my-cmd hook-echo" \
+	"Cannot define hooks by renaming commands."
+    }
+
+    with_test_prefix "post" {
+      gdb_test_exact "rename my-cmd hookpost-echo" \
+	"Cannot define hooks by renaming commands."
+    }
+  }
+}
+
+# Test that "rename" works when the prefix of the renamed command changes.
+
+proc test_rename_across_prefixes {} {
+  prepare_gdb
+
+  with_test_prefix "rename with prefixes" {
+    gdb_test_no_output "define-prefix prefix1"
+    gdb_test_no_output "define-prefix prefix2"
+    gdb_test_no_output "define-prefix prefix2 sub-prefix"
+
+    gdb_define_cmd "prefix1 foo" {
+      "echo Foo\\n"
+    }
+
+    gdb_test_no_output "rename 'prefix1 foo' 'prefix2 sub-prefix renamed-foo'"
+
+    gdb_test_exact "prefix1 foo" \
+      "Undefined prefix1 command: \"foo\".  Try \"help prefix1\"."
+    gdb_test_exact "prefix2 sub-prefix renamed-foo" "Foo"
+  }
+}
+
+# Test that renaming of prefix commands works.
+
+proc test_rename_prefixes {} {
+  prepare_gdb
+  gdb_test_no_output "set confirm off"
+
+  with_test_prefix "rename of prefixes" {
+    # Define two prefixes, each one with a subcommand.
+    foreach letter {"A" "B"} {
+      gdb_test_no_output "define-prefix prefix-${letter}"
+      gdb_define_cmd "prefix-${letter}" [subst {
+	"echo Original prefix ${letter}\\\\n"
+      }]
+      gdb_define_cmd "prefix-${letter} sub-${letter}" [subst {
+	"echo Subcommand ${letter} of prefix ${letter}\\\\n"
+      }]
+    }
+
+    gdb_test_no_output "rename prefix-A prefix-B"
+
+    # The subcommands still work as before, the rename didn't affect them.
+    gdb_test_exact "prefix-A sub-A" "Subcommand A of prefix A"
+    gdb_test_exact "prefix-B sub-B" "Subcommand B of prefix B"
+    # "prefix-B"'s implementation is what used to be "prefix-A"'s
+    # implementation.
+    gdb_test_exact "prefix-B" "Original prefix A"
+    # "prefix-A" is just a prefix command without any code associated with
+    # it, and it exists only for the sake of providing subcommands.
+    gdb_test "prefix-A" \
+      ".*\"prefix-A\" must be followed by the name of a subcommand.*"
+  }
+}
+
+# Test that "rename" works when it leads to a command being overwritten by
+# the renamed one.
+#
+# PREFIX, if not empty, if the prefix for the renamed and overwritten
+# commands.
+
+proc test_rename_redefine {prefix} {
+  global gdb_prompt
+
+  prepare_gdb
+
+  with_test_prefix "overwriting existing command by renaming, prefix=\"${prefix}\"" {
+    if { $prefix == "" } {
+      set foo_cmd "foo"
+      set bar_cmd "bar"
+    } else {
+      gdb_test_no_output "define-prefix ${prefix}"
+      set foo_cmd "${prefix} foo"
+      set bar_cmd "${prefix} bar"
+    }
+
+    # Define "foo".
+    gdb_define_cmd $foo_cmd {
+      "echo Original foo\\n"
+    }
+
+    # Define an alias to the original "foo" with the same prefix.
+    gdb_test_no_output "alias ${prefix} alias-to-original-foo-same-prefix = ${foo_cmd}"
+    # And another one but without a prefix.
+    gdb_test_no_output "alias alias-to-original-foo-no-prefix = ${foo_cmd}"
+
+    # Define hooks for the original "foo".
+    gdb_define_cmd "${prefix} hook-foo" {
+      "echo Pre-hook for original foo\\n"
+    }
+    gdb_define_cmd "${prefix} hookpost-foo" {
+      "echo Post-hook for original foo\\n"
+    }
+
+    # Define "bar".
+    gdb_define_cmd $bar_cmd {
+      "echo Original bar\\n"
+    }
+
+    # Rename "bar" to "foo", which deletes the old "foo".
+    send_gdb "rename '${bar_cmd}' '${foo_cmd}'\n"
+    gdb_expect {
+      -re "Redefine command \"foo\".*y or n. $"\
+	      {send_gdb "y\n"
+	       gdb_expect {
+		 -re "$gdb_prompt $"\
+		   {pass "rename user command"}
+		 timeout {fail "(timeout) rename user command"}
+	       }
+	     }
+      -re "$gdb_prompt $"\
+	      {fail "redefine user command"}
+      timeout {fail "(timeout) rename user command"}
+    }
+
+    # "foo" should be the new command (which used to be called "bar",
+    # hence the message).
+    gdb_test_exact $foo_cmd "Original bar"
+
+    # The aliases to the original "foo" should have been deleted.
+    gdb_test "${prefix} alias-to-original-foo-same-prefix" \
+      ".*Undefined.* command:.*"
+    gdb_test "alias-to-original-foo-no-prefix" \
+      ".*Undefined command:.*"
+
+    # The hooks for the original "foo" should have been deleted as well.
+    gdb_test "${prefix} hook-foo" \
+      ".*Undefined.* command:.*"
+    gdb_test "${prefix} hookpost-foo" \
+      ".*Undefined.* command:.*"
+  }
+}
+
+# Test that, when a command with hooks is renamed, the hooks get updated
+# as well.
+
+proc test_rename_hooks_updated {} {
+  prepare_gdb
+
+  with_test_prefix "hooks renamed if command renamed" {
+    # Define some hooks for "delete".
+    gdb_define_cmd "hook-delete" {
+      "echo Pre-hook\\n"
+    }
+    gdb_define_cmd "hookpost-delete" {
+      "echo Post-hook\\n"
+    }
+    # Define aliases for the hooks.
+    gdb_test_no_output "alias hd-pre = hook-delete"
+    gdb_test_no_output "alias hd-post = hookpost-delete"
+    # Rename "delete" to "my-prefix delete-cmd"
+    gdb_test_no_output "define-prefix my-prefix"
+    gdb_test_no_output "rename 'delete' 'my-prefix delete-cmd'"
+
+    # The renamed command must still work.
+    gdb_test_exact "my-prefix delete-cmd 9999" \
+      "Pre-hook\nNo breakpoint number 9999.\nPost-hook"
+
+    # Hooks must have been renamed as well.
+    gdb_test_exact "my-prefix hook-delete-cmd" "Pre-hook"
+    gdb_test_exact "my-prefix hookpost-delete-cmd" "Post-hook"
+    # And the aliases to the hooks must still work.
+    gdb_test_exact "hd-pre" "Pre-hook"
+    gdb_test_exact "hd-post" "Post-hook"
+    # The old names for the hooks don't work any more (as they were
+    # renamed).
+    gdb_test "hook-delete" "Undefined command.*"
+    gdb_test "hookpost-delete" "Undefined command.*"
+    # The help for the hooks and their aliases must show the correct
+    # updated names.
+    gdb_test "help my-prefix hook-delete-cmd" \
+      "my-prefix hook-delete-cmd, hd-pre.*"
+    gdb_test "help hd-pre" "my-prefix hook-delete-cmd, hd-pre.*"
+    gdb_test "help my-prefix hookpost-delete-cmd" \
+      "my-prefix hookpost-delete-cmd, hd-post.*"
+    gdb_test "help hd-post" "my-prefix hookpost-delete-cmd, hd-post.*"
+  }
+}
+
+# Helper function for defining a command called "new" and then redefining
+# it with "define -rename-existing-to old new".
+#
+# If CREATE_ALIAS is 1, then an "alias-to-new" alias to the original
+# command is created.
+#
+# If CREATE_PRE_HOOK is 1, then a hook for the original command is
+# created.
+#
+# CREATE_POST_HOOK behaves likes CREATE_PRE_HOOK except that it creates
+# a post-hook.
+
+proc define_with_rename {create_alias create_pre_hook create_post_hook} {
+  prepare_gdb
+
+  gdb_define_cmd "new" {
+    "echo Original\\n"
+  }
+
+  if {$create_alias} then {
+    gdb_test_no_output "alias alias-to-new = new"
+  }
+
+  if {$create_pre_hook} then {
+    gdb_define_cmd "hook-new" {
+      "echo Pre-hook\\n"
+    }
+  }
+
+  if {$create_post_hook} then {
+    gdb_define_cmd "hookpost-new" {
+      "echo Post-hook\\n"
+    }
+  }
+
+  gdb_define_cmd "-rename-existing-to old new" {
+    "echo Redefined: before\\n"
+    "old"
+    "echo Redefined: after\\n"
+  }
+}
+
+
+# Test that "define -rename-existing-to" works with and without aliases
+# and hooks.
+
+proc test_define_renaming_ok {} {
+  with_test_prefix "basic" {
+    define_with_rename 0 0 0
+    gdb_test_exact "new" \
+      "Redefined: before\nOriginal\nRedefined: after"
+    gdb_test_exact "old" "Original"
+  }
+
+  with_test_prefix "alias" {
+    define_with_rename 1 0 0
+    gdb_test_exact "new" \
+      "Redefined: before\nOriginal\nRedefined: after"
+    gdb_test_exact "alias-to-new" \
+      "Redefined: before\nOriginal\nRedefined: after"
+    gdb_test_exact "old" "Original"
+  }
+
+  with_test_prefix "pre-hook" {
+    define_with_rename 0 1 0
+    gdb_test_exact "new" \
+      "Pre-hook\nRedefined: before\nOriginal\nRedefined: after"
+    gdb_test_exact "old" "Original"
+  }
+
+  with_test_prefix "post-hook" {
+    define_with_rename 0 0 1
+    gdb_test_exact "new" \
+      "Redefined: before\nOriginal\nRedefined: after\nPost-hook"
+    gdb_test_exact "old" "Original"
+  }
+
+  with_test_prefix "alias, pre-hook and post-hook" {
+    define_with_rename 1 1 1
+    gdb_test_exact "new" \
+      "Pre-hook\nRedefined: before\nOriginal\nRedefined: after\nPost-hook"
+    gdb_test_exact "alias-to-new" \
+      "Pre-hook\nRedefined: before\nOriginal\nRedefined: after\nPost-hook"
+    gdb_test_exact "old" "Original"
+  }
+}
+
+# Test that renaming with "define -rename-existing-to" works for builtin
+# commands.
+
+proc test_define_renaming_builtin {} {
+  prepare_gdb
+  gdb_test_no_output "set confirm off"
+
+  with_test_prefix "rename builtin" {
+    gdb_define_cmd "-rename-existing-to original-run run" {
+      "echo Redefined run\\n"
+    }
+
+    gdb_test_exact "run" "Redefined run"
+
+    gdb_test "original-run" ".*No executable file specified.*"
+  }
+}
+
+# Test that "define -rename-existing-to" deals correctly with error
+# conditions.
+
+proc test_define_renaming_errors {} {
+  prepare_gdb
+
+  with_test_prefix "non-existing command" {
+    gdb_test_exact "define -rename-existing-to renamed nope" \
+      "Command \"nope\" does not exist, so it cannot be renamed to \"renamed\"."
+  }
+
+  with_test_prefix "same old and new name" {
+    gdb_test_exact "define -rename-existing-to run run" \
+      "Cannot rename command \"run\" as the new name is identical to the existing one."
+  }
+
+  with_test_prefix "rename to hook" {
+    gdb_define_cmd "my-cmd" {
+      "echo Command\\n"
+    }
+
+    with_test_prefix "pre" {
+      gdb_test_exact "define -rename-existing-to hook-echo my-cmd" \
+	"Cannot define hooks by renaming commands."
+    }
+
+    with_test_prefix "post" {
+      gdb_test_exact "define -rename-existing-to hookpost-echo my-cmd" \
+	"Cannot define hooks by renaming commands."
+    }
+  }
+}
+
+# Test that using "define -rename-existing-to" to rename to a different
+# prefix works.
+
+proc test_define_renaming_across_prefixes {} {
+  prepare_gdb
+
+  with_test_prefix "define -rename-existing-to prefixes" {
+    gdb_test_no_output "define-prefix prefix1"
+    gdb_test_no_output "define-prefix prefix2"
+    gdb_test_no_output "define-prefix prefix2 sub-prefix"
+
+    gdb_define_cmd "prefix1 foo" {
+      "echo First\\n"
+    }
+
+    gdb_define_cmd "-rename-existing-to 'prefix2 sub-prefix renamed-foo' prefix1 foo" {
+      "echo Second\\n"
+    }
+
+    gdb_test_exact "prefix2 sub-prefix renamed-foo" "First"
+    gdb_test_exact "prefix1 foo" "Second"
+  }
+}
+
+# Test that renaming prefixes with "define -rename-existing-to" works.
+
+proc test_define_renaming_prefixes {} {
+  prepare_gdb
+
+  with_test_prefix "define -rename-existing-to of prefixes" {
+    # Define a prefix with an implementation.
+    gdb_test_no_output "define-prefix prefix"
+    gdb_define_cmd "prefix" {
+      "echo Original prefix\\n"
+    }
+    # Define a subcommand.
+    gdb_define_cmd "prefix subcommand" {
+      "echo Subcommand\\n"
+    }
+
+    # Define a new implementation of "prefix" renaming the existing to
+    # "renamed-prefix".
+    gdb_define_cmd "-rename-existing-to renamed-prefix prefix" {
+      "echo New prefix\\n"
+    }
+
+    # "renamed-prefix" still works as a command.
+    gdb_test_exact "renamed-prefix" "Original prefix"
+    # The subcommand remains a subcommand of "prefix", not of its renamed
+    # implementation, so "renamed-prefix subcommand" doesn't exist (and
+    # never existed).
+    # This means that that "renamed-prefix subcommand" invokes the
+    # "renamed-prefix" command with "subcommand" as argument (i.e.
+    # subcommand is not a command), so the original implementation of
+    # what was "prefix" is invoked.
+    gdb_test_exact "renamed-prefix subcommand" "Original prefix"
+    # The newly defined prefix implementation works.
+    gdb_test_exact "prefix" "New prefix"
+    # And the subcommand works as well.
+    gdb_test_exact "prefix subcommand" "Subcommand"
+  }
+}
+
+# Test that "define -rename-existing-to" works when it leads to a command
+# being overwritten by the renamed one.
+#
+# PREFIX, if not empty, if the prefix for the renamed and overwritten
+# commands.
+
+proc test_define_renaming_redefine {prefix} {
+  global gdb_prompt
+
+  prepare_gdb
+
+  with_test_prefix "overwriting existing command with define -rename-existing-to, prefix=\"${prefix}\"" {
+    if { $prefix == "" } {
+      set foo_cmd "foo"
+      set bar_cmd "bar"
+    } else {
+      gdb_test_no_output "define-prefix ${prefix}"
+      set foo_cmd "${prefix} foo"
+      set bar_cmd "${prefix} bar"
+    }
+
+    # Define "foo".
+    gdb_define_cmd $foo_cmd {
+      "echo Original foo\\n"
+    }
+
+    # Define an alias to the original "foo" with the same prefix.
+    gdb_test_no_output "alias ${prefix} alias-to-original-foo-same-prefix = ${foo_cmd}"
+    # And another one but without a prefix.
+    gdb_test_no_output "alias alias-to-original-foo-no-prefix = ${foo_cmd}"
+
+    # Define "bar".
+    gdb_define_cmd $bar_cmd {
+      "echo Original bar\\n"
+    }
+
+    # Redefine "bar" while renaming the original "bar" to "foo".
+    send_gdb "define -rename-existing-to '${foo_cmd}' ${bar_cmd}\n"
+    gdb_expect {
+      -re "Redefine command \"foo\".*y or n. $"\
+	      {send_gdb "y\n"
+	       gdb_expect {
+		 -re "Type commands for definition of \"${bar_cmd}\".\r\nEnd with a line saying just \"end\".\r\n>$"\
+			 {send_gdb "echo New bar\\n\n${foo_cmd}\nend\n"
+			  gdb_expect {
+			    -re "$gdb_prompt $"\
+				    {pass "redefine user command"}
+			    timeout {fail "(timeout) redefine user command"}
+			  }
+			 }
+		 timeout {fail "(timeout) redefine user command"}
+	       }
+	      }
+      -re "$gdb_prompt $"\
+	      {fail "redefine user command"}
+      timeout {fail "(timeout) redefine user command"}
+    }
+
+    # "bar" should be the new command which calls the old orginal "bar".
+    gdb_test_exact $bar_cmd "New bar\nOriginal bar"
+
+    # The aliases to the original foo should have been deleted.
+    gdb_test "${prefix} alias-to-original-foo-same-prefix" \
+      ".*Undefined.* command:.*"
+    gdb_test "alias-to-original-foo-no-prefix" \
+      ".*Undefined command:.*"
+  }
+}
+
+with_test_prefix "command renaming" {
+  # Tests for "rename".
+  with_test_prefix "rename" {
+    test_rename_ok
+    test_rename_errors
+    test_rename_across_prefixes
+    test_rename_prefixes
+    test_rename_redefine ""
+    test_rename_redefine "my-prefix"
+    test_rename_hooks_updated
+  }
+
+  # Tests for the "define -rename-existing-to".
+  with_test_prefix "define -rename-existing-to" {
+    test_define_renaming_ok
+    test_define_renaming_builtin
+    test_define_renaming_errors
+    test_define_renaming_across_prefixes
+    test_define_renaming_prefixes
+    test_define_renaming_redefine ""
+    test_define_renaming_redefine "my-prefix"
+  }
+}
diff --git a/gdb/testsuite/gdb.python/py-rename-existing.exp b/gdb/testsuite/gdb.python/py-rename-existing.exp
new file mode 100644
index 00000000000..189633b068a
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-rename-existing.exp
@@ -0,0 +1,364 @@
+# Copyright (C) 2009-2021 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests renaming of existing
+# commands.
+
+load_lib gdb-python.exp
+load_lib completion-support.exp
+
+standard_testfile
+
+# Prepare for testing.
+#
+# This quits GDB (if running), starts a new one, and loads any required
+# external scripts.
+
+proc prepare_gdb {} {
+  global srcdir subdir testfile
+
+  gdb_exit
+  gdb_start
+  gdb_reinitialize_dir $srcdir/$subdir
+
+  # Skip all tests if Python scripting is not enabled.
+  if { [skip_python_tests] } { continue }
+
+  gdb_test_no_output "set confirm off"
+
+  # Load the code which adds commands.
+  set remote_python_file \
+    [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+  gdb_test_no_output "source ${remote_python_file}" "load python file"
+}
+
+# Add a command called CMD through the Python API renaming the existing
+# command of the same name to RENAME_EXISTING_TO.
+#
+# When invoked, the command prints messages and call the renamed command.
+# If MSG is not empty then it will be printed when the command is invoked.
+# Otherwise, the command name is printed.
+#
+# EXTRA_ARGS is passed as it to gdb.Command.__init__ after the other
+# arguments.
+
+proc add_py_cmd {cmd rename_existing_to {msg ""} {kwargs ""}} {
+  gdb_test_no_output \
+    "python TestCommand ('${cmd}', '${rename_existing_to}', '${msg}', ${kwargs})"
+}
+
+# test_sequence_exact CMD LIST
+# Like gdb_test_exact but, for convenience, it accepts a list of lines
+# instead of a single line.
+
+proc test_sequence_exact { cmd lines } {
+  set expected_string [join $lines "\n"]
+  gdb_test_exact $cmd $expected_string
+}
+
+# Test that renaming existing command works and that, after a rename,
+# aliases execute the new command.
+
+proc test_rename_simple {} {
+  with_test_prefix "rename command" {
+    prepare_gdb
+
+    gdb_define_cmd "my-cmd" {
+      "echo my-cmd original\\n"
+    }
+    gdb_test_no_output "alias my-cmd-alias = my-cmd"
+
+    add_py_cmd "my-cmd" "original-my-cmd"
+
+    set expected_list {
+      "py-before: my-cmd"
+      "my-cmd original"
+      "py-after: my-cmd"
+    }
+
+    test_sequence_exact "my-cmd" $expected_list
+    test_sequence_exact "my-cmd-alias" $expected_list
+  }
+}
+
+# Test that renaming a command multiple times works.
+
+proc test_rename_multiple {} {
+  with_test_prefix "rename command multiple times" {
+    prepare_gdb
+
+    gdb_define_cmd "my-cmd" {
+      "echo my-cmd original\\n"
+    }
+
+    add_py_cmd "my-cmd" "original-my-cmd" "first my-cmd"
+    add_py_cmd "my-cmd" "first-redefined-my-cmd" "second my-cmd"
+
+    test_sequence_exact "my-cmd" {
+      "py-before: second my-cmd"
+      "py-before: first my-cmd"
+      "my-cmd original"
+      "py-after: first my-cmd"
+      "py-after: second my-cmd"
+    }
+  }
+}
+
+# Test that trying to rename a non-existing command raises a RuntimeError.
+
+proc test_rename_non_existing {} {
+  with_test_prefix "rename non-existing" {
+    prepare_gdb
+
+    gdb_test \
+      "python TestCommand ('this_doesnt_exist', 'this_doesnt_matter')" \
+      ".*RuntimeError: Command \"this_doesnt_exist\" does not exist,\
+      so it cannot be renamed to \"this_doesnt_matter\"\..*"
+  }
+}
+
+# Test renaming of a command with a prefix.
+
+proc test_same_prefix {} {
+  with_test_prefix "rename keeping the same prefix" {
+    prepare_gdb
+
+    add_py_cmd "info args" "info renamed-args"
+
+    test_sequence_exact "info args" {
+      "py-before: info args"
+      "No frame selected."
+      "py-after: info args"
+    }
+    gdb_test_exact "info renamed-args" "No frame selected."
+  }
+}
+
+# Test renaming of a command without a prefix to a command with a prefix.
+
+proc test_rename_adding_prefix {} {
+  with_test_prefix "rename adding a prefix" {
+    prepare_gdb
+
+    gdb_define_cmd "my-cmd" {
+      "echo my-cmd original\\n"
+    }
+
+    gdb_test_no_output "define-prefix my-prefix"
+
+    add_py_cmd "my-cmd" "my-prefix my-cmd"
+
+    test_sequence_exact "my-cmd" {
+      "py-before: my-cmd"
+      "my-cmd original"
+      "py-after: my-cmd"
+    }
+    gdb_test_exact "my-prefix my-cmd" "my-cmd original"
+  }
+}
+
+# Test renaming of a command with a prefix to a command without prefix.
+
+proc test_rename_removing_prefix {} {
+  with_test_prefix "rename removing a prefix" {
+    prepare_gdb
+
+    add_py_cmd "info args" "renamed-info-args"
+
+    test_sequence_exact "info args" {
+      "py-before: info args"
+      "No frame selected."
+      "py-after: info args"
+    }
+    gdb_test_exact "renamed-info-args" "No frame selected."
+  }
+}
+
+# Test renaming of a command with prefix to a different prefix.
+
+proc test_rename_changing_prefix {} {
+  with_test_prefix "rename changing prefix" {
+    prepare_gdb
+
+    gdb_test_no_output "define-prefix my-prefix"
+
+    add_py_cmd "info args" "my-prefix args"
+
+    test_sequence_exact "info args" {
+      "py-before: info args"
+      "No frame selected."
+      "py-after: info args"
+    }
+    gdb_test_exact "my-prefix args" "No frame selected."
+  }
+}
+
+# Test that replacing a prefix command replaces its implementation but
+# doesn't affect its subcommands.
+
+proc test_replace_prefix {} {
+  with_test_prefix "replace a prefix command" {
+    prepare_gdb
+
+    # Replace the "show" command (which has subcommands).
+    add_py_cmd "show" "original-show"
+
+    # "original-show" won't print anything as it doesn't have subcommands.
+    test_sequence_exact "show" {
+      "py-before: show"
+      "py-after: show"
+    }
+
+    gdb_test_no_output "set pagination off"
+    # Subcommands still work.
+    gdb_test_exact "show pagination" "State of pagination is off."
+  }
+}
+
+# Test that prefix cannot be passed to gdb.Command.__init__ if
+# rename_existing_to is used.
+
+proc test_prefix_arg_not_allowed {} {
+  with_test_prefix "prefix argument not allowed with rename_existing_to" {
+    prepare_gdb
+
+    with_test_prefix "non-prefix command" {
+      gdb_test \
+	"python gdb.Command ('run', rename_existing_to='renamed-run', prefix=True)" \
+	".*Cannot specify argument 'prefix' with 'rename_existing_to'.*"
+      gdb_test \
+	"python gdb.Command ('run', rename_existing_to='renamed-run', prefix=False)" \
+	".*Cannot specify argument 'prefix' with 'rename_existing_to'.*"
+    }
+
+    with_test_prefix "prefix command" {
+      gdb_test \
+	"python gdb.Command ('show', rename_existing_to='renamed-show', prefix=True)" \
+	".*Cannot specify argument 'prefix' with 'rename_existing_to'.*"
+      gdb_test \
+	"python gdb.Command ('show', rename_existing_to='renamed-run', prefix=False)" \
+	".*Cannot specify argument 'prefix' with 'rename_existing_to'.*"
+    }
+  }
+}
+
+# Test that dont-repeat from a renamed command invoked by the new
+# implementation of the command prevents the repetition of the new
+# command.  This is needed to make the new implementation work
+# consistently with the original one.
+
+proc test_repeat {} {
+  with_test_prefix "repeat" {
+    prepare_gdb
+
+    # Define a command which repeats and one which doesn't.
+    gdb_define_cmd "repeat-cmd" {
+      "echo This command repeats\\n"
+    }
+    gdb_define_cmd "no-repeat-cmd" {
+      "echo This command does NOT repeat\\n"
+      "dont-repeat\\n"
+    }
+    # Then redefine them.
+    add_py_cmd "repeat-cmd" "renamed-repeat-cmd"
+    add_py_cmd "no-repeat-cmd" "renamed-no-repeat-cmd"
+
+    # The repeating command must still repeat.
+    set expected_repeat_list {
+      "py-before: repeat-cmd"
+      "This command repeats"
+      "py-after: repeat-cmd"
+    }
+    test_sequence_exact "repeat-cmd" $expected_repeat_list
+    with_test_prefix "repeating command" {
+      # Using an empty string works but is slow, probably because it
+      # waits for a timeout. A string with just whitespace does the same
+      # but fast.
+      test_sequence_exact " " $expected_repeat_list
+    }
+
+    # The non-repeating command must not repeat.
+    test_sequence_exact "no-repeat-cmd" {
+      "py-before: no-repeat-cmd"
+      "This command does NOT repeat"
+      "py-after: no-repeat-cmd"
+    }
+    with_test_prefix "non-repeating command" {
+      gdb_test_exact " " ""
+    }
+  }
+}
+
+# Test that the completer, if not specified, is copied from the original
+# command. If specified, the specified one is used.
+
+proc test_complete {} {
+  with_test_prefix "command completion" {
+    prepare_gdb
+
+    # Add a command whose arguments get completed as commands.
+    gdb_test_no_output \
+      "python gdb.Command ('my-cmd', gdb.COMMAND_NONE, gdb.COMPLETE_COMMAND)"
+
+    # Redefine the command.  As no completer was specified, it must
+    # inherit the one from the original command.
+    add_py_cmd "my-cmd" "renamed-my-cmd1"
+    with_test_prefix "completer inherited" {
+      test_gdb_complete_unique "my-cmd dele" "my-cmd delete"
+    }
+
+    # Redefine the command again.  As a completer was specified, the
+    # command must use the specified completer, not the original one.
+    add_py_cmd "my-cmd" "renamed-my-cmd2" "" "completer_class=gdb.COMPLETE_NONE"
+    with_test_prefix "completer specified" {
+      test_gdb_complete_none "my-cmd dele"
+  }
+  }
+}
+
+# Test the behaviour of command classes with renamed commands.
+
+proc test_command_class {} {
+  with_test_prefix "command class" {
+    prepare_gdb
+
+    # Rename the "run" command. The command class, as it's not specified,
+    # is inherited from the original command.
+    add_py_cmd "run" "renamed-run1"
+    gdb_test "help running" \
+      ".*renamed-run1 -- Start debugged program\..*run, r -- .*"
+
+    # Rename it again but, this time, put the new command in a different
+    # command class.
+    add_py_cmd "run" "renamed-run2" "" "command_class=gdb.COMMAND_STATUS"
+    gdb_test "help status" \
+      ".*run, r -- .*"
+  }
+}
+
+with_test_prefix "renaming commands via Python" {
+  test_rename_simple
+  test_rename_multiple
+  test_rename_non_existing
+  test_same_prefix
+  test_rename_adding_prefix
+  test_rename_removing_prefix
+  test_rename_changing_prefix
+  test_replace_prefix
+  test_prefix_arg_not_allowed
+  test_repeat
+  test_complete
+  test_command_class
+}
diff --git a/gdb/testsuite/gdb.python/py-rename-existing.py b/gdb/testsuite/gdb.python/py-rename-existing.py
new file mode 100644
index 00000000000..7db0f505c7a
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-rename-existing.py
@@ -0,0 +1,46 @@
+# Copyright (C) 2008-2021 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests renaming of existing
+# commands.
+
+import gdb
+
+class TestCommand (gdb.Command):
+
+    def __init__ (self, cmd_name, rename_existing_to, msg=None, **kwargs):
+        if not msg:
+            msg = cmd_name
+
+        self._cmd_name = cmd_name
+        self._rename_existing_to = rename_existing_to
+        self._msg = msg
+
+        gdb.Command.__init__ (
+            self, cmd_name,
+            rename_existing_to=rename_existing_to,
+            **kwargs)
+
+    def invoke (self, args, from_tty):
+        print ('py-before: %s' % self._msg)
+
+        # Invoke the original command.
+        try:
+            gdb.execute ('%s %s' % (self._rename_existing_to, args),
+                         from_tty=from_tty)
+        except gdb.error as exc:
+            print (exc)
+
+        print ('py-after: %s' % self._msg)
-- 
2.28.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 1/4] gdb: add lookup_cmd_exact to simplify a common pattern
  2021-01-17 19:02       ` Lancelot SIX
@ 2021-01-25 11:33         ` Luis Machado
  0 siblings, 0 replies; 38+ messages in thread
From: Luis Machado @ 2021-01-25 11:33 UTC (permalink / raw)
  To: Lancelot SIX, Marco Barisione; +Cc: gdb-patches

On 1/17/21 4:02 PM, Lancelot SIX via Gdb-patches wrote:
> Le Sun, Jan 17, 2021 at 10:47:38AM +0000, Marco Barisione a écrit :
>> On 10 Jan 2021, at 00:06, Lancelot SIX <lsix@lancelotsix.com> wrote:
>>>
>>> Hi
>>>
>>> I just have a few style-related remarks above.
>>>
>>> On Fri, Jan 08, 2021 at 10:07:03AM +0000, Marco Barisione via Gdb-patches wrote:
>>>> +/* See command.h.  */
>>>> +
>>>> +struct cmd_list_element *
>>>> +lookup_cmd_exact (const char *name,
>>>> +		  struct cmd_list_element *list,
>>>> +		  bool ignore_help_classes)
>>>> +{
>>>> +  const char *tem = name;
>>>> +  struct cmd_list_element *cmd = lookup_cmd (&tem, list, "", NULL, -1,
>>>
>>> Probably s/NULL/nullptr/ ?
>>>
>>>> +					     ignore_help_classes);
>>>> +  if (cmd && strcmp (name, cmd->name) != 0)
>>>
>>> I think gdb prefers explicit comparison to check for null pointers:
>>>
>>> https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards#Comparison_With_NULL_And_Zero
>>>
>>> +  if (cmd != nullptr && strcmp (name, cmd->name) != 0)
>>
>> I tried (but I noticed I didn’t succeed!) to use nullptr and compare
>> with nullptr in new code or code which already looked like that.
>> Otherwise, I tried to stick to the style used in nearby code.
>>
>> That is, if there's code like this:
>>      foo *bar = NULL;
>> When adding a new variable on the next line, I would use NULL for
>> consistency.
>>
>> What is the approach used in GDB?
>> 1. Add new code using the current style even if inconsistent with nearby
>>     code.

You should use the new style. So nullptr should be used here and 
throughout your patch series, even if inconsistent with nearby code.

>> 2. Stick to the style of nearby code.
>> 3. Also update nearby code.
>>
>>
>> -- 
>> Marco Barisione
>>
> 
> My personal approach would be to migrate to nullptr slowly but surely,
> but this is quite personal.  I’ll let a more experienced gdb maintainer
> state what is the prefered way of doing things for the project.
> 
> Independently of that matter, cmd should be compared to something in
> this expression. I guess something like
> 
> +  if (cmd != NULL && strcmp (name, cmd->name) != 0)
> 
> should be ok to maintain consistency.
> 
> Lancelot.
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PING] [PATCH v2 0/5] Add support for command renaming
  2021-01-25 11:26 ` [PATCH v2 0/5] Add support for command renaming Marco Barisione
                     ` (4 preceding siblings ...)
  2021-01-25 11:26   ` [PATCH v2 5/5] gdb: Add support for renaming commands Marco Barisione
@ 2021-02-08 17:53   ` Marco Barisione
  2021-02-15  8:27     ` [PING2] " Marco Barisione
  5 siblings, 1 reply; 38+ messages in thread
From: Marco Barisione @ 2021-02-08 17:53 UTC (permalink / raw)
  To: gdb-patches

Ping.

On Mon, 25 Jan 2021 at 11:26, Marco Barisione <mbarisione@undo.io> wrote:
> This patch series adds support for command renaming to GDB.  See the
> first email of this thread for details on the rationale and design.
>
> This second version fixes a few style issues, a bug in my code, and the
> way the prefix name for prefix commands is set.  In particular:
> * Patch 1: add lookup_cmd_exact to simplify a common pattern
>   Same as before except for style fixes.
> * Patch 2: prevent prefix commands from being hooks
>   Same as before.
> * Patch 3: update the docs for add_cmd and do_add_cmd to match reality
>   Same as before.
> * Patch 4: generate the prefix name for prefix commands on demand
>   New patch which changes how the prefix name is set/generated.
>   This simplifies a lot of existing code and the code from the next
>   patch.  Moreover, it fixes what I think could lead to a use after
>   free (based on reading code, I didn't actually reproduce any crash).
> * Patch 5: Add support for renaming commands
>   Updated version of what was before patch 4.
>   This contains a few style fixes and makes delete_cmd_by_name use
>   the lookup_cmd_exact function rather than find_cmd.
>
> Note that patches 1 to 4 are needed for the new feature but could be
> merged separately as they are improvements to the existing code.
>
> Marco Barisione (5):
>   gdb: add lookup_cmd_exact to simplify a common pattern
>   gdb: prevent prefix commands from being hooks
>   gdb: update the docs for add_cmd and do_add_cmd to match reality
>   gdb: generate the prefix name for prefix commands on demand
>   gdb: Add support for renaming commands
>
>  gdb/NEWS                                      |  26 +
>  gdb/ada-lang.c                                |   8 +-
>  gdb/arm-tdep.c                                |   4 +-
>  gdb/auto-load.c                               |   9 +-
>  gdb/breakpoint.c                              |  20 +-
>  gdb/bsd-kvm.c                                 |   2 +-
>  gdb/btrace.c                                  |   9 +-
>  gdb/cli/cli-cmds.c                            |  12 +-
>  gdb/cli/cli-decode.c                          | 731 ++++++++++++++----
>  gdb/cli/cli-decode.h                          |  29 +-
>  gdb/cli/cli-dump.c                            |  16 +-
>  gdb/cli/cli-logging.c                         |   4 +-
>  gdb/cli/cli-script.c                          | 375 +++++++--
>  gdb/cli/cli-setshow.c                         |  11 +-
>  gdb/cli/cli-style.c                           |  11 +-
>  gdb/cli/cli-style.h                           |   3 -
>  gdb/command.h                                 | 110 ++-
>  gdb/compile/compile.c                         |   2 +-
>  gdb/cp-support.c                              |   1 -
>  gdb/dcache.c                                  |   5 +-
>  gdb/doc/gdb.texinfo                           |  55 +-
>  gdb/doc/python.texi                           |  38 +-
>  gdb/dwarf2/index-cache.c                      |   4 +-
>  gdb/dwarf2/read.c                             |   4 +-
>  gdb/f-lang.c                                  |   4 +-
>  gdb/frame.c                                   |   4 +-
>  gdb/gnu-nat.c                                 |  12 +-
>  gdb/go32-nat.c                                |   2 +-
>  gdb/guile/guile.c                             |   9 +-
>  gdb/guile/scm-cmd.c                           |   2 +-
>  gdb/i386-tdep.c                               |   4 +-
>  gdb/infcmd.c                                  |   8 +-
>  gdb/language.c                                |   4 +-
>  gdb/macrocmd.c                                |   2 +-
>  gdb/maint-test-options.c                      |   3 +-
>  gdb/maint-test-settings.c                     |   2 -
>  gdb/maint.c                                   |  18 +-
>  gdb/memattr.c                                 |   4 +-
>  gdb/mips-tdep.c                               |   6 +-
>  gdb/printcmd.c                                |   2 +-
>  gdb/probe.c                                   |   3 +-
>  gdb/python/py-cmd.c                           | 228 ++++--
>  gdb/python/python-internal.h                  |   1 +
>  gdb/python/python.c                           |   6 +-
>  gdb/ravenscar-thread.c                        |   4 +-
>  gdb/record-btrace.c                           |  16 +-
>  gdb/record-full.c                             |   6 +-
>  gdb/record.c                                  |  10 +-
>  gdb/remote.c                                  |   6 +-
>  gdb/riscv-tdep.c                              |   8 +-
>  gdb/rs6000-tdep.c                             |   4 +-
>  gdb/ser-tcp.c                                 |   4 +-
>  gdb/serial.c                                  |   4 +-
>  gdb/sh-tdep.c                                 |   4 +-
>  gdb/skip.c                                    |   2 +-
>  gdb/sparc64-tdep.c                            |   2 +-
>  gdb/stack.c                                   |   8 +-
>  gdb/symfile.c                                 |   2 +-
>  gdb/symtab.c                                  |   3 +-
>  gdb/target-descriptions.c                     |   6 +-
>  gdb/target.c                                  |   2 +-
>  gdb/testsuite/gdb.base/command-renaming.exp   | 571 ++++++++++++++
>  gdb/testsuite/gdb.base/define-prefix.exp      |  24 +
>  .../gdb.python/py-rename-existing.exp         | 364 +++++++++
>  .../gdb.python/py-rename-existing.py          |  46 ++
>  gdb/thread.c                                  |   4 +-
>  gdb/top.c                                     |  10 +-
>  gdb/tracepoint.c                              |   2 +-
>  gdb/tui/tui-layout.c                          |   2 +-
>  gdb/tui/tui-win.c                             |   8 +-
>  gdb/typeprint.c                               |   6 +-
>  gdb/unittests/command-def-selftests.c         |   4 +-
>  gdb/utils.c                                   |   4 -
>  gdb/valprint.c                                |  10 +-
>  gdb/value.c                                   |   2 +-
>  gdb/windows-tdep.c                            |   2 +-
>  76 files changed, 2434 insertions(+), 529 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.base/command-renaming.exp
>  create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.exp
>  create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.py
>
> --
> 2.28.0
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PING2] [PATCH v2 0/5] Add support for command renaming
  2021-02-08 17:53   ` [PING] [PATCH v2 0/5] Add support for command renaming Marco Barisione
@ 2021-02-15  8:27     ` Marco Barisione
  2021-02-22  8:28       ` [PING 3] " Marco Barisione
  0 siblings, 1 reply; 38+ messages in thread
From: Marco Barisione @ 2021-02-15  8:27 UTC (permalink / raw)
  To: gdb-patches

Ping.

On Mon, 8 Feb 2021 at 17:53, Marco Barisione <mbarisione@undo.io> wrote:
>
> Ping.
>
> On Mon, 25 Jan 2021 at 11:26, Marco Barisione <mbarisione@undo.io> wrote:
> > This patch series adds support for command renaming to GDB.  See the
> > first email of this thread for details on the rationale and design.
> >
> > This second version fixes a few style issues, a bug in my code, and the
> > way the prefix name for prefix commands is set.  In particular:
> > * Patch 1: add lookup_cmd_exact to simplify a common pattern
> >   Same as before except for style fixes.
> > * Patch 2: prevent prefix commands from being hooks
> >   Same as before.
> > * Patch 3: update the docs for add_cmd and do_add_cmd to match reality
> >   Same as before.
> > * Patch 4: generate the prefix name for prefix commands on demand
> >   New patch which changes how the prefix name is set/generated.
> >   This simplifies a lot of existing code and the code from the next
> >   patch.  Moreover, it fixes what I think could lead to a use after
> >   free (based on reading code, I didn't actually reproduce any crash).
> > * Patch 5: Add support for renaming commands
> >   Updated version of what was before patch 4.
> >   This contains a few style fixes and makes delete_cmd_by_name use
> >   the lookup_cmd_exact function rather than find_cmd.
> >
> > Note that patches 1 to 4 are needed for the new feature but could be
> > merged separately as they are improvements to the existing code.
> >
> > Marco Barisione (5):
> >   gdb: add lookup_cmd_exact to simplify a common pattern
> >   gdb: prevent prefix commands from being hooks
> >   gdb: update the docs for add_cmd and do_add_cmd to match reality
> >   gdb: generate the prefix name for prefix commands on demand
> >   gdb: Add support for renaming commands
> >
> >  gdb/NEWS                                      |  26 +
> >  gdb/ada-lang.c                                |   8 +-
> >  gdb/arm-tdep.c                                |   4 +-
> >  gdb/auto-load.c                               |   9 +-
> >  gdb/breakpoint.c                              |  20 +-
> >  gdb/bsd-kvm.c                                 |   2 +-
> >  gdb/btrace.c                                  |   9 +-
> >  gdb/cli/cli-cmds.c                            |  12 +-
> >  gdb/cli/cli-decode.c                          | 731 ++++++++++++++----
> >  gdb/cli/cli-decode.h                          |  29 +-
> >  gdb/cli/cli-dump.c                            |  16 +-
> >  gdb/cli/cli-logging.c                         |   4 +-
> >  gdb/cli/cli-script.c                          | 375 +++++++--
> >  gdb/cli/cli-setshow.c                         |  11 +-
> >  gdb/cli/cli-style.c                           |  11 +-
> >  gdb/cli/cli-style.h                           |   3 -
> >  gdb/command.h                                 | 110 ++-
> >  gdb/compile/compile.c                         |   2 +-
> >  gdb/cp-support.c                              |   1 -
> >  gdb/dcache.c                                  |   5 +-
> >  gdb/doc/gdb.texinfo                           |  55 +-
> >  gdb/doc/python.texi                           |  38 +-
> >  gdb/dwarf2/index-cache.c                      |   4 +-
> >  gdb/dwarf2/read.c                             |   4 +-
> >  gdb/f-lang.c                                  |   4 +-
> >  gdb/frame.c                                   |   4 +-
> >  gdb/gnu-nat.c                                 |  12 +-
> >  gdb/go32-nat.c                                |   2 +-
> >  gdb/guile/guile.c                             |   9 +-
> >  gdb/guile/scm-cmd.c                           |   2 +-
> >  gdb/i386-tdep.c                               |   4 +-
> >  gdb/infcmd.c                                  |   8 +-
> >  gdb/language.c                                |   4 +-
> >  gdb/macrocmd.c                                |   2 +-
> >  gdb/maint-test-options.c                      |   3 +-
> >  gdb/maint-test-settings.c                     |   2 -
> >  gdb/maint.c                                   |  18 +-
> >  gdb/memattr.c                                 |   4 +-
> >  gdb/mips-tdep.c                               |   6 +-
> >  gdb/printcmd.c                                |   2 +-
> >  gdb/probe.c                                   |   3 +-
> >  gdb/python/py-cmd.c                           | 228 ++++--
> >  gdb/python/python-internal.h                  |   1 +
> >  gdb/python/python.c                           |   6 +-
> >  gdb/ravenscar-thread.c                        |   4 +-
> >  gdb/record-btrace.c                           |  16 +-
> >  gdb/record-full.c                             |   6 +-
> >  gdb/record.c                                  |  10 +-
> >  gdb/remote.c                                  |   6 +-
> >  gdb/riscv-tdep.c                              |   8 +-
> >  gdb/rs6000-tdep.c                             |   4 +-
> >  gdb/ser-tcp.c                                 |   4 +-
> >  gdb/serial.c                                  |   4 +-
> >  gdb/sh-tdep.c                                 |   4 +-
> >  gdb/skip.c                                    |   2 +-
> >  gdb/sparc64-tdep.c                            |   2 +-
> >  gdb/stack.c                                   |   8 +-
> >  gdb/symfile.c                                 |   2 +-
> >  gdb/symtab.c                                  |   3 +-
> >  gdb/target-descriptions.c                     |   6 +-
> >  gdb/target.c                                  |   2 +-
> >  gdb/testsuite/gdb.base/command-renaming.exp   | 571 ++++++++++++++
> >  gdb/testsuite/gdb.base/define-prefix.exp      |  24 +
> >  .../gdb.python/py-rename-existing.exp         | 364 +++++++++
> >  .../gdb.python/py-rename-existing.py          |  46 ++
> >  gdb/thread.c                                  |   4 +-
> >  gdb/top.c                                     |  10 +-
> >  gdb/tracepoint.c                              |   2 +-
> >  gdb/tui/tui-layout.c                          |   2 +-
> >  gdb/tui/tui-win.c                             |   8 +-
> >  gdb/typeprint.c                               |   6 +-
> >  gdb/unittests/command-def-selftests.c         |   4 +-
> >  gdb/utils.c                                   |   4 -
> >  gdb/valprint.c                                |  10 +-
> >  gdb/value.c                                   |   2 +-
> >  gdb/windows-tdep.c                            |   2 +-
> >  76 files changed, 2434 insertions(+), 529 deletions(-)
> >  create mode 100644 gdb/testsuite/gdb.base/command-renaming.exp
> >  create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.exp
> >  create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.py
> >
> > --
> > 2.28.0
> >

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PING 3] [PATCH v2 0/5] Add support for command renaming
  2021-02-15  8:27     ` [PING2] " Marco Barisione
@ 2021-02-22  8:28       ` Marco Barisione
  2021-03-01  8:32         ` [PING 4] " Marco Barisione
  0 siblings, 1 reply; 38+ messages in thread
From: Marco Barisione @ 2021-02-22  8:28 UTC (permalink / raw)
  To: GDB patches mailing list

Ping.

> On 15 Feb 2021, at 08:27, Marco Barisione <mbarisione@undo.io> wrote:
> 
> Ping.
> 
> On Mon, 8 Feb 2021 at 17:53, Marco Barisione <mbarisione@undo.io> wrote:
>> 
>> Ping.
>> 
>> On Mon, 25 Jan 2021 at 11:26, Marco Barisione <mbarisione@undo.io> wrote:
>>> This patch series adds support for command renaming to GDB.  See the
>>> first email of this thread for details on the rationale and design.
>>> 
>>> This second version fixes a few style issues, a bug in my code, and the
>>> way the prefix name for prefix commands is set.  In particular:
>>> * Patch 1: add lookup_cmd_exact to simplify a common pattern
>>>  Same as before except for style fixes.
>>> * Patch 2: prevent prefix commands from being hooks
>>>  Same as before.
>>> * Patch 3: update the docs for add_cmd and do_add_cmd to match reality
>>>  Same as before.
>>> * Patch 4: generate the prefix name for prefix commands on demand
>>>  New patch which changes how the prefix name is set/generated.
>>>  This simplifies a lot of existing code and the code from the next
>>>  patch.  Moreover, it fixes what I think could lead to a use after
>>>  free (based on reading code, I didn't actually reproduce any crash).
>>> * Patch 5: Add support for renaming commands
>>>  Updated version of what was before patch 4.
>>>  This contains a few style fixes and makes delete_cmd_by_name use
>>>  the lookup_cmd_exact function rather than find_cmd.
>>> 
>>> Note that patches 1 to 4 are needed for the new feature but could be
>>> merged separately as they are improvements to the existing code.
>>> 
>>> Marco Barisione (5):
>>>  gdb: add lookup_cmd_exact to simplify a common pattern
>>>  gdb: prevent prefix commands from being hooks
>>>  gdb: update the docs for add_cmd and do_add_cmd to match reality
>>>  gdb: generate the prefix name for prefix commands on demand
>>>  gdb: Add support for renaming commands
>>> 
>>> gdb/NEWS                                      |  26 +
>>> gdb/ada-lang.c                                |   8 +-
>>> gdb/arm-tdep.c                                |   4 +-
>>> gdb/auto-load.c                               |   9 +-
>>> gdb/breakpoint.c                              |  20 +-
>>> gdb/bsd-kvm.c                                 |   2 +-
>>> gdb/btrace.c                                  |   9 +-
>>> gdb/cli/cli-cmds.c                            |  12 +-
>>> gdb/cli/cli-decode.c                          | 731 ++++++++++++++----
>>> gdb/cli/cli-decode.h                          |  29 +-
>>> gdb/cli/cli-dump.c                            |  16 +-
>>> gdb/cli/cli-logging.c                         |   4 +-
>>> gdb/cli/cli-script.c                          | 375 +++++++--
>>> gdb/cli/cli-setshow.c                         |  11 +-
>>> gdb/cli/cli-style.c                           |  11 +-
>>> gdb/cli/cli-style.h                           |   3 -
>>> gdb/command.h                                 | 110 ++-
>>> gdb/compile/compile.c                         |   2 +-
>>> gdb/cp-support.c                              |   1 -
>>> gdb/dcache.c                                  |   5 +-
>>> gdb/doc/gdb.texinfo                           |  55 +-
>>> gdb/doc/python.texi                           |  38 +-
>>> gdb/dwarf2/index-cache.c                      |   4 +-
>>> gdb/dwarf2/read.c                             |   4 +-
>>> gdb/f-lang.c                                  |   4 +-
>>> gdb/frame.c                                   |   4 +-
>>> gdb/gnu-nat.c                                 |  12 +-
>>> gdb/go32-nat.c                                |   2 +-
>>> gdb/guile/guile.c                             |   9 +-
>>> gdb/guile/scm-cmd.c                           |   2 +-
>>> gdb/i386-tdep.c                               |   4 +-
>>> gdb/infcmd.c                                  |   8 +-
>>> gdb/language.c                                |   4 +-
>>> gdb/macrocmd.c                                |   2 +-
>>> gdb/maint-test-options.c                      |   3 +-
>>> gdb/maint-test-settings.c                     |   2 -
>>> gdb/maint.c                                   |  18 +-
>>> gdb/memattr.c                                 |   4 +-
>>> gdb/mips-tdep.c                               |   6 +-
>>> gdb/printcmd.c                                |   2 +-
>>> gdb/probe.c                                   |   3 +-
>>> gdb/python/py-cmd.c                           | 228 ++++--
>>> gdb/python/python-internal.h                  |   1 +
>>> gdb/python/python.c                           |   6 +-
>>> gdb/ravenscar-thread.c                        |   4 +-
>>> gdb/record-btrace.c                           |  16 +-
>>> gdb/record-full.c                             |   6 +-
>>> gdb/record.c                                  |  10 +-
>>> gdb/remote.c                                  |   6 +-
>>> gdb/riscv-tdep.c                              |   8 +-
>>> gdb/rs6000-tdep.c                             |   4 +-
>>> gdb/ser-tcp.c                                 |   4 +-
>>> gdb/serial.c                                  |   4 +-
>>> gdb/sh-tdep.c                                 |   4 +-
>>> gdb/skip.c                                    |   2 +-
>>> gdb/sparc64-tdep.c                            |   2 +-
>>> gdb/stack.c                                   |   8 +-
>>> gdb/symfile.c                                 |   2 +-
>>> gdb/symtab.c                                  |   3 +-
>>> gdb/target-descriptions.c                     |   6 +-
>>> gdb/target.c                                  |   2 +-
>>> gdb/testsuite/gdb.base/command-renaming.exp   | 571 ++++++++++++++
>>> gdb/testsuite/gdb.base/define-prefix.exp      |  24 +
>>> .../gdb.python/py-rename-existing.exp         | 364 +++++++++
>>> .../gdb.python/py-rename-existing.py          |  46 ++
>>> gdb/thread.c                                  |   4 +-
>>> gdb/top.c                                     |  10 +-
>>> gdb/tracepoint.c                              |   2 +-
>>> gdb/tui/tui-layout.c                          |   2 +-
>>> gdb/tui/tui-win.c                             |   8 +-
>>> gdb/typeprint.c                               |   6 +-
>>> gdb/unittests/command-def-selftests.c         |   4 +-
>>> gdb/utils.c                                   |   4 -
>>> gdb/valprint.c                                |  10 +-
>>> gdb/value.c                                   |   2 +-
>>> gdb/windows-tdep.c                            |   2 +-
>>> 76 files changed, 2434 insertions(+), 529 deletions(-)
>>> create mode 100644 gdb/testsuite/gdb.base/command-renaming.exp
>>> create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.exp
>>> create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.py
>>> 
>>> --
>>> 2.28.0
>>> 

-- 
Marco Barisione


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PING 4] [PATCH v2 0/5] Add support for command renaming
  2021-02-22  8:28       ` [PING 3] " Marco Barisione
@ 2021-03-01  8:32         ` Marco Barisione
  2021-03-08  9:23           ` [PING 5] " Marco Barisione
  0 siblings, 1 reply; 38+ messages in thread
From: Marco Barisione @ 2021-03-01  8:32 UTC (permalink / raw)
  To: GDB patches mailing list

Ping 4.

On Mon, 22 Feb 2021 at 08:28, Marco Barisione <mbarisione@undo.io> wrote:
>
> Ping.
>
> > On 15 Feb 2021, at 08:27, Marco Barisione <mbarisione@undo.io> wrote:
> >
> > Ping.
> >
> > On Mon, 8 Feb 2021 at 17:53, Marco Barisione <mbarisione@undo.io> wrote:
> >>
> >> Ping.
> >>
> >> On Mon, 25 Jan 2021 at 11:26, Marco Barisione <mbarisione@undo.io> wrote:
> >>> This patch series adds support for command renaming to GDB.  See the
> >>> first email of this thread for details on the rationale and design.
> >>>
> >>> This second version fixes a few style issues, a bug in my code, and the
> >>> way the prefix name for prefix commands is set.  In particular:
> >>> * Patch 1: add lookup_cmd_exact to simplify a common pattern
> >>>  Same as before except for style fixes.
> >>> * Patch 2: prevent prefix commands from being hooks
> >>>  Same as before.
> >>> * Patch 3: update the docs for add_cmd and do_add_cmd to match reality
> >>>  Same as before.
> >>> * Patch 4: generate the prefix name for prefix commands on demand
> >>>  New patch which changes how the prefix name is set/generated.
> >>>  This simplifies a lot of existing code and the code from the next
> >>>  patch.  Moreover, it fixes what I think could lead to a use after
> >>>  free (based on reading code, I didn't actually reproduce any crash).
> >>> * Patch 5: Add support for renaming commands
> >>>  Updated version of what was before patch 4.
> >>>  This contains a few style fixes and makes delete_cmd_by_name use
> >>>  the lookup_cmd_exact function rather than find_cmd.
> >>>
> >>> Note that patches 1 to 4 are needed for the new feature but could be
> >>> merged separately as they are improvements to the existing code.
> >>>
> >>> Marco Barisione (5):
> >>>  gdb: add lookup_cmd_exact to simplify a common pattern
> >>>  gdb: prevent prefix commands from being hooks
> >>>  gdb: update the docs for add_cmd and do_add_cmd to match reality
> >>>  gdb: generate the prefix name for prefix commands on demand
> >>>  gdb: Add support for renaming commands
> >>>
> >>> gdb/NEWS                                      |  26 +
> >>> gdb/ada-lang.c                                |   8 +-
> >>> gdb/arm-tdep.c                                |   4 +-
> >>> gdb/auto-load.c                               |   9 +-
> >>> gdb/breakpoint.c                              |  20 +-
> >>> gdb/bsd-kvm.c                                 |   2 +-
> >>> gdb/btrace.c                                  |   9 +-
> >>> gdb/cli/cli-cmds.c                            |  12 +-
> >>> gdb/cli/cli-decode.c                          | 731 ++++++++++++++----
> >>> gdb/cli/cli-decode.h                          |  29 +-
> >>> gdb/cli/cli-dump.c                            |  16 +-
> >>> gdb/cli/cli-logging.c                         |   4 +-
> >>> gdb/cli/cli-script.c                          | 375 +++++++--
> >>> gdb/cli/cli-setshow.c                         |  11 +-
> >>> gdb/cli/cli-style.c                           |  11 +-
> >>> gdb/cli/cli-style.h                           |   3 -
> >>> gdb/command.h                                 | 110 ++-
> >>> gdb/compile/compile.c                         |   2 +-
> >>> gdb/cp-support.c                              |   1 -
> >>> gdb/dcache.c                                  |   5 +-
> >>> gdb/doc/gdb.texinfo                           |  55 +-
> >>> gdb/doc/python.texi                           |  38 +-
> >>> gdb/dwarf2/index-cache.c                      |   4 +-
> >>> gdb/dwarf2/read.c                             |   4 +-
> >>> gdb/f-lang.c                                  |   4 +-
> >>> gdb/frame.c                                   |   4 +-
> >>> gdb/gnu-nat.c                                 |  12 +-
> >>> gdb/go32-nat.c                                |   2 +-
> >>> gdb/guile/guile.c                             |   9 +-
> >>> gdb/guile/scm-cmd.c                           |   2 +-
> >>> gdb/i386-tdep.c                               |   4 +-
> >>> gdb/infcmd.c                                  |   8 +-
> >>> gdb/language.c                                |   4 +-
> >>> gdb/macrocmd.c                                |   2 +-
> >>> gdb/maint-test-options.c                      |   3 +-
> >>> gdb/maint-test-settings.c                     |   2 -
> >>> gdb/maint.c                                   |  18 +-
> >>> gdb/memattr.c                                 |   4 +-
> >>> gdb/mips-tdep.c                               |   6 +-
> >>> gdb/printcmd.c                                |   2 +-
> >>> gdb/probe.c                                   |   3 +-
> >>> gdb/python/py-cmd.c                           | 228 ++++--
> >>> gdb/python/python-internal.h                  |   1 +
> >>> gdb/python/python.c                           |   6 +-
> >>> gdb/ravenscar-thread.c                        |   4 +-
> >>> gdb/record-btrace.c                           |  16 +-
> >>> gdb/record-full.c                             |   6 +-
> >>> gdb/record.c                                  |  10 +-
> >>> gdb/remote.c                                  |   6 +-
> >>> gdb/riscv-tdep.c                              |   8 +-
> >>> gdb/rs6000-tdep.c                             |   4 +-
> >>> gdb/ser-tcp.c                                 |   4 +-
> >>> gdb/serial.c                                  |   4 +-
> >>> gdb/sh-tdep.c                                 |   4 +-
> >>> gdb/skip.c                                    |   2 +-
> >>> gdb/sparc64-tdep.c                            |   2 +-
> >>> gdb/stack.c                                   |   8 +-
> >>> gdb/symfile.c                                 |   2 +-
> >>> gdb/symtab.c                                  |   3 +-
> >>> gdb/target-descriptions.c                     |   6 +-
> >>> gdb/target.c                                  |   2 +-
> >>> gdb/testsuite/gdb.base/command-renaming.exp   | 571 ++++++++++++++
> >>> gdb/testsuite/gdb.base/define-prefix.exp      |  24 +
> >>> .../gdb.python/py-rename-existing.exp         | 364 +++++++++
> >>> .../gdb.python/py-rename-existing.py          |  46 ++
> >>> gdb/thread.c                                  |   4 +-
> >>> gdb/top.c                                     |  10 +-
> >>> gdb/tracepoint.c                              |   2 +-
> >>> gdb/tui/tui-layout.c                          |   2 +-
> >>> gdb/tui/tui-win.c                             |   8 +-
> >>> gdb/typeprint.c                               |   6 +-
> >>> gdb/unittests/command-def-selftests.c         |   4 +-
> >>> gdb/utils.c                                   |   4 -
> >>> gdb/valprint.c                                |  10 +-
> >>> gdb/value.c                                   |   2 +-
> >>> gdb/windows-tdep.c                            |   2 +-
> >>> 76 files changed, 2434 insertions(+), 529 deletions(-)
> >>> create mode 100644 gdb/testsuite/gdb.base/command-renaming.exp
> >>> create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.exp
> >>> create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.py
> >>>
> >>> --
> >>> 2.28.0
> >>>
>
> --
> Marco Barisione
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PING 5] [PATCH v2 0/5] Add support for command renaming
  2021-03-01  8:32         ` [PING 4] " Marco Barisione
@ 2021-03-08  9:23           ` Marco Barisione
  0 siblings, 0 replies; 38+ messages in thread
From: Marco Barisione @ 2021-03-08  9:23 UTC (permalink / raw)
  To: GDB patches mailing list

Ping 5.

On Mon, 1 Mar 2021 at 08:32, Marco Barisione <mbarisione@undo.io> wrote:
>
> Ping 4.
>
> On Mon, 22 Feb 2021 at 08:28, Marco Barisione <mbarisione@undo.io> wrote:
> >
> > Ping.
> >
> > > On 15 Feb 2021, at 08:27, Marco Barisione <mbarisione@undo.io> wrote:
> > >
> > > Ping.
> > >
> > > On Mon, 8 Feb 2021 at 17:53, Marco Barisione <mbarisione@undo.io> wrote:
> > >>
> > >> Ping.
> > >>
> > >> On Mon, 25 Jan 2021 at 11:26, Marco Barisione <mbarisione@undo.io> wrote:
> > >>> This patch series adds support for command renaming to GDB.  See the
> > >>> first email of this thread for details on the rationale and design.
> > >>>
> > >>> This second version fixes a few style issues, a bug in my code, and the
> > >>> way the prefix name for prefix commands is set.  In particular:
> > >>> * Patch 1: add lookup_cmd_exact to simplify a common pattern
> > >>>  Same as before except for style fixes.
> > >>> * Patch 2: prevent prefix commands from being hooks
> > >>>  Same as before.
> > >>> * Patch 3: update the docs for add_cmd and do_add_cmd to match reality
> > >>>  Same as before.
> > >>> * Patch 4: generate the prefix name for prefix commands on demand
> > >>>  New patch which changes how the prefix name is set/generated.
> > >>>  This simplifies a lot of existing code and the code from the next
> > >>>  patch.  Moreover, it fixes what I think could lead to a use after
> > >>>  free (based on reading code, I didn't actually reproduce any crash).
> > >>> * Patch 5: Add support for renaming commands
> > >>>  Updated version of what was before patch 4.
> > >>>  This contains a few style fixes and makes delete_cmd_by_name use
> > >>>  the lookup_cmd_exact function rather than find_cmd.
> > >>>
> > >>> Note that patches 1 to 4 are needed for the new feature but could be
> > >>> merged separately as they are improvements to the existing code.
> > >>>
> > >>> Marco Barisione (5):
> > >>>  gdb: add lookup_cmd_exact to simplify a common pattern
> > >>>  gdb: prevent prefix commands from being hooks
> > >>>  gdb: update the docs for add_cmd and do_add_cmd to match reality
> > >>>  gdb: generate the prefix name for prefix commands on demand
> > >>>  gdb: Add support for renaming commands
> > >>>
> > >>> gdb/NEWS                                      |  26 +
> > >>> gdb/ada-lang.c                                |   8 +-
> > >>> gdb/arm-tdep.c                                |   4 +-
> > >>> gdb/auto-load.c                               |   9 +-
> > >>> gdb/breakpoint.c                              |  20 +-
> > >>> gdb/bsd-kvm.c                                 |   2 +-
> > >>> gdb/btrace.c                                  |   9 +-
> > >>> gdb/cli/cli-cmds.c                            |  12 +-
> > >>> gdb/cli/cli-decode.c                          | 731 ++++++++++++++----
> > >>> gdb/cli/cli-decode.h                          |  29 +-
> > >>> gdb/cli/cli-dump.c                            |  16 +-
> > >>> gdb/cli/cli-logging.c                         |   4 +-
> > >>> gdb/cli/cli-script.c                          | 375 +++++++--
> > >>> gdb/cli/cli-setshow.c                         |  11 +-
> > >>> gdb/cli/cli-style.c                           |  11 +-
> > >>> gdb/cli/cli-style.h                           |   3 -
> > >>> gdb/command.h                                 | 110 ++-
> > >>> gdb/compile/compile.c                         |   2 +-
> > >>> gdb/cp-support.c                              |   1 -
> > >>> gdb/dcache.c                                  |   5 +-
> > >>> gdb/doc/gdb.texinfo                           |  55 +-
> > >>> gdb/doc/python.texi                           |  38 +-
> > >>> gdb/dwarf2/index-cache.c                      |   4 +-
> > >>> gdb/dwarf2/read.c                             |   4 +-
> > >>> gdb/f-lang.c                                  |   4 +-
> > >>> gdb/frame.c                                   |   4 +-
> > >>> gdb/gnu-nat.c                                 |  12 +-
> > >>> gdb/go32-nat.c                                |   2 +-
> > >>> gdb/guile/guile.c                             |   9 +-
> > >>> gdb/guile/scm-cmd.c                           |   2 +-
> > >>> gdb/i386-tdep.c                               |   4 +-
> > >>> gdb/infcmd.c                                  |   8 +-
> > >>> gdb/language.c                                |   4 +-
> > >>> gdb/macrocmd.c                                |   2 +-
> > >>> gdb/maint-test-options.c                      |   3 +-
> > >>> gdb/maint-test-settings.c                     |   2 -
> > >>> gdb/maint.c                                   |  18 +-
> > >>> gdb/memattr.c                                 |   4 +-
> > >>> gdb/mips-tdep.c                               |   6 +-
> > >>> gdb/printcmd.c                                |   2 +-
> > >>> gdb/probe.c                                   |   3 +-
> > >>> gdb/python/py-cmd.c                           | 228 ++++--
> > >>> gdb/python/python-internal.h                  |   1 +
> > >>> gdb/python/python.c                           |   6 +-
> > >>> gdb/ravenscar-thread.c                        |   4 +-
> > >>> gdb/record-btrace.c                           |  16 +-
> > >>> gdb/record-full.c                             |   6 +-
> > >>> gdb/record.c                                  |  10 +-
> > >>> gdb/remote.c                                  |   6 +-
> > >>> gdb/riscv-tdep.c                              |   8 +-
> > >>> gdb/rs6000-tdep.c                             |   4 +-
> > >>> gdb/ser-tcp.c                                 |   4 +-
> > >>> gdb/serial.c                                  |   4 +-
> > >>> gdb/sh-tdep.c                                 |   4 +-
> > >>> gdb/skip.c                                    |   2 +-
> > >>> gdb/sparc64-tdep.c                            |   2 +-
> > >>> gdb/stack.c                                   |   8 +-
> > >>> gdb/symfile.c                                 |   2 +-
> > >>> gdb/symtab.c                                  |   3 +-
> > >>> gdb/target-descriptions.c                     |   6 +-
> > >>> gdb/target.c                                  |   2 +-
> > >>> gdb/testsuite/gdb.base/command-renaming.exp   | 571 ++++++++++++++
> > >>> gdb/testsuite/gdb.base/define-prefix.exp      |  24 +
> > >>> .../gdb.python/py-rename-existing.exp         | 364 +++++++++
> > >>> .../gdb.python/py-rename-existing.py          |  46 ++
> > >>> gdb/thread.c                                  |   4 +-
> > >>> gdb/top.c                                     |  10 +-
> > >>> gdb/tracepoint.c                              |   2 +-
> > >>> gdb/tui/tui-layout.c                          |   2 +-
> > >>> gdb/tui/tui-win.c                             |   8 +-
> > >>> gdb/typeprint.c                               |   6 +-
> > >>> gdb/unittests/command-def-selftests.c         |   4 +-
> > >>> gdb/utils.c                                   |   4 -
> > >>> gdb/valprint.c                                |  10 +-
> > >>> gdb/value.c                                   |   2 +-
> > >>> gdb/windows-tdep.c                            |   2 +-
> > >>> 76 files changed, 2434 insertions(+), 529 deletions(-)
> > >>> create mode 100644 gdb/testsuite/gdb.base/command-renaming.exp
> > >>> create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.exp
> > >>> create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.py
> > >>>
> > >>> --
> > >>> 2.28.0
> > >>>
> >
> > --
> > Marco Barisione
> >

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2 1/5] gdb: add lookup_cmd_exact to simplify a common pattern
  2021-01-25 11:26   ` [PATCH v2 1/5] gdb: add lookup_cmd_exact to simplify a common pattern Marco Barisione
@ 2021-03-08 18:58     ` Simon Marchi
  2021-05-07 14:47       ` Marco Barisione
  0 siblings, 1 reply; 38+ messages in thread
From: Simon Marchi @ 2021-03-08 18:58 UTC (permalink / raw)
  To: Marco Barisione, gdb-patches

On 2021-01-25 6:26 a.m., Marco Barisione via Gdb-patches wrote:
> In code dealing with commands, there's a pattern repeated a few times of
> calling lookup_cmd with some speficic arguments and then using strcmp
> on the returned command to check for an exact match.
> As a later patch would add a few more similar lines of code, this patch
> adds a new lookup_cmd_exact function which simplify this use case.
> 
> gdb/ChangeLog:
> 
> 	* cli/cli-decode.c (lookup_cmd_exact): Add.
> 	* cli/cli-script.c (do_define_command): Use lookup_cmd_exact.
> 	(define_prefix_command): Ditto.
> 	* command.h: Add lookup_cmd_exact.

This is ok, you can push it on its own right away, as it's a good 
cleanup on its own.

Simon

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2 2/5] gdb: prevent prefix commands from being hooks
  2021-01-25 11:26   ` [PATCH v2 2/5] gdb: prevent prefix commands from being hooks Marco Barisione
@ 2021-03-08 21:32     ` Simon Marchi
  2021-03-09  9:42       ` Marco Barisione
  2021-05-14 20:38       ` [PATCH v3 " Marco Barisione
  0 siblings, 2 replies; 38+ messages in thread
From: Simon Marchi @ 2021-03-08 21:32 UTC (permalink / raw)
  To: Marco Barisione, gdb-patches



On 2021-01-25 6:26 a.m., Marco Barisione via Gdb-patches wrote:
> Currently it's possible for hooks to be prefix commands:
> 
>   define-prefix hook-run
> 
>   define hook-run
>     echo Will run!\n
>   end
> 
>   define hook-run subcommand
>     echo Subcommand\n
>   end
> 
> Then:
> 
>   (gdb) run
>   Will run!
>   [...]
>   (gdb) hook-run subcommand
>   Subcommand
> 
> This doesn't seem very useful or worse, it can be confusing.  Moreover,
> this creates some obscure corner cases in a later patch adding command
> renaming.
> 
> Because of this, this patch prevents hooks to become prefixes and
> prefixes to become hooks.
> 
> An alternative approach would have been to prevent prefixes starting
> with "hook-" or "hookpost-".  The end result would have been similar
> but not identical as GDB allows commands starting with "hook-" or
> "hookpost-" which are not hooks (for instance, "define
> hook-this-doesnt-exist").  To be more consistent with this use case,
> prefixes for commands which are named like hooks but are not hooks are
> allowed.

Ahhh, changes that touch command handling are always scary stuff, we
need to be really careful not to break existing use cases or paint
ourselves in corners for future use cases.  Probably why nobody dares to
review it.  I'll give it a try, but know that it still scares me :).

One question that pops up is, what if someone ever wants to make a hook
for a prefixed command.  For example, let's say I want to hook "info
inferiors".  I don't think that's possible today, but let's say we want
to make it possible.  How would that work?

This doesn't work:

   (gdb) define hook-info inferiors
    Undefined command: "hook-info".  Try "help".

So... maybe defining "hook-info" as a prefix command would allow it?
Not sure, I didn't really think this through, but I wonder if you
thought about that case.

Simon

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2 3/5] gdb: update the docs for add_cmd and do_add_cmd to match reality
  2021-01-25 11:26   ` [PATCH v2 3/5] gdb: update the docs for add_cmd and do_add_cmd to match reality Marco Barisione
@ 2021-03-08 22:52     ` Simon Marchi
  2021-03-08 23:10       ` Simon Marchi
  2021-05-14 20:39       ` [PATCH v3 3/5] gdb: move declarations and docs for cli-decode.c to cli-decode.h Marco Barisione
  0 siblings, 2 replies; 38+ messages in thread
From: Simon Marchi @ 2021-03-08 22:52 UTC (permalink / raw)
  To: Marco Barisione, gdb-patches

Just a few nits noted below.

Another good cleanup if you feel like it would be to move all the
declarations of the functions defined in cli/cli-decode.c from command.h
to cli/cli-decode.h.  We want to standardize that a declaration in foo.h
has its definition in foo.c.

> diff --git a/gdb/command.h b/gdb/command.h
> index 827a19637a2..df40cbf7119 100644
> --- a/gdb/command.h
> +++ b/gdb/command.h
> @@ -155,18 +155,44 @@ extern bool valid_user_defined_cmd_name_p (const char *name);
>  
>  extern bool valid_cmd_char_p (int c);
>  
> -/* Const-correct variant of the above.  */
> +/* Add a command named NAME in command list *LIST.
>  
> -extern struct cmd_list_element *add_cmd (const char *, enum command_class,
> +   NAME and DOC are not duplicated. If they are not static string, they

Two spaces after period.

> +   must have been allocated with xmalloc or xstrdup and the
> +   NAME_ALLOCATED/DOC_ALLOCATED fields must be set to 1 on the returned
> +   command.
> +
> +   THECLASS is the top level category into which commands are broken down
> +   for "help" purposes.
> +
> +   FUN should be the function to execute the command; it will get two

I'd say "is the" instead of "should be".  "should be" makes it sound
like it's a suggestion but it could be something else, which is not the
case.

> +   arguments, a character string (with leading and trailing blanks already
> +   eliminated) containing the command arguments, and an integer indicating
> +   whether input comes from a TTY or not.

I think this detailed doc about the callback's parameters would be
better placed in the doc comment of the cmd_const_cfunc_ftype typedef.
In that doc, you could refer to the parameters using their names.

> +
> +   DOC is a documentation string for the command.
> +   Its first line should be a complete sentence.
> +   It should start with ? for a command that is an abbreviation
> +   or with * for a command that most users don't need to know about.
> +
> +   If NAME already existed in *LIST, all its hooks and aliases are moved
> +   to the new command.
> +
> +   Return a pointer to the added command (not necessarily the head of
> +   *LIST).  */

I really like the way this doc is structured, one paragraph per
parameter with some space in between, very legible.

Simon

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2 3/5] gdb: update the docs for add_cmd and do_add_cmd to match reality
  2021-03-08 22:52     ` Simon Marchi
@ 2021-03-08 23:10       ` Simon Marchi
  2021-05-14 20:39       ` [PATCH v3 3/5] gdb: move declarations and docs for cli-decode.c to cli-decode.h Marco Barisione
  1 sibling, 0 replies; 38+ messages in thread
From: Simon Marchi @ 2021-03-08 23:10 UTC (permalink / raw)
  To: Marco Barisione, gdb-patches

On 2021-03-08 5:52 p.m., Simon Marchi via Gdb-patches wrote:> Just a few nits noted below.
> 
> Another good cleanup if you feel like it would be to move all the
> declarations of the functions defined in cli/cli-decode.c from command.h
> to cli/cli-decode.h.  We want to standardize that a declaration in foo.h
> has its definition in foo.c.
> 
>> diff --git a/gdb/command.h b/gdb/command.h
>> index 827a19637a2..df40cbf7119 100644
>> --- a/gdb/command.h
>> +++ b/gdb/command.h
>> @@ -155,18 +155,44 @@ extern bool valid_user_defined_cmd_name_p (const char *name);
>>  
>>  extern bool valid_cmd_char_p (int c);
>>  
>> -/* Const-correct variant of the above.  */
>> +/* Add a command named NAME in command list *LIST.
>>  
>> -extern struct cmd_list_element *add_cmd (const char *, enum command_class,
>> +   NAME and DOC are not duplicated. If they are not static string, they
> 
> Two spaces after period.
> 
>> +   must have been allocated with xmalloc or xstrdup and the
>> +   NAME_ALLOCATED/DOC_ALLOCATED fields must be set to 1 on the returned
>> +   command.
>> +
>> +   THECLASS is the top level category into which commands are broken down
>> +   for "help" purposes.
>> +
>> +   FUN should be the function to execute the command; it will get two
> 
> I'd say "is the" instead of "should be".  "should be" makes it sound
> like it's a suggestion but it could be something else, which is not the
> case.
> 
>> +   arguments, a character string (with leading and trailing blanks already
>> +   eliminated) containing the command arguments, and an integer indicating
>> +   whether input comes from a TTY or not.
> 
> I think this detailed doc about the callback's parameters would be
> better placed in the doc comment of the cmd_const_cfunc_ftype typedef.
> In that doc, you could refer to the parameters using their names.
> 
>> +
>> +   DOC is a documentation string for the command.
>> +   Its first line should be a complete sentence.
>> +   It should start with ? for a command that is an abbreviation
>> +   or with * for a command that most users don't need to know about.
>> +
>> +   If NAME already existed in *LIST, all its hooks and aliases are moved
>> +   to the new command.
>> +
>> +   Return a pointer to the added command (not necessarily the head of
>> +   *LIST).  */
> 
> I really like the way this doc is structured, one paragraph per
> parameter with some space in between, very legible.
> 
> Simon
> 

Note that if you send a new version of just that patch, we can approve
merge it on its own.

Simon

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2 4/5] gdb: generate the prefix name for prefix commands on demand
  2021-01-25 11:26   ` [PATCH v2 4/5] gdb: generate the prefix name for prefix commands on demand Marco Barisione
@ 2021-03-08 23:25     ` Simon Marchi
  2021-03-16 17:00       ` Simon Marchi
  2021-05-12 11:10       ` Marco Barisione
  0 siblings, 2 replies; 38+ messages in thread
From: Simon Marchi @ 2021-03-08 23:25 UTC (permalink / raw)
  To: Marco Barisione, gdb-patches

On 2021-01-25 6:26 a.m., Marco Barisione via Gdb-patches wrote:
> Previously, the prefixname field of struct cmd_list_element was manually
> set for prefix commands.  This seems verbose and error prone as it
> required every single call to functions adding prefix commands to
> specify the prefix name while the same information can be easily
> generated.
> 
> Historically, this was not possible as the prefix field was null for
> many commands, but this was fixed in commit
> 3f4d92ebdf7f848b5ccc9e8d8e8514c64fde1183 by Philippe Waroquiers, so
> we can rely on the prefix field being set when generating the prefix
> name.

Sounds like a good idea to me.  The patch is ok to push with these nits
fixed.

> 
> This commit also fixes a free after in this scenario:

"a free" -> "a use after free"?

> * A command gets created via Python (using the gdb.Command class).
>   The prefix name member is dynamically allocated.
> * An alias to the new command is created. The alias's prefixname is set
>   to point to the prefixname for the original command with a direct
>   assignment.
> * A new command with the same name as the Python command is created.
> * The object for the original Python command gets freed and its
>   prefixname gets freed as well.
> * The alias is update to point to the new command, but its prefixname
>   is not updated so it keeps pointing to the freed one.
> 
> gdb/ChangeLog:
> 
> 	* command.h (add_prefix_cmd): Remove the prefixname argument as
> 	it can now be generated automatically.
> 	(add_basic_prefix_cmd): Ditto.
> 	(add_show_prefix_cmd): Ditto.
> 	(add_prefix_cmd_suppress_notification): Ditto.
> 	(add_abbrev_prefix_cmd): Ditto.
> 	* cli/cli-decode.h (struct cmd_list_element): Replace the
> 	prefixname member variable with a method which generates the
> 	prefix name at runtime.
> 	* python/py-cmd.c (cmdpy_destroyer): Remove code to free the
> 	prefixname member as it's now a method.
> 	(cmdpy_function): Determine if the command is a prefix by
> 	looking at prefixlist, not prefixname.
> 	(cmdpy_init): Remove code to generate the prefix name.
> 	* cli/cli-decode.c (add_alias_cmd): Do not set prefixname on
> 	aliases.
> 	(do_prefix_cmd): Do not pass the prefixname argument to
> 	help_list.
> 	(add_prefix_cmd): Remove the prefixname argument.
> 	(add_basic_prefix_cmd): Ditto.
> 	(add_show_prefix_cmd): Ditto.
> 	(add_abbrev_prefix_cmd): Ditto.
> 	(fput_command_name_styled): Use the prefixname method.
> 	(apropos_cmd): Ditto.
> 	(help_cmd): Ditto.
> 	(lookup_cmd): Ditto.
> 	(deprecated_cmd_warning): Ditto.
> 	* cli/cli-script.c (do_define_command): Do not set the prefix
> 	name.
> 	(define_prefix_command): Ditto.
> 	(show_user_1): Uupdate to use the prefixname method.
> 	* cli/cli-setshow.c (cmd_show_list): Ditto.
> 	* top.c (execute_command): Ditto.
> 	* unittests/command-def-selftests.c (check_doc): Ditto.
> 	(traverse_command_structure): Ditto.
> 	* ada-lang.c (_initialize_ada_language): Do not pass the prefix
> 	name as it's not needed any more.
> 	* arm-tdep.c (_initialize_arm_tdep): Ditto.
> 	* auto-load.c: Ditto.
> 	* breakpoint.c: Ditto.
> 	* bsd-kvm.c: Ditto.
> 	* btrace.c: Ditto.
> 	* cli/cli-cmds.c: Ditto.
> 	* cli/cli-dump.c (_initialize_cli_dump): Ditto.
> 	* cli/cli-logging.c (_initialize_cli_logging): Ditto.
> 	* cli/cli-style.c (cli_style_option::add_setshow_commands): Ditto.
> 	* cli/cli-style.h (class cli_style_option): Ditto.
> 	* compile/compile.c: Ditto.
> 	* cp-support.c (_initialize_cp_support): Ditto.
> 	* dcache.c: Ditto.
> 	* dwarf2/index-cache.c (_initialize_index_cache): Ditto.
> 	* dwarf2/read.c: Ditto.
> 	* f-lang.c (_initialize_f_language): Ditto.
> 	* frame.c: Ditto.
> 	* gnu-nat.c (add_thread_commands): Ditto.
> 	* go32-nat.c (DJGPP): Ditto.
> 	* guile/guile.c: Ditto.
> 	* guile/scm-cmd.c (gdbscm_register_command_x): Ditto.
> 	* i386-tdep.c: Ditto.
> 	* infcmd.c: Ditto.
> 	* language.c (_initialize_language): Ditto.
> 	* macrocmd.c (_initialize_macrocmd): Ditto.
> 	* maint-test-options.c: Ditto.
> 	* maint-test-settings.c: Ditto.
> 	* maint.c: Ditto.
> 	* memattr.c: Ditto.
> 	* mips-tdep.c (_initialize_mips_tdep): Ditto.
> 	* printcmd.c: Ditto.
> 	* probe.c: Ditto.
> 	* python/python.c: Ditto.
> 	* ravenscar-thread.c (_initialize_ravenscar): Ditto.
> 	* record-btrace.c (_initialize_record_btrace): Ditto.
> 	* record-full.c (_initialize_record_full): Ditto.
> 	* record.c: Ditto.
> 	* remote.c: Ditto.
> 	* riscv-tdep.c (_initialize_riscv_tdep): Ditto.
> 	* rs6000-tdep.c (_initialize_rs6000_tdep): Ditto.
> 	* ser-tcp.c: Ditto.
> 	* serial.c: Ditto.
> 	* sh-tdep.c (_initialize_sh_tdep): Ditto.
> 	* skip.c: Ditto.
> 	* sparc64-tdep.c (_initialize_sparc64_adi_tdep): Ditto.
> 	* stack.c: Ditto.
> 	* symfile.c: Ditto.
> 	* symtab.c: Ditto.
> 	* target-descriptions.c: Ditto.
> 	* target.c: Ditto.
> 	* thread.c: Ditto.
> 	* tracepoint.c: Ditto.
> 	* tui/tui-layout.c: Ditto.
> 	* tui/tui-win.c (tui_get_cmd_list): Ditto.
> 	(_initialize_tui_win): Ditto.
> 	* typeprint.c: Ditto.
> 	* utils.c (add_internal_problem_command): Ditto.
> 	* valprint.c: Ditto.
> 	* value.c: Ditto.
> 	* windows-tdep.c (init_w32_command_list): Ditto.

IMO it's not really useful to list all the spots that were just
trivially adjusted.  It's ok to write things like:

  * command.h (add_prefix_cmd): Remove prefixname parameter.  Update all
  callers.

... and then skip listing all callers.  That will also help preserve
your sanity.

> diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
> index e6907a95a84..ddcb2ea9578 100644
> --- a/gdb/cli/cli-decode.h
> +++ b/gdb/cli/cli-decode.h
> @@ -71,6 +71,26 @@ struct cmd_list_element
>  
>      DISABLE_COPY_AND_ASSIGN (cmd_list_element);
>  
> +    /* For prefix commands, retun a string containing prefix commands to

retun -> return

> +       get here: this one plus any others needed to get to it.  Ends in a
> +       space.  It is used before the word "command" in describing the
> +       commands reached through this prefix.
> +
> +       For non-prefix commands, an empty string is returned.  */
> +    std::string prefixname ()
> +      {
> +	if (prefixlist == nullptr)
> +	  /* Not a prefix command.  */
> +	  return "";
> +
> +	std::string prefixname;
> +	if (prefix != nullptr)
> +	  prefixname = prefix->prefixname ();
> +	prefixname += name;
> +	prefixname += " ";
> +	return prefixname;
> +      }

De-indent the whole block by one (see the methods just above).

Simon

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2 2/5] gdb: prevent prefix commands from being hooks
  2021-03-08 21:32     ` Simon Marchi
@ 2021-03-09  9:42       ` Marco Barisione
  2021-03-16  3:17         ` Simon Marchi
  2021-05-14 20:38       ` [PATCH v3 " Marco Barisione
  1 sibling, 1 reply; 38+ messages in thread
From: Marco Barisione @ 2021-03-09  9:42 UTC (permalink / raw)
  To: Simon Marchi; +Cc: GDB patches mailing list

On 8 Mar 2021, at 21:32, Simon Marchi <simon.marchi@polymtl.ca> wrote:
> Ahhh, changes that touch command handling are always scary stuff, we
> need to be really careful not to break existing use cases or paint
> ourselves in corners for future use cases.  Probably why nobody dares to
> review it.  I'll give it a try, but know that it still scares me :).

It did scare me and took a while because of the many corner cases,
difficult combinations of features.

My reasoning behind this is that the behaviour is currently confusing
(that is, what the user would expect doesn’t happen) and forbidding
this should allow changes in the future quite easily.

> One question that pops up is, what if someone ever wants to make a hook
> for a prefixed command.  For example, let's say I want to hook "info
> inferiors".  I don't think that's possible today, but let's say we want
> to make it possible.  How would that work?
> 
> This doesn't work:
> 
>   (gdb) define hook-info inferiors
>    Undefined command: "hook-info".  Try "help".
> 
> So... maybe defining "hook-info" as a prefix command would allow it?
> Not sure, I didn't really think this through, but I wonder if you
> thought about that case.

You can do it with “define info hook-inferiors” which I found a bit
surprising when I discovered it.


I think that hooks are incredibly useful but limited. My hope is that
my patch series will make them less needed as you can get a better and
more powerful behaviour with command renaming.

-- 
Marco Barisione


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2 2/5] gdb: prevent prefix commands from being hooks
  2021-03-09  9:42       ` Marco Barisione
@ 2021-03-16  3:17         ` Simon Marchi
  2021-05-07 14:59           ` Marco Barisione
  0 siblings, 1 reply; 38+ messages in thread
From: Simon Marchi @ 2021-03-16  3:17 UTC (permalink / raw)
  To: Marco Barisione; +Cc: GDB patches mailing list



On 2021-03-09 4:42 a.m., Marco Barisione wrote:
> On 8 Mar 2021, at 21:32, Simon Marchi <simon.marchi@polymtl.ca> wrote:
>> Ahhh, changes that touch command handling are always scary stuff, we
>> need to be really careful not to break existing use cases or paint
>> ourselves in corners for future use cases.  Probably why nobody dares to
>> review it.  I'll give it a try, but know that it still scares me :).
> 
> It did scare me and took a while because of the many corner cases,
> difficult combinations of features.
> 
> My reasoning behind this is that the behaviour is currently confusing
> (that is, what the user would expect doesn’t happen) and forbidding
> this should allow changes in the future quite easily.
> 
>> One question that pops up is, what if someone ever wants to make a hook
>> for a prefixed command.  For example, let's say I want to hook "info
>> inferiors".  I don't think that's possible today, but let's say we want
>> to make it possible.  How would that work?
>>
>> This doesn't work:
>>
>>   (gdb) define hook-info inferiors
>>    Undefined command: "hook-info".  Try "help".
>>
>> So... maybe defining "hook-info" as a prefix command would allow it?
>> Not sure, I didn't really think this through, but I wonder if you
>> thought about that case.
> 
> You can do it with “define info hook-inferiors” which I found a bit
> surprising when I discovered it.

I didn't know that!  And it's even tested:

https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/testsuite/gdb.base/define.exp;h=ff34e7f294376454fbefb868f4e3f90b6105f0d8;hb=HEAD#l296

... and documented in the manual.

> I think that hooks are incredibly useful but limited. My hope is that
> my patch series will make them less needed as you can get a better and
> more powerful behaviour with command renaming.

I mentioned in a previous review (somewhere in the default-args series,
I can't find it right now) that I was not too comfortable in having
features allowing modifying the behavior of builtin commands in
undocumented ways.  You could say that hooks are a way to do that.  But
at least, even if you define a hook-backtrace, the backtrace part still
works as usual, just that there are some things happening before.  Maybe
I'm a bit too afraid that some script I'm using breaks some other script
I'm using because it decided to rename some core command...

But I see why that would be useful (we use proc renaming in TCL for the
testsuite).  It's a tradeoff, with powerful tools you can go great
things but are also more likely to shoot yourself in the foot.  And I
guess we can't protect the users from themselves for everything,
otherwise our tool is useless.  And since the concept was deemed ok by
other reviewers / maintainers previously, I won't object to it.

Simon

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2 4/5] gdb: generate the prefix name for prefix commands on demand
  2021-03-08 23:25     ` Simon Marchi
@ 2021-03-16 17:00       ` Simon Marchi
  2021-05-12 11:10       ` Marco Barisione
  1 sibling, 0 replies; 38+ messages in thread
From: Simon Marchi @ 2021-03-16 17:00 UTC (permalink / raw)
  To: Marco Barisione, gdb-patches


On 2021-03-08 6:25 p.m., Simon Marchi via Gdb-patches wrote:
> On 2021-01-25 6:26 a.m., Marco Barisione via Gdb-patches wrote:
>> Previously, the prefixname field of struct cmd_list_element was manually
>> set for prefix commands.  This seems verbose and error prone as it
>> required every single call to functions adding prefix commands to
>> specify the prefix name while the same information can be easily
>> generated.
>>
>> Historically, this was not possible as the prefix field was null for
>> many commands, but this was fixed in commit
>> 3f4d92ebdf7f848b5ccc9e8d8e8514c64fde1183 by Philippe Waroquiers, so
>> we can rely on the prefix field being set when generating the prefix
>> name.
> 
> Sounds like a good idea to me.  The patch is ok to push with these nits
> fixed.

Oh, one more thing, the patch doesn't build with --enable-targets=all.
Please configure your GDB build with --enable-targets=all and make sure
it builds.

There may also be some places that need to be updated in files that are
specific to some platforms.  That would be all the files that are
mentioned in gdb/configure.nat.  Please make a best effort to update the
calls in these files, even if you can't build them.

Simon

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2 5/5] gdb: Add support for renaming commands
  2021-01-25 11:26   ` [PATCH v2 5/5] gdb: Add support for renaming commands Marco Barisione
@ 2021-03-23 18:45     ` Simon Marchi
  2021-05-14 20:41       ` [PATCH v3 5/5] gdb: add " Marco Barisione
  0 siblings, 1 reply; 38+ messages in thread
From: Simon Marchi @ 2021-03-23 18:45 UTC (permalink / raw)
  To: Marco Barisione, gdb-patches



On 2021-01-25 6:26 a.m., Marco Barisione via Gdb-patches wrote:
> This patch adds:
> * A "rename" command.
> * A "-rename-existing-to" option for the "define" command.
> * A "rename_existing_to" optional argument to gdb.Command.__init__
>   which matches the behaviour of "define -rename-existing-to".
> 
> The goal of this is to allow users to build on top of existing commands
> without losing their original implementation.
> Something similar could be achieved through hooks but they are limited:
> * Hooks cannot prevent the command from being executed without printing
>   any error.
> * Hooks don't get passed the arguments passed to the command.
> * Post hooks can't know if the command failed.
> * Hooks cannot be defined in Python.

The command code really gives me headaches.  I read the code but I won't
pretend like I thoroughly check it.

Here are some minor things I noted while reading it.

> @@ -160,53 +166,191 @@ set_cmd_completer_handle_brkchars (struct cmd_list_element *cmd,
>    cmd->completer_handle_brkchars = func;
>  }
>  
> -/* Like ADD_CMD, but the command function fields are not modified.  */
> +/* Update the prefix-related fields associated with command C, which must
> +   be part of the comand list *LIST, and, if not null, adds PREFIXLIST

comand -> command

> @@ -283,14 +435,19 @@ add_alias_cmd (const char *name, cmd_list_element *old,
>  {
>    if (old == 0)
>      {
> +      struct cmd_list_element **prefixlist;
> +      struct cmd_list_element *aliases;
>        struct cmd_list_element *prehook, *prehookee, *posthook, *posthookee;
> -      struct cmd_list_element *aliases = delete_cmd (name, list,
> -						     &prehook, &prehookee,
> -						     &posthook, &posthookee);
> -
> +      struct cmd_list_element *old_cmd
> +	= disconnect_cmd (name, list,
> +			  &prefixlist,
> +			  &aliases,
> +			  &prehook, &prehookee,
> +			  &posthook, &posthookee);
>        /* If this happens, it means a programmer error somewhere.  */
> -      gdb_assert (!aliases && !prehook && !prehookee
> -		  && !posthook && ! posthookee);
> +      gdb_assert (!old_cmd);

Compare pointers explicitly with nullptr:

  gdb_assert (old_cmd != nullptr);

> +      /* As OLD_CMD is null, NAME didn't exist, so the prefix list,
> +       * aliases and hooks are all null as well.  */

Remove the asterisk on the second line:

      /* As OLD_CMD is null, NAME didn't exist, so the prefix list,
         aliases and hooks are all null as well.  */

> diff --git a/gdb/command.h b/gdb/command.h
> index ca791cff809..b20737a6aee 100644
> --- a/gdb/command.h
> +++ b/gdb/command.h
> @@ -175,24 +175,37 @@ extern bool valid_cmd_char_p (int c);
>     It should start with ? for a command that is an abbreviation
>     or with * for a command that most users don't need to know about.
>  
> +   If RENAME_EXISTING_TO is not null, then the existing command called
> +   NAME in *LIST will be renamed to RENAME_EXISTING_TO in
> +   *RENAME_EXISTING_TO_LIST.  If a command called RENAME_EXISTING_TO
> +   already exists in *RENAME_EXISTING_TO_LIST, it gets deleted first.
> +   Ownership of RENAME_EXISTING_TO is not taken by this function (i.e.
> +   the caller is responsible to free it if needed).
> +
>     If NAME already existed in *LIST, all its hooks and aliases are moved
>     to the new command.
>  
>     Return a pointer to the added command (not necessarily the head of
>     *LIST).  */
>  
> -extern struct cmd_list_element *add_cmd (const char *name,
> -					 enum command_class theclass,
> -					 cmd_const_cfunc_ftype *fun,
> -					 const char *doc,
> -					 struct cmd_list_element **list);
> +extern struct cmd_list_element *add_cmd
> +		(const char *name,
> +		 enum command_class theclass,
> +		 cmd_const_cfunc_ftype *fun,
> +		 const char *doc,
> +		 struct cmd_list_element **list,
> +		 const char *rename_existing_to=nullptr,
> +		 struct cmd_list_element **rename_existing_to_list=nullptr);

Spaces around `=`, apply everywhere where it's relevant.

When wrapping like this, where you need to start the parameter list on
another line, use a single indent (two spaces).  Like this:

extern struct cmd_list_element *add_cmd
  (const char *name,
   enum command_class theclass,
   cmd_const_cfunc_ftype *fun,
   const char *doc,
   struct cmd_list_element **list,
   const char *rename_existing_to=nullptr,
   struct cmd_list_element **rename_existing_to_list=nullptr);

Optionally, you can also put multiple parameters on the same line to use
less lines.  Since we use C++, I like to get rid of `struct` or `enum`
keywords when possible, that's less verbose.

extern cmd_list_element *add_cmd
  (const char *name, command_class theclass, cmd_const_cfunc_ftype *fun,
   const char *doc, cmd_list_element **list,
   const char *rename_existing_to = nullptr,
   cmd_list_element **rename_existing_to_list = nullptr);


> diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
> index 35568594f58..22155f595c6 100644
> --- a/gdb/doc/python.texi
> +++ b/gdb/doc/python.texi
> @@ -3657,7 +3657,7 @@ You can implement new @value{GDBN} CLI commands in Python.  A CLI
>  command is implemented using an instance of the @code{gdb.Command}
>  class, most commonly using a subclass.
>  
> -@defun Command.__init__ (name, @var{command_class} @r{[}, @var{completer_class} @r{[}, @var{prefix}@r{]]})
> +@defun Command.__init__ (name, @var{command_class} @r{[}, @var{completer_class} @r{[}, @var{prefix} @r{[}, @var{rename_existing_to}@r{]]]})

I think the way this renders to is misleading:

    Command.__init__ (name, command_class [, completer_class [, prefix [, rename_existing_to]]])

The brackets are nested, so it makes it sound like rename_existing_to
can only be used when prefix is given.  And they both can only be used
when completer_class is given.

And since command_class is now sometimes optional, it should be marked
as such.

    Command.__init__ (name[, command_class] [, completer_class] [, prefix] [, rename_existing_to])

> @@ -3931,6 +3945,24 @@ registration of the command with @value{GDBN}.  Depending on how the
>  Python code is read into @value{GDBN}, you may need to import the
>  @code{gdb} module explicitly.
>  
> +The following code snippet shows how a CLI command building on top of an
> +existing command can be implemented:
> +
> +@smallexample
> +class MyQuit (gdb.Command):
> +  """Print a message before executing the "quit" command."""
> +
> +  def __init__ (self):
> +    super (MyQuit, self).__init__ ("quit", gdb.COMMAND_RUNNING,
> +                                  rename_existing_to="original-quit")

Do you use COMMAND_RUNNING on purpose here, to show that it's possible
to change the class of a command?

> @@ -450,40 +457,130 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
>        return -1;
>      }
>  
> -  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "si|iO",
> -					keywords, &name, &cmdtype,
> -					&completetype, &is_prefix_obj))
> +  /* The second argument (command_class) is marked as optional because
> +     it should not be specified if RENAME_EXISTING_TO is specified.
> +     Otherwise it's required.  */
> +  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!Os",
> +					keywords,
> +					&name,
> +					&PyInt_Type, &cmdtype_obj,
> +					&PyInt_Type, &completetype_obj,
> +					&is_prefix_obj,
> +					&rename_existing_to))
>      return -1;
>  
> -  if (cmdtype != no_class && cmdtype != class_run
> -      && cmdtype != class_vars && cmdtype != class_stack
> -      && cmdtype != class_files && cmdtype != class_support
> -      && cmdtype != class_info && cmdtype != class_breakpoint
> -      && cmdtype != class_trace && cmdtype != class_obscure
> -      && cmdtype != class_maintenance && cmdtype != class_user
> -      && cmdtype != class_tui)
> -    {
> -      PyErr_Format (PyExc_RuntimeError, _("Invalid command class argument."));
> -      return -1;
> -    }
> -
> -  if (completetype < -1 || completetype >= (int) N_COMPLETERS)
> -    {
> -      PyErr_Format (PyExc_RuntimeError,
> -		    _("Invalid completion type argument."));
> -      return -1;
> -    }
> -
>    cmd_name = gdbpy_parse_command_name (name, &cmd_list, &cmdlist);
>    if (! cmd_name)
>      return -1;
>  
> +  /* Deal with rename_existing_to before other arguments as it affects how
> +     some of them are treated.  */
> +  if (rename_existing_to != NULL)
> +    {
> +      renamed_cmd = lookup_cmd_exact (cmd_name, *cmd_list);
> +      if (renamed_cmd == nullptr)
> +	{
> +	  PyErr_Format (PyExc_RuntimeError,
> +			_("Command \"%s\" does not exist, so it cannot "
> +			  "be renamed to \"%s\"."),
> +			name, rename_existing_to);
> +	  xfree (cmd_name);
> +	  return -1;
> +	}
> +
> +      rename_existing_to_cmd_name =
> +	gdbpy_parse_command_name (rename_existing_to,
> +				  &rename_existing_to_cmd_list,
> +				  &cmdlist);
> +      if (! rename_existing_to_cmd_name)

  if (rename_existing_to_cmd_name == nullptr)

If the surrounding code is incorrect, you can fix it up too, if you want
to keep it consistent.

> +	{
> +	  xfree (cmd_name);
> +	  return -1;

Instead of xfree'ing cmd_name by hand, I'll make a patch that makes
gdbpy_parse_command_name return a gdb::unique_xmalloc_ptr.  You'll have
to update this code, but it will just make it simpler.

Here it is, I would appreciate if you could give it a look:

  https://sourceware.org/pipermail/gdb-patches/2021-March/177162.html

Thanks,

Simon

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2 1/5] gdb: add lookup_cmd_exact to simplify a common pattern
  2021-03-08 18:58     ` Simon Marchi
@ 2021-05-07 14:47       ` Marco Barisione
  0 siblings, 0 replies; 38+ messages in thread
From: Marco Barisione @ 2021-05-07 14:47 UTC (permalink / raw)
  To: Simon Marchi; +Cc: GDB patches mailing list

On Mon, 8 Mar 2021 at 18:58, Simon Marchi <simark@simark.ca> wrote:
>
> On 2021-01-25 6:26 a.m., Marco Barisione via Gdb-patches wrote:
> > In code dealing with commands, there's a pattern repeated a few times of
> > calling lookup_cmd with some speficic arguments and then using strcmp
> > on the returned command to check for an exact match.
> > As a later patch would add a few more similar lines of code, this patch
> > adds a new lookup_cmd_exact function which simplify this use case.
> >
> > gdb/ChangeLog:
> >
> >       * cli/cli-decode.c (lookup_cmd_exact): Add.
> >       * cli/cli-script.c (do_define_command): Use lookup_cmd_exact.
> >       (define_prefix_command): Ditto.
> >       * command.h: Add lookup_cmd_exact.
>
> This is ok, you can push it on its own right away, as it's a good
> cleanup on its own.

Thanks for your reviews!
This is now pushed to master (a9b49cbcd5935a713da5715799ea3b24e0a52851).

Sorry for taking a while, but I had to work on some other things.
I will now follow up on this patch series to address your review comments.

-- 
Marco Barisione

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2 2/5] gdb: prevent prefix commands from being hooks
  2021-03-16  3:17         ` Simon Marchi
@ 2021-05-07 14:59           ` Marco Barisione
  2021-05-07 19:30             ` Simon Marchi
  0 siblings, 1 reply; 38+ messages in thread
From: Marco Barisione @ 2021-05-07 14:59 UTC (permalink / raw)
  To: Simon Marchi; +Cc: GDB patches mailing list

On Tue, 16 Mar 2021 at 03:17, Simon Marchi <simon.marchi@polymtl.ca> wrote:
> I mentioned in a previous review (somewhere in the default-args series,
> I can't find it right now) that I was not too comfortable in having
> features allowing modifying the behavior of builtin commands in
> undocumented ways.  You could say that hooks are a way to do that.  But
> at least, even if you define a hook-backtrace, the backtrace part still
> works as usual, just that there are some things happening before.  Maybe
> I'm a bit too afraid that some script I'm using breaks some other script
> I'm using because it decided to rename some core command...
>
> But I see why that would be useful (we use proc renaming in TCL for the
> testsuite).  It's a tradeoff, with powerful tools you can go great
> things but are also more likely to shoot yourself in the foot.  And I
> guess we can't protect the users from themselves for everything,
> otherwise our tool is useless.  And since the concept was deemed ok by
> other reviewers / maintainers previously, I won't object to it.

You won't object to the concept, but how about this specific patch? :)

Is there anything to change? If not, should it wait for the main command
renaming one before going in?

-- 
Marco Barisione

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2 2/5] gdb: prevent prefix commands from being hooks
  2021-05-07 14:59           ` Marco Barisione
@ 2021-05-07 19:30             ` Simon Marchi
  2021-05-07 20:11               ` Marco Barisione
  0 siblings, 1 reply; 38+ messages in thread
From: Simon Marchi @ 2021-05-07 19:30 UTC (permalink / raw)
  To: Marco Barisione; +Cc: GDB patches mailing list

> You won't object to the concept, but how about this specific patch? :)

It's been a while, I don't remember the specific details.  If you
already have comments to work on, I'd wait until the next version of the
series to re-review, as I don't have too many spare cycles right now.

> Is there anything to change? If not, should it wait for the main command
> renaming one before going in?

Which other patch are you referring to?

Simon

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2 2/5] gdb: prevent prefix commands from being hooks
  2021-05-07 19:30             ` Simon Marchi
@ 2021-05-07 20:11               ` Marco Barisione
  0 siblings, 0 replies; 38+ messages in thread
From: Marco Barisione @ 2021-05-07 20:11 UTC (permalink / raw)
  To: Simon Marchi; +Cc: GDB patches mailing list

On 7 May 2021, at 20:30, Simon Marchi <simon.marchi@polymtl.ca> wrote:
>> You won't object to the concept, but how about this specific patch? :)
> 
> It's been a while, I don't remember the specific details.  If you
> already have comments to work on, I'd wait until the next version of the
> series to re-review, as I don't have too many spare cycles right now.

Sure, not urgent!
I plan on finishing updating the patches next week.

>> Is there anything to change? If not, should it wait for the main command
>> renaming one before going in?
> 
> Which other patch are you referring to?

“[PATCH v2 5/5] gdb: Add support for renaming commands”, that is
<https://sourceware.org/pipermail/gdb-patches/2021-January/175429.html>.

Which you partly reviewed in
<https://sourceware.org/pipermail/gdb-patches/2021-March/177165.html>.

-- 
Marco Barisione


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2 4/5] gdb: generate the prefix name for prefix commands on demand
  2021-03-08 23:25     ` Simon Marchi
  2021-03-16 17:00       ` Simon Marchi
@ 2021-05-12 11:10       ` Marco Barisione
  1 sibling, 0 replies; 38+ messages in thread
From: Marco Barisione @ 2021-05-12 11:10 UTC (permalink / raw)
  To: Simon Marchi; +Cc: GDB patches mailing list

On Mon, 8 Mar 2021 at 23:25, Simon Marchi <simon.marchi@polymtl.ca> wrote:
> On 2021-01-25 6:26 a.m., Marco Barisione via Gdb-patches wrote:
> > Previously, the prefixname field of struct cmd_list_element was manually
> > set for prefix commands.  This seems verbose and error prone as it
> > required every single call to functions adding prefix commands to
> > specify the prefix name while the same information can be easily
> > generated.
> >
> > Historically, this was not possible as the prefix field was null for
> > many commands, but this was fixed in commit
> > 3f4d92ebdf7f848b5ccc9e8d8e8514c64fde1183 by Philippe Waroquiers, so
> > we can rely on the prefix field being set when generating the prefix
> > name.
>
> Sounds like a good idea to me.  The patch is ok to push with these nits
> fixed.

I fixed the nits, fixed the two commands I previously forgot to update
(maintenance print arc and memory-tag), and manually double checked
all the calls to add_prefix and friends.

This is now pushed (2f822da535ba9b159174d02d7114b4fc4f7c8818).

> > gdb/ChangeLog:
> >
> > [...]
> >       * arm-tdep.c (_initialize_arm_tdep): Ditto.
> >       * auto-load.c: Ditto.
> >       * breakpoint.c: Ditto.
> >       * bsd-kvm.c: Ditto.
> >       * btrace.c: Ditto.
> >       * cli/cli-cmds.c: Ditto.
> >       * cli/cli-dump.c (_initialize_cli_dump): Ditto.
> >  [...]
>
> IMO it's not really useful to list all the spots that were just
> trivially adjusted.  It's ok to write things like:
>
>   * command.h (add_prefix_cmd): Remove prefixname parameter.  Update all
>   callers.
>
> ... and then skip listing all callers.  That will also help preserve
> your sanity.

I thought I had to list everything but glad to know I don't!

-- 
Marco Barisione

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v3 2/5] gdb: prevent prefix commands from being hooks
  2021-03-08 21:32     ` Simon Marchi
  2021-03-09  9:42       ` Marco Barisione
@ 2021-05-14 20:38       ` Marco Barisione
  1 sibling, 0 replies; 38+ messages in thread
From: Marco Barisione @ 2021-05-14 20:38 UTC (permalink / raw)
  To: gdb-patches

Currently it's possible for hooks to be prefix commands:

  define-prefix hook-run

  define hook-run
    echo Will run!\n
  end

  define hook-run subcommand
    echo Subcommand\n
  end

Then:

  (gdb) run
  Will run!
  [...]
  (gdb) hook-run subcommand
  Subcommand

This doesn't seem very useful or worse, it can be confusing.  Moreover,
this creates some obscure corner cases in a later patch adding command
renaming.

Because of this, this patch prevents hooks to become prefixes and
prefixes to become hooks.

An alternative approach would have been to prevent prefixes starting
with "hook-" or "hookpost-".  The end result would have been similar
but not identical as GDB allows commands starting with "hook-" or
"hookpost-" which are not hooks (for instance, "define
hook-this-doesnt-exist").  To be more consistent with this use case,
prefixes for commands which are named like hooks but are not hooks are
allowed.

gdb/ChangeLog:

	* cli/cli-script.c (do_define_command): Prevent prefix commands
	from becoming hooks.
	(define_prefix_command): Prevent hooks from becoming prefix
	commands.

gdb/testsuite/ChangeLog:

	* gdb.base/define-prefix.exp: Test that prefix commands cannot
	become hooks and vice versa.
---
 gdb/cli/cli-script.c                     | 25 ++++++++++++++++++------
 gdb/testsuite/gdb.base/define-prefix.exp | 24 +++++++++++++++++++++++
 2 files changed, 43 insertions(+), 6 deletions(-)

diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index eb8853a5e64..5841f545882 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -1455,6 +1455,11 @@ do_define_command (const char *comname, int from_tty,
 	  if (!query (_("Proceed? ")))
 	    error (_("Not confirmed."));
 	}
+
+      if (c != nullptr && c->prefixlist != nullptr)
+        error (_("Command \"%s\" cannot become a hook as it's already a "
+		 "prefix command."),
+	       comfull);
     }
 
   comname = xstrdup (comname);
@@ -1592,13 +1597,21 @@ define_prefix_command (const char *comname, int from_tty)
 
   c = lookup_cmd_exact (comname, *list);
 
-  if (c != nullptr && c->theclass != class_user)
-    error (_("Command \"%s\" is built-in."), comfull);
-
-  if (c != nullptr && c->prefixlist != nullptr)
+  if (c != nullptr)
     {
-      /* c is already a user defined prefix command.  */
-      return;
+      if (c->theclass != class_user)
+        error (_("Command \"%s\" is built-in."), comfull);
+
+      if (c->prefixlist != nullptr)
+        {
+          /* C is already a user defined prefix command.  */
+          return;
+        }
+
+      if (c->hookee_pre != nullptr || c->hookee_post != nullptr)
+        error (_("Command \"%s\" cannot be a prefix command as it's "
+		 "already a hook."),
+	       comfull);
     }
 
   /* If the command does not exist at all, create it.  */
diff --git a/gdb/testsuite/gdb.base/define-prefix.exp b/gdb/testsuite/gdb.base/define-prefix.exp
index 71369caaed6..a8ce5765ec7 100644
--- a/gdb/testsuite/gdb.base/define-prefix.exp
+++ b/gdb/testsuite/gdb.base/define-prefix.exp
@@ -162,3 +162,27 @@ gdb_test "define-prefix something-not-existing something-else" \
 gdb_test "define-prefix abc-prefix something-not-existing something-else" \
     "Undefined abc-prefix command: \"something-not-existing\".*"
 
+####################
+# Check error behaviour when interacting with hooks.
+
+# Define a command and hooks into it.
+gdb_define_cmd "hookee1" {}
+gdb_define_cmd "hook-hookee1" {}
+gdb_define_cmd "hookpost-hookee1" {}
+
+# Check that making the hooks into prefix commands is not allowed.
+gdb_test_exact "define-prefix hook-hookee1" \
+  "Command \"hook-hookee1\" cannot be a prefix command as it's already a hook."
+gdb_test_exact "define-prefix hookpost-hookee1" \
+  "Command \"hookpost-hookee1\" cannot be a prefix command as it's already a hook."
+
+# Define a command and prefixes which look like hooks into it (but they
+# are not).
+gdb_define_cmd "hookee2" {}
+gdb_test_no_output "define-prefix hook-hookee2" {}
+gdb_test_no_output "define-prefix hookpost-hookee2" {}
+
+gdb_test_exact "define hook-hookee2" \
+  "Command \"hook-hookee2\" cannot become a hook as it's already a prefix command."
+gdb_test_exact "define hookpost-hookee2" \
+  "Command \"hookpost-hookee2\" cannot become a hook as it's already a prefix command."
-- 
2.28.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v3 3/5] gdb: move declarations and docs for cli-decode.c to cli-decode.h
  2021-03-08 22:52     ` Simon Marchi
  2021-03-08 23:10       ` Simon Marchi
@ 2021-05-14 20:39       ` Marco Barisione
  1 sibling, 0 replies; 38+ messages in thread
From: Marco Barisione @ 2021-05-14 20:39 UTC (permalink / raw)
  To: gdb-patches

Declarations and the documentation should be in the header file for the
corresponding .c file.  This helps keeping command.h cleaner.

I also updated the documentation for add_cmd which ended up being docs
for the wrong function (do_add_cmd) and quite outdated.

gdb/ChangeLog:

	* command.h (cmd_const_cfunc_ftype): Move declaration to
	cli/cli-decode.h.
	(valid_user_defined_cmd_name_p): Ditto.
	(valid_cmd_char_p): Ditto.
	(add_alias_cmd): Ditto.
	(add_prefix_cmd): Ditto.
	(add_abbrev_prefix_cmd): Ditto.
	(cmd_const_sfunc_ftype): Ditto.
	(set_cmd_sfunc): Ditto.
	(set_cmd_completer): Ditto.
	(set_cmd_completer_handle_brkchars): Ditto.
	(cmd_cfunc_eq): Ditto.
	(set_cmd_context): Ditto.
	(get_cmd_context): Ditto.
	(CMD_LIST_AMBIGUOUS): Ditto.
	(lookup_cmd): Ditto.
	(lookup_cmd_exact): Ditto.
	(deprecate_cmd): Ditto.
	(deprecated_cmd_warning): Ditto.
	(lookup_cmd_composition): Ditto.
	(add_com): Ditto.
	(add_com_alias): Ditto.
	(add_info): Ditto.
	(add_info_alias): Ditto.
	(complete_on_cmdlist): Ditto.
	(complete_on_enum): Ditto.
	(help_list): Ditto.
	(add_setshow_enum_cmd): Ditto.
	(add_setshow_auto_boolean_cmd): Ditto.
	(add_setshow_boolean_cmd): Ditto.
	(add_setshow_filename_cmd): Ditto.
	(add_setshow_string_cmd): Ditto.
	(add_setshow_optional_filename_cmd): Ditto.
	(add_setshow_integer_cmd): Ditto.
	(add_setshow_uinteger_cmd): Ditto.
	(add_setshow_zinteger_cmd): Ditto.
	(add_setshow_zuinteger_cmd): Ditto.
	(add_setshow_zuinteger_unlimited_cmd): Ditto.
	(completer_ftype): Move to completer.h.
	(completer_handle_brkchars_ftype): Ditto.
	* cli/cli-decode.h (cmd_const_sfunc_ftype): Move declaration
	from command.h
	(valid_user_defined_cmd_name_p): Ditto.
	(valid_cmd_char_p): Ditto.
	(add_alias_cmd): Ditto.
	(add_prefix_cmd): Ditto.
	(add_abbrev_prefix_cmd): Ditto.
	(set_cmd_sfunc): Ditto.
	(set_cmd_completer): Ditto.
	(set_cmd_completer_handle_brkchars): Ditto.
	(cmd_cfunc_eq): Ditto.
	(set_cmd_context): Ditto.
	(get_cmd_context): Ditto.
	(CMD_LIST_AMBIGUOUS): Ditto.
	(lookup_cmd): Ditto.
	(deprecate_cmd): Ditto.
	(deprecated_cmd_warning): Ditto.
	(lookup_cmd_composition): Ditto.
	(add_com): Ditto.
	(add_com_alias): Ditto.
	(add_info): Ditto.
	(add_info_alias): Ditto.
	(complete_on_cmdlist): Ditto.
	(complete_on_enum): Ditto.
	(help_list): Ditto.
	(add_setshow_enum_cmd): Ditto.
	(add_setshow_auto_boolean_cmd): Ditto.
	(add_setshow_filename_cmd): Ditto.
	(add_setshow_string_cmd): Ditto.
	(add_setshow_integer_cmd): Ditto.
	(add_setshow_uinteger_cmd): Ditto.
	(add_setshow_zinteger_cmd): Ditto.
	(add_setshow_zuinteger_cmd): Ditto.
	(add_cmd): Move declaration from command.h and fix
	documentation.
	* cli/cli-decode.c (add_cmd): Move documentation to
	cli/cli-decode.h
	(add_cmd_suppress_notification): Ditto.
	(update_prefix_field_of_prefixed_commands): Ditto.
	(do_prefix_cmd): Ditto.
	(do_show_prefix_cmd): Ditto.
	(add_show_prefix_cmd): Ditto.
	(add_abbrev_prefix_cmd): Ditto.
	(add_setshow_cmd_full): Ditto.
	(help_cmd): Ditto.
	(find_command_name_length): Ditto.
	(valid_cmd_char_p): Ditto.
	(valid_user_defined_cmd_name_p): Ditto.
	(undef_cmd_error): Ditto.
	(lookup_cmd): Ditto.
	(lookup_cmd_exact): Ditto.
	(lookup_cmd_composition_1): Ditto.
	(lookup_cmd_composition): Ditto.
	(complete_on_cmdlist): Ditto.
	(cmd_func_p): Ditto.
	* (do_add_cmd): Remove outdated documentation and refer to the
	documentation for add_cmd.
	* completer.h (completer_ftype): Moved from command.h.
	(completer_handle_brkchars_ftype): Ditto.
	* breakpoint.h: Include cli/cli-decode.h.
	* charset.c: Ditto.
	* cli/cli-logging.c: Ditto.
	* cli/cli-style.c: Ditto.
	* complaints.c: Ditto.
	* copying.c: Ditto.
	* cp-abi.c: Ditto.
	* filesystem.c: Ditto.
	* gdb-demangle.c: Ditto.
	* interps.c: Ditto.
	* macrocmd.c: Ditto.
	* maint-test-options.c: Ditto.
	* maint-test-settings.c: Ditto.
	* observable.c: Ditto.
	* osabi.c: Ditto.
	* reggroups.c: Ditto.
	* ser-unix.c: Ditto.
	* serial.c: Ditto.
	* user-regs.c: Ditto.
	* xml-support.c: Ditto.
---
 gdb/breakpoint.h          |   1 +
 gdb/charset.c             |   1 +
 gdb/cli/cli-decode.c      | 155 +++----------
 gdb/cli/cli-decode.h      | 473 +++++++++++++++++++++++++++++++++++++-
 gdb/cli/cli-logging.c     |   1 +
 gdb/cli/cli-style.c       |   1 +
 gdb/command.h             | 380 +-----------------------------
 gdb/complaints.c          |   1 +
 gdb/completer.h           |  21 ++
 gdb/copying.c             |   1 +
 gdb/cp-abi.c              |   1 +
 gdb/filesystem.c          |   1 +
 gdb/gdb-demangle.c        |   1 +
 gdb/interps.c             |   1 +
 gdb/macrocmd.c            |   1 +
 gdb/maint-test-options.c  |   1 +
 gdb/maint-test-settings.c |   1 +
 gdb/observable.c          |   1 +
 gdb/osabi.c               |   1 +
 gdb/reggroups.c           |   1 +
 gdb/ser-unix.c            |   1 +
 gdb/serial.c              |   1 +
 gdb/user-regs.c           |   1 +
 gdb/xml-support.c         |   1 +
 24 files changed, 543 insertions(+), 506 deletions(-)

diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 54c5e423e10..15022892d6b 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -30,6 +30,7 @@
 #include "gdbsupport/array-view.h"
 #include "gdbsupport/function-view.h"
 #include "gdbsupport/refcounted-object.h"
+#include "cli/cli-decode.h"
 #include "cli/cli-script.h"
 
 struct block;
diff --git a/gdb/charset.c b/gdb/charset.c
index 9fb1a1f0b67..6fb3cdfaeb7 100644
--- a/gdb/charset.c
+++ b/gdb/charset.c
@@ -19,6 +19,7 @@
 
 #include "defs.h"
 #include "charset.h"
+#include "cli/cli-decode.h"
 #include "gdbcmd.h"
 #include "gdb_obstack.h"
 #include "gdbsupport/gdb_wait.h"
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 32edb526c87..f13bbbf60d9 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -160,23 +160,7 @@ set_cmd_completer_handle_brkchars (struct cmd_list_element *cmd,
   cmd->completer_handle_brkchars = func;
 }
 
-/* Add element named NAME.
-   Space for NAME and DOC must be allocated by the caller.
-   CLASS is the top level category into which commands are broken down
-   for "help" purposes.
-   FUN should be the function to execute the command;
-   it will get a character string as argument, with leading
-   and trailing blanks already eliminated.
-
-   DOC is a documentation string for the command.
-   Its first line should be a complete sentence.
-   It should start with ? for a command that is an abbreviation
-   or with * for a command that most users don't need to know about.
-
-   Add this command to command list *LIST.
-
-   Returns a pointer to the added command (not necessarily the head 
-   of *LIST).  */
+/* Like ADD_CMD, but the command function fields are not modified.  */
 
 static struct cmd_list_element *
 do_add_cmd (const char *name, enum command_class theclass,
@@ -227,6 +211,8 @@ do_add_cmd (const char *name, enum command_class theclass,
   return c;
 }
 
+/* See cli-decode.h.  */
+
 struct cmd_list_element *
 add_cmd (const char *name, enum command_class theclass,
 	 const char *doc, struct cmd_list_element **list)
@@ -237,6 +223,8 @@ add_cmd (const char *name, enum command_class theclass,
   return result;
 }
 
+/* See cli-decode.h.  */
+
 struct cmd_list_element *
 add_cmd (const char *name, enum command_class theclass,
 	 cmd_const_cfunc_ftype *fun,
@@ -247,7 +235,7 @@ add_cmd (const char *name, enum command_class theclass,
   return result;
 }
 
-/* Add an element with a suppress notification to the LIST of commands.  */
+/* See cli-decode.h.  */
 
 struct cmd_list_element *
 add_cmd_suppress_notification (const char *name, enum command_class theclass,
@@ -263,16 +251,7 @@ add_cmd_suppress_notification (const char *name, enum command_class theclass,
   return element;
 }
 
-
-/* Deprecates a command CMD.
-   REPLACEMENT is the name of the command which should be used in
-   place of this command, or NULL if no such command exists.
-
-   This function does not check to see if command REPLACEMENT exists
-   since gdb may not have gotten around to adding REPLACEMENT when
-   this function is called.
-
-   Returns a pointer to the deprecated command.  */
+/* See cli-decode.h.  */
 
 struct cmd_list_element *
 deprecate_cmd (struct cmd_list_element *cmd, const char *replacement)
@@ -372,11 +351,7 @@ update_prefix_field_of_prefixed_commands (struct cmd_list_element *c)
     }
 }
 
-
-/* Like add_cmd but adds an element for a command prefix: a name that
-   should be followed by a subcommand to be looked up in another
-   command list.  PREFIXLIST should be the address of the variable
-   containing that list.  */
+/* See cli-decode.h.  */
 
 struct cmd_list_element *
 add_prefix_cmd (const char *name, enum command_class theclass,
@@ -410,7 +385,7 @@ do_prefix_cmd (const char *args, int from_tty, struct cmd_list_element *c)
 	     all_commands, gdb_stdout);
 }
 
-/* See command.h.  */
+/* See cli-decode.h.  */
 
 struct cmd_list_element *
 add_basic_prefix_cmd (const char *name, enum command_class theclass,
@@ -433,7 +408,7 @@ do_show_prefix_cmd (const char *args, int from_tty, struct cmd_list_element *c)
   cmd_show_list (*c->prefixlist, from_tty);
 }
 
-/* See command.h.  */
+/* See cli-decode.h.  */
 
 struct cmd_list_element *
 add_show_prefix_cmd (const char *name, enum command_class theclass,
@@ -447,8 +422,7 @@ add_show_prefix_cmd (const char *name, enum command_class theclass,
   return cmd;
 }
 
-/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the
-   new command list element.  */
+/* See cli-decode.h.  */
 
 struct cmd_list_element *
 add_prefix_cmd_suppress_notification
@@ -465,7 +439,7 @@ add_prefix_cmd_suppress_notification
   return element;
 }
 
-/* Like add_prefix_cmd but sets the abbrev_flag on the new command.  */
+/* See cli-decode.h.  */
 
 struct cmd_list_element *
 add_abbrev_prefix_cmd (const char *name, enum command_class theclass,
@@ -481,7 +455,8 @@ add_abbrev_prefix_cmd (const char *name, enum command_class theclass,
   return c;
 }
 
-/* This is an empty "cfunc".  */
+/* See cli-decode.h.  */
+
 void
 not_just_help_class_command (const char *args, int from_tty)
 {
@@ -581,10 +556,7 @@ add_setshow_cmd_full (const char *name,
     *show_result = show;
 }
 
-/* Add element named NAME to command list LIST (the list for set or
-   some sublist thereof).  CLASS is as in add_cmd.  ENUMLIST is a list
-   of strings which may follow NAME.  VAR is address of the variable
-   which will contain the matching string (from ENUMLIST).  */
+/* See cli-decode.h.  */
 
 void
 add_setshow_enum_cmd (const char *name,
@@ -1274,18 +1246,8 @@ help_cmd (const char *command, struct ui_file *stream)
 		    c->hook_post->name);
 }
 
-/*
- * Get a specific kind of help on a command list.
- *
- * LIST is the list.
- * CMDTYPE is the prefix to use in the title string.
- * CLASS is the class with which to list the nodes of this list (see
- * documentation for help_cmd_list below),  As usual, ALL_COMMANDS for
- * everything, ALL_CLASSES for just classes, and non-negative for only things
- * in a specific class.
- * and STREAM is the output stream on which to print things.
- * If you call this routine with a class >= 0, it recurses.
- */
+/* See cli-decode.h.  */
+
 void
 help_list (struct cmd_list_element *list, const char *cmdtype,
 	   enum command_class theclass, struct ui_file *stream)
@@ -1583,7 +1545,7 @@ find_command_name_length (const char *text)
   return p - text;
 }
 
-/* See command.h.  */
+/* See cli-decode.h.  */
 
 bool
 valid_cmd_char_p (int c)
@@ -1594,7 +1556,7 @@ valid_cmd_char_p (int c)
   return isalnum (c) || c == '-' || c == '_' || c == '.';
 }
 
-/* See command.h.  */
+/* See cli-decode.h.  */
 
 bool
 valid_user_defined_cmd_name_p (const char *name)
@@ -1615,7 +1577,7 @@ valid_user_defined_cmd_name_p (const char *name)
   return true;
 }
 
-/* See command.h.  */
+/* See cli-decode.h.  */
 
 struct cmd_list_element *
 lookup_cmd_1 (const char **text, struct cmd_list_element *clist,
@@ -1751,24 +1713,7 @@ undef_cmd_error (const char *cmdtype, const char *q)
 	 cmdtype);
 }
 
-/* Look up the contents of *LINE as a command in the command list LIST.
-   LIST is a chain of struct cmd_list_element's.
-   If it is found, return the struct cmd_list_element for that command,
-   update *LINE to point after the command name, at the first argument
-   and update *DEFAULT_ARGS (if DEFAULT_ARGS is not null) to the default
-   args to prepend to the user provided args when running the command.
-   Note that if the found cmd_list_element is found via an alias,
-   the default args of the alias are returned.
-
-   If not found, call error if ALLOW_UNKNOWN is zero
-   otherwise (or if error returns) return zero.
-   Call error if specified command is ambiguous,
-   unless ALLOW_UNKNOWN is negative.
-   CMDTYPE precedes the word "command" in the error message.
-
-   If IGNORE_HELP_CLASSES is nonzero, ignore any command list
-   elements which are actually help classes rather than commands (i.e.
-   the function field of the struct cmd_list_element is 0).  */
+/* See cli-decode.h.  */
 
 struct cmd_list_element *
 lookup_cmd (const char **line, struct cmd_list_element *list,
@@ -1872,7 +1817,7 @@ lookup_cmd (const char **line, struct cmd_list_element *list,
   return 0;
 }
 
-/* See command.h.  */
+/* See cli-decode.h.  */
 
 struct cmd_list_element *
 lookup_cmd_exact (const char *name,
@@ -1887,28 +1832,8 @@ lookup_cmd_exact (const char *name,
   return cmd;
 }
 
-/* We are here presumably because an alias or command in TEXT is
-   deprecated and a warning message should be generated.  This
-   function decodes TEXT and potentially generates a warning message
-   as outlined below.
-   
-   Example for 'set endian big' which has a fictitious alias 'seb'.
-   
-   If alias wasn't used in TEXT, and the command is deprecated:
-   "warning: 'set endian big' is deprecated." 
-   
-   If alias was used, and only the alias is deprecated:
-   "warning: 'seb' an alias for the command 'set endian big' is deprecated."
-   
-   If alias was used and command is deprecated (regardless of whether
-   the alias itself is deprecated:
-   
-   "warning: 'set endian big' (seb) is deprecated."
+/* See cli-decode.h.  */
 
-   After the message has been sent, clear the appropriate flags in the
-   command and/or the alias so the user is no longer bothered.
-   
-*/
 void
 deprecated_cmd_warning (const char *text, struct cmd_list_element *list)
 {
@@ -2064,20 +1989,7 @@ lookup_cmd_composition_1 (const char *text,
     }
 }
 
-/* Look up the contents of TEXT as a command in the command list 'cmdlist'.
-   Return 1 on success, 0 on failure.
-
-   If TEXT refers to an alias, *ALIAS will point to that alias.
-
-   If TEXT is a subcommand (i.e. one that is preceded by a prefix
-   command) set *PREFIX_CMD.
-
-   Set *CMD to point to the command TEXT indicates.
-
-   If any of *ALIAS, *PREFIX_CMD, or *CMD cannot be determined or do not
-   exist, they are NULL when we return.
-
-*/
+/* See cli-decode.h.  */
 
 int
 lookup_cmd_composition (const char *text,
@@ -2090,13 +2002,7 @@ lookup_cmd_composition (const char *text,
 
 /* Helper function for SYMBOL_COMPLETION_FUNCTION.  */
 
-/* Return a vector of char pointers which point to the different
-   possible completions in LIST of TEXT.
-
-   WORD points in the same buffer as TEXT, and completions should be
-   returned relative to this position.  For example, suppose TEXT is
-   "foo" and we want to complete to "foobar".  If WORD is "oo", return
-   "oobar"; if WORD is "baz/foo", return "baz/foobar".  */
+/* See cli-decode.h.  */
 
 void
 complete_on_cmdlist (struct cmd_list_element *list,
@@ -2149,12 +2055,7 @@ complete_on_cmdlist (struct cmd_list_element *list,
 
 /* Helper function for SYMBOL_COMPLETION_FUNCTION.  */
 
-/* Add the different possible completions in ENUMLIST of TEXT.
-
-   WORD points in the same buffer as TEXT, and completions should be
-   returned relative to this position.  For example, suppose TEXT is "foo"
-   and we want to complete to "foobar".  If WORD is "oo", return
-   "oobar"; if WORD is "baz/foo", return "baz/foobar".  */
+/* See cli-decode.h.  */
 
 void
 complete_on_enum (completion_tracker &tracker,
@@ -2170,16 +2071,16 @@ complete_on_enum (completion_tracker &tracker,
       tracker.add_completion (make_completion_match_str (name, text, word));
 }
 
+/* See cli-decode.h.  */
 
-/* Check function pointer.  */
 int
 cmd_func_p (struct cmd_list_element *cmd)
 {
   return (cmd->func != NULL);
 }
 
+/* See cli-decode.h.  */
 
-/* Call the command function.  */
 void
 cmd_func (struct cmd_list_element *cmd, const char *args, int from_tty)
 {
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index da6013a6cc5..54057a149be 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -37,9 +37,23 @@ enum cmd_types
   show_cmd
 };
 
+struct cmd_list_element;
+
+typedef void cmd_const_sfunc_ftype (const char *args, int from_tty,
+				    cmd_list_element *c);
+
+/* A function implementing a command, see add_cmd and related functions.
+
+   ARGS is a string containing the arguments to the command (with leading
+   and trailing blanks already eliminated).
+
+   FROM_TTY is a flag specifying whether this command was originated from
+   the user invoking it interactively.  */
+
+typedef void cmd_const_cfunc_ftype (const char *args, int from_tty);
+
 /* This structure records one command'd definition.  */
 
-
 struct cmd_list_element
 {
   cmd_list_element (const char *name_, enum command_class theclass_,
@@ -256,8 +270,349 @@ struct cmd_list_element
   int *suppress_notification = nullptr;
 };
 
+/* API to the manipulation of command lists.  */
+
+/* Return TRUE if NAME is a valid user-defined command name.
+   This is a stricter subset of all gdb commands,
+   see find_command_name_length.  */
+
+extern bool valid_user_defined_cmd_name_p (const char *name);
+
+/* Return TRUE if C is a valid command character.  */
+
+extern bool valid_cmd_char_p (int c);
+
+/* Add a command named NAME in command list *LIST.
+
+   NAME and DOC are not duplicated.  If they are not static string, they
+   must have been allocated with xmalloc or xstrdup and the
+   NAME_ALLOCATED/DOC_ALLOCATED fields must be set to 1 on the returned
+   command.
+
+   THECLASS is the top level category into which commands are broken down
+   for "help" purposes.
+
+   FUN is the function which implements the command.
+
+   DOC is a documentation string for the command.
+   Its first line should be a complete sentence.
+   It should start with ? for a command that is an abbreviation
+   or with * for a command that most users don't need to know about.
+
+   If NAME already existed in *LIST, all its hooks and aliases are moved
+   to the new command.
+
+   Return a pointer to the added command (not necessarily the head of
+   *LIST).  */
+
+extern cmd_list_element *add_cmd (const char *name,
+				  command_class theclass,
+				  cmd_const_cfunc_ftype *fun,
+				  const char *doc,
+				  cmd_list_element **list);
+
+/* Like add_cmd, but no command function is specified.  */
+
+extern cmd_list_element *add_cmd (const char *name,
+				  command_class theclass,
+				  const char *doc,
+				  cmd_list_element **list);
+
+/* Add an element with a suppress notification to the LIST of commands.  */
+
+extern cmd_list_element *add_cmd_suppress_notification
+  (const char *name, command_class theclass, cmd_const_cfunc_ftype *fun,
+   const char *doc, cmd_list_element **list, int *suppress_notification);
+
+extern cmd_list_element *add_alias_cmd (const char *name,
+					const char *oldname,
+					command_class theclass,
+					int abbrev_flag,
+					cmd_list_element **list);
+
+extern cmd_list_element *add_alias_cmd (const char *name,
+					cmd_list_element *old,
+					command_class theclass,
+					int abbrev_flag,
+					cmd_list_element **list);
+
+/* Like add_cmd but adds an element for a command prefix: a name that
+   should be followed by a subcommand to be looked up in another
+   command list.  PREFIXLIST should be the address of the variable
+   containing that list.  */
+
+extern cmd_list_element *add_prefix_cmd (const char *name,
+					 command_class theclass,
+					 cmd_const_cfunc_ftype *fun,
+					 const char *doc,
+					 cmd_list_element **prefixlist,
+					 int allow_unknown,
+					 cmd_list_element **list);
+
+/* Like add_prefix_cmd, but sets the callback to a function that
+   simply calls help_list.  */
+
+extern cmd_list_element *add_basic_prefix_cmd
+  (const char *name, command_class theclass, const char *doc,
+   cmd_list_element **prefixlist, int allow_unknown,
+   cmd_list_element **list);
+
+/* Like add_prefix_cmd, but useful for "show" prefixes.  This sets the
+   callback to a function that simply calls cmd_show_list.  */
+
+extern  cmd_list_element *add_show_prefix_cmd
+ (const char *name, command_class theclass, const char *doc,
+  cmd_list_element **prefixlist, int allow_unknown,
+  cmd_list_element **list);
+
+/* Like ADD_PREFIX_CMD but sets the suppress_notification pointer on the
+   new command list element.  */
+
+extern cmd_list_element *add_prefix_cmd_suppress_notification
+  (const char *name, command_class theclass, cmd_const_cfunc_ftype *fun,
+   const char *doc, cmd_list_element **prefixlist, int allow_unknown,
+   cmd_list_element **list, int *suppress_notification);
+
+/* Like add_prefix_cmd but sets the abbrev_flag on the new command.  */
+
+extern cmd_list_element *add_abbrev_prefix_cmd
+  (const char *name, command_class theclass, cmd_const_cfunc_ftype *fun,
+   const char *doc, cmd_list_element **prefixlist, int allow_unknown,
+   cmd_list_element **list);
+
+extern void set_cmd_sfunc (cmd_list_element *cmd,
+			   cmd_const_sfunc_ftype *sfunc);
+
+extern void set_cmd_completer (cmd_list_element *, completer_ftype *);
+
+/* Set the completer_handle_brkchars callback.  */
+
+extern void set_cmd_completer_handle_brkchars
+  (cmd_list_element *, completer_handle_brkchars_ftype *);
+
+/* HACK: cagney/2002-02-23: Code, mostly in tracepoints.c, grubs
+   around in cmd objects to test the value of the commands sfunc().  */
+extern int cmd_cfunc_eq (cmd_list_element *cmd,
+			 cmd_const_cfunc_ftype *cfun);
+
+/* Each command object has a local context attached to it.  */
+
+extern void set_cmd_context (cmd_list_element *cmd, void *context);
+extern void *get_cmd_context (cmd_list_element *cmd);
+
+/* Flag for an ambiguous cmd_list result.  */
+
+#define CMD_LIST_AMBIGUOUS ((cmd_list_element *) -1)
+
+/* Look up the contents of *LINE as a command in the command list LIST.
+   LIST is a chain of struct cmd_list_element's.
+   If it is found, return the struct cmd_list_element for that command,
+   update *LINE to point after the command name, at the first argument
+   and update *DEFAULT_ARGS (if DEFAULT_ARGS is not null) to the default
+   args to prepend to the user provided args when running the command.
+   Note that if the found cmd_list_element is found via an alias,
+   the default args of the alias are returned.
+
+   If not found, call error if ALLOW_UNKNOWN is zero
+   otherwise (or if error returns) return zero.
+   Call error if specified command is ambiguous,
+   unless ALLOW_UNKNOWN is negative.
+   CMDTYPE precedes the word "command" in the error message.
+
+   If IGNORE_HELP_CLASSES is nonzero, ignore any command list
+   elements which are actually help classes rather than commands (i.e.
+   the function field of the struct cmd_list_element is 0).  */
+
+extern cmd_list_element *lookup_cmd (const char **line,
+				     cmd_list_element *list,
+				     const char *cmdtype,
+				     std::string *default_args,
+				     int allow_unknown,
+				     int ignore_help_classes);
+
+/* This routine takes a line of TEXT and a CLIST in which to start the
+   lookup.  When it returns it will have incremented the text pointer past
+   the section of text it matched, set *RESULT_LIST to point to the list in
+   which the last word was matched, and will return a pointer to the cmd
+   list element which the text matches.  It will return NULL if no match at
+   all was possible.  It will return -1 (cast appropriately, ick) if ambigous
+   matches are possible; in this case *RESULT_LIST will be set to point to
+   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.  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.
+
+   In the case of an ambiguous return (-1), *RESULT_LIST will be set to point
+   at the prefix_command (ie. the best match) *or* (special case) will be NULL
+   if no prefix command was ever found.  For example, in the case of "info a",
+   "info" matches without ambiguity, but "a" could be "args" or "address", so
+   *RESULT_LIST is set to the cmd_list_element for "info".  So in this case
+   RESULT_LIST should not be interpreted as a pointer to the beginning of a
+   list; it simply points to a specific command.  In the case of an ambiguous
+   return *TEXT is advanced past the last non-ambiguous prefix (e.g.
+   "info t" can be "info types" or "info target"; upon return *TEXT has been
+   advanced past "info ").
+
+   If RESULT_LIST is NULL, don't set *RESULT_LIST (but don't otherwise
+   affect the operation).
+
+   This routine does *not* modify the text pointed to by TEXT.
+
+   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 NULL).
+
+   When LOOKUP_FOR_COMPLETION_P is true the completion is being requested
+   for the completion engine, no warnings should be printed.  */
+
+extern cmd_list_element *lookup_cmd_1
+  (const char **text, cmd_list_element *clist,
+   cmd_list_element **result_list, std::string *default_args,
+   int ignore_help_classes, bool lookup_for_completion_p = false);
+
+/* Look up the command called NAME in the command list LIST.
+
+   Unlike LOOKUP_CMD, partial matches are ignored and only exact matches
+   on NAME are considered.
+
+   LIST is a chain of struct cmd_list_element's.
+
+   If IGNORE_HELP_CLASSES is true (the default), 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 null).
+
+   If found, return the struct cmd_list_element for that command,
+   otherwise return NULLPTR.  */
+
+extern cmd_list_element *lookup_cmd_exact
+  (const char *name, cmd_list_element *list,
+   bool ignore_help_classes = true);
+
+/* Deprecates a command CMD.
+   REPLACEMENT is the name of the command which should be used in
+   place of this command, or NULL if no such command exists.
+
+   This function does not check to see if command REPLACEMENT exists
+   since gdb may not have gotten around to adding REPLACEMENT when
+   this function is called.
+
+   Returns a pointer to the deprecated command.  */
+
+extern cmd_list_element *deprecate_cmd (cmd_list_element *cmd,
+					const char *replacement);
+
+/* We are here presumably because an alias or command in TEXT is
+   deprecated and a warning message should be generated.  This
+   function decodes TEXT and potentially generates a warning message
+   as outlined below.
+
+   Example for 'set endian big' which has a fictitious alias 'seb'.
+
+   If alias wasn't used in TEXT, and the command is deprecated:
+   "warning: 'set endian big' is deprecated."
+
+   If alias was used, and only the alias is deprecated:
+   "warning: 'seb' an alias for the command 'set endian big' is deprecated."
+
+   If alias was used and command is deprecated (regardless of whether
+   the alias itself is deprecated:
+
+   "warning: 'set endian big' (seb) is deprecated."
+
+   After the message has been sent, clear the appropriate flags in the
+   command and/or the alias so the user is no longer bothered.  */
+
+extern void deprecated_cmd_warning (const char *text,
+				    cmd_list_element *list);
+
+/* Look up the contents of TEXT as a command in the command list 'cmdlist'.
+   Return 1 on success, 0 on failure.
+
+   If TEXT refers to an alias, *ALIAS will point to that alias.
+
+   If TEXT is a subcommand (i.e. one that is preceded by a prefix
+   command) set *PREFIX_CMD.
+
+   Set *CMD to point to the command TEXT indicates.
+
+   If any of *ALIAS, *PREFIX_CMD, or *CMD cannot be determined or do not
+   exist, they are NULL when we return.  */
+
+extern int lookup_cmd_composition (const char *text,
+				   cmd_list_element **alias,
+				   cmd_list_element **prefix_cmd,
+				   cmd_list_element **cmd);
+
+extern cmd_list_element *add_com (const char *name,
+				  command_class theclass,
+				  cmd_const_cfunc_ftype *fun,
+				  const char *doc);
+
+extern cmd_list_element *add_com_alias (const char *name,
+					const char *oldname,
+					command_class theclass,
+					int abbrev_flag);
+
+extern cmd_list_element *add_com_suppress_notification
+  (const char *name, command_class theclass, cmd_const_cfunc_ftype *fun,
+   const char *doc, int *supress_notification);
+
+extern cmd_list_element *add_info (const char *name,
+				   cmd_const_cfunc_ftype *fun,
+				   const char *doc);
+
+extern cmd_list_element *add_info_alias (const char *name,
+					 const char *oldname,
+					 int abbrev_flag);
+
+/* Return a vector of char pointers which point to the different
+   possible completions in LIST of TEXT.
+
+   WORD points in the same buffer as TEXT, and completions should be
+   returned relative to this position.  For example, suppose TEXT is
+   "foo" and we want to complete to "foobar".  If WORD is "oo", return
+   "oobar"; if WORD is "baz/foo", return "baz/foobar".  */
+
+extern void complete_on_cmdlist (cmd_list_element *list,
+				 completion_tracker &tracker,
+				 const char *text, const char *word,
+				 int ignore_help_classes);
+
+/* Add the different possible completions in ENUMLIST of TEXT.
+
+   WORD points in the same buffer as TEXT, and completions should be
+   returned relative to this position.  For example, suppose TEXT is "foo"
+   and we want to complete to "foobar".  If WORD is "oo", return
+   "oobar"; if WORD is "baz/foo", return "baz/foobar".  */
+
+extern void complete_on_enum (completion_tracker &tracker,
+			      const char *const *enumlist,
+			      const char *text, const char *word);
+
 /* Functions that implement commands about CLI commands.  */
 
+/* Get a specific kind of help on a command list.
+ *
+ * LIST is the list.
+ * CMDTYPE is the prefix to use in the title string.
+ * CLASS is the class with which to list the nodes of this list (see
+ * documentation for help_cmd_list below),  As usual, ALL_COMMANDS for
+ * everything, ALL_CLASSES for just classes, and non-negative for only things
+ * in a specific class.
+ * and STREAM is the output stream on which to print things.
+ * If you call this routine with a class >= 0, it recurses.  */
+
+extern void help_list (cmd_list_element *list, const char *cmdtype,
+		       command_class theclass, ui_file *stream);
+
 extern void help_cmd (const char *, struct ui_file *);
 
 extern void apropos_cmd (struct ui_file *, struct cmd_list_element *,
@@ -291,4 +646,120 @@ extern int cli_user_command_p (struct cmd_list_element *);
 
 extern int find_command_name_length (const char *);
 
+/* Support for adding set and show commands.  */
+
+/* Add element named NAME to command list LIST (the list for set or
+   some sublist thereof).  CLASS is as in add_cmd.  ENUMLIST is a list
+   of strings which may follow NAME.  VAR is address of the variable
+   which will contain the matching string (from ENUMLIST).  */
+
+extern void add_setshow_enum_cmd (const char *name,
+				  command_class theclass,
+				  const char *const *enumlist,
+				  const char **var, const char *set_doc,
+				  const char *show_doc,
+				  const char *help_doc,
+				  cmd_const_sfunc_ftype *set_func,
+				  show_value_ftype *show_func,
+				  cmd_list_element **set_list,
+				  cmd_list_element **show_list,
+				  void *context = nullptr);
+
+extern void add_setshow_auto_boolean_cmd (const char *name,
+					  command_class theclass,
+					  auto_boolean *var,
+					  const char *set_doc,
+					  const char *show_doc,
+					  const char *help_doc,
+					  cmd_const_sfunc_ftype *set_func,
+					  show_value_ftype *show_func,
+					  cmd_list_element **set_list,
+					  cmd_list_element **show_list);
+
+extern cmd_list_element * add_setshow_boolean_cmd
+  (const char *name, command_class theclass, bool *var,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   cmd_const_sfunc_ftype *set_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern void add_setshow_filename_cmd (const char *name,
+				      command_class theclass, char **var,
+				      const char *set_doc,
+				      const char *show_doc,
+				      const char *help_doc,
+				      cmd_const_sfunc_ftype *set_func,
+				      show_value_ftype *show_func,
+				      cmd_list_element **set_list,
+				      cmd_list_element **show_list);
+
+extern void add_setshow_string_cmd (const char *name,
+				    command_class theclass, char **var,
+				    const char *set_doc,
+				    const char *show_doc,
+				    const char *help_doc,
+				    cmd_const_sfunc_ftype *set_func,
+				    show_value_ftype *show_func,
+				    cmd_list_element **set_list,
+				    cmd_list_element **show_list);
+
+extern cmd_list_element *add_setshow_string_noescape_cmd
+  (const char *name, command_class theclass, char **var,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   cmd_const_sfunc_ftype *set_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern void add_setshow_optional_filename_cmd
+  (const char *name, command_class theclass, char **var,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   cmd_const_sfunc_ftype *set_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern void add_setshow_integer_cmd (const char *name,
+				     command_class theclass, int *var,
+				     const char *set_doc,
+				     const char *show_doc,
+				     const char *help_doc,
+				     cmd_const_sfunc_ftype *set_func,
+				     show_value_ftype *show_func,
+				     cmd_list_element **set_list,
+				     cmd_list_element **show_list);
+
+extern void add_setshow_uinteger_cmd (const char *name,
+				      command_class theclass,
+				      unsigned int *var,
+				      const char *set_doc,
+				      const char *show_doc,
+				      const char *help_doc,
+				      cmd_const_sfunc_ftype *set_func,
+				      show_value_ftype *show_func,
+				      cmd_list_element **set_list,
+				      cmd_list_element **show_list);
+
+extern void add_setshow_zinteger_cmd (const char *name,
+				      command_class theclass, int *var,
+				      const char *set_doc,
+				      const char *show_doc,
+				      const char *help_doc,
+				      cmd_const_sfunc_ftype *set_func,
+				      show_value_ftype *show_func,
+				      cmd_list_element **set_list,
+				      cmd_list_element **show_list);
+
+extern void add_setshow_zuinteger_cmd (const char *name,
+				       command_class theclass,
+				       unsigned int *var,
+				       const char *set_doc,
+				       const char *show_doc,
+				       const char *help_doc,
+				       cmd_const_sfunc_ftype *set_func,
+				       show_value_ftype *show_func,
+				       cmd_list_element **set_list,
+				       cmd_list_element **show_list);
+
+extern void add_setshow_zuinteger_unlimited_cmd
+  (const char *name, command_class theclass, int *var,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   cmd_const_sfunc_ftype *set_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
 #endif /* CLI_CLI_DECODE_H */
diff --git a/gdb/cli/cli-logging.c b/gdb/cli/cli-logging.c
index dfedd7599a2..44e6d2991af 100644
--- a/gdb/cli/cli-logging.c
+++ b/gdb/cli/cli-logging.c
@@ -21,6 +21,7 @@
 #include "gdbcmd.h"
 #include "ui-out.h"
 #include "interps.h"
+#include "cli/cli-decode.h"
 #include "cli/cli-style.h"
 
 static char *saved_filename;
diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c
index 25a1d888f86..4f95d7883d0 100644
--- a/gdb/cli/cli-style.c
+++ b/gdb/cli/cli-style.c
@@ -19,6 +19,7 @@
 
 #include "defs.h"
 #include "cli/cli-cmds.h"
+#include "cli/cli-decode.h"
 #include "cli/cli-setshow.h"
 #include "cli/cli-style.h"
 #include "source-cache.h"
diff --git a/gdb/command.h b/gdb/command.h
index 33feb19166c..3e3543927a4 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -128,8 +128,6 @@ var_types;
 /* This structure records one command'd definition.  */
 struct cmd_list_element;
 
-typedef void cmd_const_cfunc_ftype (const char *args, int from_tty);
-
 /* This structure specifies notifications to be suppressed by a cli
    command interpreter.  */
 
@@ -141,252 +139,12 @@ struct cli_suppress_notification
 
 extern struct cli_suppress_notification cli_suppress_notification;
 
-/* Forward-declarations of the entry-points of cli/cli-decode.c.  */
-
-/* API to the manipulation of command lists.  */
-
-/* Return TRUE if NAME is a valid user-defined command name.
-   This is a stricter subset of all gdb commands,
-   see find_command_name_length.  */
-
-extern bool valid_user_defined_cmd_name_p (const char *name);
-
-/* Return TRUE if C is a valid command character.  */
-
-extern bool valid_cmd_char_p (int c);
-
-/* Const-correct variant of the above.  */
-
-extern struct cmd_list_element *add_cmd (const char *, enum command_class,
-					 cmd_const_cfunc_ftype *fun,
-					 const char *,
-					 struct cmd_list_element **);
-
-/* Like add_cmd, but no command function is specified.  */
-
-extern struct cmd_list_element *add_cmd (const char *, enum command_class,
-					 const char *,
-					 struct cmd_list_element **);
-
-extern struct cmd_list_element *add_cmd_suppress_notification
-			(const char *name, enum command_class theclass,
-			 cmd_const_cfunc_ftype *fun, const char *doc,
-			 struct cmd_list_element **list,
-			 int *suppress_notification);
-
-extern struct cmd_list_element *add_alias_cmd (const char *, const char *,
-					       enum command_class, int,
-					       struct cmd_list_element **);
-
-extern struct cmd_list_element *add_alias_cmd (const char *,
-					       cmd_list_element *,
-					       enum command_class, int,
-					       struct cmd_list_element **);
-
-
-extern struct cmd_list_element *add_prefix_cmd (const char *, enum command_class,
-						cmd_const_cfunc_ftype *fun,
-						const char *,
-						struct cmd_list_element **,
-						int,
-						struct cmd_list_element **);
-
-/* Like add_prefix_cmd, but sets the callback to a function that
-   simply calls help_list.  */
-
-extern struct cmd_list_element *add_basic_prefix_cmd
-  (const char *, enum command_class, const char *, struct cmd_list_element **,
-   int, struct cmd_list_element **);
-
-/* Like add_prefix_cmd, but useful for "show" prefixes.  This sets the
-   callback to a function that simply calls cmd_show_list.  */
-
-extern struct cmd_list_element *add_show_prefix_cmd
-  (const char *, enum command_class, const char *, struct cmd_list_element **,
-   int, struct cmd_list_element **);
-
-extern struct cmd_list_element *add_prefix_cmd_suppress_notification
-			(const char *name, enum command_class theclass,
-			 cmd_const_cfunc_ftype *fun,
-			 const char *doc, struct cmd_list_element **prefixlist,
-			 int allow_unknown,
-			 struct cmd_list_element **list,
-			 int *suppress_notification);
-
-extern struct cmd_list_element *add_abbrev_prefix_cmd (const char *,
-						       enum command_class,
-						       cmd_const_cfunc_ftype *fun,
-						       const char *,
-						       struct cmd_list_element
-						       **, int,
-						       struct cmd_list_element
-						       **);
-
-typedef void cmd_const_sfunc_ftype (const char *args, int from_tty,
-				    struct cmd_list_element *c);
-extern void set_cmd_sfunc (struct cmd_list_element *cmd,
-			   cmd_const_sfunc_ftype *sfunc);
-
-/* A completion routine.  Add possible completions to tracker.
-
-   TEXT is the text beyond what was matched for the command itself
-   (leading whitespace is skipped).  It stops where we are supposed to
-   stop completing (rl_point) and is '\0' terminated.  WORD points in
-   the same buffer as TEXT, and completions should be returned
-   relative to this position.  For example, suppose TEXT is "foo" and
-   we want to complete to "foobar".  If WORD is "oo", return "oobar";
-   if WORD is "baz/foo", return "baz/foobar".  */
-typedef void completer_ftype (struct cmd_list_element *,
-			      completion_tracker &tracker,
-			      const char *text, const char *word);
-
-/* Same, but for set_cmd_completer_handle_brkchars.  */
-typedef void completer_handle_brkchars_ftype (struct cmd_list_element *,
-					      completion_tracker &tracker,
-					      const char *text, const char *word);
-
-extern void set_cmd_completer (struct cmd_list_element *, completer_ftype *);
-
-/* Set the completer_handle_brkchars callback.  */
-
-extern void set_cmd_completer_handle_brkchars (struct cmd_list_element *,
-					       completer_handle_brkchars_ftype *);
-
-/* HACK: cagney/2002-02-23: Code, mostly in tracepoints.c, grubs
-   around in cmd objects to test the value of the commands sfunc().  */
-extern int cmd_cfunc_eq (struct cmd_list_element *cmd,
-			 cmd_const_cfunc_ftype *cfun);
-
-/* Each command object has a local context attached to it.  */
-extern void set_cmd_context (struct cmd_list_element *cmd,
-			     void *context);
-extern void *get_cmd_context (struct cmd_list_element *cmd);
-
-
 /* Execute CMD's pre/post hook.  Throw an error if the command fails.
    If already executing this pre/post hook, or there is no pre/post
    hook, the call is silently ignored.  */
 extern void execute_cmd_pre_hook (struct cmd_list_element *cmd);
 extern void execute_cmd_post_hook (struct cmd_list_element *cmd);
 
-/* Flag for an ambiguous cmd_list result.  */
-#define CMD_LIST_AMBIGUOUS ((struct cmd_list_element *) -1)
-
-extern struct cmd_list_element *lookup_cmd (const char **,
-					    struct cmd_list_element *,
-					    const char *,
-					    std::string *,
-					    int, int);
-
-/* This routine takes a line of TEXT and a CLIST in which to start the
-   lookup.  When it returns it will have incremented the text pointer past
-   the section of text it matched, set *RESULT_LIST to point to the list in
-   which the last word was matched, and will return a pointer to the cmd
-   list element which the text matches.  It will return NULL if no match at
-   all was possible.  It will return -1 (cast appropriately, ick) if ambigous
-   matches are possible; in this case *RESULT_LIST will be set to point to
-   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.  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.
-
-   In the case of an ambiguous return (-1), *RESULT_LIST will be set to point
-   at the prefix_command (ie. the best match) *or* (special case) will be NULL
-   if no prefix command was ever found.  For example, in the case of "info a",
-   "info" matches without ambiguity, but "a" could be "args" or "address", so
-   *RESULT_LIST is set to the cmd_list_element for "info".  So in this case
-   RESULT_LIST should not be interpreted as a pointer to the beginning of a
-   list; it simply points to a specific command.  In the case of an ambiguous
-   return *TEXT is advanced past the last non-ambiguous prefix (e.g.
-   "info t" can be "info types" or "info target"; upon return *TEXT has been
-   advanced past "info ").
-
-   If RESULT_LIST is NULL, don't set *RESULT_LIST (but don't otherwise
-   affect the operation).
-
-   This routine does *not* modify the text pointed to by TEXT.
-
-   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 NULL).
-
-   When LOOKUP_FOR_COMPLETION_P is true the completion is being requested
-   for the completion engine, no warnings should be printed.  */
-
-extern struct cmd_list_element *lookup_cmd_1
-	(const char **text, struct cmd_list_element *clist,
-	 struct cmd_list_element **result_list, std::string *default_args,
-	 int ignore_help_classes, bool lookup_for_completion_p = false);
-
-/* Look up the command called NAME in the command list LIST.
-
-   Unlike LOOKUP_CMD, partial matches are ignored and only exact matches
-   on NAME are considered.
-
-   LIST is a chain of struct cmd_list_element's.
-
-   If IGNORE_HELP_CLASSES is true (the default), 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 null).
-
-   If found, return the struct cmd_list_element for that command,
-   otherwise return NULLPTR.  */
-
-extern struct cmd_list_element *lookup_cmd_exact
-			(const char *name,
-			 struct cmd_list_element *list,
-			 bool ignore_help_classes = true);
-
-extern struct cmd_list_element *deprecate_cmd (struct cmd_list_element *,
-					       const char * );
-
-extern void deprecated_cmd_warning (const char *, struct cmd_list_element *);
-
-extern int lookup_cmd_composition (const char *text,
-				   struct cmd_list_element **alias,
-				   struct cmd_list_element **prefix_cmd,
-				   struct cmd_list_element **cmd);
-
-extern struct cmd_list_element *add_com (const char *, enum command_class,
-					 cmd_const_cfunc_ftype *fun,
-					 const char *);
-
-extern struct cmd_list_element *add_com_alias (const char *, const char *,
-					       enum command_class, int);
-
-extern struct cmd_list_element *add_com_suppress_notification
-		       (const char *name, enum command_class theclass,
-			cmd_const_cfunc_ftype *fun, const char *doc,
-			int *supress_notification);
-
-extern struct cmd_list_element *add_info (const char *,
-					  cmd_const_cfunc_ftype *fun,
-					  const char *);
-
-extern struct cmd_list_element *add_info_alias (const char *, const char *,
-						int);
-
-extern void complete_on_cmdlist (struct cmd_list_element *,
-				 completion_tracker &tracker,
-				 const char *, const char *, int);
-
-extern void complete_on_enum (completion_tracker &tracker,
-			      const char *const *enumlist,
-			      const char *, const char *);
-
-/* Functions that implement commands about CLI commands.  */
-
-extern void help_list (struct cmd_list_element *, const char *,
-		       enum command_class, struct ui_file *);
-
 /* Method for show a set/show variable's VALUE on FILE.  If this
    method isn't supplied deprecated_show_value_hack() is called (which
    is not good).  */
@@ -398,142 +156,6 @@ typedef void (show_value_ftype) (struct ui_file *file,
    instead print the value out directly.  */
 extern show_value_ftype deprecated_show_value_hack;
 
-extern void add_setshow_enum_cmd (const char *name,
-				  enum command_class theclass,
-				  const char *const *enumlist,
-				  const char **var,
-				  const char *set_doc,
-				  const char *show_doc,
-				  const char *help_doc,
-				  cmd_const_sfunc_ftype *set_func,
-				  show_value_ftype *show_func,
-				  struct cmd_list_element **set_list,
-				  struct cmd_list_element **show_list,
-				  void *context = nullptr);
-
-extern void add_setshow_auto_boolean_cmd (const char *name,
-					  enum command_class theclass,
-					  enum auto_boolean *var,
-					  const char *set_doc,
-					  const char *show_doc,
-					  const char *help_doc,
-					  cmd_const_sfunc_ftype *set_func,
-					  show_value_ftype *show_func,
-					  struct cmd_list_element **set_list,
-					  struct cmd_list_element **show_list);
-
-extern cmd_list_element *
-  add_setshow_boolean_cmd (const char *name,
-			   enum command_class theclass,
-			   bool *var,
-			   const char *set_doc, const char *show_doc,
-			   const char *help_doc,
-			   cmd_const_sfunc_ftype *set_func,
-			   show_value_ftype *show_func,
-			   struct cmd_list_element **set_list,
-			   struct cmd_list_element **show_list);
-
-extern void add_setshow_filename_cmd (const char *name,
-				      enum command_class theclass,
-				      char **var,
-				      const char *set_doc,
-				      const char *show_doc,
-				      const char *help_doc,
-				      cmd_const_sfunc_ftype *set_func,
-				      show_value_ftype *show_func,
-				      struct cmd_list_element **set_list,
-				      struct cmd_list_element **show_list);
-
-extern void add_setshow_string_cmd (const char *name,
-				    enum command_class theclass,
-				    char **var,
-				    const char *set_doc,
-				    const char *show_doc,
-				    const char *help_doc,
-				    cmd_const_sfunc_ftype *set_func,
-				    show_value_ftype *show_func,
-				    struct cmd_list_element **set_list,
-				    struct cmd_list_element **show_list);
-
-extern struct cmd_list_element *add_setshow_string_noescape_cmd
-		      (const char *name,
-		       enum command_class theclass,
-		       char **var,
-		       const char *set_doc,
-		       const char *show_doc,
-		       const char *help_doc,
-		       cmd_const_sfunc_ftype *set_func,
-		       show_value_ftype *show_func,
-		       struct cmd_list_element **set_list,
-		       struct cmd_list_element **show_list);
-
-extern void add_setshow_optional_filename_cmd (const char *name,
-					       enum command_class theclass,
-					       char **var,
-					       const char *set_doc,
-					       const char *show_doc,
-					       const char *help_doc,
-					       cmd_const_sfunc_ftype *set_func,
-					       show_value_ftype *show_func,
-					       struct cmd_list_element **set_list,
-					       struct cmd_list_element **show_list);
-
-extern void add_setshow_integer_cmd (const char *name,
-				     enum command_class theclass,
-				     int *var,
-				     const char *set_doc,
-				     const char *show_doc,
-				     const char *help_doc,
-				     cmd_const_sfunc_ftype *set_func,
-				     show_value_ftype *show_func,
-				     struct cmd_list_element **set_list,
-				     struct cmd_list_element **show_list);
-
-extern void add_setshow_uinteger_cmd (const char *name,
-				      enum command_class theclass,
-				      unsigned int *var,
-				      const char *set_doc,
-				      const char *show_doc,
-				      const char *help_doc,
-				      cmd_const_sfunc_ftype *set_func,
-				      show_value_ftype *show_func,
-				      struct cmd_list_element **set_list,
-				      struct cmd_list_element **show_list);
-
-extern void add_setshow_zinteger_cmd (const char *name,
-				      enum command_class theclass,
-				      int *var,
-				      const char *set_doc,
-				      const char *show_doc,
-				      const char *help_doc,
-				      cmd_const_sfunc_ftype *set_func,
-				      show_value_ftype *show_func,
-				      struct cmd_list_element **set_list,
-				      struct cmd_list_element **show_list);
-
-extern void add_setshow_zuinteger_cmd (const char *name,
-				       enum command_class theclass,
-				       unsigned int *var,
-				       const char *set_doc,
-				       const char *show_doc,
-				       const char *help_doc,
-				       cmd_const_sfunc_ftype *set_func,
-				       show_value_ftype *show_func,
-				       struct cmd_list_element **set_list,
-				       struct cmd_list_element **show_list);
-
-extern void
-  add_setshow_zuinteger_unlimited_cmd (const char *name,
-				       enum command_class theclass,
-				       int *var,
-				       const char *set_doc,
-				       const char *show_doc,
-				       const char *help_doc,
-				       cmd_const_sfunc_ftype *set_func,
-				       show_value_ftype *show_func,
-				       struct cmd_list_element **set_list,
-				       struct cmd_list_element **show_list);
-
 /* Do a "show" command for each thing on a command list.  */
 
 extern void cmd_show_list (struct cmd_list_element *, int);
@@ -596,9 +218,11 @@ extern void save_command_line (const char *cmd);
 extern void not_just_help_class_command (const char *, int);
 
 /* Check function pointer.  */
+
 extern int cmd_func_p (struct cmd_list_element *cmd);
 
 /* Call the command function.  */
+
 extern void cmd_func (struct cmd_list_element *cmd,
 		      const char *args, int from_tty);
 
diff --git a/gdb/complaints.c b/gdb/complaints.c
index fecbcd74ce5..b36c201ca75 100644
--- a/gdb/complaints.c
+++ b/gdb/complaints.c
@@ -18,6 +18,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "cli/cli-decode.h"
 #include "complaints.h"
 #include "command.h"
 #include "gdbcmd.h"
diff --git a/gdb/completer.h b/gdb/completer.h
index 240490f0c05..9aa05bcd26a 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -601,6 +601,27 @@ extern char *gdb_completion_word_break_characters (void);
    not "const char *".  */
 extern void set_rl_completer_word_break_characters (const char *break_chars);
 
+/* A completion routine.  Add possible completions to tracker.
+
+   TEXT is the text beyond what was matched for the command itself
+   (leading whitespace is skipped).  It stops where we are supposed to
+   stop completing (rl_point) and is '\0' terminated.  WORD points in
+   the same buffer as TEXT, and completions should be returned
+   relative to this position.  For example, suppose TEXT is "foo" and
+   we want to complete to "foobar".  If WORD is "oo", return "oobar";
+   if WORD is "baz/foo", return "baz/foobar".  */
+
+typedef void completer_ftype (cmd_list_element *,
+			      completion_tracker &tracker,
+			      const char *text, const char *word);
+
+/* Same, but for set_cmd_completer_handle_brkchars.  */
+
+typedef void completer_handle_brkchars_ftype (cmd_list_element *,
+					      completion_tracker &tracker,
+					      const char *text,
+					      const char *word);
+
 /* Get the matching completer_handle_brkchars_ftype function for FN.
    FN is one of the core completer functions above (filename,
    location, symbol, etc.).  This function is useful for cases when
diff --git a/gdb/copying.c b/gdb/copying.c
index 63183450a0e..2cedc9e410a 100644
--- a/gdb/copying.c
+++ b/gdb/copying.c
@@ -3,6 +3,7 @@
    Modify copying.awk instead.  <== */
 
 #include "defs.h"
+#include "cli/cli-decode.h"
 #include "command.h"
 #include "gdbcmd.h"
 
diff --git a/gdb/cp-abi.c b/gdb/cp-abi.c
index f831b2b34b3..2284ea6ca70 100644
--- a/gdb/cp-abi.c
+++ b/gdb/cp-abi.c
@@ -20,6 +20,7 @@
 #include "defs.h"
 #include "value.h"
 #include "cp-abi.h"
+#include "cli/cli-decode.h"
 #include "command.h"
 #include "gdbcmd.h"
 #include "ui-out.h"
diff --git a/gdb/filesystem.c b/gdb/filesystem.c
index f28d24fa1b9..ff8a09690d5 100644
--- a/gdb/filesystem.c
+++ b/gdb/filesystem.c
@@ -19,6 +19,7 @@
 
 #include "defs.h"
 #include "filesystem.h"
+#include "cli/cli-decode.h"
 #include "gdbarch.h"
 #include "gdbcmd.h"
 
diff --git a/gdb/gdb-demangle.c b/gdb/gdb-demangle.c
index 24e92009d73..9000b2d02c4 100644
--- a/gdb/gdb-demangle.c
+++ b/gdb/gdb-demangle.c
@@ -24,6 +24,7 @@
    to a styles of demangling, and GDB specific.  */
 
 #include "defs.h"
+#include "cli/cli-decode.h"
 #include "cli/cli-utils.h" /* for skip_to_space */
 #include "command.h"
 #include "gdbcmd.h"
diff --git a/gdb/interps.c b/gdb/interps.c
index ec19822b571..918ad28d244 100644
--- a/gdb/interps.c
+++ b/gdb/interps.c
@@ -30,6 +30,7 @@
    them take over the input in their resume proc.  */
 
 #include "defs.h"
+#include "cli/cli-decode.h"
 #include "gdbcmd.h"
 #include "ui-out.h"
 #include "gdbsupport/event-loop.h"
diff --git a/gdb/macrocmd.c b/gdb/macrocmd.c
index 4e4a68d5c1d..015df3f5101 100644
--- a/gdb/macrocmd.c
+++ b/gdb/macrocmd.c
@@ -22,6 +22,7 @@
 #include "macrotab.h"
 #include "macroexp.h"
 #include "macroscope.h"
+#include "cli/cli-decode.h"
 #include "cli/cli-style.h"
 #include "cli/cli-utils.h"
 #include "command.h"
diff --git a/gdb/maint-test-options.c b/gdb/maint-test-options.c
index b773e759bb7..a16fbe55ddc 100644
--- a/gdb/maint-test-options.c
+++ b/gdb/maint-test-options.c
@@ -19,6 +19,7 @@
 
 #include "defs.h"
 #include "gdbcmd.h"
+#include "cli/cli-decode.h"
 #include "cli/cli-option.h"
 
 /* This file defines three "maintenance test-options" subcommands to
diff --git a/gdb/maint-test-settings.c b/gdb/maint-test-settings.c
index 0ce88905518..e4d2889538c 100644
--- a/gdb/maint-test-settings.c
+++ b/gdb/maint-test-settings.c
@@ -19,6 +19,7 @@
 
 
 #include "defs.h"
+#include "cli/cli-decode.h"
 #include "gdbcmd.h"
 
 /* Command list for "maint set test-settings".  */
diff --git a/gdb/observable.c b/gdb/observable.c
index 51f5edb0a1f..9d340f23d83 100644
--- a/gdb/observable.c
+++ b/gdb/observable.c
@@ -19,6 +19,7 @@
 
 #include "defs.h"
 #include "observable.h"
+#include "cli/cli-decode.h"
 #include "command.h"
 #include "gdbcmd.h"
 
diff --git a/gdb/osabi.c b/gdb/osabi.c
index b1603c09c85..165ae61929f 100644
--- a/gdb/osabi.c
+++ b/gdb/osabi.c
@@ -21,6 +21,7 @@
 
 #include "osabi.h"
 #include "arch-utils.h"
+#include "cli/cli-decode.h"
 #include "gdbcmd.h"
 #include "command.h"
 #include "gdb_bfd.h"
diff --git a/gdb/reggroups.c b/gdb/reggroups.c
index 96ff1d3d97d..3a2119aaf47 100644
--- a/gdb/reggroups.c
+++ b/gdb/reggroups.c
@@ -21,6 +21,7 @@
 
 #include "defs.h"
 #include "arch-utils.h"
+#include "cli/cli-decode.h"
 #include "reggroups.h"
 #include "gdbtypes.h"
 #include "regcache.h"
diff --git a/gdb/ser-unix.c b/gdb/ser-unix.c
index e97dc2f925d..9abdb70e1d6 100644
--- a/gdb/ser-unix.c
+++ b/gdb/ser-unix.c
@@ -33,6 +33,7 @@
 #include "gdbsupport/filestuff.h"
 #include <termios.h>
 #include "inflow.h"
+#include "cli/cli-decode.h"
 
 struct hardwire_ttystate
   {
diff --git a/gdb/serial.c b/gdb/serial.c
index 719cd66c08c..71033a9a35e 100644
--- a/gdb/serial.c
+++ b/gdb/serial.c
@@ -21,6 +21,7 @@
 #include <ctype.h>
 #include "serial.h"
 #include "gdbcmd.h"
+#include "cli/cli-decode.h"
 #include "cli/cli-utils.h"
 
 /* Is serial being debugged?  */
diff --git a/gdb/user-regs.c b/gdb/user-regs.c
index 5845eb13a5a..1e4b5e95a65 100644
--- a/gdb/user-regs.c
+++ b/gdb/user-regs.c
@@ -26,6 +26,7 @@
 #include "arch-utils.h"
 #include "command.h"
 #include "cli/cli-cmds.h"
+#include "cli/cli-decode.h"
 
 /* A table of user registers.
 
diff --git a/gdb/xml-support.c b/gdb/xml-support.c
index 5ba1e53bf92..26c7e5de2eb 100644
--- a/gdb/xml-support.c
+++ b/gdb/xml-support.c
@@ -21,6 +21,7 @@
 #include "gdbcmd.h"
 #include "xml-builtin.h"
 #include "xml-support.h"
+#include "cli/cli-decode.h"
 #include "gdbsupport/filestuff.h"
 #include "safe-ctype.h"
 #include <vector>
-- 
2.28.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v3 5/5] gdb: add support for renaming commands
  2021-03-23 18:45     ` Simon Marchi
@ 2021-05-14 20:41       ` Marco Barisione
  0 siblings, 0 replies; 38+ messages in thread
From: Marco Barisione @ 2021-05-14 20:41 UTC (permalink / raw)
  To: gdb-patches

This patch adds:
* A "rename" command.
* A "-rename-existing-to" option for the "define" command.
* A "rename_existing_to" optional argument to gdb.Command.__init__
  which matches the behaviour of "define -rename-existing-to".

The goal of this is to allow users to build on top of existing commands
without losing their original implementation.
Something similar could be achieved through hooks but they are limited:
* Hooks cannot prevent the command from being executed without printing
  any error.
* Hooks don't get passed the arguments passed to the command.
* Post hooks can't know if the command failed.
* Hooks cannot be defined in Python.

gdb/ChangeLog:

	* NEWS: Add items for the new rename command, the new
	-rename-existing-to option for define and the new
	rename_existing_to argument for gdb.Command.__init__.
	* cli/cli-decode.c (delete_cmd): Rename to disconnect_cmd.
	(disconnect_cmd): Disconnect a command from other commands and
	lists rather than deleting the command.
	(delete_cmd_by_name): Add.
	(update_prefix_links): Add.
	(update_cmd): Add.
	(do_add_cmd): Add ability to rename existing commands rather
	than redefining them.
	(add_cmd): Ditto.
	(add_prefix_cmd): Ditto.
	(add_alias_cmd): Update to use disconnect_cmd.
	(break_cmd_relationships): Add.
	(rename_hook): Add.
	(user_defined_command): Move from cli/cli-script.c.
	(rename_cmd): Add.
	* cli/cli-decode.h (struct cmd_list_element): Call the destroyer
	function from the destructor.
	(add_cmd): Add ability to rename existing commands rather than
	redefining them.
	(add_prefix_cmd): Ditto.
	(rename_cmd): Add.
	(user_defined_command): Add declaration of previously static
	function.
	* cli/cli-script.c (do_define_command): Add ability to rename
	existing commands rather than redefining them.
	(user_defined_command): Move to cli/cli-decode.c
	(check_command_redefinition): Add.
	(enum cmd_hook_type): Move from do_define_command to the global
	scope.
	(get_hook_type): Add.
	(HOOK_STRING): Make a variable in get_hook_type.
	(HOOK_LEN): Ditto.
	(HOOK_POST_STRING): Ditto.
	(HOOK_POST_LEN): Ditto.
	(struct define_cmd_opts): Add.
	(make_define_cmd_options_def_group): Add.
	(define_command): Add -rename-existing-to option.
	(do_rename_command): Add.
	(rename_command): Add.
	(_initialize_cli_script): Add option parsing for the define
	command and add the rename command.
	* python/py-cmd.c (cmdpy_init): Add rename_existing_to argument.
	* python/python-internal.h (PyInt_Type): Add for compatibility
	between Python 2 and 3.

gdb/doc/ChangeLog:

	* gdb.texinfo: Document the new rename command and the new
	-rename-existing-to option for define.
	* python.texi: Document the new rename_existing_to argument for
	gdb.Command.__init__.

gdb/testsuite/ChangeLog:

	* gdb.base/command-renaming.exp: New test.
	* gdb.python/py-rename-existing.exp: New test.
	* gdb.python/py-rename-existing.py: New test.
---
 gdb/NEWS                                      |  26 +
 gdb/cli/cli-decode.c                          | 644 +++++++++++++++---
 gdb/cli/cli-decode.h                          |  52 +-
 gdb/cli/cli-script.c                          | 326 +++++++--
 gdb/doc/gdb.texinfo                           |  55 +-
 gdb/doc/python.texi                           |  38 +-
 gdb/python/py-cmd.c                           | 175 +++--
 gdb/python/python-internal.h                  |   1 +
 gdb/testsuite/gdb.base/command-renaming.exp   | 571 ++++++++++++++++
 .../gdb.python/py-rename-existing.exp         | 364 ++++++++++
 .../gdb.python/py-rename-existing.py          |  44 ++
 11 files changed, 2063 insertions(+), 233 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/command-renaming.exp
 create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.exp
 create mode 100644 gdb/testsuite/gdb.python/py-rename-existing.py

diff --git a/gdb/NEWS b/gdb/NEWS
index ab678acec8b..d10f55a4128 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -87,6 +87,11 @@ show print memory-tag-violations
   when printing pointers and addresses.  Architecture support for memory
   tagging is required for this option to have an effect.
 
+rename OLD_NAME NEW_NAME
+  Rename the command called OLD_NAME to NEW_NAME.  All subcommands, hooks
+  and aliases associated to OLD_NAME are moved to refer to NEW_NAME.
+  If a command name contains spaces, it must be enclosed in double quotes.
+
 maintenance flush symbol-cache
 maintenance flush register-cache
   These new commands are equivalent to the already existing commands
@@ -188,6 +193,27 @@ ptype[/FLAGS] TYPE | EXPRESSION
   offsets of struct members.  Default behavior is given by 'show print
   type hex'.
 
+define [-rename-existing-to NEW_NAME_FOR_EXISTING_COMMAND] COMMAND_NAME
+  Added a -rename-existing-to option which causes the existing command
+  called COMMAND_NAME to be renamed to NEW_NAME_FOR_EXISTING_COMMAND
+  instead of being redefined and overwritten.  This allows user-defined
+  commands to modify existing commands and extend them rather than
+  replacing them completely.
+  The behaviour is similar to using the "rename" command followed by
+  "define", but "define -rename-existing-to" keeps aliases, hooks and
+  subcommands associated to the newly defined COMMAND_NAME command.
+  If NEW_NAME_FOR_EXISTING_COMMAND contains spaces, it must be enclosed in
+  double quotes, unlike COMMAND_NAME which must not be enclosed in quotes.
+
+* Python API
+
+  ** Added a new rename_existing_to optional parameter to gdb.Command.
+     If specified, the existing command with the same name as the new
+     command gets renamed to the value of rename_existing_to instead of
+     being redefined and overwritten.  This allows Python code to modify
+     existing commands and extend them rather than replacing them
+     completely.  See also "define -rename-existing-to".
+
 * Removed targets and native configurations
 
 ARM Symbian			arm*-*-symbianelf*
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index f13bbbf60d9..ab2a9474e84 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -30,12 +30,18 @@
 
 static void undef_cmd_error (const char *, const char *);
 
-static struct cmd_list_element *delete_cmd (const char *name,
-					    struct cmd_list_element **list,
-					    struct cmd_list_element **prehook,
-					    struct cmd_list_element **prehookee,
-					    struct cmd_list_element **posthook,
-					    struct cmd_list_element **posthookee);
+static cmd_list_element *disconnect_cmd (const char *name,
+					 cmd_list_element **list,
+					 cmd_list_element ***prefixlist,
+					 cmd_list_element **aliases,
+					 cmd_list_element **prehook,
+					 cmd_list_element **prehookee,
+					 cmd_list_element **posthook,
+					 cmd_list_element **posthookee);
+
+static bool delete_cmd_by_name (const char *name,
+				cmd_list_element **list,
+				cmd_list_element ***prefixlist);
 
 static struct cmd_list_element *find_cmd (const char *command,
 					  int len,
@@ -160,64 +166,201 @@ set_cmd_completer_handle_brkchars (struct cmd_list_element *cmd,
   cmd->completer_handle_brkchars = func;
 }
 
-/* Like ADD_CMD, but the command function fields are not modified.  */
+/* Update the prefix-related fields associated with command C, which must
+   be part of the command list *LIST, and, if not null, adds PREFIXLIST
+   as subcommands of C.
 
-static struct cmd_list_element *
-do_add_cmd (const char *name, enum command_class theclass,
-	    const char *doc, struct cmd_list_element **list)
+   In particular, this function always updates the relationship of C with
+   the command it's a subcommand of (if any) and, if PREFIXLIST is not
+   null:
+   * Makes PREFIXLIST the list of subcommands of C (which makes C a prefix
+     command);
+   * Updates the subcommands in PREFIXLIST accordingly.  */
+
+static void
+update_prefix_links (cmd_list_element *c, cmd_list_element **list,
+		     cmd_list_element **prefixlist)
+{
+  /* Update the subcommands to point to C as their prefix.  */
+  gdb_assert (c->prefixlist == nullptr);
+  c->prefixlist = prefixlist;
+  if (c->prefixlist != nullptr)
+    {
+      for (cmd_list_element *iter = *(c->prefixlist);
+	   iter != nullptr;
+	   iter = iter->next)
+	iter->prefix = c;
+    }
+
+  /* Find the prefix cmd of C, and assign it to C->prefix.
+     See also add_prefix_cmd and update_prefix_field_of_prefixed_commands.  */
+  gdb_assert (c->prefix == nullptr);
+  cmd_list_element *prefixcmd = lookup_cmd_for_prefixlist (list, cmdlist);
+  c->prefix = prefixcmd;
+}
+
+/* Update command C, by adding it to *LIST and updating it from the other
+   arguments.
+
+   C must not already be in any command list, or have any subcommands,
+   aliases or hooks associated.
+
+   If NEW_NAME is not null, then the command name is updated to the
+   specified name.
+
+   If PREFIXLIST is not null, then the *PREFIXLIST commands become
+   subcommands of C.
+
+   If ALIASES is not null, then the specified commands become aliases of
+   C.
+
+   The various HOOKs, if not null, specify commands hooking into C or
+   commands hooked into C.  */
+
+static void
+update_cmd (cmd_list_element *c, cmd_list_element **list,
+	    const char *new_name = nullptr,
+	    cmd_list_element **prefixlist = nullptr,
+	    cmd_list_element *aliases = nullptr,
+	    cmd_list_element *hook_pre = nullptr,
+	    cmd_list_element *hookee_pre = nullptr,
+	    cmd_list_element *hook_post = nullptr,
+	    cmd_list_element *hookee_post = nullptr)
 {
-  struct cmd_list_element *c = new struct cmd_list_element (name, theclass,
-							    doc);
-  struct cmd_list_element *p, *iter;
-
-  /* Turn each alias of the old command into an alias of the new
-     command.  */
-  c->aliases = delete_cmd (name, list, &c->hook_pre, &c->hookee_pre,
-			   &c->hook_post, &c->hookee_post);
-  for (iter = c->aliases; iter; iter = iter->alias_chain)
+  /* Update the name, if specified.  */
+  if (new_name != nullptr)
+    {
+      if (c->name_allocated)
+	xfree ((char *)c->name);
+
+      c->name = xstrdup (new_name);
+      c->name_allocated = true;
+    }
+
+  /* Add C to the command list LIST in the appropriate (sorted) position.  */
+  if (*list == nullptr || strcmp ((*list)->name, c->name) >= 0)
+    {
+      c->next = *list;
+      *list = c;
+    }
+  else
+    {
+      cmd_list_element *p = *list;
+      while (p->next != nullptr && strcmp (p->next->name, c->name) <= 0)
+	p = p->next;
+      c->next = p->next;
+      p->next = c;
+    }
+
+  /* Update prefix information.  */
+  update_prefix_links (c, list, prefixlist);
+
+  /* Update the aliases.  */
+  gdb_assert (c->aliases == nullptr);
+  c->aliases = aliases;
+  for (cmd_list_element *iter = c->aliases;
+       iter != nullptr;
+       iter = iter->alias_chain)
     iter->cmd_pointer = c;
-  if (c->hook_pre)
+
+  /* Updates hooks and hookees.  */
+  gdb_assert (c->hook_pre == nullptr);
+  c->hook_pre = hook_pre;
+  if (c->hook_pre != nullptr)
     c->hook_pre->hookee_pre = c;
-  if (c->hookee_pre)
+
+  gdb_assert (c->hookee_pre == nullptr);
+  c->hookee_pre = hookee_pre;
+  if (c->hookee_pre != nullptr)
     c->hookee_pre->hook_pre = c;
-  if (c->hook_post)
+
+  gdb_assert (c->hook_post == nullptr);
+  c->hook_post = hook_post;
+  if (c->hook_post != nullptr)
     c->hook_post->hookee_post = c;
-  if (c->hookee_post)
+
+  gdb_assert (c->hookee_post == nullptr);
+  c->hookee_post = hookee_post;
+  if (c->hookee_post != nullptr)
     c->hookee_post->hook_post = c;
+}
 
-  if (*list == NULL || strcmp ((*list)->name, name) >= 0)
+/* Like ADD_CMD, but the command function fields are not modified.  */
+
+static cmd_list_element *
+do_add_cmd (const char *name, command_class theclass, const char *doc,
+	    cmd_list_element **list,
+	    const char *rename_existing_to = nullptr,
+	    cmd_list_element **rename_existing_to_list = nullptr)
+{
+  /* If a command called RENAME_EXISTING_TO already exists, delete it.  */
+  cmd_list_element **deleted_cmd_prefixlist = nullptr;
+  if (rename_existing_to != nullptr)
     {
-      c->next = *list;
-      *list = c;
+      gdb_assert (rename_existing_to_list != nullptr);
+      delete_cmd_by_name (rename_existing_to, rename_existing_to_list,
+			  &deleted_cmd_prefixlist);
     }
-  else
+
+  /* Create the new command.  */
+  cmd_list_element *c = new cmd_list_element (name, theclass, doc);
+
+  /* Remove the old NAME command from LIST and disconnect all the
+     subcommands, aliases, hooks as well.  */
+  cmd_list_element **prefixlist;
+  cmd_list_element *aliases;
+  cmd_list_element *hook_pre, *hookee_pre, *hook_post, *hookee_post;
+  cmd_list_element *existing_cmd = disconnect_cmd (name, list,
+						   &prefixlist,
+						   &aliases,
+						   &hook_pre, &hookee_pre,
+						   &hook_post, &hookee_post);
+
+  /* Add the new command to LIST turning aliases and hooks of the old
+     command into aliases/hooks of the new command.  */
+  update_cmd (c, list,
+	      /* new_name = */ nullptr,
+	      prefixlist,
+	      aliases,
+	      hook_pre, hookee_pre, hook_post, hookee_post);
+
+  /* Finally, rename the existing command or, if renaming is not
+     requested, delete it.  */
+  if (rename_existing_to != nullptr)
     {
-      p = *list;
-      while (p->next && strcmp (p->next->name, name) <= 0)
+      gdb_assert (existing_cmd != nullptr);
+      update_cmd (existing_cmd, rename_existing_to_list,
+		  rename_existing_to,
+		  deleted_cmd_prefixlist);
+      if (prefixlist)
 	{
-	  p = p->next;
+	  /* The existing command was a prefix command so it must remain
+	     a prefix command (with no subcommands).  This is needed as
+	     the implementation of the command could try to access its
+	     subcommands.  See define_prefix_command for details on how
+	     prefix lists work. */
+	  gdb_assert (existing_cmd->prefixlist == nullptr);
+	  existing_cmd->prefixlist = new cmd_list_element*;
+	  *(existing_cmd->prefixlist) = nullptr;
 	}
-      c->next = p->next;
-      p->next = c;
     }
-
-  /* Search the prefix cmd of C, and assigns it to C->prefix.
-     See also add_prefix_cmd and update_prefix_field_of_prefixed_commands.  */
-  struct cmd_list_element *prefixcmd = lookup_cmd_for_prefixlist (list,
-								  cmdlist);
-  c->prefix = prefixcmd;
-
+  else
+    delete existing_cmd;
 
   return c;
 }
 
 /* See cli-decode.h.  */
 
-struct cmd_list_element *
+cmd_list_element *
 add_cmd (const char *name, enum command_class theclass,
-	 const char *doc, struct cmd_list_element **list)
+	 const char *doc, cmd_list_element **list,
+	 const char *rename_existing_to,
+	 cmd_list_element **rename_existing_to_list)
 {
-  cmd_list_element *result = do_add_cmd (name, theclass, doc, list);
+  cmd_list_element *result = do_add_cmd (name, theclass, doc, list,
+					 rename_existing_to,
+					 rename_existing_to_list);
   result->func = NULL;
   result->function.const_cfunc = NULL;
   return result;
@@ -225,12 +368,16 @@ add_cmd (const char *name, enum command_class theclass,
 
 /* See cli-decode.h.  */
 
-struct cmd_list_element *
+cmd_list_element *
 add_cmd (const char *name, enum command_class theclass,
 	 cmd_const_cfunc_ftype *fun,
-	 const char *doc, struct cmd_list_element **list)
+	 const char *doc, cmd_list_element **list,
+	 const char *rename_existing_to,
+	 cmd_list_element **rename_existing_to_list)
 {
-  cmd_list_element *result = do_add_cmd (name, theclass, doc, list);
+  cmd_list_element *result = do_add_cmd (name, theclass, doc, list,
+					 rename_existing_to,
+					 rename_existing_to_list);
   set_cmd_cfunc (result, fun);
   return result;
 }
@@ -274,18 +421,22 @@ add_alias_cmd (const char *name, cmd_list_element *old,
 {
   if (old == 0)
     {
-      struct cmd_list_element *prehook, *prehookee, *posthook, *posthookee;
-      struct cmd_list_element *aliases = delete_cmd (name, list,
-						     &prehook, &prehookee,
-						     &posthook, &posthookee);
-
+      cmd_list_element **prefixlist;
+      cmd_list_element *aliases;
+      cmd_list_element *prehook, *prehookee, *posthook, *posthookee;
+      cmd_list_element *old_cmd = disconnect_cmd (name, list,
+						  &prefixlist,
+						  &aliases,
+						  &prehook, &prehookee,
+						  &posthook, &posthookee);
       /* If this happens, it means a programmer error somewhere.  */
-      gdb_assert (!aliases && !prehook && !prehookee
-		  && !posthook && ! posthookee);
+      gdb_assert (old_cmd == nullptr);
+      /* As OLD_CMD is null, NAME didn't exist, so the prefix list,
+	 aliases and hooks are all null as well.  */
       return 0;
     }
 
-  struct cmd_list_element *c = add_cmd (name, theclass, old->doc, list);
+  cmd_list_element *c = add_cmd (name, theclass, old->doc, list);
 
   /* If OLD->DOC can be freed, we should make another copy.  */
   if (old->doc_allocated)
@@ -353,13 +504,17 @@ update_prefix_field_of_prefixed_commands (struct cmd_list_element *c)
 
 /* See cli-decode.h.  */
 
-struct cmd_list_element *
+cmd_list_element *
 add_prefix_cmd (const char *name, enum command_class theclass,
 		cmd_const_cfunc_ftype *fun,
-		const char *doc, struct cmd_list_element **prefixlist,
-		int allow_unknown, struct cmd_list_element **list)
+		const char *doc, cmd_list_element **prefixlist,
+		int allow_unknown, cmd_list_element **list,
+		const char *rename_existing_to,
+		cmd_list_element **rename_existing_to_list)
 {
-  struct cmd_list_element *c = add_cmd (name, theclass, fun, doc, list);
+  cmd_list_element *c = add_cmd (name, theclass, fun, doc, list,
+				 rename_existing_to,
+				 rename_existing_to_list);
 
   c->prefixlist = prefixlist;
   c->allow_unknown = allow_unknown;
@@ -872,76 +1027,339 @@ add_setshow_zuinteger_cmd (const char *name, enum command_class theclass,
 			NULL, NULL);
 }
 
-/* Remove the command named NAME from the command list.  Return the
-   list commands which were aliased to the deleted command.  If the
-   command had no aliases, return NULL.  The various *HOOKs are set to
-   the pre- and post-hook commands for the deleted command.  If the
-   command does not have a hook, the corresponding out parameter is
-   set to NULL.  */
-
-static struct cmd_list_element *
-delete_cmd (const char *name, struct cmd_list_element **list,
-	    struct cmd_list_element **prehook,
-	    struct cmd_list_element **prehookee,
-	    struct cmd_list_element **posthook,
-	    struct cmd_list_element **posthookee)
+/* Break the relationships between C and other commands.
+
+   This is done by removing subcommands, aliases and hooks from C and
+   making the other commands not point to C any more.
+
+   *PREFIXLIST is set to point to the list of subcommands for the removed
+   command.
+
+   *ALIASES is set to the list of commands which were aliased to C.  If
+   the command had no aliases, it's set to nullptr.
+
+   *ALIASES is set to the removed aliases.  If the command has no aliases,
+   it's set to nullptr.
+
+   The various *HOOKs are set to the pre- and post-hook commands for C.
+   If the command does not have a hook, the corresponding out parameter
+   is set to nullptr.  */
+
+static void
+break_cmd_relationships (cmd_list_element *c,
+			 cmd_list_element ***prefixlist,
+			 cmd_list_element **aliases,
+			 cmd_list_element **hook_pre,
+			 cmd_list_element **hookee_pre,
+			 cmd_list_element **hook_post,
+			 cmd_list_element **hookee_post)
 {
-  struct cmd_list_element *iter;
-  struct cmd_list_element **previous_chain_ptr;
-  struct cmd_list_element *aliases = NULL;
+  /* Make the subcommands not point to this command any more. */
+  if (c->prefixlist != nullptr)
+    {
+      for (cmd_list_element *iter = *(c->prefixlist);
+	   iter != nullptr;
+	   iter = iter->next)
+	iter->prefix = nullptr;
+    }
+
+  /* Remove the subcommands.  */
+  *prefixlist = c->prefixlist;
+  c->prefixlist = nullptr;
+
+  /* Make the aliases not point to this command any more. */
+  for (cmd_list_element *iter = c->aliases;
+       iter != nullptr;
+       iter = iter->alias_chain)
+    iter->cmd_pointer = nullptr;
+
+  /* Remove the aliases. */
+  *aliases = c->aliases;
+  c->aliases = nullptr;
+
+  /* If this command was an alias, remove it from the list of aliases.  */
+  if (c->cmd_pointer != nullptr)
+    {
+      cmd_list_element **prevp = &c->cmd_pointer->aliases;
+      cmd_list_element *a = *prevp;
 
-  *prehook = NULL;
-  *prehookee = NULL;
-  *posthook = NULL;
-  *posthookee = NULL;
-  previous_chain_ptr = list;
+      while (a != c)
+	{
+	  prevp = &a->alias_chain;
+	  a = *prevp;
+	}
+      *prevp = c->alias_chain;
+    }
+
+  /* Save the previous hook lists in the output parameters.  */
+  *hook_pre = c->hook_pre;
+  *hookee_pre = c->hookee_pre;
+  *hook_post = c->hook_post;
+  *hookee_post = c->hookee_post;
+
+  /* Break the hook relationships with other commands.  */
+  if (c->hook_pre)
+    c->hook_pre->hookee_pre = nullptr;
+  if (c->hookee_pre)
+    c->hookee_pre->hook_pre = nullptr;
+  if (c->hook_post)
+    c->hook_post->hookee_post = nullptr;
+  if (c->hookee_post)
+    c->hookee_post->hook_post = nullptr;
+
+  /* And finally remove the hooks.  */
+  c->hook_pre = nullptr;
+  c->hookee_pre = nullptr;
+  c->hook_post = nullptr;
+  c->hookee_post = nullptr;
+}
+
+/* Disconnect the command named NAME from the command list *LIST, from
+   its subcommands, and all associated aliases and hooks.
+
+   *PREFIXLIST is set to point to the list of subcommands for the removed
+   command.
+
+   *ALIASES is set to the list of commands which were aliased to the
+   removed command.  If the command has no aliases, it's set to nullptr.
+
+   The various *HOOKs are set to the pre- and post-hook commands for the
+   removed command.  If the command does not have a hook, the
+   corresponding out parameter is set to nullptr.
+
+   Return the diconnected command if it was found.  Otherwise, return
+   nullptr (and the output parameters are all set to nullptr as well).  */
+
+static cmd_list_element *
+disconnect_cmd (const char *name, cmd_list_element **list,
+		cmd_list_element ***prefixlist,
+		cmd_list_element **aliases,
+		cmd_list_element **hook_pre,
+		cmd_list_element **hookee_pre,
+		cmd_list_element **hook_post,
+		cmd_list_element **hookee_post)
+{
+  cmd_list_element **previous_chain_ptr = list;
 
-  for (iter = *previous_chain_ptr; iter; iter = *previous_chain_ptr)
+  for (cmd_list_element *iter = *previous_chain_ptr;
+       iter != nullptr; iter = *previous_chain_ptr)
     {
       if (strcmp (iter->name, name) == 0)
 	{
-	  if (iter->destroyer)
-	    iter->destroyer (iter, iter->context);
-	  if (iter->hookee_pre)
-	    iter->hookee_pre->hook_pre = 0;
-	  *prehook = iter->hook_pre;
-	  *prehookee = iter->hookee_pre;
-	  if (iter->hookee_post)
-	    iter->hookee_post->hook_post = 0;
-	  *posthook = iter->hook_post;
-	  *posthookee = iter->hookee_post;
-
-	  /* Update the link.  */
+	  /* Found the only command with the specified name.  */
+	  break_cmd_relationships (iter,
+				   prefixlist,
+				   aliases,
+				   hook_pre, hookee_pre,
+				   hook_post, hookee_post);
+	  /* Update the link in the command list, that is remove the
+	     command from its command list.  */
 	  *previous_chain_ptr = iter->next;
+	  /* And finally unlink from the prefix command.  */
+	  iter->prefix = nullptr;
 
-	  aliases = iter->aliases;
-
-	  /* If this command was an alias, remove it from the list of
-	     aliases.  */
-	  if (iter->cmd_pointer)
-	    {
-	      struct cmd_list_element **prevp = &iter->cmd_pointer->aliases;
-	      struct cmd_list_element *a = *prevp;
-
-	      while (a != iter)
-		{
-		  prevp = &a->alias_chain;
-		  a = *prevp;
-		}
-	      *prevp = iter->alias_chain;
-	    }
-
-	  delete iter;
-
-	  /* We won't see another command with the same name.  */
-	  break;
+	  return iter;
 	}
       else
 	previous_chain_ptr = &iter->next;
     }
 
-  return aliases;
+  /* Command not found. */
+
+  *prefixlist = nullptr;
+  *aliases = nullptr;
+  *hook_pre = nullptr;
+  *hookee_pre = nullptr;
+  *hook_post = nullptr;
+  *hookee_post = nullptr;
+
+  return nullptr;
 }
+
+/* Delete command C and all associated aliases and hooks.
+
+   Subcommands are not modified.  If C had subcommands, it's the caller's
+   responsibility to either assign the subcommands to another command or
+   to delete them.
+
+   If PREFIXLIST is not null, *PREFIXLIST is set to point to the list of
+   subcommands previously associated with C.
+   If PREFIXLIST is null, then C must be an alias or hook which cannot
+   have subcommands.  */
+
+static void
+delete_cmd (cmd_list_element *c, cmd_list_element ***prefixlist)
+{
+  /* The PREFIXLIST argument is required for all command types which could
+     have subcommands.  That is, PREFIXLIST can be null only if C is a
+     hook or alias.  */
+  gdb_assert (prefixlist != nullptr
+	      || (c->cmd_pointer == nullptr
+		  || c->hookee_pre == nullptr
+		  || c->hookee_post == nullptr));
+
+  /* Find the list for C.  */
+  cmd_list_element **list;
+  if (c->prefix != nullptr)
+    list = c->prefix->prefixlist;
+  else
+    list = &cmdlist;
+
+  /* Delete C itself.  */
+  cmd_list_element **prefixlist_tmp;
+  cmd_list_element *aliases;
+  cmd_list_element *hook_pre, *hookee_pre, *hook_post, *hookee_post;
+  cmd_list_element *actual_removed_cmd
+    = disconnect_cmd (c->name, list, &prefixlist_tmp, &aliases,
+		      &hook_pre, &hookee_pre, &hook_post, &hookee_post);
+  gdb_assert (actual_removed_cmd == c);
+
+  if (prefixlist != nullptr)
+    *prefixlist = prefixlist_tmp;
+  else
+    /* We asserted earlier that, if PREFIXLIST is null, then C is either
+       a hook or an alias, so prefixlist_tmp cannot be null.  */
+    gdb_assert (prefixlist_tmp == nullptr);
+
+  /* Delete all aliases.  */
+  for (cmd_list_element *alias_iter = aliases;
+       alias_iter != nullptr; alias_iter = alias_iter->alias_chain)
+    delete_cmd (alias_iter, nullptr);
+
+  /* Delete hooks.  */
+  if (hook_pre != nullptr)
+    delete_cmd (hook_pre, nullptr);
+  if (hook_post != nullptr)
+    delete_cmd (hook_post, nullptr);
+
+  /* There's nothing to do with hookee_pre and hookee_post.  If they are
+     set then C is a hook and, if C is a hook, deleting it doesn't mean
+     that the hookee must be deleted.  */
+
+  delete c;
+}
+
+/* Delete the command called NAME in *LIST and all associated aliases and
+   hooks.
+
+   See DELETE_CMD for details on PREFIXLIST and whether it can be null.
+
+   Return TRUE if the command was found and deleted, FALSE otherwise.  */
+
+static bool
+delete_cmd_by_name (const char *name, cmd_list_element **list,
+		    cmd_list_element ***prefixlist)
+{
+  cmd_list_element *cmd = lookup_cmd_exact (name, *list);
+  if (cmd != nullptr)
+    {
+      delete_cmd (cmd, prefixlist);
+      return true;
+    }
+  else
+    {
+      if (prefixlist != nullptr)
+	*prefixlist = nullptr;
+      return false;
+    }
+}
+
+/* Rename HOOK to match the command OLD_CMD_NAME in *OLD_LIST which was
+   renamed to NEW_CMD_NAME in NEW_LIST.  */
+
+static void
+rename_hook (cmd_list_element *hook,
+	     const char *old_cmd_name, cmd_list_element **old_list,
+	     const char *new_cmd_name, cmd_list_element **new_list)
+{
+  cmd_list_element **prefixlist;
+  cmd_list_element *aliases;
+  cmd_list_element *hook_pre, *hookee_pre, *hook_post, *hookee_post;
+  cmd_list_element *cmd = disconnect_cmd (hook->name, old_list,
+					  &prefixlist,
+					  &aliases,
+					  &hook_pre, &hookee_pre,
+					  &hook_post, &hookee_post);
+
+  /* Rename the hook command keeping the part that comes before the
+     hookee's name (i.e. "hook-" or "hookpost-").  */
+  std::string old_hook_name (hook->name);
+  size_t cmd_name_offset = old_hook_name.rfind (old_cmd_name);
+  gdb_assert (cmd_name_offset != std::string::npos);
+  std::string new_hook_name
+    = old_hook_name.substr (0, cmd_name_offset) + new_cmd_name;
+
+  update_cmd (cmd, new_list,
+	      new_hook_name.c_str (),
+	      prefixlist,
+	      aliases,
+	      hook_pre, hookee_pre, hook_post, hookee_post);
+}
+
+/* See command.h.  */
+
+void
+user_defined_command (const char *ignore, int from_tty)
+{
+}
+
+/* See command.h.  */
+
+cmd_list_element *
+rename_cmd (const char *old_name, cmd_list_element **old_list,
+	    const char *new_name, cmd_list_element **new_list)
+{
+  /* Remove the command from its current list.  */
+  cmd_list_element **old_name_prefixlist;
+  cmd_list_element *aliases;
+  cmd_list_element *hook_pre, *hookee_pre, *hook_post, *hookee_post;
+  cmd_list_element *cmd = disconnect_cmd (old_name, old_list,
+					  &old_name_prefixlist,
+					  &aliases,
+					  &hook_pre, &hookee_pre,
+					  &hook_post, &hookee_post);
+  if (cmd == nullptr)
+    {
+      /* No command called OLD_NAME exists in OLD_LIST.  */
+      return nullptr;
+    }
+
+  /* If there was already a NEW_NAME command then it must be deleted.
+     Its subcommands, if present, are moved to the renamed command.  */
+  cmd_list_element **new_name_prefixlist;
+  delete_cmd_by_name (new_name, new_list, &new_name_prefixlist);
+
+  /* Re-insert the command but in NEW_LIST.  */
+  update_cmd (cmd, new_list,
+	      new_name,
+	      new_name_prefixlist,
+	      aliases,
+	      hook_pre, hookee_pre, hook_post, hookee_post);
+
+  /* Rename the hooks as well.  */
+  if (hook_pre)
+    rename_hook (hook_pre, old_name, old_list, new_name, new_list);
+  if (hook_post)
+    rename_hook (hook_post, old_name, old_list, new_name, new_list);
+
+  /* If the old command had subcommands, then a new prefix-only command
+     must be created so they can be preserved.  */
+  if (old_name_prefixlist != nullptr && *old_name_prefixlist != nullptr)
+    {
+      cmd_list_element *prefix_for_old_name
+	  = add_cmd (xstrdup (old_name), class_user, user_defined_command,
+		     xstrdup ("User-defined."), old_list);
+      /* The new prefix is a prefix only so passing invalid subcommands
+	 must lead to an error.  */
+      prefix_for_old_name->allow_unknown = false;
+
+      update_prefix_links (prefix_for_old_name, old_list,
+			   old_name_prefixlist);
+    }
+
+  return cmd;
+}
+
 \f
 /* Shorthands to the commands above.  */
 
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 54057a149be..0e36af85afa 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -77,6 +77,8 @@ struct cmd_list_element
 
   ~cmd_list_element ()
   {
+    if (destroyer != nullptr)
+      destroyer (this, context);
     if (doc && doc_allocated)
       xfree ((char *) doc);
     if (name_allocated)
@@ -299,24 +301,31 @@ extern bool valid_cmd_char_p (int c);
    It should start with ? for a command that is an abbreviation
    or with * for a command that most users don't need to know about.
 
+   If RENAME_EXISTING_TO is not null, then the existing command called
+   NAME in *LIST will be renamed to RENAME_EXISTING_TO in
+   *RENAME_EXISTING_TO_LIST.  If a command called RENAME_EXISTING_TO
+   already exists in *RENAME_EXISTING_TO_LIST, it gets deleted first.
+   Ownership of RENAME_EXISTING_TO is not taken by this function (i.e.
+   the caller is responsible to free it if needed).
+
    If NAME already existed in *LIST, all its hooks and aliases are moved
    to the new command.
 
    Return a pointer to the added command (not necessarily the head of
    *LIST).  */
 
-extern cmd_list_element *add_cmd (const char *name,
-				  command_class theclass,
-				  cmd_const_cfunc_ftype *fun,
-				  const char *doc,
-				  cmd_list_element **list);
+extern cmd_list_element *add_cmd
+  (const char *name, command_class theclass, cmd_const_cfunc_ftype *fun,
+   const char *doc, cmd_list_element **list,
+   const char *rename_existing_to = nullptr,
+   cmd_list_element **rename_existing_to_list = nullptr);
 
 /* Like add_cmd, but no command function is specified.  */
 
-extern cmd_list_element *add_cmd (const char *name,
-				  command_class theclass,
-				  const char *doc,
-				  cmd_list_element **list);
+extern cmd_list_element *add_cmd
+  (const char *name, command_class theclass, const char *doc,
+   cmd_list_element **list, const char *rename_existing_to = nullptr,
+   cmd_list_element **rename_existing_to_list = nullptr);
 
 /* Add an element with a suppress notification to the LIST of commands.  */
 
@@ -341,13 +350,11 @@ extern cmd_list_element *add_alias_cmd (const char *name,
    command list.  PREFIXLIST should be the address of the variable
    containing that list.  */
 
-extern cmd_list_element *add_prefix_cmd (const char *name,
-					 command_class theclass,
-					 cmd_const_cfunc_ftype *fun,
-					 const char *doc,
-					 cmd_list_element **prefixlist,
-					 int allow_unknown,
-					 cmd_list_element **list);
+extern cmd_list_element *add_prefix_cmd
+  (const char *name, command_class theclass, cmd_const_cfunc_ftype *fun,
+   const char *doc, cmd_list_element **prefixlist, int allow_unknown,
+   cmd_list_element **list, const char *rename_existing_to = nullptr,
+   cmd_list_element **rename_existing_to_list = nullptr);
 
 /* Like add_prefix_cmd, but sets the callback to a function that
    simply calls help_list.  */
@@ -385,6 +392,19 @@ extern void set_cmd_sfunc (cmd_list_element *cmd,
 
 extern void set_cmd_completer (cmd_list_element *, completer_ftype *);
 
+/* Rename the command called OLD_NAME in *OLD_LIST to NEW_NAME in
+   *NEW_LIST.
+
+   Return the renamed command or, if no such command exists, NULLPTR.  */
+
+extern cmd_list_element *rename_cmd (const char *old_name,
+				     cmd_list_element **old_list,
+				     const char *new_name,
+				     cmd_list_element **new_list);
+
+/* Placeholder in the command data structures for user-defined commands.  */
+extern void user_defined_command (const char *ignore, int from_tty);
+
 /* Set the completer_handle_brkchars callback.  */
 
 extern void set_cmd_completer_handle_brkchars
diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index 5841f545882..c12b03ec71a 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -30,6 +30,7 @@
 #include "cli/cli-decode.h"
 #include "cli/cli-script.h"
 #include "cli/cli-style.h"
+#include "cli/cli-utils.h"
 
 #include "extension.h"
 #include "interps.h"
@@ -49,7 +50,8 @@ recurse_read_control_structure
      gdb::function_view<void (const char *)> validator);
 
 static void do_define_command (const char *comname, int from_tty,
-			       const counted_command_line *commands);
+			       const counted_command_line *commands,
+			       const char *rename_existing_to = nullptr);
 
 static void do_document_command (const char *comname, int from_tty,
                                  const counted_command_line *commands);
@@ -1368,84 +1370,157 @@ validate_comname (const char **comname)
   return list;
 }
 
-/* This is just a placeholder in the command data structures.  */
+/* Ask the user if they really want to redefine the command called
+   CMD->name, thus overwriting CMD.
+
+   Throws an error if the users says no.  */
+
 static void
-user_defined_command (const char *ignore, int from_tty)
+check_command_redefinition (cmd_list_element *cmd)
 {
+  int q;
+
+  if (cmd->theclass == class_user || cmd->theclass == class_alias)
+    {
+      /* If C is a prefix command that was previously defined,
+	 tell the user its subcommands will be kept, and ask
+	 if ok to redefine the command.  */
+      if (cmd->prefixlist != nullptr)
+	q = (cmd->user_commands.get () == nullptr
+	     || query (_("Keeping subcommands of prefix command \"%s\".\n"
+			 "Redefine command \"%s\"? "), cmd->name, cmd->name));
+      else
+	q = query (_("Redefine command \"%s\"? "), cmd->name);
+    }
+  else
+    q = query (_("Really redefine built-in command \"%s\"? "), cmd->name);
+  if (!q)
+    error (_("Command \"%s\" not redefined."), cmd->name);
+}
+
+/* Whether a command is a hook into another command and which type of hook
+   it is.  */
+
+enum cmd_hook_type
+{
+  /* Not a hook.  */
+  CMD_NO_HOOK = 0,
+  /* Hook executed before the associated command.  */
+  CMD_PRE_HOOK,
+  /* Hook executed after the associated command.  */
+  CMD_POST_HOOK
+};
+
+/* Given a command name NAME, returns an element of CMD_HOOK_TYPE
+   identifying whether NAME is a hook name and which type of hook it is.
+
+   If HOOKEE_NAME is not null, it's set to point to the position in NAME
+   identifying the name of the command hooked by NAME.  */
+
+static cmd_hook_type
+get_hook_type (const char *name, const char **hookee_name = nullptr)
+{
+  static const char hook_pre_prefix[] = "hook-";
+  static const char hook_post_prefix[] = "hookpost-";
+  static const size_t hook_pre_prefix_len = sizeof hook_pre_prefix - 1;
+  static const size_t hook_post_prefix_len = sizeof hook_post_prefix - 1;
+
+  cmd_hook_type type;
+  const char *hookee_name_tmp;
+
+  if (strncmp (name, hook_pre_prefix, hook_pre_prefix_len) == 0)
+    {
+      hookee_name_tmp = name + hook_pre_prefix_len;
+      type = CMD_PRE_HOOK;
+    }
+  else if (strncmp (name, hook_post_prefix, hook_post_prefix_len) == 0)
+    {
+      hookee_name_tmp = name + hook_post_prefix_len;
+      type = CMD_POST_HOOK;
+    }
+  else
+    {
+      hookee_name_tmp = nullptr;
+      type = CMD_NO_HOOK;
+    }
+
+  if (hookee_name != nullptr)
+    *hookee_name = hookee_name_tmp;
+
+  return type;
 }
 
 /* Define a user-defined command.  If COMMANDS is NULL, then this is a
    top-level call and the commands will be read using
    read_command_lines.  Otherwise, it is a "define" command in an
    existing command and the commands are provided.  In the
-   non-top-level case, various prompts and warnings are disabled.  */
+   non-top-level case, various prompts and warnings are disabled.
+
+   If RENAME_EXISTING_TO is non-null, then the existing command called
+   COMNAME will be renamed to RENAME_EXISTING_TO instead of being
+   overwritten.  */
 
 static void
 do_define_command (const char *comname, int from_tty,
-		   const counted_command_line *commands)
+		   const counted_command_line *commands,
+		   const char *rename_existing_to)
 {
-  enum cmd_hook_type
-    {
-      CMD_NO_HOOK = 0,
-      CMD_PRE_HOOK,
-      CMD_POST_HOOK
-    };
-  struct cmd_list_element *c, *newc, *hookc = 0, **list;
-  const char *comfull;
-  int  hook_type      = CMD_NO_HOOK;
-  int  hook_name_size = 0;
-   
-#define	HOOK_STRING	"hook-"
-#define	HOOK_LEN 5
-#define HOOK_POST_STRING "hookpost-"
-#define HOOK_POST_LEN    9
+  cmd_list_element *c, *newc, *hookc = 0, **list;
+  cmd_list_element *rename_existing_to_cmd = nullptr;
+  cmd_list_element **rename_existing_to_list;
+  const char *comfull, *rename_existing_to_full;
+  int hook_type;
+  const char *hookee_name = nullptr;
 
   comfull = comname;
   list = validate_comname (&comname);
 
+  rename_existing_to_full = rename_existing_to;
+  if (rename_existing_to != nullptr)
+    rename_existing_to_list = validate_comname (&rename_existing_to);
+  else
+    rename_existing_to_list = nullptr; /* Not renaming.  */
+
+  if (rename_existing_to_full != nullptr &&
+      strcmp (rename_existing_to_full, comfull) == 0)
+    error (_("Cannot rename command \"%s\" as the new name is identical "
+	     "to the existing one."),
+	   comfull);
+
   c = lookup_cmd_exact (comname, *list);
+  if (rename_existing_to_list != nullptr)
+      rename_existing_to_cmd = lookup_cmd_exact (rename_existing_to,
+                                                 *rename_existing_to_list);
 
-  if (c && commands == nullptr)
+  if (commands == nullptr)
     {
-      int q;
+      /* Check whether the user is trying to redefine an existing command.  */
+      if (rename_existing_to_cmd != nullptr)
+	check_command_redefinition (rename_existing_to_cmd);
+      else if (c != nullptr && rename_existing_to == nullptr)
+	check_command_redefinition (c);
+    }
 
-      if (c->theclass == class_user || c->theclass == class_alias)
-	{
-	  /* if C is a prefix command that was previously defined,
-	     tell the user its subcommands will be kept, and ask
-	     if ok to redefine the command.  */
-	  if (c->prefixlist != nullptr)
-	    q = (c->user_commands.get () == nullptr
-		 || query (_("Keeping subcommands of prefix command \"%s\".\n"
-			     "Redefine command \"%s\"? "), c->name, c->name));
-	  else
-	    q = query (_("Redefine command \"%s\"? "), c->name);
-	}
-      else
-	q = query (_("Really redefine built-in command \"%s\"? "), c->name);
-      if (!q)
-	error (_("Command \"%s\" not redefined."), c->name);
+  /* Checks on the validity of renames.  */
+  if (rename_existing_to != nullptr)
+    {
+      if (c == nullptr)
+	error (_("Command \"%s\" does not exist, so it cannot "
+		 "be renamed to \"%s\"."),
+		 comfull, rename_existing_to_full);
+
+      if (get_hook_type (rename_existing_to) != CMD_NO_HOOK)
+	error (_("Cannot define hooks by renaming commands."));
     }
 
   /* If this new command is a hook, then mark the command which it
      is hooking.  Note that we allow hooking `help' commands, so that
      we can hook the `stop' pseudo-command.  */
-
-  if (!strncmp (comname, HOOK_STRING, HOOK_LEN))
-    {
-       hook_type      = CMD_PRE_HOOK;
-       hook_name_size = HOOK_LEN;
-    }
-  else if (!strncmp (comname, HOOK_POST_STRING, HOOK_POST_LEN))
-    {
-      hook_type      = CMD_POST_HOOK;
-      hook_name_size = HOOK_POST_LEN;
-    }
-
+  hook_type = get_hook_type (comname, &hookee_name);
   if (hook_type != CMD_NO_HOOK)
     {
       /* Look up cmd it hooks.  */
-      hookc = lookup_cmd_exact (comname + hook_name_size, *list,
+      hookc = lookup_cmd_exact (hookee_name, *list,
 				/* ignore_help_classes = */ false);
       if (!hookc && commands == nullptr)
 	{
@@ -1462,6 +1537,8 @@ do_define_command (const char *comname, int from_tty,
 	       comfull);
     }
 
+  /* ADD_CMD takes ownership of the command name, but not of
+     RENAME_EXISTING_TO.  */
   comname = xstrdup (comname);
 
   counted_command_line cmds;
@@ -1480,7 +1557,8 @@ do_define_command (const char *comname, int from_tty,
 
     newc = add_cmd (comname, class_user, user_defined_command,
 		    (c != nullptr && c->theclass == class_user)
-		    ? c->doc : xstrdup ("User-defined."), list);
+		    ? c->doc : xstrdup ("User-defined."), list,
+                    rename_existing_to, rename_existing_to_list);
     newc->user_commands = std::move (cmds);
 
     /* If we define or re-define a command that was previously defined
@@ -1516,10 +1594,118 @@ do_define_command (const char *comname, int from_tty,
     }
 }
 
+/* The options for the "define" command.  */
+
+struct define_cmd_opts
+{
+  /* For "-rename-existing-to".  */
+  char *rename_existing_to = nullptr;
+
+  ~define_cmd_opts ()
+  {
+    xfree (rename_existing_to);
+  }
+};
+
+static const gdb::option::option_def define_cmd_option_defs[] = {
+
+  gdb::option::string_option_def<define_cmd_opts> {
+    "rename-existing-to",
+    [] (define_cmd_opts *opts) { return &opts->rename_existing_to; },
+    nullptr,
+    N_("Rename the existing command called NAME to the specified name.\n\
+This is useful to allow the new implementation of a command to invoke\n\
+the original implementation.  Existing subcommands, hooks and aliases\n\
+for the original command are moved to the newly defined one."),
+  },
+
+};
+
+/* Create an option_def_group for the "define" command's options, with
+   OPTS as context.  */
+
+static inline gdb::option::option_def_group
+make_define_cmd_options_def_group (define_cmd_opts *opts)
+{
+  return {{define_cmd_option_defs}, opts};
+}
+
+/* Implementation of the "define" command.  */
+
+static void
+define_command (const char *arg, int from_tty)
+{
+  define_cmd_opts opts;
+
+  auto grp = make_define_cmd_options_def_group (&opts);
+  gdb::option::process_options
+    (&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp);
+
+  do_define_command (arg, from_tty, nullptr, opts.rename_existing_to);
+}
+
+/* Rename command OLD_NAME to NEW_NAME.  */
+
+static void
+do_rename_command (const char *old_name, const char *new_name, int from_tty)
+{
+  const char *old_name_full = old_name;
+  cmd_list_element **old_list = validate_comname (&old_name);
+
+  const char *new_name_full = new_name;
+  cmd_list_element **new_list = validate_comname (&new_name);
+
+  if (strcmp (old_name_full, new_name_full) == 0)
+    error (_("Cannot rename command \"%s\" as the new name is identical "
+	     "to the existing one."),
+	   old_name_full);
+
+  if (get_hook_type (old_name) != CMD_NO_HOOK)
+    error (_("Cannot rename hooks."));
+
+  if (get_hook_type (new_name) != CMD_NO_HOOK)
+    error (_("Cannot define hooks by renaming commands."));
+
+  /* Are we trying to redefine a command already called NEW_NAME_FULL?  */
+  cmd_list_element *existing_cmd_with_new_name = lookup_cmd_exact (new_name,
+								   *new_list);
+  if (existing_cmd_with_new_name != nullptr)
+    check_command_redefinition (existing_cmd_with_new_name);
+
+  cmd_list_element *cmd = rename_cmd (old_name, old_list,
+				      new_name, new_list);
+  if (cmd == nullptr)
+    error (_("Command \"%s\" does not exist, so it cannot be renamed "
+	     "to \"%s\"."),
+	   old_name_full, new_name_full);
+}
+
+/* Implementation of the "rename" command.  */
+
 static void
-define_command (const char *comname, int from_tty)
+rename_command (const char *arg, int from_tty)
 {
-  do_define_command (comname, from_tty, nullptr);
+  if (arg == nullptr)
+    arg = "";
+
+  std::string old_name = extract_string_maybe_quoted (&arg);
+  std::string new_name = extract_string_maybe_quoted (&arg);
+
+  arg = skip_spaces (arg);
+
+  if (*arg != '\0')
+    {
+      error (_("Too many arguments: %s\n"
+	       "If you are trying to use a command name with spaces, "
+	       "use quotes."),
+	     arg);
+    }
+  else if (old_name.empty ())
+    error (_("Arguments required (name of command to rename and new name)."));
+  else if (new_name.empty ())
+    error (_("Argument required (new name for \"%s\")."), old_name.c_str ());
+
+  do_rename_command (old_name.c_str (), new_name.c_str (), from_tty);
 }
 
 /* Document a user-defined command.  If COMMANDS is NULL, then this is a
@@ -1706,7 +1892,7 @@ _initialize_cli_script ()
 {
   struct cmd_list_element *c;
 
-  /* "document", "define" and "define-prefix" use command_completer,
+  /* "document", "define", "define-prefix" and "rename" use command_completer,
      as this helps the user to either type the command name and/or
      its prefixes.  */
   document_cmd_element = add_com ("document", class_support, document_command,
@@ -1715,15 +1901,27 @@ Document a user-defined command.\n\
 Give command name as argument.  Give documentation on following lines.\n\
 End with a line of just \"end\"."));
   set_cmd_completer (document_cmd_element, command_completer);
-  define_cmd_element = add_com ("define", class_support, define_command, _("\
+
+  const auto define_opts = make_define_cmd_options_def_group (nullptr);
+  static const std::string define_help
+    = gdb::option::build_help (_("\
 Define a new command name.  Command name is argument.\n\
+Usage: define [-rename-existing-to NEW_NAME_FOR_EXISTING_COMMAND] NAME\n\
+\n\
+Options:\n\
+%OPTIONS%\n\
+\n\
 Definition appears on following lines, one command per line.\n\
 End with a line of just \"end\".\n\
 Use the \"document\" command to give documentation for the new command.\n\
 Commands defined in this way may accept an unlimited number of arguments\n\
 accessed via $arg0 .. $argN.  $argc tells how many arguments have\n\
-been passed."));
+been passed."),
+			       define_opts);
+  define_cmd_element = add_com ("define", class_support, define_command,
+				define_help.c_str ());
   set_cmd_completer (define_cmd_element, command_completer);
+
   c = add_com ("define-prefix", class_support, define_prefix_command,
 	   _("\
 Define or mark a command as a user-defined prefix command.\n\
@@ -1732,6 +1930,20 @@ other user defined commands.\n\
 If the command already exists, it is changed to a prefix command."));
   set_cmd_completer (c, command_completer);
 
+  c = add_com ("rename", class_support, rename_command, _("\
+Rename a command.\n\
+Usage: rename OLD_NAME NEW_NAME\n\
+\n\
+If a command called NEW_NAME exists, it's overwritten.  Any subcommands\n\
+of NEW_NAME are not modified.\n\
+\n\
+If OLD_NAME has subcommands, they are preserved and OLD_NAME becomes a\n\
+prefix-only command, see the \"define-prefix\" command.\n\
+\n\
+Aliases and hooks are preserved and, after the rename, will point to\n\
+NEW_NAME."));
+  set_cmd_completer (c, command_completer);
+
   while_cmd_element = add_com ("while", class_support, while_command, _("\
 Execute nested commands WHILE the conditional expression is non zero.\n\
 The conditional expression must follow the word `while' and must in turn be\n\
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 56f37eb2288..89d950b48c7 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -27239,18 +27239,41 @@ define adder
 end
 @end smallexample
 
+It's possible to replace existing commands and build on top of them, by
+using the @code{-rename-existing-to} option:
+
+@smallexample
+define -rename-existing-to original-run run
+  echo Will now run a program!\n
+  # Invoke the original "run" command which was renamed to
+  # "original-run".
+  original-run
+end
+@end smallexample
+
 @table @code
 
 @kindex define
-@item define @var{commandname}
-Define a command named @var{commandname}.  If there is already a command
-by that name, you are asked to confirm that you want to redefine it.
+@item define @r{[}-rename-existing-to @var{new-name}@r{]} @var{commandname}
+Define a command named @var{commandname}.
 The argument @var{commandname} may be a bare command name consisting of letters,
 numbers, dashes, dots, and underscores.  It may also start with any
 predefined or user-defined prefix command.
 For example, @samp{define target my-target} creates
 a user-defined @samp{target my-target} command.
 
+By default, if there is already a command by that name, you are asked to
+confirm that you want to redefine it.
+If @code{-rename-existing-to} is specified, the existing command called
+@var{commandname} is renamed to @var{new-name} first.  All subcommands,
+aliases (@pxref{Aliases}) and hooks (@pxref{Hooks}) for @var{commandname}
+will refer to the newly defined @var{commandname}, not to the renamed
+command.  This is different from using the @code{rename} command
+(@pxref{rename command}) and @code{define} separately as, in that case,
+subcommands, aliases and hooks refer to the renamed command.
+If @var{new-name} contains spaces, it must be enclosed in double quotes,
+unlike @var{commandname} which should not be enclosed in quotes.
+
 The definition of the command is made up of other @value{GDBN} command lines,
 which are given following the @code{define} command.  The end of these
 commands is marked by a line containing @code{end}.
@@ -27309,6 +27332,32 @@ command def
 (gdb)
 @end example
 
+@kindex rename
+@cindex rename command
+@anchor{rename command}
+@item rename @var{old-name} @var{new-name}
+Rename the command called @var{old-name} to @var{new-name}.
+All subcommands, aliases (@pxref{Aliases}) and hooks (@pxref{Hooks}) for
+@var{old-name} will refer to @var{new-name} after the rename.
+If a command name contains spaces, it must be enclosed in double quotes.
+
+Example:
+@example
+(gdb) define-prefix abc
+(gdb) define abc def
+Type commands for definition of "abc def".
+End with a line saying just "end".
+>echo this is the command\n
+>end
+(gdb) abc def
+this is the command
+(gdb) rename "abc def" renamed-command
+(gdb) abc def
+Undefined abc command: "def".  Try "help abc".
+(gdb) renamed-command
+this is the command
+@end example
+
 @kindex dont-repeat
 @cindex don't repeat command
 @item dont-repeat
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index f1ca6f7ed12..f0b3a8f3a1e 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -3739,7 +3739,7 @@ You can implement new @value{GDBN} CLI commands in Python.  A CLI
 command is implemented using an instance of the @code{gdb.Command}
 class, most commonly using a subclass.
 
-@defun Command.__init__ (name, @var{command_class} @r{[}, @var{completer_class} @r{[}, @var{prefix}@r{]]})
+@defun Command.__init__ (name @r{[}, @var{command_class}@r{]} @r{[}, @var{completer_class}@r{]} @r{[}, @var{prefix}@r{]} @r{[}, @var{rename_existing_to}@r{]})
 The object initializer for @code{Command} registers the new command
 with @value{GDBN}.  This initializer is normally invoked from the
 subclass' own @code{__init__} method.
@@ -3753,7 +3753,9 @@ There is no support for multi-line commands.
 
 @var{command_class} should be one of the @samp{COMMAND_} constants
 defined below.  This argument tells @value{GDBN} how to categorize the
-new command in the help system.
+new command in the help system.  This argument is required unless
+@var{rename_existing_to} is specified, in which case it defaults to the
+value used by the existing command being renamed.
 
 @var{completer_class} is an optional argument.  If given, it should be
 one of the @samp{COMPLETE_} constants defined below.  This argument
@@ -3764,7 +3766,19 @@ error will occur when completion is attempted.
 
 @var{prefix} is an optional argument.  If @code{True}, then the new
 command is a prefix command; sub-commands of this command may be
-registered.
+registered.  If @var{rename_existing_to} is set, then @var{prefix} must
+not be set as whether the command is a prefix command or not is inherited
+from the existing command being renamed.
+
+@var{rename_existing_to} is an optional argument.  If set, then the
+existing command called @var{name} is renamed to @var{rename_existing_to}.
+Existing subcommands, aliases and hooks are preserved and will refer to
+the newly defined command.  Additionally, then @var{prefix} must be unset
+as whether the new command is a prefix or not will depend on whether the
+original command was a prefix command or not.
+When renaming an existing command, @var{command_class} and
+@var{completer_class}, if unspecified, are copied from the existing
+command.
 
 The help text for the new command is taken from the Python
 documentation string for the command's class, if there is one.  If no
@@ -4013,6 +4027,24 @@ registration of the command with @value{GDBN}.  Depending on how the
 Python code is read into @value{GDBN}, you may need to import the
 @code{gdb} module explicitly.
 
+The following code snippet shows how a CLI command building on top of an
+existing command can be implemented:
+
+@smallexample
+class MyQuit (gdb.Command):
+  """Print a message before executing the "quit" command."""
+
+  def __init__ (self):
+    super (MyQuit, self).__init__ ("quit",
+                                   rename_existing_to="original-quit")
+
+  def invoke (self, arg, from_tty):
+    print ("Will now quit!")
+    gdb.execute ("original-quit %s" % arg, from_tty=from_tty)
+
+MyQuit ()
+@end smallexample
+
 @node Parameters In Python
 @subsubsection Parameters In Python
 
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index 6f7794cdf53..45dddeee650 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -429,14 +429,20 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 {
   cmdpy_object *obj = (cmdpy_object *) self;
   const char *name;
-  int cmdtype;
-  int completetype = -1;
-  char *docstring = NULL;
-  struct cmd_list_element **cmd_list;
+  long cmdtype;
+  long completetype = -1;
+  char *docstring = nullptr;
+  cmd_list_element **cmd_list;
   static const char *keywords[] = { "name", "command_class", "completer_class",
-				    "prefix", NULL };
-  PyObject *is_prefix_obj = NULL;
+				    "prefix", "rename_existing_to",
+				    nullptr };
+  PyObject *cmdtype_obj = nullptr;
+  PyObject *completetype_obj = nullptr;
+  PyObject *is_prefix_obj = nullptr;
   bool is_prefix = false;
+  const char *rename_existing_to = nullptr;
+  cmd_list_element *renamed_cmd = nullptr;
+  cmd_list_element **rename_existing_to_cmd_list = nullptr;
 
   if (obj->command)
     {
@@ -447,37 +453,107 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
       return -1;
     }
 
-  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "si|iO",
-					keywords, &name, &cmdtype,
-					&completetype, &is_prefix_obj))
+  /* The second argument (command_class) is marked as optional because
+     it should not be specified if RENAME_EXISTING_TO is specified.
+     Otherwise it's required.  */
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!Os",
+					keywords,
+					&name,
+					&PyInt_Type, &cmdtype_obj,
+					&PyInt_Type, &completetype_obj,
+					&is_prefix_obj,
+					&rename_existing_to))
     return -1;
 
-  if (cmdtype != no_class && cmdtype != class_run
-      && cmdtype != class_vars && cmdtype != class_stack
-      && cmdtype != class_files && cmdtype != class_support
-      && cmdtype != class_info && cmdtype != class_breakpoint
-      && cmdtype != class_trace && cmdtype != class_obscure
-      && cmdtype != class_maintenance && cmdtype != class_user
-      && cmdtype != class_tui)
-    {
-      PyErr_Format (PyExc_RuntimeError, _("Invalid command class argument."));
-      return -1;
-    }
-
-  if (completetype < -1 || completetype >= (int) N_COMPLETERS)
-    {
-      PyErr_Format (PyExc_RuntimeError,
-		    _("Invalid completion type argument."));
-      return -1;
-    }
-
   gdb::unique_xmalloc_ptr<char> cmd_name
     = gdbpy_parse_command_name (name, &cmd_list, &cmdlist);
   if (cmd_name == nullptr)
     return -1;
 
-  if (is_prefix_obj != NULL)
+  /* Deal with rename_existing_to before other arguments as it affects how
+     some of them are treated.  */
+  gdb::unique_xmalloc_ptr<char> rename_existing_to_cmd_name = nullptr;
+  if (rename_existing_to != nullptr)
     {
+      renamed_cmd = lookup_cmd_exact (cmd_name.get (), *cmd_list);
+      if (renamed_cmd == nullptr)
+	{
+	  PyErr_Format (PyExc_RuntimeError,
+			_("Command \"%s\" does not exist, so it cannot "
+			  "be renamed to \"%s\"."),
+			name, rename_existing_to);
+	  return -1;
+	}
+
+      rename_existing_to_cmd_name =
+	gdbpy_parse_command_name (rename_existing_to,
+				  &rename_existing_to_cmd_list,
+				  &cmdlist);
+      if (rename_existing_to_cmd_name == nullptr)
+	return -1;
+    }
+
+  if (cmdtype_obj == nullptr)
+    {
+      /* COMMAND_CLASS is optional only when rename_existing_to is
+	 specified.  This error matches the phrasing of the error the
+	 interpreter would raise if COMMAND_CLASS was not marked as
+	 optional in gdb_PyArg_ParseTupleAndKeywords.  */
+      if (renamed_cmd == nullptr)
+	{
+	  PyErr_Format (PyExc_TypeError,
+			_("function missing required argument "
+			  "'command_class' (pos 2)"));
+	  return -1;
+	}
+
+      cmdtype = renamed_cmd->theclass;
+    }
+  else
+    {
+      cmdtype = PyInt_AsLong (cmdtype_obj);
+      if (PyErr_Occurred ())
+	return -1;
+      if (cmdtype != no_class && cmdtype != class_run
+	  && cmdtype != class_vars && cmdtype != class_stack
+	  && cmdtype != class_files && cmdtype != class_support
+	  && cmdtype != class_info && cmdtype != class_breakpoint
+	  && cmdtype != class_trace && cmdtype != class_obscure
+	  && cmdtype != class_maintenance && cmdtype != class_user
+	  && cmdtype != class_tui)
+	{
+	  PyErr_Format (PyExc_RuntimeError,
+			_("Invalid command class argument."));
+	  return -1;
+	}
+    }
+
+  if (completetype_obj != nullptr)
+    {
+      completetype = PyInt_AsLong (completetype_obj);
+      if (PyErr_Occurred ())
+	return -1;
+    }
+
+  if (completetype < -1 || completetype >= (int) N_COMPLETERS)
+    {
+      PyErr_Format (PyExc_RuntimeError,
+		    _("Invalid completion type argument."));
+      return -1;
+    }
+
+  if (is_prefix_obj != nullptr)
+    {
+      if (renamed_cmd != nullptr)
+	{
+	  PyErr_Format
+	    (PyExc_TypeError,
+	     _("Cannot specify argument 'prefix' with 'rename_existing_to'.  "
+	       "Whether the command is a prefix command depends on the "
+	       "renamed command."));
+	  return -1;
+	}
+
       int cmp = PyObject_IsTrue (is_prefix_obj);
       if (cmp < 0)
 	return -1;
@@ -496,7 +572,7 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 	    return -1;
 	}
     }
-  if (! docstring)
+  if (docstring == nullptr)
     docstring = xstrdup (_("This command is not documented."));
 
   gdbpy_ref<> self_ref = gdbpy_ref<>::new_reference (self);
@@ -512,14 +588,17 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 	  /* If we have our own "invoke" method, then allow unknown
 	     sub-commands.  */
 	  allow_unknown = PyObject_HasAttr (self, invoke_cst);
-	  cmd = add_prefix_cmd (cmd_name.get (),
-				(enum command_class) cmdtype,
-				NULL, docstring, &obj->sub_list,
-				allow_unknown, cmd_list);
+	  cmd = add_prefix_cmd (cmd_name.get (), (command_class) cmdtype,
+				nullptr, docstring, &obj->sub_list,
+				allow_unknown, cmd_list,
+				rename_existing_to_cmd_name.get (),
+				rename_existing_to_cmd_list);
 	}
       else
-	cmd = add_cmd (cmd_name.get (), (enum command_class) cmdtype,
-		       docstring, cmd_list);
+	cmd = add_cmd (cmd_name.get (), (command_class) cmdtype,
+		       docstring, cmd_list,
+		       rename_existing_to_cmd_name.get (),
+		       rename_existing_to_cmd_list);
 
       /* If successful, the above takes ownership of the name, since we set
          name_allocated, so release it.  */
@@ -533,11 +612,25 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 
       obj->command = cmd;
       set_cmd_context (cmd, self_ref.release ());
-      set_cmd_completer (cmd, ((completetype == -1) ? cmdpy_completer
-			       : completers[completetype].completer));
-      if (completetype == -1)
-	set_cmd_completer_handle_brkchars (cmd,
-					   cmdpy_completer_handle_brkchars);
+
+      if (completetype_obj == nullptr && renamed_cmd != nullptr)
+	{
+	    /* No explicit completer was passed and the existing command
+	       was renamed, so use the command completer information from
+	       the original command.  */
+	    set_cmd_completer (cmd, renamed_cmd->completer);
+	    set_cmd_completer_handle_brkchars
+	      (cmd, renamed_cmd->completer_handle_brkchars);
+	}
+      else
+	{
+	  set_cmd_completer (cmd, ((completetype == -1) ? cmdpy_completer
+				   : completers[completetype].completer));
+	  if (completetype == -1)
+	    set_cmd_completer_handle_brkchars (cmd,
+					       cmdpy_completer_handle_brkchars);
+
+	}
     }
   catch (const gdb_exception &except)
     {
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 690d2fb43c0..2700cf29b0d 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -95,6 +95,7 @@
 #define Py_TPFLAGS_HAVE_ITER 0
 #define Py_TPFLAGS_CHECKTYPES 0
 
+#define PyInt_Type PyLong_Type
 #define PyInt_Check PyLong_Check
 #define PyInt_AsLong PyLong_AsLong
 #define PyInt_AsSsize_t PyLong_AsSsize_t
diff --git a/gdb/testsuite/gdb.base/command-renaming.exp b/gdb/testsuite/gdb.base/command-renaming.exp
new file mode 100644
index 00000000000..238a735dfb0
--- /dev/null
+++ b/gdb/testsuite/gdb.base/command-renaming.exp
@@ -0,0 +1,571 @@
+# Copyright (C) 2009-2021 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests renaming of commands.
+
+standard_testfile
+
+clean_restart
+
+proc prepare_gdb {} {
+  gdb_exit
+  gdb_start
+}
+
+# Test basic usage of the "rename" command.
+#
+proc test_rename_ok {} {
+  prepare_gdb
+
+  with_test_prefix "basic" {
+    gdb_test_no_output "rename run my-run"
+    # The old "run" command doesn't exist any more.
+    gdb_test_exact "run" \
+      "Undefined command: \"run\".  Try \"help\"."
+    # The new one works.
+    gdb_test "my-run" ".*No executable file specified.*"
+    # The "r" alias still works.
+    gdb_test "r" ".*No executable file specified.*"
+  }
+
+  with_test_prefix "hooks" {
+    gdb_define_cmd "original" {
+      "echo Original\\n"
+    }
+    gdb_test_no_output "alias alias-original = original"
+    gdb_define_cmd "hook-original" {
+      "echo Before original\\n"
+    }
+    gdb_define_cmd "hookpost-original" {
+      "echo After original\\n"
+    }
+
+    gdb_test_no_output "rename original renamed"
+    # The old "original" command doesn't exist any more.
+    gdb_test_exact "original" \
+      "Undefined command: \"original\".  Try \"help\"."
+    # The new one works, including hooks.
+    gdb_test_exact "renamed" \
+      "Before original\nOriginal\nAfter original"
+    # The "alias-original" alias still works, including hooks.
+    gdb_test_exact "alias-original" \
+      "Before original\nOriginal\nAfter original"
+  }
+}
+
+# Test that "rename" deals correctly with error conditions.
+
+proc test_rename_errors {} {
+  prepare_gdb
+
+  with_test_prefix "non-existing command" {
+    gdb_test_exact "rename nope renamed" \
+      "Command \"nope\" does not exist, so it cannot be renamed to \"renamed\"."
+  }
+
+  with_test_prefix "same old and new name" {
+    gdb_test_exact "rename run run" \
+      "Cannot rename command \"run\" as the new name is identical to the existing one."
+  }
+
+  with_test_prefix "rename to hook" {
+    gdb_define_cmd "my-cmd" {
+      "echo Command\\n"
+    }
+
+    with_test_prefix "pre" {
+      gdb_test_exact "rename my-cmd hook-echo" \
+	"Cannot define hooks by renaming commands."
+    }
+
+    with_test_prefix "post" {
+      gdb_test_exact "rename my-cmd hookpost-echo" \
+	"Cannot define hooks by renaming commands."
+    }
+  }
+}
+
+# Test that "rename" works when the prefix of the renamed command changes.
+
+proc test_rename_across_prefixes {} {
+  prepare_gdb
+
+  with_test_prefix "rename with prefixes" {
+    gdb_test_no_output "define-prefix prefix1"
+    gdb_test_no_output "define-prefix prefix2"
+    gdb_test_no_output "define-prefix prefix2 sub-prefix"
+
+    gdb_define_cmd "prefix1 foo" {
+      "echo Foo\\n"
+    }
+
+    gdb_test_no_output "rename 'prefix1 foo' 'prefix2 sub-prefix renamed-foo'"
+
+    gdb_test_exact "prefix1 foo" \
+      "Undefined prefix1 command: \"foo\".  Try \"help prefix1\"."
+    gdb_test_exact "prefix2 sub-prefix renamed-foo" "Foo"
+  }
+}
+
+# Test that renaming of prefix commands works.
+
+proc test_rename_prefixes {} {
+  prepare_gdb
+  gdb_test_no_output "set confirm off"
+
+  with_test_prefix "rename of prefixes" {
+    # Define two prefixes, each one with a subcommand.
+    foreach letter {"A" "B"} {
+      gdb_test_no_output "define-prefix prefix-${letter}"
+      gdb_define_cmd "prefix-${letter}" [subst {
+	"echo Original prefix ${letter}\\\\n"
+      }]
+      gdb_define_cmd "prefix-${letter} sub-${letter}" [subst {
+	"echo Subcommand ${letter} of prefix ${letter}\\\\n"
+      }]
+    }
+
+    gdb_test_no_output "rename prefix-A prefix-B"
+
+    # The subcommands still work as before, the rename didn't affect them.
+    gdb_test_exact "prefix-A sub-A" "Subcommand A of prefix A"
+    gdb_test_exact "prefix-B sub-B" "Subcommand B of prefix B"
+    # "prefix-B"'s implementation is what used to be "prefix-A"'s
+    # implementation.
+    gdb_test_exact "prefix-B" "Original prefix A"
+    # "prefix-A" is just a prefix command without any code associated with
+    # it, and it exists only for the sake of providing subcommands.
+    gdb_test "prefix-A" \
+      ".*\"prefix-A\" must be followed by the name of a subcommand.*"
+  }
+}
+
+# Test that "rename" works when it leads to a command being overwritten by
+# the renamed one.
+#
+# PREFIX, if not empty, if the prefix for the renamed and overwritten
+# commands.
+
+proc test_rename_redefine {prefix} {
+  global gdb_prompt
+
+  prepare_gdb
+
+  with_test_prefix "overwriting existing command by renaming, prefix=\"${prefix}\"" {
+    if { $prefix == "" } {
+      set foo_cmd "foo"
+      set bar_cmd "bar"
+    } else {
+      gdb_test_no_output "define-prefix ${prefix}"
+      set foo_cmd "${prefix} foo"
+      set bar_cmd "${prefix} bar"
+    }
+
+    # Define "foo".
+    gdb_define_cmd $foo_cmd {
+      "echo Original foo\\n"
+    }
+
+    # Define an alias to the original "foo" with the same prefix.
+    gdb_test_no_output "alias ${prefix} alias-to-original-foo-same-prefix = ${foo_cmd}"
+    # And another one but without a prefix.
+    gdb_test_no_output "alias alias-to-original-foo-no-prefix = ${foo_cmd}"
+
+    # Define hooks for the original "foo".
+    gdb_define_cmd "${prefix} hook-foo" {
+      "echo Pre-hook for original foo\\n"
+    }
+    gdb_define_cmd "${prefix} hookpost-foo" {
+      "echo Post-hook for original foo\\n"
+    }
+
+    # Define "bar".
+    gdb_define_cmd $bar_cmd {
+      "echo Original bar\\n"
+    }
+
+    # Rename "bar" to "foo", which deletes the old "foo".
+    send_gdb "rename '${bar_cmd}' '${foo_cmd}'\n"
+    gdb_expect {
+      -re "Redefine command \"foo\".*y or n. $"\
+	      {send_gdb "y\n"
+	       gdb_expect {
+		 -re "$gdb_prompt $"\
+		   {pass "rename user command"}
+		 timeout {fail "(timeout) rename user command"}
+	       }
+	     }
+      -re "$gdb_prompt $"\
+	      {fail "redefine user command"}
+      timeout {fail "(timeout) rename user command"}
+    }
+
+    # "foo" should be the new command (which used to be called "bar",
+    # hence the message).
+    gdb_test_exact $foo_cmd "Original bar"
+
+    # The aliases to the original "foo" should have been deleted.
+    gdb_test "${prefix} alias-to-original-foo-same-prefix" \
+      ".*Undefined.* command:.*"
+    gdb_test "alias-to-original-foo-no-prefix" \
+      ".*Undefined command:.*"
+
+    # The hooks for the original "foo" should have been deleted as well.
+    gdb_test "${prefix} hook-foo" \
+      ".*Undefined.* command:.*"
+    gdb_test "${prefix} hookpost-foo" \
+      ".*Undefined.* command:.*"
+  }
+}
+
+# Test that, when a command with hooks is renamed, the hooks get updated
+# as well.
+
+proc test_rename_hooks_updated {} {
+  prepare_gdb
+
+  with_test_prefix "hooks renamed if command renamed" {
+    # Define some hooks for "delete".
+    gdb_define_cmd "hook-delete" {
+      "echo Pre-hook\\n"
+    }
+    gdb_define_cmd "hookpost-delete" {
+      "echo Post-hook\\n"
+    }
+    # Define aliases for the hooks.
+    gdb_test_no_output "alias hd-pre = hook-delete"
+    gdb_test_no_output "alias hd-post = hookpost-delete"
+    # Rename "delete" to "my-prefix delete-cmd"
+    gdb_test_no_output "define-prefix my-prefix"
+    gdb_test_no_output "rename 'delete' 'my-prefix delete-cmd'"
+
+    # The renamed command must still work.
+    gdb_test_exact "my-prefix delete-cmd 9999" \
+      "Pre-hook\nNo breakpoint number 9999.\nPost-hook"
+
+    # Hooks must have been renamed as well.
+    gdb_test_exact "my-prefix hook-delete-cmd" "Pre-hook"
+    gdb_test_exact "my-prefix hookpost-delete-cmd" "Post-hook"
+    # And the aliases to the hooks must still work.
+    gdb_test_exact "hd-pre" "Pre-hook"
+    gdb_test_exact "hd-post" "Post-hook"
+    # The old names for the hooks don't work any more (as they were
+    # renamed).
+    gdb_test "hook-delete" "Undefined command.*"
+    gdb_test "hookpost-delete" "Undefined command.*"
+    # The help for the hooks and their aliases must show the correct
+    # updated names.
+    gdb_test "help my-prefix hook-delete-cmd" \
+      "my-prefix hook-delete-cmd, hd-pre.*"
+    gdb_test "help hd-pre" "my-prefix hook-delete-cmd, hd-pre.*"
+    gdb_test "help my-prefix hookpost-delete-cmd" \
+      "my-prefix hookpost-delete-cmd, hd-post.*"
+    gdb_test "help hd-post" "my-prefix hookpost-delete-cmd, hd-post.*"
+  }
+}
+
+# Helper function for defining a command called "new" and then redefining
+# it with "define -rename-existing-to old new".
+#
+# If CREATE_ALIAS is 1, then an "alias-to-new" alias to the original
+# command is created.
+#
+# If CREATE_PRE_HOOK is 1, then a hook for the original command is
+# created.
+#
+# CREATE_POST_HOOK behaves likes CREATE_PRE_HOOK except that it creates
+# a post-hook.
+
+proc define_with_rename {create_alias create_pre_hook create_post_hook} {
+  prepare_gdb
+
+  gdb_define_cmd "new" {
+    "echo Original\\n"
+  }
+
+  if {$create_alias} then {
+    gdb_test_no_output "alias alias-to-new = new"
+  }
+
+  if {$create_pre_hook} then {
+    gdb_define_cmd "hook-new" {
+      "echo Pre-hook\\n"
+    }
+  }
+
+  if {$create_post_hook} then {
+    gdb_define_cmd "hookpost-new" {
+      "echo Post-hook\\n"
+    }
+  }
+
+  gdb_define_cmd "-rename-existing-to old new" {
+    "echo Redefined: before\\n"
+    "old"
+    "echo Redefined: after\\n"
+  }
+}
+
+
+# Test that "define -rename-existing-to" works with and without aliases
+# and hooks.
+
+proc test_define_renaming_ok {} {
+  with_test_prefix "basic" {
+    define_with_rename 0 0 0
+    gdb_test_exact "new" \
+      "Redefined: before\nOriginal\nRedefined: after"
+    gdb_test_exact "old" "Original"
+  }
+
+  with_test_prefix "alias" {
+    define_with_rename 1 0 0
+    gdb_test_exact "new" \
+      "Redefined: before\nOriginal\nRedefined: after"
+    gdb_test_exact "alias-to-new" \
+      "Redefined: before\nOriginal\nRedefined: after"
+    gdb_test_exact "old" "Original"
+  }
+
+  with_test_prefix "pre-hook" {
+    define_with_rename 0 1 0
+    gdb_test_exact "new" \
+      "Pre-hook\nRedefined: before\nOriginal\nRedefined: after"
+    gdb_test_exact "old" "Original"
+  }
+
+  with_test_prefix "post-hook" {
+    define_with_rename 0 0 1
+    gdb_test_exact "new" \
+      "Redefined: before\nOriginal\nRedefined: after\nPost-hook"
+    gdb_test_exact "old" "Original"
+  }
+
+  with_test_prefix "alias, pre-hook and post-hook" {
+    define_with_rename 1 1 1
+    gdb_test_exact "new" \
+      "Pre-hook\nRedefined: before\nOriginal\nRedefined: after\nPost-hook"
+    gdb_test_exact "alias-to-new" \
+      "Pre-hook\nRedefined: before\nOriginal\nRedefined: after\nPost-hook"
+    gdb_test_exact "old" "Original"
+  }
+}
+
+# Test that renaming with "define -rename-existing-to" works for builtin
+# commands.
+
+proc test_define_renaming_builtin {} {
+  prepare_gdb
+  gdb_test_no_output "set confirm off"
+
+  with_test_prefix "rename builtin" {
+    gdb_define_cmd "-rename-existing-to original-run run" {
+      "echo Redefined run\\n"
+    }
+
+    gdb_test_exact "run" "Redefined run"
+
+    gdb_test "original-run" ".*No executable file specified.*"
+  }
+}
+
+# Test that "define -rename-existing-to" deals correctly with error
+# conditions.
+
+proc test_define_renaming_errors {} {
+  prepare_gdb
+
+  with_test_prefix "non-existing command" {
+    gdb_test_exact "define -rename-existing-to renamed nope" \
+      "Command \"nope\" does not exist, so it cannot be renamed to \"renamed\"."
+  }
+
+  with_test_prefix "same old and new name" {
+    gdb_test_exact "define -rename-existing-to run run" \
+      "Cannot rename command \"run\" as the new name is identical to the existing one."
+  }
+
+  with_test_prefix "rename to hook" {
+    gdb_define_cmd "my-cmd" {
+      "echo Command\\n"
+    }
+
+    with_test_prefix "pre" {
+      gdb_test_exact "define -rename-existing-to hook-echo my-cmd" \
+	"Cannot define hooks by renaming commands."
+    }
+
+    with_test_prefix "post" {
+      gdb_test_exact "define -rename-existing-to hookpost-echo my-cmd" \
+	"Cannot define hooks by renaming commands."
+    }
+  }
+}
+
+# Test that using "define -rename-existing-to" to rename to a different
+# prefix works.
+
+proc test_define_renaming_across_prefixes {} {
+  prepare_gdb
+
+  with_test_prefix "define -rename-existing-to prefixes" {
+    gdb_test_no_output "define-prefix prefix1"
+    gdb_test_no_output "define-prefix prefix2"
+    gdb_test_no_output "define-prefix prefix2 sub-prefix"
+
+    gdb_define_cmd "prefix1 foo" {
+      "echo First\\n"
+    }
+
+    gdb_define_cmd "-rename-existing-to 'prefix2 sub-prefix renamed-foo' prefix1 foo" {
+      "echo Second\\n"
+    }
+
+    gdb_test_exact "prefix2 sub-prefix renamed-foo" "First"
+    gdb_test_exact "prefix1 foo" "Second"
+  }
+}
+
+# Test that renaming prefixes with "define -rename-existing-to" works.
+
+proc test_define_renaming_prefixes {} {
+  prepare_gdb
+
+  with_test_prefix "define -rename-existing-to of prefixes" {
+    # Define a prefix with an implementation.
+    gdb_test_no_output "define-prefix prefix"
+    gdb_define_cmd "prefix" {
+      "echo Original prefix\\n"
+    }
+    # Define a subcommand.
+    gdb_define_cmd "prefix subcommand" {
+      "echo Subcommand\\n"
+    }
+
+    # Define a new implementation of "prefix" renaming the existing to
+    # "renamed-prefix".
+    gdb_define_cmd "-rename-existing-to renamed-prefix prefix" {
+      "echo New prefix\\n"
+    }
+
+    # "renamed-prefix" still works as a command.
+    gdb_test_exact "renamed-prefix" "Original prefix"
+    # The subcommand remains a subcommand of "prefix", not of its renamed
+    # implementation, so "renamed-prefix subcommand" doesn't exist (and
+    # never existed).
+    # This means that that "renamed-prefix subcommand" invokes the
+    # "renamed-prefix" command with "subcommand" as argument (i.e.
+    # subcommand is not a command), so the original implementation of
+    # what was "prefix" is invoked.
+    gdb_test_exact "renamed-prefix subcommand" "Original prefix"
+    # The newly defined prefix implementation works.
+    gdb_test_exact "prefix" "New prefix"
+    # And the subcommand works as well.
+    gdb_test_exact "prefix subcommand" "Subcommand"
+  }
+}
+
+# Test that "define -rename-existing-to" works when it leads to a command
+# being overwritten by the renamed one.
+#
+# PREFIX, if not empty, if the prefix for the renamed and overwritten
+# commands.
+
+proc test_define_renaming_redefine {prefix} {
+  global gdb_prompt
+
+  prepare_gdb
+
+  with_test_prefix "overwriting existing command with define -rename-existing-to, prefix=\"${prefix}\"" {
+    if { $prefix == "" } {
+      set foo_cmd "foo"
+      set bar_cmd "bar"
+    } else {
+      gdb_test_no_output "define-prefix ${prefix}"
+      set foo_cmd "${prefix} foo"
+      set bar_cmd "${prefix} bar"
+    }
+
+    # Define "foo".
+    gdb_define_cmd $foo_cmd {
+      "echo Original foo\\n"
+    }
+
+    # Define an alias to the original "foo" with the same prefix.
+    gdb_test_no_output "alias ${prefix} alias-to-original-foo-same-prefix = ${foo_cmd}"
+    # And another one but without a prefix.
+    gdb_test_no_output "alias alias-to-original-foo-no-prefix = ${foo_cmd}"
+
+    # Define "bar".
+    gdb_define_cmd $bar_cmd {
+      "echo Original bar\\n"
+    }
+
+    # Redefine "bar" while renaming the original "bar" to "foo".
+    send_gdb "define -rename-existing-to '${foo_cmd}' ${bar_cmd}\n"
+    gdb_expect {
+      -re "Redefine command \"foo\".*y or n. $"\
+	      {send_gdb "y\n"
+	       gdb_expect {
+		 -re "Type commands for definition of \"${bar_cmd}\".\r\nEnd with a line saying just \"end\".\r\n>$"\
+			 {send_gdb "echo New bar\\n\n${foo_cmd}\nend\n"
+			  gdb_expect {
+			    -re "$gdb_prompt $"\
+				    {pass "redefine user command"}
+			    timeout {fail "(timeout) redefine user command"}
+			  }
+			 }
+		 timeout {fail "(timeout) redefine user command"}
+	       }
+	      }
+      -re "$gdb_prompt $"\
+	      {fail "redefine user command"}
+      timeout {fail "(timeout) redefine user command"}
+    }
+
+    # "bar" should be the new command which calls the old orginal "bar".
+    gdb_test_exact $bar_cmd "New bar\nOriginal bar"
+
+    # The aliases to the original foo should have been deleted.
+    gdb_test "${prefix} alias-to-original-foo-same-prefix" \
+      ".*Undefined.* command:.*"
+    gdb_test "alias-to-original-foo-no-prefix" \
+      ".*Undefined command:.*"
+  }
+}
+
+with_test_prefix "command renaming" {
+  # Tests for "rename".
+  with_test_prefix "rename" {
+    test_rename_ok
+    test_rename_errors
+    test_rename_across_prefixes
+    test_rename_prefixes
+    test_rename_redefine ""
+    test_rename_redefine "my-prefix"
+    test_rename_hooks_updated
+  }
+
+  # Tests for the "define -rename-existing-to".
+  with_test_prefix "define -rename-existing-to" {
+    test_define_renaming_ok
+    test_define_renaming_builtin
+    test_define_renaming_errors
+    test_define_renaming_across_prefixes
+    test_define_renaming_prefixes
+    test_define_renaming_redefine ""
+    test_define_renaming_redefine "my-prefix"
+  }
+}
diff --git a/gdb/testsuite/gdb.python/py-rename-existing.exp b/gdb/testsuite/gdb.python/py-rename-existing.exp
new file mode 100644
index 00000000000..189633b068a
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-rename-existing.exp
@@ -0,0 +1,364 @@
+# Copyright (C) 2009-2021 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests renaming of existing
+# commands.
+
+load_lib gdb-python.exp
+load_lib completion-support.exp
+
+standard_testfile
+
+# Prepare for testing.
+#
+# This quits GDB (if running), starts a new one, and loads any required
+# external scripts.
+
+proc prepare_gdb {} {
+  global srcdir subdir testfile
+
+  gdb_exit
+  gdb_start
+  gdb_reinitialize_dir $srcdir/$subdir
+
+  # Skip all tests if Python scripting is not enabled.
+  if { [skip_python_tests] } { continue }
+
+  gdb_test_no_output "set confirm off"
+
+  # Load the code which adds commands.
+  set remote_python_file \
+    [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+  gdb_test_no_output "source ${remote_python_file}" "load python file"
+}
+
+# Add a command called CMD through the Python API renaming the existing
+# command of the same name to RENAME_EXISTING_TO.
+#
+# When invoked, the command prints messages and call the renamed command.
+# If MSG is not empty then it will be printed when the command is invoked.
+# Otherwise, the command name is printed.
+#
+# EXTRA_ARGS is passed as it to gdb.Command.__init__ after the other
+# arguments.
+
+proc add_py_cmd {cmd rename_existing_to {msg ""} {kwargs ""}} {
+  gdb_test_no_output \
+    "python TestCommand ('${cmd}', '${rename_existing_to}', '${msg}', ${kwargs})"
+}
+
+# test_sequence_exact CMD LIST
+# Like gdb_test_exact but, for convenience, it accepts a list of lines
+# instead of a single line.
+
+proc test_sequence_exact { cmd lines } {
+  set expected_string [join $lines "\n"]
+  gdb_test_exact $cmd $expected_string
+}
+
+# Test that renaming existing command works and that, after a rename,
+# aliases execute the new command.
+
+proc test_rename_simple {} {
+  with_test_prefix "rename command" {
+    prepare_gdb
+
+    gdb_define_cmd "my-cmd" {
+      "echo my-cmd original\\n"
+    }
+    gdb_test_no_output "alias my-cmd-alias = my-cmd"
+
+    add_py_cmd "my-cmd" "original-my-cmd"
+
+    set expected_list {
+      "py-before: my-cmd"
+      "my-cmd original"
+      "py-after: my-cmd"
+    }
+
+    test_sequence_exact "my-cmd" $expected_list
+    test_sequence_exact "my-cmd-alias" $expected_list
+  }
+}
+
+# Test that renaming a command multiple times works.
+
+proc test_rename_multiple {} {
+  with_test_prefix "rename command multiple times" {
+    prepare_gdb
+
+    gdb_define_cmd "my-cmd" {
+      "echo my-cmd original\\n"
+    }
+
+    add_py_cmd "my-cmd" "original-my-cmd" "first my-cmd"
+    add_py_cmd "my-cmd" "first-redefined-my-cmd" "second my-cmd"
+
+    test_sequence_exact "my-cmd" {
+      "py-before: second my-cmd"
+      "py-before: first my-cmd"
+      "my-cmd original"
+      "py-after: first my-cmd"
+      "py-after: second my-cmd"
+    }
+  }
+}
+
+# Test that trying to rename a non-existing command raises a RuntimeError.
+
+proc test_rename_non_existing {} {
+  with_test_prefix "rename non-existing" {
+    prepare_gdb
+
+    gdb_test \
+      "python TestCommand ('this_doesnt_exist', 'this_doesnt_matter')" \
+      ".*RuntimeError: Command \"this_doesnt_exist\" does not exist,\
+      so it cannot be renamed to \"this_doesnt_matter\"\..*"
+  }
+}
+
+# Test renaming of a command with a prefix.
+
+proc test_same_prefix {} {
+  with_test_prefix "rename keeping the same prefix" {
+    prepare_gdb
+
+    add_py_cmd "info args" "info renamed-args"
+
+    test_sequence_exact "info args" {
+      "py-before: info args"
+      "No frame selected."
+      "py-after: info args"
+    }
+    gdb_test_exact "info renamed-args" "No frame selected."
+  }
+}
+
+# Test renaming of a command without a prefix to a command with a prefix.
+
+proc test_rename_adding_prefix {} {
+  with_test_prefix "rename adding a prefix" {
+    prepare_gdb
+
+    gdb_define_cmd "my-cmd" {
+      "echo my-cmd original\\n"
+    }
+
+    gdb_test_no_output "define-prefix my-prefix"
+
+    add_py_cmd "my-cmd" "my-prefix my-cmd"
+
+    test_sequence_exact "my-cmd" {
+      "py-before: my-cmd"
+      "my-cmd original"
+      "py-after: my-cmd"
+    }
+    gdb_test_exact "my-prefix my-cmd" "my-cmd original"
+  }
+}
+
+# Test renaming of a command with a prefix to a command without prefix.
+
+proc test_rename_removing_prefix {} {
+  with_test_prefix "rename removing a prefix" {
+    prepare_gdb
+
+    add_py_cmd "info args" "renamed-info-args"
+
+    test_sequence_exact "info args" {
+      "py-before: info args"
+      "No frame selected."
+      "py-after: info args"
+    }
+    gdb_test_exact "renamed-info-args" "No frame selected."
+  }
+}
+
+# Test renaming of a command with prefix to a different prefix.
+
+proc test_rename_changing_prefix {} {
+  with_test_prefix "rename changing prefix" {
+    prepare_gdb
+
+    gdb_test_no_output "define-prefix my-prefix"
+
+    add_py_cmd "info args" "my-prefix args"
+
+    test_sequence_exact "info args" {
+      "py-before: info args"
+      "No frame selected."
+      "py-after: info args"
+    }
+    gdb_test_exact "my-prefix args" "No frame selected."
+  }
+}
+
+# Test that replacing a prefix command replaces its implementation but
+# doesn't affect its subcommands.
+
+proc test_replace_prefix {} {
+  with_test_prefix "replace a prefix command" {
+    prepare_gdb
+
+    # Replace the "show" command (which has subcommands).
+    add_py_cmd "show" "original-show"
+
+    # "original-show" won't print anything as it doesn't have subcommands.
+    test_sequence_exact "show" {
+      "py-before: show"
+      "py-after: show"
+    }
+
+    gdb_test_no_output "set pagination off"
+    # Subcommands still work.
+    gdb_test_exact "show pagination" "State of pagination is off."
+  }
+}
+
+# Test that prefix cannot be passed to gdb.Command.__init__ if
+# rename_existing_to is used.
+
+proc test_prefix_arg_not_allowed {} {
+  with_test_prefix "prefix argument not allowed with rename_existing_to" {
+    prepare_gdb
+
+    with_test_prefix "non-prefix command" {
+      gdb_test \
+	"python gdb.Command ('run', rename_existing_to='renamed-run', prefix=True)" \
+	".*Cannot specify argument 'prefix' with 'rename_existing_to'.*"
+      gdb_test \
+	"python gdb.Command ('run', rename_existing_to='renamed-run', prefix=False)" \
+	".*Cannot specify argument 'prefix' with 'rename_existing_to'.*"
+    }
+
+    with_test_prefix "prefix command" {
+      gdb_test \
+	"python gdb.Command ('show', rename_existing_to='renamed-show', prefix=True)" \
+	".*Cannot specify argument 'prefix' with 'rename_existing_to'.*"
+      gdb_test \
+	"python gdb.Command ('show', rename_existing_to='renamed-run', prefix=False)" \
+	".*Cannot specify argument 'prefix' with 'rename_existing_to'.*"
+    }
+  }
+}
+
+# Test that dont-repeat from a renamed command invoked by the new
+# implementation of the command prevents the repetition of the new
+# command.  This is needed to make the new implementation work
+# consistently with the original one.
+
+proc test_repeat {} {
+  with_test_prefix "repeat" {
+    prepare_gdb
+
+    # Define a command which repeats and one which doesn't.
+    gdb_define_cmd "repeat-cmd" {
+      "echo This command repeats\\n"
+    }
+    gdb_define_cmd "no-repeat-cmd" {
+      "echo This command does NOT repeat\\n"
+      "dont-repeat\\n"
+    }
+    # Then redefine them.
+    add_py_cmd "repeat-cmd" "renamed-repeat-cmd"
+    add_py_cmd "no-repeat-cmd" "renamed-no-repeat-cmd"
+
+    # The repeating command must still repeat.
+    set expected_repeat_list {
+      "py-before: repeat-cmd"
+      "This command repeats"
+      "py-after: repeat-cmd"
+    }
+    test_sequence_exact "repeat-cmd" $expected_repeat_list
+    with_test_prefix "repeating command" {
+      # Using an empty string works but is slow, probably because it
+      # waits for a timeout. A string with just whitespace does the same
+      # but fast.
+      test_sequence_exact " " $expected_repeat_list
+    }
+
+    # The non-repeating command must not repeat.
+    test_sequence_exact "no-repeat-cmd" {
+      "py-before: no-repeat-cmd"
+      "This command does NOT repeat"
+      "py-after: no-repeat-cmd"
+    }
+    with_test_prefix "non-repeating command" {
+      gdb_test_exact " " ""
+    }
+  }
+}
+
+# Test that the completer, if not specified, is copied from the original
+# command. If specified, the specified one is used.
+
+proc test_complete {} {
+  with_test_prefix "command completion" {
+    prepare_gdb
+
+    # Add a command whose arguments get completed as commands.
+    gdb_test_no_output \
+      "python gdb.Command ('my-cmd', gdb.COMMAND_NONE, gdb.COMPLETE_COMMAND)"
+
+    # Redefine the command.  As no completer was specified, it must
+    # inherit the one from the original command.
+    add_py_cmd "my-cmd" "renamed-my-cmd1"
+    with_test_prefix "completer inherited" {
+      test_gdb_complete_unique "my-cmd dele" "my-cmd delete"
+    }
+
+    # Redefine the command again.  As a completer was specified, the
+    # command must use the specified completer, not the original one.
+    add_py_cmd "my-cmd" "renamed-my-cmd2" "" "completer_class=gdb.COMPLETE_NONE"
+    with_test_prefix "completer specified" {
+      test_gdb_complete_none "my-cmd dele"
+  }
+  }
+}
+
+# Test the behaviour of command classes with renamed commands.
+
+proc test_command_class {} {
+  with_test_prefix "command class" {
+    prepare_gdb
+
+    # Rename the "run" command. The command class, as it's not specified,
+    # is inherited from the original command.
+    add_py_cmd "run" "renamed-run1"
+    gdb_test "help running" \
+      ".*renamed-run1 -- Start debugged program\..*run, r -- .*"
+
+    # Rename it again but, this time, put the new command in a different
+    # command class.
+    add_py_cmd "run" "renamed-run2" "" "command_class=gdb.COMMAND_STATUS"
+    gdb_test "help status" \
+      ".*run, r -- .*"
+  }
+}
+
+with_test_prefix "renaming commands via Python" {
+  test_rename_simple
+  test_rename_multiple
+  test_rename_non_existing
+  test_same_prefix
+  test_rename_adding_prefix
+  test_rename_removing_prefix
+  test_rename_changing_prefix
+  test_replace_prefix
+  test_prefix_arg_not_allowed
+  test_repeat
+  test_complete
+  test_command_class
+}
diff --git a/gdb/testsuite/gdb.python/py-rename-existing.py b/gdb/testsuite/gdb.python/py-rename-existing.py
new file mode 100644
index 00000000000..f0b4a3bd182
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-rename-existing.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2008-2021 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests renaming of existing
+# commands.
+
+import gdb
+
+
+class TestCommand(gdb.Command):
+    def __init__(self, cmd_name, rename_existing_to, msg=None, **kwargs):
+        if not msg:
+            msg = cmd_name
+
+        self._cmd_name = cmd_name
+        self._rename_existing_to = rename_existing_to
+        self._msg = msg
+
+        gdb.Command.__init__(
+            self, cmd_name, rename_existing_to=rename_existing_to, **kwargs
+        )
+
+    def invoke(self, args, from_tty):
+        print("py-before: %s" % self._msg)
+
+        # Invoke the original command.
+        try:
+            gdb.execute("%s %s" % (self._rename_existing_to, args), from_tty=from_tty)
+        except gdb.error as exc:
+            print(exc)
+
+        print("py-after: %s" % self._msg)
-- 
2.28.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

end of thread, other threads:[~2021-05-14 20:41 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-08 10:07 [PATCH 0/4] Add support for command renaming Marco Barisione
2021-01-08 10:07 ` [PATCH 1/4] gdb: add lookup_cmd_exact to simplify a common pattern Marco Barisione
2021-01-10  0:06   ` Lancelot SIX
2021-01-17 10:47     ` Marco Barisione
2021-01-17 19:02       ` Lancelot SIX
2021-01-25 11:33         ` Luis Machado
2021-01-08 10:07 ` [PATCH 2/4] gdb: prevent prefix commands from being hooks Marco Barisione
2021-01-08 10:07 ` [PATCH 3/4] gdb: update the docs for add_cmd and do_add_cmd to match reality Marco Barisione
2021-01-08 10:07 ` [PATCH 4/4] gdb: Add support for renaming commands Marco Barisione
2021-01-08 10:30   ` Eli Zaretskii
2021-01-25 11:26 ` [PATCH v2 0/5] Add support for command renaming Marco Barisione
2021-01-25 11:26   ` [PATCH v2 1/5] gdb: add lookup_cmd_exact to simplify a common pattern Marco Barisione
2021-03-08 18:58     ` Simon Marchi
2021-05-07 14:47       ` Marco Barisione
2021-01-25 11:26   ` [PATCH v2 2/5] gdb: prevent prefix commands from being hooks Marco Barisione
2021-03-08 21:32     ` Simon Marchi
2021-03-09  9:42       ` Marco Barisione
2021-03-16  3:17         ` Simon Marchi
2021-05-07 14:59           ` Marco Barisione
2021-05-07 19:30             ` Simon Marchi
2021-05-07 20:11               ` Marco Barisione
2021-05-14 20:38       ` [PATCH v3 " Marco Barisione
2021-01-25 11:26   ` [PATCH v2 3/5] gdb: update the docs for add_cmd and do_add_cmd to match reality Marco Barisione
2021-03-08 22:52     ` Simon Marchi
2021-03-08 23:10       ` Simon Marchi
2021-05-14 20:39       ` [PATCH v3 3/5] gdb: move declarations and docs for cli-decode.c to cli-decode.h Marco Barisione
2021-01-25 11:26   ` [PATCH v2 4/5] gdb: generate the prefix name for prefix commands on demand Marco Barisione
2021-03-08 23:25     ` Simon Marchi
2021-03-16 17:00       ` Simon Marchi
2021-05-12 11:10       ` Marco Barisione
2021-01-25 11:26   ` [PATCH v2 5/5] gdb: Add support for renaming commands Marco Barisione
2021-03-23 18:45     ` Simon Marchi
2021-05-14 20:41       ` [PATCH v3 5/5] gdb: add " Marco Barisione
2021-02-08 17:53   ` [PING] [PATCH v2 0/5] Add support for command renaming Marco Barisione
2021-02-15  8:27     ` [PING2] " Marco Barisione
2021-02-22  8:28       ` [PING 3] " Marco Barisione
2021-03-01  8:32         ` [PING 4] " Marco Barisione
2021-03-08  9:23           ` [PING 5] " Marco Barisione

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).