diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 90ce6ae..48f2063 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,7 @@
+2017-04-25  Tom Tromey  <tom@tromey.com>
+
+	* stack.c (backtrace_command): Rewrite command line parsing.
+
 2017-04-25  Yao Qi  <yao.qi@linaro.org>
 
 	* aarch64-tdep.c (aarch64_gdbarch_init): Don't call
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index 9be035c..cf2226c 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,7 @@
+2017-04-25  Tom Tromey  <tom@tromey.com>
+
+	* gdb.texinfo (Backtrace): Describe options individually.
+
 2017-04-21  Simon Marchi  <simon.marchi@ericsson.com>
 
 	* gdb.texinfo (GDB/MI Thread Information): Add missing
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f2e6156..a9f12fd 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7199,39 +7199,27 @@ frame (frame zero), followed by its caller (frame one), and on up the
 stack.
 
 @anchor{backtrace-command}
-@table @code
 @kindex backtrace
 @kindex bt @r{(@code{backtrace})}
-@item backtrace
-@itemx bt
-Print a backtrace of the entire stack: one line per frame for all
-frames in the stack.
-
-You can stop the backtrace at any time by typing the system interrupt
-character, normally @kbd{Ctrl-c}.
-
-@item backtrace @var{n}
-@itemx bt @var{n}
-Similar, but print only the innermost @var{n} frames.
-
-@item backtrace -@var{n}
-@itemx bt -@var{n}
-Similar, but print only the outermost @var{n} frames.
-
-@item backtrace full
-@itemx bt full
-@itemx bt full @var{n}
-@itemx bt full -@var{n}
-Print the values of the local variables also.  As described above,
-@var{n} specifies the number of frames to print.
-
-@item backtrace no-filters
-@itemx bt no-filters
-@itemx bt no-filters @var{n}
-@itemx bt no-filters -@var{n}
-@itemx bt no-filters full
-@itemx bt no-filters full @var{n}
-@itemx bt no-filters full -@var{n}
+Print a backtrace of the entire stack, use the @code{backtrace}
+command, or its alias @code{bt}.  This command will print one line per
+frame for all frames in the stack.  You can stop the backtrace at any
+time by typing the system interrupt character, normally @kbd{Ctrl-c}.
+@code{backtrace} can accept some arguments:
+
+@table @code
+@item @var{n}
+@itemx @var{n}
+Print only the innermost @var{n} frames.
+
+@item -@var{n}
+@itemx -@var{n}
+Print only the outermost @var{n} frames.
+
+@item full
+Print the values of the local variables also.
+
+@item no-filters
 Do not run Python frame filters on this backtrace.  @xref{Frame
 Filter API}, for more information.  Additionally use @ref{disable
 frame-filter all} to turn off all frame filters.  This is only
diff --git a/gdb/stack.c b/gdb/stack.c
index 7f8a51c..c2fc3f1 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -1868,65 +1868,46 @@ backtrace_command_1 (char *count_exp, int show_locals, int no_filters,
 static void
 backtrace_command (char *arg, int from_tty)
 {
-  struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
-  int fulltrace_arg = -1, arglen = 0, argc = 0, no_filters  = -1;
-  int user_arg = 0;
+  bool fulltrace = false;
+  bool filters = true;
 
   if (arg)
     {
-      char **argv;
-      int i;
+      bool done = false;
 
-      argv = gdb_buildargv (arg);
-      make_cleanup_freeargv (argv);
-      argc = 0;
-      for (i = 0; argv[i]; i++)
+      while (!done)
 	{
 	  unsigned int j;
+	  char *this_arg = extract_arg (&arg);
 
-	  for (j = 0; j < strlen (argv[i]); j++)
-	    argv[i][j] = TOLOWER (argv[i][j]);
+	  if (this_arg == NULL)
+	    break;
+
+	  struct cleanup *arg_cleanup = make_cleanup (xfree, this_arg);
+
+	  for (j = 0; j < strlen (this_arg); j++)
+	    this_arg[j] = TOLOWER (this_arg[j]);
 
-	  if (no_filters < 0 && subset_compare (argv[i], "no-filters"))
-	    no_filters = argc;
+	  if (subset_compare (this_arg, "no-filters"))
+	    filters = false;
+	  else if (subset_compare (this_arg, "full"))
+	    fulltrace = true;
 	  else
 	    {
-	      if (fulltrace_arg < 0 && subset_compare (argv[i], "full"))
-		fulltrace_arg = argc;
-	      else
-		{
-		  user_arg++;
-		  arglen += strlen (argv[i]);
-		}
+	      /* Not a recognized argument, so stop.  */
+	      done = true;
 	    }
-	  argc++;
-	}
-      arglen += user_arg;
-      if (fulltrace_arg >= 0 || no_filters >= 0)
-	{
-	  if (arglen > 0)
-	    {
-	      arg = (char *) xmalloc (arglen + 1);
-	      make_cleanup (xfree, arg);
-	      arg[0] = 0;
-	      for (i = 0; i < argc; i++)
-		{
-		  if (i != fulltrace_arg && i != no_filters)
-		    {
-		      strcat (arg, argv[i]);
-		      strcat (arg, " ");
-		    }
-		}
-	    }
-	  else
-	    arg = NULL;
+
+	  do_cleanups (arg_cleanup);
 	}
-    }
 
-  backtrace_command_1 (arg, fulltrace_arg >= 0 /* show_locals */,
-		       no_filters >= 0 /* no frame-filters */, from_tty);
+      arg = skip_spaces (arg);
+      if (*arg == '\0')
+	arg = NULL;
+    }
 
-  do_cleanups (old_chain);
+  backtrace_command_1 (arg, fulltrace /* show_locals */,
+		       !filters /* no frame-filters */, from_tty);
 }
 
 /* Iterate over the local variables of a block B, calling CB with
