diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index 4fc9c70259..c70c0e4fb3 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -29,6 +29,7 @@
 #include "cli/cli-cmds.h"
 #include "cli/cli-decode.h"
 #include "cli/cli-script.h"
+#include "cli/cli-style.h"
 
 #include "extension.h"
 #include "interps.h"
@@ -1444,10 +1445,22 @@ do_define_command (const char *comname, int from_tty,
   else
     cmds = *commands;
 
-  newc = add_cmd (comname, class_user, user_defined_command,
-		  (c && c->theclass == class_user)
-		  ? c->doc : xstrdup ("User-defined."), list);
-  newc->user_commands = std::move (cmds);
+  {
+    struct cmd_list_element **c_prefixlist = c ? c->prefixlist : nullptr;
+    const char *c_prefixname = c ? c->prefixname : nullptr;
+
+    newc = add_cmd (comname, class_user, user_defined_command,
+		    (c && c->theclass == class_user)
+		    ? c->doc : xstrdup ("User-defined."), list);
+    newc->user_commands = std::move (cmds);
+
+    if (c_prefixlist != nullptr)
+      {
+	newc->prefixlist = c_prefixlist;
+	newc->prefixname = c_prefixname;
+	newc->allow_unknown = newc->user_commands.get () != nullptr;
+    }
+  }
 
   /* If this new command is a hook, then mark both commands as being
      tied.  */
@@ -1522,6 +1535,52 @@ document_command (const char *comname, int from_tty)
     c->doc = doc;
   }
 }
+
+/* Implementation of the "prefix-define" command.  */
+
+static void
+prefix_define_command (const char *comname, int from_tty)
+{
+  struct cmd_list_element *c, **list;
+  const char *tem;
+  const char *comfull;
+  char *prefixname;
+
+  comfull = comname;
+  list = validate_comname (&comname);
+
+  /* Look it up, and verify that we got an exact match.  */
+  tem = comname;
+  c = lookup_cmd (&tem, *list, "", -1, 1);
+  if (c && strcmp (comname, c->name) != 0)
+    c = nullptr;
+
+  if (c && c->theclass != class_user)
+    error (_("Command \"%s\" is built-in."), comfull);
+
+  if (c && c->prefixlist != nullptr)
+    {
+      /* c is already a user defined prefix command.  */
+      return;
+    }
+
+  if (c == nullptr)
+    {
+      comname = xstrdup (comname);
+      c = add_cmd (comname, class_user, user_defined_command,
+		   xstrdup ("User-defined."), list);
+    }
+
+  c->prefixlist = new struct cmd_list_element*;
+  *(c->prefixlist) = nullptr;
+  prefixname = (char *) xmalloc (strlen (comfull) + 2);
+  prefixname[0] = 0;
+  strcat (prefixname, comfull);
+  strcat (prefixname, " ");
+  c->prefixname = prefixname;
+  c->allow_unknown = c->user_commands.get () != nullptr;
+}
+
 
 /* Used to implement source_command.  */
 
@@ -1562,7 +1621,21 @@ void
 show_user_1 (struct cmd_list_element *c, const char *prefix, const char *name,
 	     struct ui_file *stream)
 {
-  struct command_line *cmdlines;
+  if (cli_user_command_p (c))
+    {
+      struct command_line *cmdlines = c->user_commands.get ();
+
+      fprintf_filtered (stream, "User %scommand \"",
+			c->prefixlist == NULL ? "" : "prefix ");
+      fprintf_styled (stream, title_style.style (), "%s%s",
+		      prefix, name);
+      fprintf_filtered (stream, "\":\n");
+      if (cmdlines)
+	{
+	  print_command_lines (current_uiout, cmdlines, 1);
+	  fputs_filtered ("\n", stream);
+	}
+    }
 
   if (c->prefixlist != NULL)
     {
@@ -1571,25 +1644,23 @@ show_user_1 (struct cmd_list_element *c, const char *prefix, const char *name,
       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);
-      return;
     }
 
-  cmdlines = c->user_commands.get ();
-  fprintf_filtered (stream, "User command \"%s%s\":\n", prefix, name);
-
-  if (!cmdlines)
-    return;
-  print_command_lines (current_uiout, cmdlines, 1);
-  fputs_filtered ("\n", stream);
 }
 
 void
 _initialize_cli_script (void)
 {
-  add_com ("document", class_support, document_command, _("\
+  struct cmd_list_element *c;
+
+  /* "document", "define" and "prefix-define" use command_completer,
+     as this helps the user to either type the command name and/or
+     its prefixes.  */
+  c = add_com ("document", class_support, document_command, _("\
 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 (c, command_completer);
   define_cmd_element = add_com ("define", class_support, define_command, _("\
 Define a new command name.  Command name is argument.\n\
 Definition appears on following lines, one command per line.\n\
@@ -1598,6 +1669,14 @@ 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."));
+  set_cmd_completer (define_cmd_element, command_completer);
+  c = add_com ("prefix-define", class_support, prefix_define_command,
+	   _("\
+Define or mark a command as a user-defined prefix command.\n\
+User defined prefix commands can be used as prefix commands for\n\
+other user defined commands.\n\
+If the command already exists, it is changed to a prefix command."));
+  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\
diff --git a/gdb/top.c b/gdb/top.c
index a1a08e0b99..abd07bd6fe 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -615,6 +615,16 @@ execute_command (const char *p, int from_tty)
       /* c->user_commands would be NULL in the case of a python command.  */
       if (c->theclass == class_user && c->user_commands)
 	execute_user_command (c, arg);
+      else if (c->theclass == class_user
+	       && c->prefixlist && !c->allow_unknown)
+	/* If this is a user defined prefix that does not allow unknown,
+	   report the list of subcommands.  */
+	{
+	  printf_unfiltered
+	    ("\"%.*s\" must be followed by the name of an %s command.\n",
+	     (int) strlen (c->prefixname) - 1, c->prefixname, c->name);
+	  help_list (*c->prefixlist, c->prefixname, all_commands, gdb_stdout);
+	}
       else if (c->type == set_cmd)
 	do_set_command (arg, from_tty, c);
       else if (c->type == show_cmd)
