public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2 06/24] Fix "set enum-command value garbage"
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (3 preceding siblings ...)
  2019-05-30 19:53 ` [PATCH v2 02/24] Fix latent bug with custom word point completers Pedro Alves
@ 2019-05-30 19:53 ` Pedro Alves
  2019-06-03 18:51   ` Tom Tromey
  2019-05-30 19:54 ` [PATCH v2 13/24] Make "print" and "compile print" support -OPT options Pedro Alves
                   ` (19 subsequent siblings)
  24 siblings, 1 reply; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:53 UTC (permalink / raw)
  To: gdb-patches

With enum commands, we currently fail to notice garbage after the
value.

Currently:

  (gdb) set print entry-values compact foo
  (gdb) show print entry-values foo
  Printing of function arguments at function entry is "compact".

After this fix:

 (gdb) set print entry-values compact foo
  Garbage after item "compact": "foo"

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* cli/cli-setshow.c (do_set_command) <var_enum>: Detect garbage
	after item.
---
 gdb/cli/cli-setshow.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 96d7bf5c3c0..47e50941e3b 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -413,6 +413,11 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	if (nmatches > 1)
 	  error (_("Ambiguous item \"%s\"."), arg);
 
+	const char *after = skip_spaces (arg + len);
+	if (*after != '\0')
+	  error (_("Garbage after item \"%.*s\": \"%s\""),
+		 len, arg, after);
+
 	if (*(const char **) c->var != match)
 	  {
 	    *(const char **) c->var = match;
-- 
2.14.5

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

* [PATCH v2 15/24] "set print raw frame-arguments" -> "set print raw-frame-arguments"
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
@ 2019-05-30 19:53 ` Pedro Alves
  2019-05-31  5:48   ` Eli Zaretskii
  2019-05-30 19:53 ` [PATCH v2 11/24] number_or_range_parser::get_number, don't treat "1 -" as a range Pedro Alves
                   ` (23 subsequent siblings)
  24 siblings, 1 reply; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:53 UTC (permalink / raw)
  To: gdb-patches

A following patch will introduce options for the "backtrace" command,
based on some "set print" and "set backtrace" settings.  There's one
setting in particular that is a bit annoying if we want to describe
the backtrace options and the settings commands using the same data
structures:

  "set print raw frame-arguments"

The problem is that space between "raw" and "frame-arguments".

Calling the option

  "bt -raw frame-arguments"

would be odd.  So I'm calling the option

  "bt -raw-frame-arguments"

instead.

And for consistency, this patch renames the set/show commands to:

 "set print raw-frame-arguments"
 "show print raw-frame-arguments"

I.e., dash instead of space.  The old commands are left in place, but
marked deprecated.

We need to adjust a couple testcases, because the relevant tests use
gdb_test_no_output and the old commands are no longer silent:

  (gdb) set print raw frame-arguments on
  Warning: command 'set print raw frame-arguments' is deprecated.
  Use 'set print raw-frame-arguments'.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* NEWS (New commands): Mention set/show print raw-frame-arguments,
	and that "set/show print raw frame-arguments" are now deprecated.

	* cli/cli-decode.c (add_setshow_boolean_cmd): Now returns the
	command.
	* command.h (add_setshow_boolean_cmd): Return cmd_list_element *.
	* stack.c (_initialize_stack): Install "set/show print
	raw-frame-arguments", and deprecate "set/show print raw
	frame-arguments".
	* valprint.c (_initialize_valprint): Deprecate "set/show print
	raw".

gdb/doc/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.texinfo (Print Settings): Document "set/show print
	raw-frame-arguments" instead of "set/show print raw
	frame-arguments".

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.guile/scm-frame-args.exp: Use "set print
	raw-frame-arguments" instead of "set print raw frame-arguments".
	* gdb.python/py-frame-args.exp: Likewise.
---
 gdb/doc/gdb.texinfo                        |  6 +++---
 gdb/NEWS                                   |  8 ++++++++
 gdb/cli/cli-decode.c                       |  8 ++++++--
 gdb/command.h                              | 19 ++++++++++---------
 gdb/stack.c                                | 16 ++++++++++++++--
 gdb/testsuite/gdb.guile/scm-frame-args.exp |  4 ++--
 gdb/testsuite/gdb.python/py-frame-args.exp |  4 ++--
 gdb/valprint.c                             | 19 +++++++++++++------
 8 files changed, 58 insertions(+), 26 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 72dd826d373..92d1aae40a2 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -10441,16 +10441,16 @@ thus speeding up the display of each Ada frame.
 @item show print frame-arguments
 Show how the value of arguments should be displayed when printing a frame.
 
-@item set print raw frame-arguments on
+@item set print raw-frame-arguments on
 Print frame arguments in raw, non pretty-printed, form.
 
-@item set print raw frame-arguments off
+@item set print raw-frame-arguments off
 Print frame arguments in pretty-printed form, if there is a pretty-printer
 for the value (@pxref{Pretty Printing}),
 otherwise print the value in raw form.
 This is the default.
 
-@item show print raw frame-arguments
+@item show print raw-frame-arguments
 Show whether to print frame arguments in raw form.
 
 @anchor{set print entry-values}
diff --git a/gdb/NEWS b/gdb/NEWS
index 4b5bd07d0af..969c4c4ea88 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -61,6 +61,14 @@ maint test-settings KIND
   A set of commands used by the testsuite for exercising the settings
   infrastructure.
 
+set print raw-frame-arguments
+show print raw-frame-arguments
+
+  These commands replace the similarly-named "set/show print raw
+  frame-arguments" commands (now with a dash instead of a space).  The
+  old commands are now deprecated and may be removed in a future
+  release.
+
 * New MI commands
 
 -complete
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 0370b125615..2cd2f14207a 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -584,8 +584,10 @@ const char * const boolean_enums[] = { "on", "off", NULL };
 /* Add element named NAME to both the set and show command LISTs (the
    list for set/show or some sublist thereof).  CLASS is as in
    add_cmd.  VAR is address of the variable which will contain the
-   value.  SET_DOC and SHOW_DOC are the documentation strings.  */
-void
+   value.  SET_DOC and SHOW_DOC are the documentation strings.
+   Returns the new command element.  */
+
+cmd_list_element *
 add_setshow_boolean_cmd (const char *name, enum command_class theclass, int *var,
 			 const char *set_doc, const char *show_doc,
 			 const char *help_doc,
@@ -602,6 +604,8 @@ add_setshow_boolean_cmd (const char *name, enum command_class theclass, int *var
 			set_list, show_list,
 			&c, NULL);
   c->enums = boolean_enums;
+
+  return c;
 }
 
 /* Add element named NAME to both the set and show command LISTs (the
diff --git a/gdb/command.h b/gdb/command.h
index 35006cc339e..cf3b0ca61e8 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -328,15 +328,16 @@ extern void add_setshow_auto_boolean_cmd (const char *name,
 					  struct cmd_list_element **set_list,
 					  struct cmd_list_element **show_list);
 
-extern void add_setshow_boolean_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 cmd_list_element *
+  add_setshow_boolean_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_filename_cmd (const char *name,
 				      enum command_class theclass,
diff --git a/gdb/stack.c b/gdb/stack.c
index 408c795e385..d1b195c84d6 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -3106,14 +3106,26 @@ Usage: func NAME"));
 			_("Show printing of non-scalar frame arguments"),
 			NULL, NULL, NULL, &setprintlist, &showprintlist);
 
-  add_setshow_boolean_cmd ("frame-arguments", no_class,
+  /* Install "set print raw frame-arguments", a deprecated spelling of
+     "set print raw-frame-arguments".  */
+  cmd = add_setshow_boolean_cmd ("frame-arguments", no_class,
+				 &print_raw_frame_arguments, _("\
+Set whether to print frame arguments in raw form."), _("\
+Show whether to print frame arguments in raw form."), _("\
+If set, frame arguments are printed in raw form, bypassing any\n\
+pretty-printers for that value."),
+				 NULL, NULL,
+				 &setprintrawlist, &showprintrawlist);
+  deprecate_cmd (cmd, "set print raw-frame-arguments");
+
+  add_setshow_boolean_cmd ("raw-frame-arguments", no_class,
 			   &print_raw_frame_arguments, _("\
 Set whether to print frame arguments in raw form."), _("\
 Show whether to print frame arguments in raw form."), _("\
 If set, frame arguments are printed in raw form, bypassing any\n\
 pretty-printers for that value."),
 			   NULL, NULL,
-			   &setprintrawlist, &showprintrawlist);
+			   &setprintlist, &showprintlist);
 
   add_setshow_auto_boolean_cmd ("disassemble-next-line", class_stack,
 			        &disassemble_next_line, _("\
diff --git a/gdb/testsuite/gdb.guile/scm-frame-args.exp b/gdb/testsuite/gdb.guile/scm-frame-args.exp
index 9a531f08e8d..cae21677eaf 100644
--- a/gdb/testsuite/gdb.guile/scm-frame-args.exp
+++ b/gdb/testsuite/gdb.guile/scm-frame-args.exp
@@ -38,7 +38,7 @@ gdb_continue_to_breakpoint "break-here" ".* break-here .*"
 
 # Test all combinations with raw off.
 
-gdb_test_no_output "set print raw frame-arguments off"
+gdb_test_no_output "set print raw-frame-arguments off"
 
 gdb_test_no_output "set print frame-arguments none"
 gdb_test "frame" ".*foo \\(x=\[.\]{3}, ss=\[.\]{3}\\).*" \
@@ -57,7 +57,7 @@ gdb_test "frame" \
 
 # Test all combinations with raw on.
 
-gdb_test_no_output "set print raw frame-arguments on"
+gdb_test_no_output "set print raw-frame-arguments on"
 
 gdb_test_no_output "set print frame-arguments none"
 gdb_test "frame" ".*foo \\(x=\[.\]{3}, ss=\[.\]{3}\\).*" \
diff --git a/gdb/testsuite/gdb.python/py-frame-args.exp b/gdb/testsuite/gdb.python/py-frame-args.exp
index a32473568bb..b921330ddb6 100644
--- a/gdb/testsuite/gdb.python/py-frame-args.exp
+++ b/gdb/testsuite/gdb.python/py-frame-args.exp
@@ -36,7 +36,7 @@ gdb_continue_to_breakpoint "break-here" ".* break-here .*"
 
 # Test all combinations with raw off.
 
-gdb_test_no_output "set print raw frame-arguments off"
+gdb_test_no_output "set print raw-frame-arguments off"
 
 gdb_test_no_output "set print frame-arguments none"
 gdb_test "frame" ".*foo \\(x=\[.\]{3}, ss=\[.\]{3}\\).*" \
@@ -53,7 +53,7 @@ gdb_test "frame" \
 
 # Test all combinations with raw on.
 
-gdb_test_no_output "set print raw frame-arguments on"
+gdb_test_no_output "set print raw-frame-arguments on"
 
 gdb_test_no_output "set print frame-arguments none"
 gdb_test "frame" ".*foo \\(x=\[.\]{3}, ss=\[.\]{3}\\).*" \
diff --git a/gdb/valprint.c b/gdb/valprint.c
index e3197e69192..6f9b47da80c 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -3248,6 +3248,8 @@ make_value_print_options_def_group (value_print_options *opts)
 void
 _initialize_valprint (void)
 {
+  cmd_list_element *cmd;
+
   add_prefix_cmd ("print", no_class, set_print,
 		  _("Generic command for setting how things print."),
 		  &setprintlist, "set print ", 0, &setlist);
@@ -3261,13 +3263,18 @@ _initialize_valprint (void)
   add_alias_cmd ("p", "print", no_class, 1, &showlist);
   add_alias_cmd ("pr", "print", no_class, 1, &showlist);
 
-  add_prefix_cmd ("raw", no_class, set_print_raw,
-		  _("\
+  cmd = add_prefix_cmd ("raw", no_class, set_print_raw,
+			_("\
 Generic command for setting what things to print in \"raw\" mode."),
-		  &setprintrawlist, "set print raw ", 0, &setprintlist);
-  add_prefix_cmd ("raw", no_class, show_print_raw,
-		  _("Generic command for showing \"print raw\" settings."),
-		  &showprintrawlist, "show print raw ", 0, &showprintlist);
+			&setprintrawlist, "set print raw ", 0,
+			&setprintlist);
+  deprecate_cmd (cmd, nullptr);
+
+  cmd = add_prefix_cmd ("raw", no_class, show_print_raw,
+			_("Generic command for showing \"print raw\" settings."),
+			&showprintrawlist, "show print raw ", 0,
+			&showprintlist);
+  deprecate_cmd (cmd, nullptr);
 
   gdb::option::add_setshow_cmds_for_options
     (class_support, &user_print_options, value_print_option_defs,
-- 
2.14.5

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

* [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options
@ 2019-05-30 19:53 Pedro Alves
  2019-05-30 19:53 ` [PATCH v2 15/24] "set print raw frame-arguments" -> "set print raw-frame-arguments" Pedro Alves
                   ` (24 more replies)
  0 siblings, 25 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:53 UTC (permalink / raw)
  To: gdb-patches

Here's v2 of this patch series, which I believe addresses all comments
against v1 so far.  The main change in v2 is that patch #15 is
completely different, hence the repost.  In v1, I was adding a way to
rename a command.  In v2, after discussion with Philippe, I'm adding
new "set/show print raw-frame-arguments" (dash) commands and
deprecating "set/show print raw frame-arguments" (space).
Documentation for that change is in that same patch.  Eli,
documentation-wise, nothing else changed in the series other than
addressing your comments in the previous iteration.

~~~~~~

This all started a couple years ago with the idea that one reason
people sometimes complain about gdb's defaults, such as "set print
object" or "set print static-members" is that we don't have an easy
way to override the global print settings.

I.e., how if we had options support in the print command, so you could
do this:

  (gdb) print -pretty obj

instead of:

  (gdb) set print pretty on
  (gdb) print obj
  (gdb) set print pretty off

then the defaults, while they'd still matter, wouldn't matter so much.

I'm a strong believer of TAB-completion driving the CLI, and that is
one of the reasons why I like this patchset.  If you're like me,
you're pressing TAB all the time.  It allows dynamic discovery of
options/features, and makes it not a problem to have longer option
names:

 (gdb) p -[TAB]
 -address         -elements        -pretty          -symbol
 -array           -null-stop       -repeats         -union
 -array-indexes   -object          -static-members  -vtbl

The approach I took is that command options have a similar syntax and
names to the global "set" command settings.  The above should be
familiar, if you're familiar with the "set print" settings:

  (gdb) set print[TAB]
  address                max-symbolic-offset    symbol
  array                  null-stop              symbol-filename
  array-indexes          object                 symbol-loading
  asm-demangle           pascal_static-members  thread-events
  demangle               pretty                 type
  elements               raw                    union
  entry-values           repeats                vtbl
  frame-arguments        sevenbit-strings       
  inferior-events        static-members         

So we have boolean, integer and enum options, just like we have the
"set" command counterpart types.  In fact, the data structures used to
describe a command's options can (and are) used to install the
corresponding "set" commands.

This results in options like:

 (gdb) print -elements 100 -object off -- *myobj
 (gdb) backtrace -frame-arguments scalars
 (gdb) frame apply all -past-main

Also, command options are abbreviatable, boolean arguments are
optional (on is assumed), and you can write 0/1 instead of on/off too,
so you could replace the above with

 (gdb) print -e 100 -o 0 -- *myobj
 (gdb) backtrace -fr s
 (gdb) f ap a -past-m

BTW, you'll notice that the framework doesn't split arguments in an
array of arguments, getopt-/buildargv- style.  The reason for that is
that many commands in gdb accept raw, unformatted arguments,
expressions, and we can't split those up around whitespace.

So the series fixes a number of latent bugs exposed while writing
this, adds the options infrastructure proper along with its testsuite
(patch #12), and converts a number of commands to use the
infrastructure: "print"/"compile print", "compile code/file",
"backtrace", and "thread/frame apply" & friends.

Patch #9 adds testing of the "set"/"show" commands' parsing/completion
mechanics.

For print in particular, I settled on requiring "--" if you specify
any option.  I'm open to opinions otherwise, but please read through
the series, and particularly patch #13 for more details around that
choice.

Documentation is mostly in the last patch of the series, though there
are bits in patch #09 and patch #15 too.

Pedro Alves (24):
  Fix latent bug in custom word point completion handling
  Fix latent bug with custom word point completers
  Fix TID parser bug
  Make check_for_argument skip whitespace after arg itself
  Allow "unlimited" abbreviations
  Fix "set enum-command value garbage"
  Remove "show" command completers, "set" command completers for string
    commands
  gdb.base/completion.exp: Fix comment typo
  New set/show testing framework (gdb.base/settings.exp)
  boolean/auto-boolean commands, make "o" ambiguous
  number_or_range_parser::get_number, don't treat "1 -" as a range
  Introduce generic command options framework
  Make "print" and "compile print" support -OPT options
  Migrate rest of compile commands to new options framework
  "set print raw frame-arguments" -> "set print raw-frame-arguments"
  Make "backtrace" support -OPT options
  "backtrace full/no-filters/hide" completer
  lib/completion-support.exp: Add test_gdb_completion_offers_commands
  Introduce complete_command
  Make "frame apply" support -OPT options
  "thread apply 1 -- -" vs "frame apply level 0 -- -"
  Make "thread apply" use the gdb::option framework
  Delete parse_flags/parse_flags_qcs
  NEWS and manual changes for command options changes

 gdb/doc/gdb.texinfo                        | 344 +++++++++--
 gdb/NEWS                                   | 103 ++++
 gdb/Makefile.in                            |   3 +
 gdb/ax-gdb.c                               |   2 -
 gdb/breakpoint.c                           |   5 +-
 gdb/cli/cli-decode.c                       |  27 +-
 gdb/cli/cli-decode.h                       |   4 +
 gdb/cli/cli-option.c                       | 732 +++++++++++++++++++++++
 gdb/cli/cli-option.h                       | 335 +++++++++++
 gdb/cli/cli-setshow.c                      | 290 +++++----
 gdb/cli/cli-setshow.h                      |  27 +
 gdb/cli/cli-utils.c                        | 124 ++--
 gdb/cli/cli-utils.h                        |  62 +-
 gdb/command.h                              |  19 +-
 gdb/compile/compile.c                      | 215 ++++---
 gdb/completer.c                            |  84 ++-
 gdb/completer.h                            |  20 +-
 gdb/cp-valprint.c                          |  57 --
 gdb/frame.c                                |  84 +--
 gdb/frame.h                                |  55 +-
 gdb/maint-test-options.c                   | 460 ++++++++++++++
 gdb/maint-test-settings.c                  | 257 ++++++++
 gdb/mi/mi-cmd-stack.c                      |  23 +-
 gdb/printcmd.c                             |  94 ++-
 gdb/python/py-framefilter.c                |   3 +-
 gdb/stack.c                                | 620 ++++++++++++++-----
 gdb/stack.h                                |   5 +
 gdb/testsuite/gdb.base/completion.exp      |   2 +-
 gdb/testsuite/gdb.base/options.c           |  33 ++
 gdb/testsuite/gdb.base/options.exp         | 923 +++++++++++++++++++++++++++++
 gdb/testsuite/gdb.base/settings.c          |  23 +
 gdb/testsuite/gdb.base/settings.exp        | 542 +++++++++++++++++
 gdb/testsuite/gdb.compile/compile.exp      |  15 +-
 gdb/testsuite/gdb.guile/scm-frame-args.exp |   4 +-
 gdb/testsuite/gdb.multi/tids.exp           |  16 +-
 gdb/testsuite/gdb.python/py-frame-args.exp |   4 +-
 gdb/testsuite/lib/completion-support.exp   |  66 ++-
 gdb/thread.c                               | 285 ++++++---
 gdb/tid-parse.c                            |  16 +-
 gdb/tid-parse.h                            |   3 +
 gdb/unittests/cli-utils-selftests.c        | 133 -----
 gdb/valprint.c                             | 280 ++++++---
 gdb/valprint.h                             |  20 +-
 43 files changed, 5497 insertions(+), 922 deletions(-)
 create mode 100644 gdb/cli/cli-option.c
 create mode 100644 gdb/cli/cli-option.h
 create mode 100644 gdb/maint-test-options.c
 create mode 100644 gdb/maint-test-settings.c
 create mode 100644 gdb/testsuite/gdb.base/options.c
 create mode 100644 gdb/testsuite/gdb.base/options.exp
 create mode 100644 gdb/testsuite/gdb.base/settings.c
 create mode 100644 gdb/testsuite/gdb.base/settings.exp

-- 
2.14.5

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

* [PATCH v2 11/24] number_or_range_parser::get_number, don't treat "1 -" as a range
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
  2019-05-30 19:53 ` [PATCH v2 15/24] "set print raw frame-arguments" -> "set print raw-frame-arguments" Pedro Alves
@ 2019-05-30 19:53 ` Pedro Alves
  2019-05-30 19:53 ` [PATCH v2 01/24] Fix latent bug in custom word point completion handling Pedro Alves
                   ` (22 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:53 UTC (permalink / raw)
  To: gdb-patches

While adding -OPT options to "frame apply level", I noticed that:

 (gdb) frame apply level 0 -[TAB]

wasn't completing on the supported options.  This commit fixes it.
We'll get instead:

  (gdb) frame apply level 0 -
  -c           -past-entry  -past-main   -q           -s

I added the isspace check because this case:

  (gdb) frame apply level 0-

can't be an option.

Tests for this will be in a new gdb.base/options.exp file, in a
following patch.  It will exercise all of:

  (gdb) frame apply level 0-
  (gdb) frame apply level 0 -
  (gdb) frame apply level 0 --
  (gdb) frame apply level 0 -- -

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* cli/cli-utils.c (number_or_range_parser::get_number): Do not
	parse a range if "-" is at the end of the string.
---
 gdb/cli/cli-utils.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/gdb/cli/cli-utils.c b/gdb/cli/cli-utils.c
index a24fe9278c7..23296cee9c3 100644
--- a/gdb/cli/cli-utils.c
+++ b/gdb/cli/cli-utils.c
@@ -233,10 +233,18 @@ number_or_range_parser::get_number ()
       /* Default case: state->m_cur_tok is pointing either to a solo
 	 number, or to the first number of a range.  */
       m_last_retval = get_number_trailer (&m_cur_tok, '-');
-      /* If get_number_trailer has found a -, it might be the start
-	 of a command option.  So, do not parse a range if the - is
-	 followed by an alpha.  */
-      if (*m_cur_tok == '-' && !isalpha (*(m_cur_tok + 1)))
+      /* If get_number_trailer has found a '-' preceded by a space, it
+	 might be the start of a command option.  So, do not parse a
+	 range if the '-' is followed by an alpha or another '-'.  We
+	 might also be completing something like
+	 "frame apply level 0 -" and we prefer treating that "-" as an
+	 option rather than an incomplete range, so check for end of
+	 string as well.  */
+      if (m_cur_tok[0] == '-'
+	  && !(isspace (m_cur_tok[-1])
+	       && (isalpha (m_cur_tok[1])
+		   || m_cur_tok[1] == '-'
+		   || m_cur_tok[1] == '\0')))
 	{
 	  const char **temp;
 
-- 
2.14.5

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

* [PATCH v2 01/24] Fix latent bug in custom word point completion handling
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
  2019-05-30 19:53 ` [PATCH v2 15/24] "set print raw frame-arguments" -> "set print raw-frame-arguments" Pedro Alves
  2019-05-30 19:53 ` [PATCH v2 11/24] number_or_range_parser::get_number, don't treat "1 -" as a range Pedro Alves
@ 2019-05-30 19:53 ` Pedro Alves
  2019-05-30 19:53 ` [PATCH v2 02/24] Fix latent bug with custom word point completers Pedro Alves
                   ` (21 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:53 UTC (permalink / raw)
  To: gdb-patches

Without this fix, if we switch the "print" completer to custom word
point handling, we regress gdb.base/completion.exp like this:

 (gdb) p "break1.c FAIL: gdb.base/completion.exp: complete 'p "break1' (timeout)

The problem is that completing an expression that starts with double
quotes, and resolves to a filename, like this:

 (gdb) p "break1[TAB]

would change from this, with current master:

 (gdb) p "break1.c"|
         ^^^^^^^^^^|
                   \- cursor here

to this:

 (gdb) p "break1.c |
         ^^^^^^^^^^|
                   \- quote replaced by space

The issue is that completer.c:advance_to_completion_word misses
telling the completion tracker to emulate readline's handling of
completing a string when rl_find_completion_word returns a delimiter.

This commit fixes the latent bug.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* completer.c (advance_to_completion_word): Handle delimiters.
---
 gdb/completer.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/gdb/completer.c b/gdb/completer.c
index 24c84466307..d652cb8a24f 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -365,11 +365,18 @@ advance_to_expression_complete_word_point (completion_tracker &tracker,
   info.quote_characters = gdb_completer_quote_characters;
   info.basic_quote_characters = rl_basic_quote_characters;
 
+  int delimiter;
   const char *start
-    = gdb_rl_find_completion_word (&info, NULL, NULL, text);
+    = gdb_rl_find_completion_word (&info, NULL, &delimiter, text);
 
   tracker.advance_custom_word_point_by (start - text);
 
+  if (delimiter)
+    {
+      tracker.set_quote_char (delimiter);
+      tracker.set_suppress_append_ws (true);
+    }
+
   return start;
 }
 
-- 
2.14.5

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

* [PATCH v2 02/24] Fix latent bug with custom word point completers
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (2 preceding siblings ...)
  2019-05-30 19:53 ` [PATCH v2 01/24] Fix latent bug in custom word point completion handling Pedro Alves
@ 2019-05-30 19:53 ` Pedro Alves
  2019-05-30 19:53 ` [PATCH v2 06/24] Fix "set enum-command value garbage" Pedro Alves
                   ` (20 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:53 UTC (permalink / raw)
  To: gdb-patches

Completion routines that use a custom word point, and that then
recurse into complete_line (e.g., if we make "thread apply" a custom
word point completer, and complete on the command passed as argument),
we stumble on this latent bug:

 (gdb) thread apply all pri[TAB]
 (gdb) thread apply all priprint

The problem is that there's a spot in complete_line_internal_1 that
rewinds the completion word but does not reflect that change in the
custom word point in the tracker.  This patch fixes it.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* completer.c (complete_line_internal_1): Rewind completion word
	point.
	(completion_tracker::advance_custom_word_point_by): Change
	parameter type to int.
	* completer.h (completion_tracker::advance_custom_word_point_by):
	Likewise.
---
 gdb/completer.c | 5 ++++-
 gdb/completer.h | 2 +-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/gdb/completer.c b/gdb/completer.c
index d652cb8a24f..b2768ede769 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -1405,6 +1405,9 @@ complete_line_internal_1 (completion_tracker &tracker,
 		    break;
 		}
 
+	      /* Move the custom word point back too.  */
+	      tracker.advance_custom_word_point_by (q - p);
+
 	      if (reason != handle_brkchars)
 		complete_on_cmdlist (result_list, tracker, q, word,
 				     ignore_help_classes);
@@ -1983,7 +1986,7 @@ completion_tracker::recompute_lowest_common_denominator
 /* See completer.h.  */
 
 void
-completion_tracker::advance_custom_word_point_by (size_t len)
+completion_tracker::advance_custom_word_point_by (int len)
 {
   m_custom_word_point += len;
 }
diff --git a/gdb/completer.h b/gdb/completer.h
index 38a0132ca4d..27371b63a57 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -357,7 +357,7 @@ public:
   { m_custom_word_point = point; }
 
   /* Advance the custom word point by LEN.  */
-  void advance_custom_word_point_by (size_t len);
+  void advance_custom_word_point_by (int len);
 
   /* Whether to tell readline to skip appending a whitespace after the
      completion.  See m_suppress_append_ws.  */
-- 
2.14.5

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

* [PATCH v2 07/24] Remove "show" command completers, "set" command completers for string commands
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (7 preceding siblings ...)
  2019-05-30 19:54 ` [PATCH v2 24/24] NEWS and manual changes for command options changes Pedro Alves
@ 2019-05-30 19:54 ` Pedro Alves
  2019-06-03 18:55   ` Tom Tromey
  2019-05-30 19:54 ` [PATCH v2 05/24] Allow "unlimited" abbreviations Pedro Alves
                   ` (15 subsequent siblings)
  24 siblings, 1 reply; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:54 UTC (permalink / raw)
  To: gdb-patches

The default command completer is symbol_completer, but it makes no
sense for a "show" command to complete on symbols, or anything else,
really.

I wonder whether we should instead make the default be no completer.
That seems like a much larger/complicated audit/change, so I'd like to
move forward with this version, as it'll be covered by tests.  I
noticed this because a following patch will add a new
gdb.base/settings.exp testcase that exercises all sorts of details of
settings commands, including completing the show commands, using new
representative "maint test-settings <type or settings command>"
commands.

Also remove the completer for var_string and var_string_noescape
commands.  No point in completing symbols when GDB is expecting a
string.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* cli/cli-decode.c (add_setshow_cmd_full): Remove "show"
	completer.
	(add_setshow_string_cmd, add_setshow_string_noescape_cmd): Remove
	"set" completers.
---
 gdb/cli/cli-decode.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 72e2a970097..80158593b38 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -508,6 +508,9 @@ add_setshow_cmd_full (const char *name,
 			      full_show_doc, show_list);
   show->doc_allocated = 1;
   show->show_value_func = show_func;
+  /* Disable the default symbol completer.  Doesn't make much sense
+     for the "show" command to complete on anything.  */
+  set_cmd_completer (show, nullptr);
 
   if (set_result != NULL)
     *set_result = set;
@@ -632,11 +635,16 @@ add_setshow_string_cmd (const char *name, enum command_class theclass,
 			struct cmd_list_element **set_list,
 			struct cmd_list_element **show_list)
 {
+  cmd_list_element *set_cmd;
+
   add_setshow_cmd_full (name, theclass, var_string, var,
 			set_doc, show_doc, help_doc,
 			set_func, show_func,
 			set_list, show_list,
-			NULL, NULL);
+			&set_cmd, NULL);
+
+  /* Disable the default symbol completer.  */
+  set_cmd_completer (set_cmd, nullptr);
 }
 
 /* Add element named NAME to both the set and show command LISTs (the
@@ -658,6 +666,10 @@ add_setshow_string_noescape_cmd (const char *name, enum command_class theclass,
 			set_func, show_func,
 			set_list, show_list,
 			&set_cmd, NULL);
+
+  /* Disable the default symbol completer.  */
+  set_cmd_completer (set_cmd, nullptr);
+
   return set_cmd;
 }
 
-- 
2.14.5

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

* [PATCH v2 24/24] NEWS and manual changes for command options changes
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (6 preceding siblings ...)
  2019-05-30 19:54 ` [PATCH v2 08/24] gdb.base/completion.exp: Fix comment typo Pedro Alves
@ 2019-05-30 19:54 ` Pedro Alves
  2019-05-30 19:54 ` [PATCH v2 07/24] Remove "show" command completers, "set" command completers for string commands Pedro Alves
                   ` (16 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:54 UTC (permalink / raw)
  To: gdb-patches

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* NEWS (New commands): Mention "maint test-options
	require-delimiter", "maint test-options unknown-is-error", "maint
	test-options unknown-is-operand" and "maint show
	test-options-completion-result".
	(New command options, command completion): New section.
	(Completion improvements): New section.
	Mention that you can abbreviate "unlimited".

gdb/doc/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.texinfo (Command Completion): Mention command options too.
	(Command Options): New node.
	(Threads): Add anchors.  Extend descriptions of the "taas" and
	"tfaas" commands.
	(Backtrace): Describe new options of the "backtrace" command.  Add
	anchors.
	(Frame Apply): Describe new options of the "frame apply" and
	"faas" commands.  Add anchors.
	(Data): Describe new options of the "print" command.  Add anchors.
	(Compiling and Injecting Code): Mention options of the "compile
	print" command.
	(Maintenance Commands): Mention "maint test-options" subcommands
	and the "maint show test-options-completion-result" command.
---
 gdb/doc/gdb.texinfo | 332 ++++++++++++++++++++++++++++++++++++++++++++++------
 gdb/NEWS            |  91 ++++++++++++++
 2 files changed, 389 insertions(+), 34 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 92d1aae40a2..99aa38030f6 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -1497,6 +1497,7 @@ show you the alternatives available, if there is more than one possibility).
 @menu
 * Command Syntax::              How to give commands to @value{GDBN}
 * Completion::                  Command completion
+* Command Options::             Command options
 * Help::                        How to ask @value{GDBN} for help
 @end menu
 
@@ -1559,7 +1560,8 @@ for editing.
 @value{GDBN} can fill in the rest of a word in a command for you, if there is
 only one possibility; it can also show you what the valid possibilities
 are for the next word in a command, at any time.  This works for @value{GDBN}
-commands, @value{GDBN} subcommands, and the names of symbols in your program.
+commands, @value{GDBN} subcommands, command options, and the names of symbols
+in your program.
 
 Press the @key{TAB} key whenever you want @value{GDBN} to fill out the rest
 of a word.  If there is only one possibility, @value{GDBN} fills in the
@@ -1759,6 +1761,70 @@ struct ui_file
 @}
 @end smallexample
 
+@node Command Options
+@section Command options
+
+@cindex command options
+Some commands accept options starting with a leading dash.  For
+example, @code{print -pretty}.  Similarly to command names, you can
+abbreviate a @value{GDBN} option to the first few letters of the
+option name, if that abbreviation is unambiguous, and you can also use
+the @key{TAB} key to get @value{GDBN} to fill out the rest of a word
+in an option (or to show you the alternatives available, if there is
+more than one possibility).
+
+@cindex command options, raw input
+Some commands take raw input as argument.  For example, the print
+command processes arbitrary expressions in any of the languages
+supported by @value{GDBN}.  With such commands, because raw input may
+start with a leading dash that would be confused with an option or any
+of its abbreviations, e.g.@: @code{print -r} (short for @code{print
+-raw} or printing negative @code{r}?), if you specify any command
+option, then you must use a double-dash (@code{--}) delimiter to
+indicate the end of options.
+
+@cindex command options, boolean
+
+Some options are described as accepting an argument which can be
+either @code{on} or @code{off}.  These are known as @dfn{boolean
+options}.  Similarly to boolean settings commands---@code{on} and
+@code{off} are the typical values, but any of @code{1}, @code{yes} and
+@code{enable} can also be used as ``true'' value, and any of @code{0},
+@code{no} and @code{disable} can also be used as ``false'' value.  You
+can also omit a ``true'' value, as it is implied by default.
+
+For example, these are equivalent:
+
+@smallexample
+(@value{GDBP}) print -object on -pretty off -element unlimited -- *myptr
+(@value{GDBP}) p -o -p 0 -e u -- *myptr
+@end smallexample
+
+You can discover the set of options some command accepts by completing
+on @code{-} after the command name.  For example:
+
+@smallexample
+(@value{GDBP}) print -@key{TAB}@key{TAB}
+-address         -max-depth       -repeats         -vtbl
+-array           -null-stop       -static-members
+-array-indexes   -object          -symbol
+-elements        -pretty          -union
+@end smallexample
+
+Completion will in some cases guide you with a suggestion of what kind
+of argument an option expects.  For example:
+
+@smallexample
+(@value{GDBP}) print -elements @key{TAB}@key{TAB}
+NUMBER     unlimited
+@end smallexample
+
+Here, the option expects a number (e.g., @code{100}), not literal
+@code{NUMBER}.  Such metasyntactical arguments are always presented in
+uppercase.
+
+(For more on using the @code{print} command, see @ref{Data, ,Examining
+Data}.)
 
 @node Help
 @section Getting Help
@@ -3180,6 +3246,7 @@ As with the @samp{[New @dots{}]} message, the form of the text after
 @samp{Switching to} depends on your system's conventions for identifying
 threads.
 
+@anchor{thread apply all}
 @kindex thread apply
 @cindex apply command to several threads
 @item thread apply [@var{thread-id-list} | all [-ascending]] [@var{flag}]@dots{} @var{command}
@@ -3221,14 +3288,17 @@ Flags @code{-c} and @code{-s} cannot be used together.
 
 @kindex taas
 @cindex apply command to all threads (ignoring errors and empty output)
-@item taas @var{command}
-Shortcut for @code{thread apply all -s @var{command}}.
+@item taas [@var{option}]@dots{} @var{command}
+Shortcut for @code{thread apply all -s [@var{option}]@dots{} @var{command}}.
 Applies @var{command} on all threads, ignoring errors and empty output.
 
+The @code{taas} command accepts the same options as the @code{thread
+apply all} command.  @xref{thread apply all}.
+
 @kindex tfaas
 @cindex apply a command to all frames of all threads (ignoring errors and empty output)
-@item tfaas @var{command}
-Shortcut for @code{thread apply all -s frame apply all -s @var{command}}.
+@item tfaas [@var{option}]@dots{} @var{command}
+Shortcut for @code{thread apply all -s -- frame apply all -s [@var{option}]@dots{} @var{command}}.
 Applies @var{command} on all frames of all threads, ignoring errors
 and empty output.  Note that the flag @code{-s} is specified twice:
 The first @code{-s} ensures that @code{thread apply} only shows the thread
@@ -3244,6 +3314,8 @@ is, using:
 (@value{GDBP}) tfaas p some_local_var_i_do_not_remember_where_it_is
 @end smallexample
 
+The @code{tfaas} command accepts the same options as the @code{frame
+apply} command.  @xref{frame apply}.
 
 @kindex thread name
 @cindex name a thread
@@ -7485,10 +7557,11 @@ printed.  You can stop the backtrace at any time by typing the system
 interrupt character, normally @kbd{Ctrl-c}.
 
 @table @code
-@item backtrace [@var{args}@dots{}]
-@itemx bt [@var{args}@dots{}]
-Print the backtrace of the entire stack.  The optional @var{args} can
-be one of the following:
+@item backtrace [@var{option}]@dots{} [@var{qualifier}]@dots{} [@var{count}]
+@itemx bt [@var{option}]@dots{} [@var{qualifier}]@dots{} [@var{count}]
+Print the backtrace of the entire stack.
+
+The optional @var{count} can be one of the following:
 
 @table @code
 @item @var{n}
@@ -7500,24 +7573,69 @@ number.
 @itemx -@var{n}
 Print only the outermost @var{n} frames, where @var{n} is a positive
 number.
+@end table
 
-@item full
+Options:
+
+@table @code
+@item -full
 Print the values of the local variables also.  This can be combined
-with a number to limit the number of frames shown.
+with the optional @var{count} to limit the number of frames shown.
 
-@item no-filters
+@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
 relevant when @value{GDBN} has been configured with @code{Python}
 support.
 
-@item hide
+@item -hide
 A Python frame filter might decide to ``elide'' some frames.  Normally
 such elided frames are still printed, but they are indented relative
-to the filtered frames that cause them to be elided.  The @code{hide}
+to the filtered frames that cause them to be elided.  The @code{-hide}
 option causes elided frames to not be printed at all.
 @end table
+
+The @code{backtrace} command also supports a number of options that
+allow overriding relevant global print settings as set by @code{set
+backtrace} and @code{set print} subcommands:
+
+@table @code
+@item -past-main [@code{on}|@code{off}]
+Set whether backtraces should continue past @code{main}.  Related setting:
+@ref{set backtrace past-main}.
+
+@item -past-entry [@code{on}|@code{off}]
+Set whether backtraces should continue past the entry point of a program.
+Related setting: @ref{set backtrace past-entry}.
+
+@item -entry-values @code{no}|@code{only}|@code{preferred}|@code{if-needed}|@code{both}|@code{compact}|@code{default}
+Set printing of function arguments at function entry.
+Related setting: @ref{set print entry-values}.
+
+@item -frame-arguments @code{all}|@code{scalars}|@code{none}
+Set printing of non-scalar frame arguments.
+Related setting: @ref{set print frame-arguments}.
+
+@item -raw-frame-arguments [@code{on}|@code{off}]
+Set whether to print frame arguments in raw form.
+Related setting: @ref{set print raw-frame-arguments}.
+@end table
+
+The optional @var{qualifier} is maintained for backward compatibility.
+It can be one of the following:
+
+@table @code
+@item full
+Equivalent to the @code{-full} option.
+
+@item no-filters
+Equivalent to the @code{-no-filters} option.
+
+@item hide
+Equivalent to the @code{-hide} option.
+@end table
+
 @end table
 
 @kindex where
@@ -7615,6 +7733,7 @@ in a backtrace, you can change this behavior:
 @table @code
 @item set backtrace past-main
 @itemx set backtrace past-main on
+@anchor{set backtrace past-main}
 @kindex set backtrace
 Backtraces will continue past the user entry point.
 
@@ -7628,6 +7747,7 @@ Display the current user entry point backtrace policy.
 
 @item set backtrace past-entry
 @itemx set backtrace past-entry on
+@anchor{set backtrace past-entry}
 Backtraces will continue past the internal entry point of an application.
 This entry point is encoded by the linker when the application is built,
 and is likely before the user entry point @code{main} (or equivalent) is called.
@@ -7642,6 +7762,7 @@ Display the current internal entry point backtrace policy.
 @item set backtrace limit @var{n}
 @itemx set backtrace limit 0
 @itemx set backtrace limit unlimited
+@anchor{set backtrace limit}
 @cindex backtrace limit
 Limit the backtrace to @var{n} levels.  A value of @code{unlimited}
 or zero means unlimited levels.
@@ -7935,10 +8056,11 @@ tfaas i lo -q -t lock_something_t
 
 @node Frame Apply
 @section Applying a Command to Several Frames.
+@anchor{frame apply}
 @kindex frame apply
 @cindex apply command to several frames
 @table @code
-@item frame apply [all | @var{count} | @var{-count} | level @var{level}@dots{}] [@var{flag}]@dots{} @var{command}
+@item frame apply [all | @var{count} | @var{-count} | level @var{level}@dots{}] [@var{option}]@dots{} @var{command}
 The @code{frame apply} command allows you to apply the named
 @var{command} to one or more frames.
 
@@ -7964,23 +8086,28 @@ at levels 2, 3, 4, 6, 7, 8, and then again on frame at level 3.
 
 @end table
 
-@end table
-
 Note that the frames on which @code{frame apply} applies a command are
 also influenced by the @code{set backtrace} settings such as @code{set
-backtrace past-main} and @code{set backtrace limit N}.  See
+backtrace past-main} and @code{set backtrace limit N}.
 @xref{Backtrace,,Backtraces}.
 
-The @var{flag} arguments control what output to produce and how to handle
-errors raised when applying @var{command} to a frame.  @var{flag}
-must start with a @code{-} directly followed by one letter in
-@code{qcs}.  If several flags are provided, they must be given
-individually, such as @code{-c -q}.
+The @code{frame apply} command also supports a number of options that
+allow overriding relevant @code{set backtrace} settings:
+
+@table @code
+@item -past-main [@code{on}|@code{off}]
+Whether backtraces should continue past @code{main}.
+Related setting: @ref{set backtrace past-main}.
+
+@item -past-entry [@code{on}|@code{off}]
+Whether backtraces should continue past the entry point of a program.
+Related setting: @ref{set backtrace past-entry}.
+@end table
 
 By default, @value{GDBN} displays some frame information before the
 output produced by @var{command}, and an error raised during the
 execution of a @var{command} will abort @code{frame apply}.  The
-following flags can be used to fine-tune this behavior:
+following options can be used to fine-tune these behaviors:
 
 @table @code
 @item -c
@@ -8033,7 +8160,7 @@ $5 = (void *) 0xffffd1f0
 @end group
 @end smallexample
 
-If flag @code{-q} is given, no frame information is printed:
+If the flag @code{-q} is given, no frame information is printed:
 @smallexample
 @group
 (gdb) frame apply all -q p $sp
@@ -8043,6 +8170,8 @@ $13 = (void *) 0xffffd1f0
 @end group
 @end smallexample
 
+@end table
+
 @table @code
 
 @kindex faas
@@ -8058,6 +8187,9 @@ is, using:
 (@value{GDBP}) faas p some_local_var_i_do_not_remember_where_it_is
 @end smallexample
 
+The @code{faas} command accepts the same options as the @code{frame
+apply} command.  @xref{frame apply}.
+
 Note that the command @code{tfaas @var{command}} applies @var{command}
 on all frames of all threads.  See @xref{Threads,,Threads}.
 @end table
@@ -9170,16 +9302,111 @@ Different Languages}).  It may also print the expression using a
 Python-based pretty-printer (@pxref{Pretty Printing}).
 
 @table @code
-@item print @var{expr}
-@itemx print /@var{f} @var{expr}
+@item print [[@var{options}] --] @var{expr}
+@itemx print [[@var{options}] --] /@var{f} @var{expr}
 @var{expr} is an expression (in the source language).  By default the
 value of @var{expr} is printed in a format appropriate to its data type;
 you can choose a different format by specifying @samp{/@var{f}}, where
 @var{f} is a letter specifying the format; see @ref{Output Formats,,Output
 Formats}.
 
-@item print
-@itemx print /@var{f}
+@anchor{print options}
+The @code{print} command supports a number of options that allow
+overriding relevant global print settings as set by @code{set print}
+subcommands:
+
+@table @code
+@item -address [@code{on}|@code{off}]
+Set printing of addresses.
+Related setting: @ref{set print address}.
+
+@item -array [@code{on}|@code{off}]
+Pretty formatting of arrays.
+Related setting: @ref{set print array}.
+
+@item -array-indexes [@code{on}|@code{off}]
+Set printing of array indexes.
+Related setting: @ref{set print array-indexes}.
+
+@item -elements @var{number-of-elements}|@code{unlimited}
+Set limit on string chars or array elements to print.  The value
+@code{unlimited} causes there to be no limit.  Related setting:
+@ref{set print elements}.
+
+@item -max-depth @var{depth}|@code{unlimited}
+Set the threshold after which nested structures are replaced with
+ellipsis.  Related setting: @ref{set print max-depth}.
+
+@item -null-stop [@code{on}|@code{off}]
+Set printing of char arrays to stop at first null char.  Related
+setting: @ref{set print null-stop}.
+
+@item -object [@code{on}|@code{off}]
+Set printing C@t{++} virtual function tables.  Related setting:
+@ref{set print object}.
+
+@item -pretty [@code{on}|@code{off}]
+Set pretty formatting of structures.  Related setting: @ref{set print
+pretty}.
+
+@item -repeats @var{number-of-repeats}|@code{unlimited}
+Set threshold for repeated print elements.  @code{unlimited} causes
+all elements to be individually printed.  Related setting: @ref{set
+print repeats}.
+
+@item -static-members [@code{on}|@code{off}]
+Set printing C@t{++} static members.  Related setting: @ref{set print
+static-members}.
+
+@item -symbol [@code{on}|@code{off}]
+Set printing of symbol names when printing pointers.  Related setting:
+@ref{set print symbol}.
+
+@item -union [@code{on}|@code{off}]
+Set printing of unions interior to structures.  Related setting:
+@ref{set print union}.
+
+@item -vtbl [@code{on}|@code{off}]
+Set printing of C++ virtual function tables.  Related setting:
+@ref{set print vtbl}.
+@end table
+
+Because the @code{print} command accepts arbitrary expressions which
+may look like options (including abbreviations), if you specify any
+command option, then you must use a double dash (@code{--}) to mark
+the end of option processing.
+
+For example, this prints the value of the @code{-r} expression:
+
+@smallexample
+(@value{GDBP}) print -r
+@end smallexample
+
+While this repeats the last value in the value history (see below)
+with the @code{-raw} option in effect:
+
+@smallexample
+(@value{GDBP}) print -r --
+@end smallexample
+
+Here is an example including both on option and an expression:
+
+@smallexample
+@group
+(@value{GDBP}) print -pretty -- *myptr
+$1 = @{
+  next = 0x0,
+  flags = @{
+    sweet = 1,
+    sour = 1
+  @},
+  meat = 0x54 "Pork"
+@}
+@end group
+@end smallexample
+
+@item print [@var{options}]
+@itemx print [@var{options}] /@var{f}
 @cindex reprint the last value
 If you omit @var{expr}, @value{GDBN} displays the last value again (from the
 @dfn{value history}; @pxref{Value History, ,Value History}).  This allows you to
@@ -10220,6 +10447,7 @@ These settings are useful for debugging programs in any language:
 
 @table @code
 @kindex set print
+@anchor{set print address}
 @item set print address
 @itemx set print address on
 @cindex print/don't print memory addresses
@@ -10331,6 +10559,7 @@ the appropriate @code{set print} options turned on.
 You can also enable @samp{/a}-like formatting all the time using
 @samp{set print symbol on}:
 
+@anchor{set print symbol}
 @table @code
 @item set print symbol on
 Tell @value{GDBN} to print the symbol corresponding to an address, if
@@ -10349,6 +10578,7 @@ address.
 Other settings control how different kinds of objects are printed:
 
 @table @code
+@anchor{set print array}
 @item set print array
 @itemx set print array on
 @cindex pretty print arrays
@@ -10363,6 +10593,7 @@ Show whether compressed or pretty format is selected for displaying
 arrays.
 
 @cindex print array indexes
+@anchor{set print array-indexes}
 @item set print array-indexes
 @itemx set print array-indexes on
 Print the index of each element when displaying arrays.  May be more
@@ -10376,6 +10607,7 @@ Stop printing element indexes when displaying arrays.
 Show whether the index of each element is printed when displaying
 arrays.
 
+@anchor{set print elements}
 @item set print elements @var{number-of-elements}
 @itemx set print elements unlimited
 @cindex number of array elements to print
@@ -10392,6 +10624,7 @@ that the number of elements to print is unlimited.
 Display the number of elements of a large array that @value{GDBN} will print.
 If the number is 0, then the printing is unlimited.
 
+@anchor{set print frame-arguments}
 @item set print frame-arguments @var{value}
 @kindex set print frame-arguments
 @cindex printing frame argument values
@@ -10441,6 +10674,7 @@ thus speeding up the display of each Ada frame.
 @item show print frame-arguments
 Show how the value of arguments should be displayed when printing a frame.
 
+@anchor{set print raw-frame-arguments}
 @item set print raw-frame-arguments on
 Print frame arguments in raw, non pretty-printed, form.
 
@@ -10568,6 +10802,7 @@ entry resolution see @ref{set debug entry-values}.
 Show the method being used for printing of frame argument values at function
 entry.
 
+@anchor{set print repeats}
 @item set print repeats @var{number-of-repeats}
 @itemx set print repeats unlimited
 @cindex repeated array elements
@@ -10584,6 +10819,7 @@ is 10.
 Display the current threshold for printing repeated identical
 elements.
 
+@anchor{set print max-depth}
 @item set print max-depth @var{depth}
 @item set print max-depth unlimited
 @cindex printing nested structures
@@ -10642,6 +10878,7 @@ language, for most languages @code{@{...@}} is used, but Fortran uses
 Display the current threshold after which nested structures are
 replaces with ellipsis.
 
+@anchor{set print null-stop}
 @item set print null-stop
 @cindex @sc{null} elements in arrays
 Cause @value{GDBN} to stop printing the characters of an array when the first
@@ -10653,6 +10890,7 @@ The default is off.
 Show whether @value{GDBN} stops printing an array on the first
 @sc{null} character.
 
+@anchor{set print pretty}
 @item set print pretty on
 @cindex print structures in indented form
 @cindex indentation in structure display
@@ -10704,6 +10942,7 @@ international character sets, and is the default.
 @item show print sevenbit-strings
 Show whether or not @value{GDBN} is printing only seven-bit characters.
 
+@anchor{set print union}
 @item set print union on
 @cindex unions in structures, printing
 Tell @value{GDBN} to print unions which are contained in structures
@@ -10793,6 +11032,7 @@ decoding style by inspecting your program.
 @item show demangle-style
 Display the encoding style currently in use for decoding C@t{++} symbols.
 
+@anchor{set print object}
 @item set print object
 @itemx set print object on
 @cindex derived type of an object, printing
@@ -10812,6 +11052,7 @@ virtual function table.  This is the default setting.
 @item show print object
 Show whether actual, or declared, object types are displayed.
 
+@anchor{set print static-members}
 @item set print static-members
 @itemx set print static-members on
 @cindex static members of C@t{++} objects
@@ -10836,6 +11077,7 @@ Do not print static members when displaying a Pascal object.
 Show whether Pascal static members are printed or not.
 
 @c These don't work with HP ANSI C++ yet.
+@anchor{set print vtbl}
 @item set print vtbl
 @itemx set print vtbl on
 @cindex pretty print C@t{++} virtual function tables
@@ -18966,17 +19208,18 @@ compile file /home/user/example.c
 @end table
 
 @table @code
-@item compile print @var{expr}
-@itemx compile print /@var{f} @var{expr}
+@item compile print [[@var{options}] --] @var{expr}
+@itemx compile print [[@var{options}] --] /@var{f} @var{expr}
 Compile and execute @var{expr} with the compiler language found as the
 current language in @value{GDBN} (@pxref{Languages}).  By default the
 value of @var{expr} is printed in a format appropriate to its data type;
 you can choose a different format by specifying @samp{/@var{f}}, where
 @var{f} is a letter specifying the format; see @ref{Output Formats,,Output
-Formats}.
+Formats}.  The @code{compile print} command accepts the same options
+as the @code{print} command; see @ref{print options}.
 
-@item compile print
-@itemx compile print /@var{f}
+@item compile print [[@var{options}] --]
+@itemx compile print [[@var{options}] --] /@var{f}
 @cindex reprint the last value
 Alternatively you can enter the expression (source code producing it) as
 multiple lines of text.  To enter this mode, invoke the @samp{compile print}
@@ -37093,6 +37336,27 @@ If section was not specified, the section in which the symbol was found
 is also printed.  For dynamically linked executables, the name of
 executable or shared library containing the symbol is printed as well.
 
+@kindex maint test-options
+@item maint test-options require-delimiter
+@itemx maint test-options unknown-is-error
+@itemx maint test-options unknown-is-operand
+These commands are used by the testsuite to validate the command
+options framework.  The @code{require-delimiter} variant requires a
+double-dash delimiter to indicate end of options.  The
+@code{unknown-is-error} and @code{unknown-is-operand} do not.  The
+@code{unknown-is-error} variant throws an error on unknown option,
+while @code{unknown-is-operand} treats unknown options as the start of
+the command's operands.  When run, the commands output the result of
+the processed options.  When completed, the commands store the
+internal result of completion in a variable exposed by the @code{maint
+show test-options-completion-result} command.
+
+@kindex maint show test-options-completion-result
+@item maint show test-options-completion-result
+Shows the result of completing the @code{maint test-options}
+subcommands.  This is used by the testsuite to validate completion
+support in the command options framework.
+
 @kindex maint test-settings
 @item maint test-settings set @var{kind}
 @itemx maint test-settings show @var{kind}
diff --git a/gdb/NEWS b/gdb/NEWS
index 969c4c4ea88..98965392981 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -69,6 +69,97 @@ show print raw-frame-arguments
   old commands are now deprecated and may be removed in a future
   release.
 
+maint test-options require-delimiter
+maint test-options unknown-is-error
+maint test-options unknown-is-operand
+maint show test-options-completion-result
+  Commands used by the testsuite to validate the command options
+  framework.
+
+* New command options, command completion
+
+  GDB now has a standard infrastructure to support dash-style command
+  options ('-OPT').  One benefit is that commands that use it can
+  easily support completion of command line arguments.  Try "CMD
+  -[TAB]" or "help CMD" to find options supported by a command.  Over
+  time, we intend to migrate most commands to this infrastructure.  A
+  number of commands got support for new command options in this
+  release:
+
+  ** The "print" and "compile print" commands now support a number of
+     options that allow overriding relevant global print settings as
+     set by "set print" subcommands:
+
+      -address [on|off]
+      -array [on|off]
+      -array-indexes [on|off]
+      -elements NUMBER|unlimited
+      -null-stop [on|off]
+      -object [on|off]
+      -pretty [on|off]
+      -repeats NUMBER|unlimited
+      -static-members [on|off]
+      -symbol [on|off]
+      -union [on|off]
+      -vtbl [on|off]
+
+     Note that because the "print"/"compile print" commands accept
+     arbitrary expressions which may look like options (including
+     abbreviations), if you specify any command option, then you must
+     use a double dash ("--") to mark the end of argument processing.
+
+  ** The "backtrace" command now supports a number of options that
+     allow overriding relevant global print settings as set by "set
+     backtrace" and "set print" subcommands:
+
+      -entry-values no|only|preferred|if-needed|both|compact|default
+      -frame-arguments all|scalars|none
+      -raw-frame-arguments [on|off]
+      -past-main [on|off]
+      -past-entry [on|off]
+
+     In addition, the full/no-filters/hide qualifiers are now also
+     exposed as command options too:
+
+      -full
+      -no-filters
+      -hide
+
+  ** The "frame apply", "tfaas" and "faas" commands similarly now
+     support the following options:
+
+      -past-main [on|off]
+      -past-entry [on|off]
+
+   All options above can also be abbreviated.  The argument of boolean
+   (on/off) options can be 0/1 too, and also the argument is assumed
+   "on" if omitted.  This allows writing compact command invocations,
+   like for example:
+
+    (gdb) p -r -p -o 0 -- *myptr
+
+   The above is equivalent to:
+
+    (gdb) print -raw -pretty -object off -- *myptr
+
+* Completion improvements
+
+  ** GDB can now complete the options of the "thread apply all" and
+     "taas" commands, and their "-ascending" option can now be
+     abbreviated.
+
+  ** GDB can now complete the options of the "compile file" and
+     "compile code" commands.  The "compile file" command now
+     completes on filenames.
+
+  ** GDB can now complete the backtrace command's
+     "full/no-filters/hide" qualifiers.
+
+* In settings, you can now abbreviate "unlimited".
+
+  E.g., "set print elements u" is now equivalent to "set print
+  elements unlimited".
+
 * New MI commands
 
 -complete
-- 
2.14.5

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

* [PATCH v2 03/24] Fix TID parser bug
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (13 preceding siblings ...)
  2019-05-30 19:54 ` [PATCH v2 12/24] Introduce generic command options framework Pedro Alves
@ 2019-05-30 19:54 ` Pedro Alves
  2019-05-30 19:59 ` [PATCH v2 22/24] Make "thread apply" use the gdb::option framework Pedro Alves
                   ` (9 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:54 UTC (permalink / raw)
  To: gdb-patches

I noticed this inconsistency in the error messages below:

 (gdb) print --1
 Left operand of assignment is not an lvalue.

 (gdb) thread apply 1 print --1

 Thread 1 (Thread 0x7ffff7fb6740 (LWP 17805)):
 inverted range

The "inverted range" error happens because get_number_trailer returns
0 to indicate error, but number_or_range_parser::get_number is not
checking for that.  I tried detected the error there, but that doesn't
work because number_of_range_parser is used in places that _do_ want
to legitimately handle 0.  IMO we should fix get_number_trailer's
interface or use something else when we want to parse 0 too.

I've decided to fix it in a different way, similarly to how
number_or_range_parser::finished was changed in commit 529c08b25ec7
("Add helper functions parse_flags and parse_flags_qcs").

Seems like a good change, even if we tweaked
number_or_range_parser::get_number, as it simplifies
thread_apply_command and makes them consistent with
number_or_range_parser::finished().

We now get the same error message in both cases:

 (gdb) print --1
 Left operand of assignment is not an lvalue.

 (gdb) thread apply 1 print --1

 Thread 1 (Thread 0x7ffff7fb6740 (LWP 17805)):
 Left operand of assignment is not an lvalue.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* thread.c (thread_apply_command): Adjust TID parsing.
	* tid-parse.c (tid_range_parser::finished): Ensure parsing end is
	detected before end of string.
	(tid_is_in_list): Error out if LIST is invalid.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.multi/tids.exp: Adjust expected output.  Add "thread apply 1
	foo --1" test.
---
 gdb/testsuite/gdb.multi/tids.exp | 16 ++++++++++++++--
 gdb/thread.c                     | 15 ++++++---------
 gdb/tid-parse.c                  | 10 +++++++++-
 3 files changed, 29 insertions(+), 12 deletions(-)

diff --git a/gdb/testsuite/gdb.multi/tids.exp b/gdb/testsuite/gdb.multi/tids.exp
index 617a1b0299c..3b0e1c1860a 100644
--- a/gdb/testsuite/gdb.multi/tids.exp
+++ b/gdb/testsuite/gdb.multi/tids.exp
@@ -350,8 +350,13 @@ with_test_prefix "two inferiors" {
 	thr_apply_info_thr_error "${prefix}1-" "inverted range"
 	thr_apply_info_thr_error "${prefix}2-1" "inverted range"
 	thr_apply_info_thr_error "${prefix}2-\$one" "inverted range"
-	thr_apply_info_thr_error "${prefix}-1" "negative value"
-	thr_apply_info_thr_error "${prefix}-\$one" "negative value"
+	if {$prefix == ""} {
+	    thr_apply_info_thr_error "${prefix}-1" "Invalid thread ID: -1"
+	    thr_apply_info_thr_error "${prefix}-\$one" "Invalid thread ID: -\\\$one"
+	} else {
+	    thr_apply_info_thr_error "${prefix}-1" "negative value"
+	    thr_apply_info_thr_error "${prefix}-\$one" "negative value"
+	}
 	thr_apply_info_thr_error "${prefix}\$minus_one" \
 	    "negative value: ${prefix_re}\\\$minus_one"
 
@@ -374,6 +379,13 @@ with_test_prefix "two inferiors" {
 	gdb_test "thread apply 1.*" $output
     }
 
+    # Check that thread ID list parsing stops at the non-number token
+    # "foo" in a corner case where the "foo" is followed by hyphens.
+    # In this corner case, GDB used to skip past "foo", and then parse
+    # "--1" as a tid range for the current inferior.
+    gdb_test "thread apply 1 foo --1" \
+	"Undefined command: \"foo\".  Try \"help\"\\."
+
     # Check that we do parse the inferior number and don't confuse it.
     gdb_test "info threads 3.1" \
 	"No threads match '3.1'\."
diff --git a/gdb/thread.c b/gdb/thread.c
index 9a6a7735950..a84dbf9fa1e 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -1560,7 +1560,6 @@ thread_apply_command (const char *tidlist, int from_tty)
 {
   qcs_flags flags;
   const char *cmd = NULL;
-  const char *cmd_or_flags;
   tid_range_parser parser;
 
   if (tidlist == NULL || *tidlist == '\000')
@@ -1572,17 +1571,15 @@ thread_apply_command (const char *tidlist, int from_tty)
       int inf_num, thr_start, thr_end;
 
       if (!parser.get_tid_range (&inf_num, &thr_start, &thr_end))
-	{
-	  cmd = parser.cur_tok ();
-	  break;
-	}
+	break;
     }
 
-  cmd_or_flags = cmd;
-  while (cmd != NULL && parse_flags_qcs ("thread apply", &cmd, &flags))
+  cmd = parser.cur_tok ();
+
+  while (parse_flags_qcs ("thread apply", &cmd, &flags))
     ;
 
-  if (cmd == NULL)
+  if (*cmd == '\0')
     error (_("Please specify a command following the thread ID list"));
 
   if (tidlist == cmd || !isalpha (cmd[0]))
@@ -1591,7 +1588,7 @@ thread_apply_command (const char *tidlist, int from_tty)
   scoped_restore_current_thread restore_thread;
 
   parser.init (tidlist, current_inferior ()->num);
-  while (!parser.finished () && parser.cur_tok () < cmd_or_flags)
+  while (!parser.finished ())
     {
       struct thread_info *tp = NULL;
       struct inferior *inf;
diff --git a/gdb/tid-parse.c b/gdb/tid-parse.c
index 828362ea94b..07d7d2c3b2a 100644
--- a/gdb/tid-parse.c
+++ b/gdb/tid-parse.c
@@ -139,7 +139,13 @@ tid_range_parser::finished () const
   switch (m_state)
     {
     case STATE_INFERIOR:
-      return *m_cur_tok == '\0';
+      /* Parsing is finished when at end of string or null string,
+	 or we are not in a range and not in front of an integer, negative
+	 integer, convenience var or negative convenience var.  */
+      return (*m_cur_tok == '\0'
+	      || !(isdigit (*m_cur_tok)
+		   || *m_cur_tok == '$'
+		   || *m_cur_tok == '*'));
     case STATE_THREAD_RANGE:
     case STATE_STAR_RANGE:
       return m_range_parser.finished ();
@@ -311,6 +317,8 @@ tid_is_in_list (const char *list, int default_inferior,
     return 1;
 
   tid_range_parser parser (list, default_inferior);
+  if (parser.finished ())
+    invalid_thread_id_error (parser.cur_tok ());
   while (!parser.finished ())
     {
       int tmp_inf, tmp_thr_start, tmp_thr_end;
-- 
2.14.5

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

* [PATCH v2 16/24] Make "backtrace" support -OPT options
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (11 preceding siblings ...)
  2019-05-30 19:54 ` [PATCH v2 10/24] boolean/auto-boolean commands, make "o" ambiguous Pedro Alves
@ 2019-05-30 19:54 ` Pedro Alves
  2019-05-30 19:54 ` [PATCH v2 12/24] Introduce generic command options framework Pedro Alves
                   ` (11 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:54 UTC (permalink / raw)
  To: gdb-patches

This adds support for comand options to the "backtrace" command.  We'll get:

  (gdb) bt -
  -entry-values         -hide                 -past-main
  -frame-arguments      -no-filters           -raw-frame-arguments
  -full                 -past-entry

~~~~
(gdb) help backtrace
Print backtrace of all stack frames, or innermost COUNT frames.
Usage: backtrace [OPTION]... [QUALIFIER]... [COUNT | -COUNT]

Options:
  -entry-values no|only|preferred|if-needed|both|compact|default
    Set printing of function arguments at function entry
    GDB can sometimes determine the values of function arguments at entry,
    in addition to their current values.  This option tells GDB whether
    to print the current value, the value at entry (marked as val@entry),
    or both.  Note that one or both of these values may be <optimized out>.

  -frame-arguments all|scalars|none
    Set printing of non-scalar frame arguments

  -raw-frame-arguments [on|off]
    Set whether to print frame arguments in raw form.
    If set, frame arguments are printed in raw form, bypassing any
    pretty-printers for that value.

  -past-main [on|off]
    Set whether backtraces should continue past "main".
    Normally the caller of "main" is not of interest, so GDB will terminate
    the backtrace at "main".  Set this if you need to see the rest
    of the stack trace.

  -past-entry [on|off]
    Set whether backtraces should continue past the entry point of a program.
    Normally there are no callers beyond the entry point of a program, so GDB
    will terminate the backtrace there.  Set this if you need to see
    the rest of the stack trace.

  -full
    Print values of local variables.

  -no-filters
    Prohibit frame filters from executing on a backtrace.

  -hide
    Causes Python frame filter elided frames to not be printed.

For backward compatibility, the following qualifiers are supported:

   full       - same as -full option.
   no-filters - same as -no-filters option.
   hide       - same as -hide.

With a negative COUNT, print outermost -COUNT frames.
~~~~

Implementation wise, this:

- Moves relevant options/settings globals to structures.
- Tweaks a number of functions to pass down references to such structures.
- Adds option_def structures describing the options/settings.
- Makes backtrace_command parse the options, with gdb::option::process_options.
- Tweaks "backtrace"'s help to describe the new options.
- Adds testcases.

Note that backtrace is a PROCESS_OPTIONS_UNKNOWN_IS_OPERAND command,
because of the "-COUNT" argument.

The COUNT/-COUNT argument is currently parsed as an expression.  I
considered whether it would be prudent here to require "--", but
concluded that the risk of causing a significant breakage here is much
lower compared to "print", since printing the expression is not the
whole point of the "backtrace" command.  Seems OK to me to require
typing "backtrace -past-main -- -p" if the user truly wants to refer
to the negative of a backtrace count stored in an inferior variable
called "p".

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* frame.c: Include "cli/cli-option.h.
	(user_set_backtrace_options): New.
	(backtrace_past_main, backtrace_past_entry, backtrace_limit):
	Delete.
	(get_prev_frame): Adjust.
	(boolean_option_def, uinteger_option_def)
	(set_backtrace_option_defs): New.
	(_initialize_frame): Adjust and use
	gdb::option::add_setshow_cmds_for_options to install "set
	backtrace past-main" and "set backtrace past-entry".
	* frame.h: Include "cli/cli-option.h".
	(struct frame_print_options): Forward declare.
	(print_frame_arguments_all, print_frame_arguments_scalars)
	(print_frame_arguments_none): Declare.
	(print_entry_values): Delete declaration.
	(struct frame_print_options, user_frame_print_options): New.
	(struct set_backtrace_options): New.
	(set_backtrace_option_defs, user_set_backtrace_options): Declare.
	* mi/mi-cmd-stack.c (mi_cmd_stack_list_frames)
	(mi_cmd_stack_list_locals, mi_cmd_stack_list_args)
	(mi_cmd_stack_list_variables): Pass down USER_FRAME_PRINT_OPTIONS.
	(list_args_or_locals): Add frame_print_options parameter.
	(mi_cmd_stack_info_frame): Pass down USER_FRAME_PRINT_OPTIONS.
	* python/py-framefilter.c (enumerate_args): Pass down
	USER_FRAME_PRINT_OPTIONS.
	* stack.c: Include "cli/cli-option.h".
	(print_frame_arguments_all, print_frame_arguments_scalars)
	(print_frame_arguments_none): Declare.
	(print_raw_frame_arguments, print_entry_values): Delete.
	(user_frame_print_options): New.
	(boolean_option_def, enum_option_def, frame_print_option_defs):
	New.
	(struct backtrace_cmd_options): New.
	(bt_flag_option_def): New.
	(backtrace_command_option_defs): New.
	(print_stack_frame): Pass down USER_FRAME_PRINT_OPTIONS.
	(print_frame_arg, read_frame_arg, print_frame_args)
	(print_frame_info, print_frame): Add frame_print_options parameter
	and use it.
	(info_frame_command_core): Pass down USER_FRAME_PRINT_OPTIONS.
	(backtrace_command_1): Add frame_print_options and
	backtrace_cmd_options parameters and use them.
	(make_backtrace_options_def_group): New.
	(backtrace_command): Process command options with
	gdb::option::process_options.
	(backtrace_command_completer): New.
	(_initialize_stack): Extend "backtrace"'s help to mention
	supported options.  Install completer for "backtrace".
	Install some settings commands with add_setshow_cmds_for_options.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/options.exp (test-backtrace): New.
	(top level): Call it.
---
 gdb/frame.c                        |  84 ++++++----
 gdb/frame.h                        |  55 +++++-
 gdb/mi/mi-cmd-stack.c              |  23 ++-
 gdb/python/py-framefilter.c        |   3 +-
 gdb/stack.c                        | 336 ++++++++++++++++++++++++++-----------
 gdb/testsuite/gdb.base/options.exp |  60 ++++++-
 6 files changed, 416 insertions(+), 145 deletions(-)

diff --git a/gdb/frame.c b/gdb/frame.c
index f4303d13cc1..84e0397db98 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -42,6 +42,7 @@
 #include "tracepoint.h"
 #include "hashtab.h"
 #include "valprint.h"
+#include "cli/cli-option.h"
 
 /* The sentinel frame terminates the innermost end of the frame chain.
    If unwound, it returns the information needed to construct an
@@ -52,6 +53,9 @@
 
 static struct frame_info *sentinel_frame;
 
+/* The values behind the global "set backtrace ..." settings.  */
+set_backtrace_options user_set_backtrace_options;
+
 static struct frame_info *get_prev_frame_raw (struct frame_info *this_frame);
 static const char *frame_stop_reason_symbol_string (enum unwind_stop_reason reason);
 
@@ -295,9 +299,8 @@ show_frame_debug (struct ui_file *file, int from_tty,
   fprintf_filtered (file, _("Frame debugging is %s.\n"), value);
 }
 
-/* Flag to indicate whether backtraces should stop at main et.al.  */
+/* Implementation of "show backtrace past-main".  */
 
-static int backtrace_past_main;
 static void
 show_backtrace_past_main (struct ui_file *file, int from_tty,
 			  struct cmd_list_element *c, const char *value)
@@ -308,7 +311,8 @@ show_backtrace_past_main (struct ui_file *file, int from_tty,
 		    value);
 }
 
-static int backtrace_past_entry;
+/* Implementation of "show backtrace past-entry".  */
+
 static void
 show_backtrace_past_entry (struct ui_file *file, int from_tty,
 			   struct cmd_list_element *c, const char *value)
@@ -318,7 +322,8 @@ show_backtrace_past_entry (struct ui_file *file, int from_tty,
 		    value);
 }
 
-static unsigned int backtrace_limit = UINT_MAX;
+/* Implementation of "show backtrace limit".  */
+
 static void
 show_backtrace_limit (struct ui_file *file, int from_tty,
 		      struct cmd_list_element *c, const char *value)
@@ -2276,7 +2281,7 @@ get_prev_frame (struct frame_info *this_frame)
      point inside the main function.  */
   if (this_frame->level >= 0
       && get_frame_type (this_frame) == NORMAL_FRAME
-      && !backtrace_past_main
+      && !user_set_backtrace_options.backtrace_past_main
       && frame_pc_p
       && inside_main_func (this_frame))
     /* Don't unwind past main().  Note, this is done _before_ the
@@ -2293,7 +2298,7 @@ get_prev_frame (struct frame_info *this_frame)
      being 1-based and the level being 0-based, and the other accounts for
      the level of the new frame instead of the level of the current
      frame.  */
-  if (this_frame->level + 2 > backtrace_limit)
+  if (this_frame->level + 2 > user_set_backtrace_options.backtrace_limit)
     {
       frame_debug_got_null_frame (this_frame, "backtrace limit exceeded");
       return NULL;
@@ -2323,7 +2328,7 @@ get_prev_frame (struct frame_info *this_frame)
      application.  */
   if (this_frame->level >= 0
       && get_frame_type (this_frame) == NORMAL_FRAME
-      && !backtrace_past_entry
+      && !user_set_backtrace_options.backtrace_past_entry
       && frame_pc_p
       && inside_entry_func (this_frame))
     {
@@ -2896,6 +2901,39 @@ show_backtrace_cmd (const char *args, int from_tty)
   cmd_show_list (show_backtrace_cmdlist, from_tty, "");
 }
 
+/* Definition of the "set backtrace" settings that are exposed as
+   "backtrace" command options.  */
+
+using boolean_option_def
+  = gdb::option::boolean_option_def<set_backtrace_options>;
+using uinteger_option_def
+  = gdb::option::uinteger_option_def<set_backtrace_options>;
+
+const gdb::option::option_def set_backtrace_option_defs[] = {
+
+  boolean_option_def {
+    "past-main",
+    [] (set_backtrace_options *opt) { return &opt->backtrace_past_main; },
+    show_backtrace_past_main, /* show_cmd_cb */
+    N_("Set whether backtraces should continue past \"main\"."),
+    N_("Show whether backtraces should continue past \"main\"."),
+    N_("Normally the caller of \"main\" is not of interest, so GDB will terminate\n\
+the backtrace at \"main\".  Set this if you need to see the rest\n\
+of the stack trace."),
+  },
+
+  boolean_option_def {
+    "past-entry",
+    [] (set_backtrace_options *opt) { return &opt->backtrace_past_entry; },
+    show_backtrace_past_entry, /* show_cmd_cb */
+    N_("Set whether backtraces should continue past the entry point of a program."),
+    N_("Show whether backtraces should continue past the entry point of a program."),
+    N_("Normally there are no callers beyond the entry point of a program, so GDB\n\
+will terminate the backtrace there.  Set this if you need to see\n\
+the rest of the stack trace."),
+  },
+};
+
 void
 _initialize_frame (void)
 {
@@ -2916,34 +2954,8 @@ Show backtrace variables such as the backtrace limit"),
 		  &show_backtrace_cmdlist, "show backtrace ",
 		  0/*allow-unknown*/, &showlist);
 
-  add_setshow_boolean_cmd ("past-main", class_obscure,
-			   &backtrace_past_main, _("\
-Set whether backtraces should continue past \"main\"."), _("\
-Show whether backtraces should continue past \"main\"."), _("\
-Normally the caller of \"main\" is not of interest, so GDB will terminate\n\
-the backtrace at \"main\".  Set this variable if you need to see the rest\n\
-of the stack trace."),
-			   NULL,
-			   show_backtrace_past_main,
-			   &set_backtrace_cmdlist,
-			   &show_backtrace_cmdlist);
-
-  add_setshow_boolean_cmd ("past-entry", class_obscure,
-			   &backtrace_past_entry, _("\
-Set whether backtraces should continue past the entry point of a program."),
-			   _("\
-Show whether backtraces should continue past the entry point of a program."),
-			   _("\
-Normally there are no callers beyond the entry point of a program, so GDB\n\
-will terminate the backtrace there.  Set this variable if you need to see\n\
-the rest of the stack trace."),
-			   NULL,
-			   show_backtrace_past_entry,
-			   &set_backtrace_cmdlist,
-			   &show_backtrace_cmdlist);
-
   add_setshow_uinteger_cmd ("limit", class_obscure,
-			    &backtrace_limit, _("\
+			    &user_set_backtrace_options.backtrace_limit, _("\
 Set an upper bound on the number of backtrace levels."), _("\
 Show the upper bound on the number of backtrace levels."), _("\
 No more than the specified number of frames can be displayed or examined.\n\
@@ -2953,6 +2965,10 @@ Literal \"unlimited\" or zero means no limit."),
 			    &set_backtrace_cmdlist,
 			    &show_backtrace_cmdlist);
 
+  gdb::option::add_setshow_cmds_for_options
+    (class_stack, &user_set_backtrace_options,
+     set_backtrace_option_defs, &set_backtrace_cmdlist, &show_backtrace_cmdlist);
+
   /* Debug this files internals.  */
   add_setshow_zuinteger_cmd ("frame", class_maintenance, &frame_debug,  _("\
 Set frame debugging."), _("\
diff --git a/gdb/frame.h b/gdb/frame.h
index 0a0baf46a0c..a79eeeeab16 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -70,6 +70,7 @@
    */
 
 #include "language.h"
+#include "cli/cli-option.h"
 
 struct symtab_and_line;
 struct frame_unwind;
@@ -78,6 +79,7 @@ struct block;
 struct gdbarch;
 struct ui_file;
 struct ui_out;
+struct frame_print_options;
 
 /* Status of a given frame's stack.  */
 
@@ -753,7 +755,8 @@ extern void print_stack_frame (struct frame_info *, int print_level,
 			       enum print_what print_what,
 			       int set_current_sal);
 
-extern void print_frame_info (struct frame_info *, int print_level,
+extern void print_frame_info (const frame_print_options &fp_opts,
+			      struct frame_info *, int print_level,
 			      enum print_what print_what, int args,
 			      int set_current_sal);
 
@@ -764,6 +767,12 @@ extern int deprecated_frame_register_read (struct frame_info *frame, int regnum,
 
 /* From stack.c.  */
 
+/* The possible choices of "set print frame-arguments".  */
+extern const char print_frame_arguments_all[];
+extern const char print_frame_arguments_scalars[];
+extern const char print_frame_arguments_none[];
+
+/* The possible choices of "set print entry-values".  */
 extern const char print_entry_values_no[];
 extern const char print_entry_values_only[];
 extern const char print_entry_values_preferred[];
@@ -771,7 +780,22 @@ extern const char print_entry_values_if_needed[];
 extern const char print_entry_values_both[];
 extern const char print_entry_values_compact[];
 extern const char print_entry_values_default[];
-extern const char *print_entry_values;
+
+/* Data for the frame-printing "set print" settings exposed as command
+   options.  */
+
+struct frame_print_options
+{
+  const char *print_frame_arguments = print_frame_arguments_scalars;
+  const char *print_entry_values = print_entry_values_default;
+
+  /* If non-zero, don't invoke pretty-printers for frame
+     arguments.  */
+  int print_raw_frame_arguments;
+};
+
+/* The values behind the global "set print ..." settings.  */
+extern frame_print_options user_frame_print_options;
 
 /* Inferior function parameter value read in from a frame.  */
 
@@ -800,7 +824,8 @@ struct frame_arg
   const char *entry_kind;
 };
 
-extern void read_frame_arg (struct symbol *sym, struct frame_info *frame,
+extern void read_frame_arg (const frame_print_options &fp_opts,
+			    symbol *sym, frame_info *frame,
 			    struct frame_arg *argp,
 			    struct frame_arg *entryargp);
 extern void read_frame_local (struct symbol *sym, struct frame_info *frame,
@@ -881,4 +906,28 @@ extern struct frame_info *skip_tailcall_frames (struct frame_info *frame);
 
 extern struct frame_info *skip_unwritable_frames (struct frame_info *frame);
 
+/* Data for the "set backtrace" settings.  */
+
+struct set_backtrace_options
+{
+  /* Flag to indicate whether backtraces should continue past
+     main.  */
+  int backtrace_past_main = 0;
+
+  /* Flag to indicate whether backtraces should continue past
+     entry.  */
+  int backtrace_past_entry = 0;
+
+  /* Upper bound on the number of backtrace levels.  Note this is not
+     exposed as a command option, because "backtrace" and "frame
+     apply" already have other means to set a frame count limit.  */
+  unsigned int backtrace_limit = UINT_MAX;
+};
+
+/* The corresponding option definitions.  */
+extern const gdb::option::option_def set_backtrace_option_defs[2];
+
+/* The values behind the global "set backtrace ..." settings.  */
+extern set_backtrace_options user_set_backtrace_options;
+
 #endif /* !defined (FRAME_H)  */
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 0dc0b55bc91..80bc7a1968e 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -39,7 +39,8 @@
 
 enum what_to_list { locals, arguments, all };
 
-static void list_args_or_locals (enum what_to_list what,
+static void list_args_or_locals (const frame_print_options &fp_opts,
+				 enum what_to_list what,
 				 enum print_values values,
 				 struct frame_info *fi,
 				 int skip_unavailable);
@@ -175,7 +176,8 @@ mi_cmd_stack_list_frames (const char *command, char **argv, int argc)
 	  QUIT;
 	  /* Print the location and the address always, even for level 0.
 	     If args is 0, don't print the arguments.  */
-	  print_frame_info (fi, 1, LOC_AND_ADDRESS, 0 /* args */, 0);
+	  print_frame_info (user_frame_print_options,
+			    fi, 1, LOC_AND_ADDRESS, 0 /* args */, 0);
 	}
     }
 }
@@ -275,7 +277,8 @@ mi_cmd_stack_list_locals (const char *command, char **argv, int argc)
       if "--no-frame-filters" has been specified from the command.  */
    if (! frame_filters || raw_arg  || result == EXT_LANG_BT_NO_FILTERS)
      {
-       list_args_or_locals (locals, print_value, frame,
+       list_args_or_locals (user_frame_print_options,
+			    locals, print_value, frame,
 			    skip_unavailable);
      }
 }
@@ -389,7 +392,8 @@ mi_cmd_stack_list_args (const char *command, char **argv, int argc)
 	  QUIT;
 	  ui_out_emit_tuple tuple_emitter (uiout, "frame");
 	  uiout->field_int ("level", i);
-	  list_args_or_locals (arguments, print_values, fi, skip_unavailable);
+	  list_args_or_locals (user_frame_print_options,
+			       arguments, print_values, fi, skip_unavailable);
 	}
     }
 }
@@ -465,7 +469,8 @@ mi_cmd_stack_list_variables (const char *command, char **argv, int argc)
       if "--no-frame-filters" has been specified from the command.  */
    if (! frame_filters || raw_arg  || result == EXT_LANG_BT_NO_FILTERS)
      {
-       list_args_or_locals (all, print_value, frame,
+       list_args_or_locals (user_frame_print_options,
+			    all, print_value, frame,
 			    skip_unavailable);
      }
 }
@@ -557,7 +562,8 @@ list_arg_or_local (const struct frame_arg *arg, enum what_to_list what,
    are available.  */
 
 static void
-list_args_or_locals (enum what_to_list what, enum print_values values,
+list_args_or_locals (const frame_print_options &fp_opts,
+		     enum what_to_list what, enum print_values values,
 		     struct frame_info *fi, int skip_unavailable)
 {
   const struct block *block;
@@ -652,7 +658,7 @@ list_args_or_locals (enum what_to_list what, enum print_values values,
 		    {
 		case PRINT_ALL_VALUES:
 		  if (SYMBOL_IS_ARGUMENT (sym))
-		    read_frame_arg (sym2, fi, &arg, &entryarg);
+		    read_frame_arg (fp_opts, sym2, fi, &arg, &entryarg);
 		  else
 		    read_frame_local (sym2, fi, &arg);
 		    }
@@ -764,5 +770,6 @@ mi_cmd_stack_info_frame (const char *command, char **argv, int argc)
   if (argc > 0)
     error (_("-stack-info-frame: No arguments allowed"));
 
-  print_frame_info (get_selected_frame (NULL), 1, LOC_AND_ADDRESS, 0, 1);
+  print_frame_info (user_frame_print_options,
+		    get_selected_frame (NULL), 1, LOC_AND_ADDRESS, 0, 1);
 }
diff --git a/gdb/python/py-framefilter.c b/gdb/python/py-framefilter.c
index 017ea90f619..95ad410f23f 100644
--- a/gdb/python/py-framefilter.c
+++ b/gdb/python/py-framefilter.c
@@ -481,7 +481,8 @@ enumerate_args (PyObject *iter,
 	      return EXT_LANG_BT_ERROR;
 	    }
 
-	  read_frame_arg (sym, frame, &arg, &entryarg);
+	  read_frame_arg (user_frame_print_options,
+			  sym, frame, &arg, &entryarg);
 
 	  gdb::unique_xmalloc_ptr<char> arg_holder (arg.error);
 	  gdb::unique_xmalloc_ptr<char> entry_holder (entryarg.error);
diff --git a/gdb/stack.c b/gdb/stack.c
index d1b195c84d6..5e878d3c887 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -52,16 +52,22 @@
 #include "extension.h"
 #include "observable.h"
 #include "common/def-vector.h"
+#include "cli/cli-option.h"
 
 /* The possible choices of "set print frame-arguments", and the value
    of this setting.  */
 
-static const char *const print_frame_arguments_choices[] =
-  {"all", "scalars", "none", NULL};
-static const char *print_frame_arguments = "scalars";
+const char print_frame_arguments_all[] = "all";
+const char print_frame_arguments_scalars[] = "scalars";
+const char print_frame_arguments_none[] = "none";
 
-/* If non-zero, don't invoke pretty-printers for frame arguments.  */
-static int print_raw_frame_arguments;
+static const char *const print_frame_arguments_choices[] =
+{
+  print_frame_arguments_all,
+  print_frame_arguments_scalars,
+  print_frame_arguments_none,
+  NULL
+};
 
 /* The possible choices of "set print entry-values", and the value
    of this setting.  */
@@ -84,7 +90,85 @@ static const char *const print_entry_values_choices[] =
   print_entry_values_default,
   NULL
 };
-const char *print_entry_values = print_entry_values_default;
+
+/* See frame.h.  */
+frame_print_options user_frame_print_options;
+
+/* Option definitions for some frame-related "set print ..."
+   settings.  */
+
+using boolean_option_def
+  = gdb::option::boolean_option_def<frame_print_options>;
+using enum_option_def
+  = gdb::option::enum_option_def<frame_print_options>;
+
+static const gdb::option::option_def frame_print_option_defs[] = {
+
+  enum_option_def {
+    "entry-values",
+    print_entry_values_choices,
+    [] (frame_print_options *opt) { return &opt->print_entry_values; },
+    NULL, /* show_cmd_cb */
+    N_("Set printing of function arguments at function entry"),
+    N_("Show printing of function arguments at function entry"),
+    N_("GDB can sometimes determine the values of function arguments at entry,\n\
+in addition to their current values.  This option tells GDB whether\n\
+to print the current value, the value at entry (marked as val@entry),\n\
+or both.  Note that one or both of these values may be <optimized out>."),
+  },
+
+  enum_option_def {
+    "frame-arguments",
+    print_frame_arguments_choices,
+    [] (frame_print_options *opt) { return &opt->print_frame_arguments; },
+    NULL, /* show_cmd_cb */
+    N_("Set printing of non-scalar frame arguments"),
+    N_("Show printing of non-scalar frame arguments"),
+    NULL /* help_doc */
+  },
+
+  boolean_option_def {
+    "raw-frame-arguments",
+    [] (frame_print_options *opt) { return &opt->print_raw_frame_arguments; },
+    NULL, /* show_cmd_cb */
+    N_("Set whether to print frame arguments in raw form."),
+    N_("Show whether to print frame arguments in raw form."),
+    N_("If set, frame arguments are printed in raw form, bypassing any\n\
+pretty-printers for that value.")
+  },
+};
+
+/* Options for the "backtrace" command.  */
+
+struct backtrace_cmd_options
+{
+  int full = 0;
+  int no_filters = 0;
+  int hide = 0;
+};
+
+using bt_flag_option_def
+  = gdb::option::flag_option_def<backtrace_cmd_options>;
+
+static const gdb::option::option_def backtrace_command_option_defs[] = {
+  bt_flag_option_def {
+    "full",
+    [] (backtrace_cmd_options *opt) { return &opt->full; },
+    N_("Print values of local variables.")
+  },
+
+  bt_flag_option_def {
+    "no-filters",
+    [] (backtrace_cmd_options *opt) { return &opt->no_filters; },
+    N_("Prohibit frame filters from executing on a backtrace."),
+  },
+
+  bt_flag_option_def {
+    "hide",
+    [] (backtrace_cmd_options *opt) { return &opt->hide; },
+    N_("Causes Python frame filter elided frames to not be printed."),
+  },
+};
 
 /* Prototypes for local functions.  */
 
@@ -93,7 +177,8 @@ static void print_frame_local_vars (struct frame_info *frame,
 				    const char *regexp, const char *t_regexp,
 				    int num_tabs, struct ui_file *stream);
 
-static void print_frame (struct frame_info *frame, int print_level,
+static void print_frame (const frame_print_options &opts,
+			 frame_info *frame, int print_level,
 			 enum print_what print_what,  int print_args,
 			 struct symtab_and_line sal);
 
@@ -177,7 +262,8 @@ print_stack_frame (struct frame_info *frame, int print_level,
 
   try
     {
-      print_frame_info (frame, print_level, print_what, 1 /* print_args */,
+      print_frame_info (user_frame_print_options,
+			frame, print_level, print_what, 1 /* print_args */,
 			set_current_sal);
       if (set_current_sal)
 	set_current_sal_from_frame (frame);
@@ -225,7 +311,8 @@ print_frame_nameless_args (struct frame_info *frame, long start, int num,
    iff it should not be printed accoring to user settings.  */
 
 static void
-print_frame_arg (const struct frame_arg *arg)
+print_frame_arg (const frame_print_options &fp_opts,
+		 const struct frame_arg *arg)
 {
   struct ui_out *uiout = current_uiout;
 
@@ -269,7 +356,7 @@ print_frame_arg (const struct frame_arg *arg)
 	  try
 	    {
 	      const struct language_defn *language;
-	      struct value_print_options opts;
+	      struct value_print_options vp_opts;
 
 	      /* Avoid value_print because it will deref ref parameters.  We
 		 just want to print their addresses.  Print ??? for args whose
@@ -286,14 +373,15 @@ print_frame_arg (const struct frame_arg *arg)
 	      else
 		language = current_language;
 
-	      get_no_prettyformat_print_options (&opts);
-	      opts.deref_ref = 1;
-	      opts.raw = print_raw_frame_arguments;
+	      get_no_prettyformat_print_options (&vp_opts);
+	      vp_opts.deref_ref = 1;
+	      vp_opts.raw = fp_opts.print_raw_frame_arguments;
 
 	      /* True in "summary" mode, false otherwise.  */
-	      opts.summary = !strcmp (print_frame_arguments, "scalars");
+	      vp_opts.summary
+		= fp_opts.print_frame_arguments == print_frame_arguments_scalars;
 
-	      common_val_print (arg->val, &stb, 2, &opts, language);
+	      common_val_print (arg->val, &stb, 2, &vp_opts, language);
 	    }
 	  catch (const gdb_exception_error &except)
 	    {
@@ -333,15 +421,16 @@ read_frame_local (struct symbol *sym, struct frame_info *frame,
    exception.  */
 
 void
-read_frame_arg (struct symbol *sym, struct frame_info *frame,
+read_frame_arg (const frame_print_options &fp_opts,
+		symbol *sym, frame_info *frame,
 	        struct frame_arg *argp, struct frame_arg *entryargp)
 {
   struct value *val = NULL, *entryval = NULL;
   char *val_error = NULL, *entryval_error = NULL;
   int val_equal = 0;
 
-  if (print_entry_values != print_entry_values_only
-      && print_entry_values != print_entry_values_preferred)
+  if (fp_opts.print_entry_values != print_entry_values_only
+      && fp_opts.print_entry_values != print_entry_values_preferred)
     {
       try
 	{
@@ -356,8 +445,8 @@ read_frame_arg (struct symbol *sym, struct frame_info *frame,
 
   if (SYMBOL_COMPUTED_OPS (sym) != NULL
       && SYMBOL_COMPUTED_OPS (sym)->read_variable_at_entry != NULL
-      && print_entry_values != print_entry_values_no
-      && (print_entry_values != print_entry_values_if_needed
+      && fp_opts.print_entry_values != print_entry_values_no
+      && (fp_opts.print_entry_values != print_entry_values_if_needed
 	  || !val || value_optimized_out (val)))
     {
       try
@@ -379,8 +468,8 @@ read_frame_arg (struct symbol *sym, struct frame_info *frame,
       if (entryval != NULL && value_optimized_out (entryval))
 	entryval = NULL;
 
-      if (print_entry_values == print_entry_values_compact
-	  || print_entry_values == print_entry_values_default)
+      if (fp_opts.print_entry_values == print_entry_values_compact
+	  || fp_opts.print_entry_values == print_entry_values_default)
 	{
 	  /* For MI do not try to use print_entry_values_compact for ARGP.  */
 
@@ -463,7 +552,7 @@ read_frame_arg (struct symbol *sym, struct frame_info *frame,
 
   if (entryval == NULL)
     {
-      if (print_entry_values == print_entry_values_preferred)
+      if (fp_opts.print_entry_values == print_entry_values_preferred)
 	{
 	  gdb_assert (val == NULL);
 
@@ -477,18 +566,18 @@ read_frame_arg (struct symbol *sym, struct frame_info *frame,
 	      strcpy (val_error, except.what ());
 	    }
 	}
-      if (print_entry_values == print_entry_values_only
-	  || print_entry_values == print_entry_values_both
-	  || (print_entry_values == print_entry_values_preferred
+      if (fp_opts.print_entry_values == print_entry_values_only
+	  || fp_opts.print_entry_values == print_entry_values_both
+	  || (fp_opts.print_entry_values == print_entry_values_preferred
 	      && (!val || value_optimized_out (val))))
 	{
 	  entryval = allocate_optimized_out_value (SYMBOL_TYPE (sym));
 	  entryval_error = NULL;
 	}
     }
-  if ((print_entry_values == print_entry_values_compact
-       || print_entry_values == print_entry_values_if_needed
-       || print_entry_values == print_entry_values_preferred)
+  if ((fp_opts.print_entry_values == print_entry_values_compact
+       || fp_opts.print_entry_values == print_entry_values_if_needed
+       || fp_opts.print_entry_values == print_entry_values_preferred)
       && (!val || value_optimized_out (val)) && entryval != NULL)
     {
       val = NULL;
@@ -500,8 +589,9 @@ read_frame_arg (struct symbol *sym, struct frame_info *frame,
   argp->error = val_error ? xstrdup (val_error) : NULL;
   if (!val && !val_error)
     argp->entry_kind = print_entry_values_only;
-  else if ((print_entry_values == print_entry_values_compact
-	   || print_entry_values == print_entry_values_default) && val_equal)
+  else if ((fp_opts.print_entry_values == print_entry_values_compact
+	   || fp_opts.print_entry_values == print_entry_values_default)
+	   && val_equal)
     {
       argp->entry_kind = print_entry_values_compact;
       gdb_assert (!current_uiout->is_mi_like_p ());
@@ -528,7 +618,8 @@ read_frame_arg (struct symbol *sym, struct frame_info *frame,
    ints of arguments according to the stack frame".  */
 
 static void
-print_frame_args (struct symbol *func, struct frame_info *frame,
+print_frame_args (const frame_print_options &fp_opts,
+		  struct symbol *func, struct frame_info *frame,
 		  int num, struct ui_file *stream)
 {
   struct ui_out *uiout = current_uiout;
@@ -540,7 +631,8 @@ print_frame_args (struct symbol *func, struct frame_info *frame,
   /* Number of ints of arguments that we have printed so far.  */
   int args_printed = 0;
   /* True if we should print arguments, false otherwise.  */
-  int print_args = strcmp (print_frame_arguments, "none");
+  bool print_args
+    = fp_opts.print_frame_arguments != print_frame_arguments_none;
 
   if (func)
     {
@@ -674,10 +766,10 @@ print_frame_args (struct symbol *func, struct frame_info *frame,
 	      entryarg.entry_kind = print_entry_values_no;
 	    }
 	  else
-	    read_frame_arg (sym, frame, &arg, &entryarg);
+	    read_frame_arg (fp_opts, sym, frame, &arg, &entryarg);
 
 	  if (arg.entry_kind != print_entry_values_only)
-	    print_frame_arg (&arg);
+	    print_frame_arg (fp_opts, &arg);
 
 	  if (entryarg.entry_kind != print_entry_values_no)
 	    {
@@ -687,7 +779,7 @@ print_frame_args (struct symbol *func, struct frame_info *frame,
 		  uiout->wrap_hint ("    ");
 		}
 
-	      print_frame_arg (&entryarg);
+	      print_frame_arg (fp_opts, &entryarg);
 	    }
 
 	  xfree (arg.error);
@@ -778,7 +870,8 @@ do_gdb_disassembly (struct gdbarch *gdbarch,
    messages.  */
 
 void
-print_frame_info (struct frame_info *frame, int print_level,
+print_frame_info (const frame_print_options &fp_opts,
+		  frame_info *frame, int print_level,
 		  enum print_what print_what, int print_args,
 		  int set_current_sal)
 {
@@ -855,7 +948,7 @@ print_frame_info (struct frame_info *frame, int print_level,
 		    || print_what == SRC_AND_LOC);
 
   if (location_print || !sal.symtab)
-    print_frame (frame, print_level, print_what, print_args, sal);
+    print_frame (fp_opts, frame, print_level, print_what, print_args, sal);
 
   source_print = (print_what == SRC_LINE || print_what == SRC_AND_LOC);
 
@@ -1128,7 +1221,8 @@ find_frame_funname (struct frame_info *frame, enum language *funlang,
 }
 
 static void
-print_frame (struct frame_info *frame, int print_level,
+print_frame (const frame_print_options &fp_opts,
+	     frame_info *frame, int print_level,
 	     enum print_what print_what, int print_args,
 	     struct symtab_and_line sal)
 {
@@ -1198,7 +1292,7 @@ print_frame (struct frame_info *frame, int print_level,
 	  ui_out_emit_list list_emitter (uiout, "args");
 	  try
 	    {
-	      print_frame_args (func, frame, numargs, gdb_stdout);
+	      print_frame_args (fp_opts, func, frame, numargs, gdb_stdout);
 	    }
 	  catch (const gdb_exception_error &e)
 	    {
@@ -1477,7 +1571,8 @@ info_frame_command_core (struct frame_info *fi, bool selected_frame_p)
 	    else
 	      printf_filtered (" %d args: ", numargs);
 	  }
-	print_frame_args (func, fi, numargs, gdb_stdout);
+	print_frame_args (user_frame_print_options,
+			  func, fi, numargs, gdb_stdout);
 	puts_filtered ("\n");
       }
   }
@@ -1791,8 +1886,10 @@ static frame_command_helper <select_frame_command_core> select_frame_cmd;
    frames.  */
 
 static void
-backtrace_command_1 (const char *count_exp, frame_filter_flags flags,
-		     int no_filters, int from_tty)
+backtrace_command_1 (const frame_print_options &fp_opts,
+		     const backtrace_cmd_options &bt_opts,
+		     const char *count_exp, int from_tty)
+
 {
   struct frame_info *fi;
   int count;
@@ -1821,7 +1918,14 @@ backtrace_command_1 (const char *count_exp, frame_filter_flags flags,
       count = -1;
     }
 
-  if (! no_filters)
+  frame_filter_flags flags = 0;
+
+  if (bt_opts.full)
+    flags |= PRINT_LOCALS;
+  if (bt_opts.hide)
+    flags |= PRINT_HIDE;
+
+  if (!bt_opts.no_filters)
     {
       enum ext_lang_frame_args arg_type;
 
@@ -1829,9 +1933,9 @@ backtrace_command_1 (const char *count_exp, frame_filter_flags flags,
       if (from_tty)
 	flags |= PRINT_MORE_FRAMES;
 
-      if (!strcmp (print_frame_arguments, "scalars"))
+      if (fp_opts.print_frame_arguments == print_frame_arguments_scalars)
 	arg_type = CLI_SCALAR_VALUES;
-      else if (!strcmp (print_frame_arguments, "all"))
+      else if (fp_opts.print_frame_arguments == print_frame_arguments_all)
 	arg_type = CLI_ALL_VALUES;
       else
 	arg_type = NO_VALUES;
@@ -1842,8 +1946,8 @@ backtrace_command_1 (const char *count_exp, frame_filter_flags flags,
     }
 
   /* Run the inbuilt backtrace if there are no filters registered, or
-     "no-filters" has been specified from the command.  */
-  if (no_filters ||  result == EXT_LANG_BT_NO_FILTERS)
+     "-no-filters" has been specified from the command.  */
+  if (bt_opts.no_filters || result == EXT_LANG_BT_NO_FILTERS)
     {
       struct frame_info *trailing;
 
@@ -1869,7 +1973,7 @@ backtrace_command_1 (const char *count_exp, frame_filter_flags flags,
 	     hand, perhaps the code does or could be fixed to make sure
 	     the frame->prev field gets set to NULL in that case).  */
 
-	  print_frame_info (fi, 1, LOCATION, 1, 0);
+	  print_frame_info (fp_opts, fi, 1, LOCATION, 1, 0);
 	  if ((flags & PRINT_LOCALS) != 0)
 	    {
 	      struct frame_id frame_id = get_frame_id (fi);
@@ -1908,17 +2012,38 @@ backtrace_command_1 (const char *count_exp, frame_filter_flags flags,
     }
 }
 
+/* Create an option_def_group array grouping all the "backtrace"
+   options, with FP_OPTS, BT_CMD_OPT, SET_BT_OPTS as contexts.  */
+
+static inline std::array<gdb::option::option_def_group, 3>
+make_backtrace_options_def_group (frame_print_options *fp_opts,
+				  backtrace_cmd_options *bt_cmd_opts,
+				  set_backtrace_options *set_bt_opts)
+{
+  return {{
+    { {frame_print_option_defs}, fp_opts },
+    { {set_backtrace_option_defs}, set_bt_opts },
+    { {backtrace_command_option_defs}, bt_cmd_opts }
+  }};
+}
+
 static void
 backtrace_command (const char *arg, int from_tty)
 {
-  bool filters = true;
-  frame_filter_flags flags = 0;
+  frame_print_options fp_opts = user_frame_print_options;
+  backtrace_cmd_options bt_cmd_opts;
+  set_backtrace_options set_bt_opts = user_set_backtrace_options;
 
-  if (arg)
-    {
-      bool done = false;
+  auto grp
+    = make_backtrace_options_def_group (&fp_opts, &bt_cmd_opts, &set_bt_opts);
+  gdb::option::process_options
+    (&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp);
 
-      while (!done)
+  /* Parse non-'-'-prefixed qualifiers, for backwards
+     compatibility.  */
+  if (arg != NULL)
+    {
+      while (true)
 	{
 	  const char *save_arg = arg;
 	  std::string this_arg = extract_arg (&arg);
@@ -1927,16 +2052,16 @@ backtrace_command (const char *arg, int from_tty)
 	    break;
 
 	  if (subset_compare (this_arg.c_str (), "no-filters"))
-	    filters = false;
+	    bt_cmd_opts.no_filters = true;
 	  else if (subset_compare (this_arg.c_str (), "full"))
-	    flags |= PRINT_LOCALS;
+	    bt_cmd_opts.full = true;
 	  else if (subset_compare (this_arg.c_str (), "hide"))
-	    flags |= PRINT_HIDE;
+	    bt_cmd_opts.hide = true;
 	  else
 	    {
 	      /* Not a recognized argument, so stop.  */
 	      arg = save_arg;
-	      done = true;
+	      break;
 	    }
 	}
 
@@ -1944,7 +2069,29 @@ backtrace_command (const char *arg, int from_tty)
 	arg = NULL;
     }
 
-  backtrace_command_1 (arg, flags, !filters /* no frame-filters */, from_tty);
+  /* These options are handled quite deep in the unwind machinery, so
+     we get to pass them down by swapping globals.  */
+  scoped_restore restore_set_backtrace_options
+    = make_scoped_restore (&user_set_backtrace_options, set_bt_opts);
+
+  backtrace_command_1 (fp_opts, bt_cmd_opts, arg, from_tty);
+}
+
+/* Completer for the "backtrace" command.  */
+
+static void
+backtrace_command_completer (struct cmd_list_element *ignore,
+			     completion_tracker &tracker,
+			     const char *text, const char */*word*/)
+{
+  const auto group
+    = make_backtrace_options_def_group (nullptr, nullptr, nullptr);
+  if (gdb::option::complete_options
+      (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
+    return;
+
+  const char *word = advance_to_expression_complete_word_point (tracker, text);
+  expression_completer (ignore, tracker, text, word);
 }
 
 /* Iterate over the local variables of a block B, calling CB with
@@ -3030,13 +3177,30 @@ Usage: select-frame level LEVEL"),
 			 &select_frame_cmd_list,
 			 &cli_suppress_notification.user_selected_context);
 
-  add_com ("backtrace", class_stack, backtrace_command, _("\
+  const auto backtrace_opts
+    = make_backtrace_options_def_group (nullptr, nullptr, nullptr);
+
+  static std::string backtrace_help
+    = gdb::option::build_help (N_("\
 Print backtrace of all stack frames, or innermost COUNT frames.\n\
-Usage: backtrace [QUALIFIERS]... [COUNT]\n\
-With a negative argument, print outermost -COUNT frames.\n\
-Use of the 'full' qualifier also prints the values of the local variables.\n\
-Use of the 'no-filters' qualifier prohibits frame filters from executing\n\
-on this backtrace."));
+Usage: backtrace [OPTION]... [QUALIFIER]... [COUNT | -COUNT]\n\
+\n\
+Options:\n\
+%OPTIONS%\
+For backward compatibility, the following qualifiers are supported:\n\
+\n\
+   full       - same as -full option.\n\
+   no-filters - same as -no-filters option.\n\
+   hide       - same as -hide.\n\
+\n\
+With a negative COUNT, print outermost -COUNT frames."),
+			       backtrace_opts);
+
+  cmd_list_element *c = add_com ("backtrace", class_stack,
+				 backtrace_command,
+				 backtrace_help.c_str ());
+  set_cmd_completer_handle_brkchars (c, backtrace_command_completer);
+
   add_com_alias ("bt", "backtrace", class_stack, 0);
 
   add_com_alias ("where", "backtrace", class_alias, 0);
@@ -3100,33 +3264,20 @@ Prints the argument variables of the current stack frame.\n"),
 Select the stack frame that contains NAME.\n\
 Usage: func NAME"));
 
-  add_setshow_enum_cmd ("frame-arguments", class_stack,
-			print_frame_arguments_choices, &print_frame_arguments,
-			_("Set printing of non-scalar frame arguments"),
-			_("Show printing of non-scalar frame arguments"),
-			NULL, NULL, NULL, &setprintlist, &showprintlist);
-
   /* Install "set print raw frame-arguments", a deprecated spelling of
      "set print raw-frame-arguments".  */
-  cmd = add_setshow_boolean_cmd ("frame-arguments", no_class,
-				 &print_raw_frame_arguments, _("\
+  cmd = add_setshow_boolean_cmd
+    ("frame-arguments", no_class,
+     &user_frame_print_options.print_raw_frame_arguments,
+     _("\
 Set whether to print frame arguments in raw form."), _("\
 Show whether to print frame arguments in raw form."), _("\
 If set, frame arguments are printed in raw form, bypassing any\n\
 pretty-printers for that value."),
-				 NULL, NULL,
-				 &setprintrawlist, &showprintrawlist);
+     NULL, NULL,
+     &setprintrawlist, &showprintrawlist);
   deprecate_cmd (cmd, "set print raw-frame-arguments");
 
-  add_setshow_boolean_cmd ("raw-frame-arguments", no_class,
-			   &print_raw_frame_arguments, _("\
-Set whether to print frame arguments in raw form."), _("\
-Show whether to print frame arguments in raw form."), _("\
-If set, frame arguments are printed in raw form, bypassing any\n\
-pretty-printers for that value."),
-			   NULL, NULL,
-			   &setprintlist, &showprintlist);
-
   add_setshow_auto_boolean_cmd ("disassemble-next-line", class_stack,
 			        &disassemble_next_line, _("\
 Set whether to disassemble next source line or insn when execution stops."),
@@ -3147,16 +3298,7 @@ source line."),
 			        &setlist, &showlist);
   disassemble_next_line = AUTO_BOOLEAN_FALSE;
 
-  add_setshow_enum_cmd ("entry-values", class_stack,
-			print_entry_values_choices, &print_entry_values,
-			_("Set printing of function arguments at function "
-			  "entry"),
-			_("Show printing of function arguments at function "
-			  "entry"),
-			_("\
-GDB can sometimes determine the values of function arguments at entry,\n\
-in addition to their current values.  This option tells GDB whether\n\
-to print the current value, the value at entry (marked as val@entry),\n\
-or both.  Note that one or both of these values may be <optimized out>."),
-			NULL, NULL, &setprintlist, &showprintlist);
+  gdb::option::add_setshow_cmds_for_options
+    (class_stack, &user_frame_print_options,
+     frame_print_option_defs, &setprintlist, &showprintlist);
 }
diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp
index 3b4e7ee189f..17573460b4d 100644
--- a/gdb/testsuite/gdb.base/options.exp
+++ b/gdb/testsuite/gdb.base/options.exp
@@ -20,8 +20,11 @@
 # The test uses the "maintenance test-options" subcommands to exercise
 # TAB-completion and option processing.
 #
-# It also tests option integration in various commands, including
-# "print" and "compile print".
+# It also tests option integration in various commands, including:
+#
+#  - print
+#  - compile print
+#  - backtrace
 
 load_lib completion-support.exp
 
@@ -231,6 +234,56 @@ proc_with_prefix test-print {{prefix ""}} {
 	"Left operand of assignment is not an lvalue\\."
 }
 
+# Basic option-machinery + "backtrace" command integration tests.
+proc_with_prefix test-backtrace {} {
+    clean_restart
+
+    test_gdb_complete_unique "backtrace" "backtrace"
+    test_gdb_complete_none "backtrace "
+
+    gdb_test "backtrace -" "Ambiguous option at: -"
+    gdb_test "backtrace --" "No stack\\."
+    gdb_test "backtrace -- -" "No stack\\."
+
+    test_gdb_complete_multiple "backtrace " "-" "" {
+	"-entry-values"
+	"-frame-arguments"
+	"-full"
+	"-hide"
+	"-no-filters"
+	"-past-entry"
+	"-past-main"
+	"-raw-frame-arguments"
+    }
+
+    global binfile
+    clean_restart $binfile
+
+    if ![runto_main] {
+	fail "cannot run to main"
+	return
+    }
+
+    # COUNT in "backtrace COUNT" is parsed as an expression.  Check
+    # that we complete expressions.
+
+    test_gdb_complete_unique \
+	"backtrace xxx" \
+	"backtrace xxx1"
+
+    test_gdb_complete_unique \
+	"backtrace -xxx" \
+	"backtrace -xxx1"
+
+    test_gdb_complete_unique \
+	"backtrace 1 + xxx" \
+	"backtrace 1 + xxx1"
+
+    test_gdb_complete_unique \
+	"backtrace (1 + xxx" \
+	"backtrace (1 + xxx1"
+}
+
 # Miscellaneous tests.
 proc_with_prefix test-misc {variant} {
     global all_options
@@ -674,3 +727,6 @@ test-print ""
 if ![skip_compile_feature_tests] {
     test-print "compile "
 }
+
+# Basic "backtrace" integration tests.
+test-backtrace
-- 
2.14.5

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

* [PATCH v2 21/24] "thread apply 1 -- -" vs "frame apply level 0 -- -"
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (9 preceding siblings ...)
  2019-05-30 19:54 ` [PATCH v2 05/24] Allow "unlimited" abbreviations Pedro Alves
@ 2019-05-30 19:54 ` Pedro Alves
  2019-05-30 19:54 ` [PATCH v2 10/24] boolean/auto-boolean commands, make "o" ambiguous Pedro Alves
                   ` (13 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:54 UTC (permalink / raw)
  To: gdb-patches

With the following patch, we'll be able to explicitly tell "thread
apply" where options end, using the "--" delimiter.  A test added by
that patch caught a pre-existing inconsistency:

 (gdb) thread apply 1 -- -
 Invalid thread ID: -

 (gdb) frame apply level 0 -- -
 #0  main () at threads.c:55
 Cannot enable the TUI when output is not a terminal

Above, "thread apply" did not try to run the command, while "frame
apply level" did.  ("-" is a valid TUI command.)

That "-" is past "--", so it should have not been confused with an
invalid TID, in the "thread apply" case.

That error actually doesn't come from the TID parser, but instead from
thread_apply_command directly.

So that error/check needs tweaking.  The next question is what to
tweak it to.

"-" is actually a valid TUI command:

 (gdb) help -
 Scroll window backward.
 Usage: - [WIN] [N]

(gdb) frame apply level 0 -- -
#0  main () at threads.c:55
Cannot enable the TUI when output is not a terminal

While I don't imagine it being useful to use that "-" command with
"thread apply" or "frame apply level", the fact is that you can use it
with "frame apply level", but not with "thread apply".  And since it's
an actual command, pedantically it seems right to allow it.

That's what this commit does.

Note: simply removing the "isalpha" check regresses
gdb.multi/tids.exp -- see related commit 3f5b7598805c.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* thread.c (thread_apply_command): Check for invalid TID with
	isdigit instead of !isalpha.
---
 gdb/thread.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gdb/thread.c b/gdb/thread.c
index 24906fa7d60..ea87f51c6e6 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -1583,7 +1583,7 @@ thread_apply_command (const char *tidlist, int from_tty)
   if (*cmd == '\0')
     error (_("Please specify a command following the thread ID list"));
 
-  if (tidlist == cmd || !isalpha (cmd[0]))
+  if (tidlist == cmd || isdigit (cmd[0]))
     invalid_thread_id_error (cmd);
 
   scoped_restore_current_thread restore_thread;
-- 
2.14.5

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

* [PATCH v2 13/24] Make "print" and "compile print" support -OPT options
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (4 preceding siblings ...)
  2019-05-30 19:53 ` [PATCH v2 06/24] Fix "set enum-command value garbage" Pedro Alves
@ 2019-05-30 19:54 ` Pedro Alves
  2019-06-03 19:20   ` Tom Tromey
  2019-05-30 19:54 ` [PATCH v2 08/24] gdb.base/completion.exp: Fix comment typo Pedro Alves
                   ` (18 subsequent siblings)
  24 siblings, 1 reply; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:54 UTC (permalink / raw)
  To: gdb-patches

This patch adds support for "print -option optval --", etc.
Likewise for "compile print".

We'll get:

~~~~~~
(gdb) help print
Print value of expression EXP.
Usage: print [[OPTION]... --] [/FMT] [EXP]

Options:
  -address [on|off]
    Set printing of addresses.

  -array [on|off]
    Set pretty formatting of arrays.

  -array-indexes [on|off]
    Set printing of array indexes.

  -elements NUMBER|unlimited
    Set limit on string chars or array elements to print.
    "unlimited" causes there to be no limit.

  -max-depth NUMBER|unlimited
    Set maximum print depth for nested structures, unions and arrays.
    When structures, unions, or arrays are nested beyond this depth then they
    will be replaced with either '{...}' or '(...)' depending on the language.
    Use "unlimited" to print the complete structure.

-null-stop [on|off]
    Set printing of char arrays to stop at first null char.

  -object [on|off]
    Set printing of C++ virtual function tables.

  -pretty [on|off]
    Set pretty formatting of structures.

  -repeats NUMBER|unlimited
    Set threshold for repeated print elements.
    "unlimited" causes all elements to be individually printed.

  -static-members [on|off]
    Set printing of C++ static members.

  -symbol [on|off]
    Set printing of symbol names when printing pointers.

  -union [on|off]
    Set printing of unions interior to structures.

  -vtbl [on|off]
    Set printing of C++ virtual function tables.

Note: because this command accepts arbitrary expressions, if you
specify any command option, you must use a double dash ("--")
to mark the end of option processing.  E.g.: "print -o -- myobj".
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I want to highlight the comment above about "--".

At first, I thought we could make the print command parse the options,
and if the option wasn't recognized, fallback to parsing as an
expression.  Then, if the user wanted to disambiguate, he'd use the
"--" option delimiter.  For example, if you had a variable called
"object" and you wanted to print its negative, you'd have to do:

  (gdb) print -- -object

After getting that working, I saw that gdb.pascal/floats.exp
regressed, in these tests:

 gdb_test "print -r" " = -1\\.2(499.*|5|500.*)"
 gdb_test "print -(r)" " = -1.2(499.*|5|500.*)"
 gdb_test "print -(r + s)" " = -3\\.4(499.*|5|500.*)"

It's the first one that I found most concerning.  It regressed because
"-r" is the abbreviation of "-raw".  I realized then that the behavior
change was a bit risker than I'd like, considering scripts, wrappers
around gdb, etc., and even user expectation.  So instead, I made the
print command _require_ the "--" options delimiter if you want to
specify any option.  So:

  (gdb) print -r

is parsed as an expression, and

  (gdb) print -r --

is parsed as an option.

I noticed that that's also what lldb's expr (the equivalent of print)
does to handle the same problem.

Going back the options themselves, note that:

 - you can shorten option names, as long as unambiguous.
 - For boolean options, 0/1 stand for off/on.
 - For boolean options, "true" is implied.

So these are all equivalent:

 (gdb) print -object on -static-members off -pretty on -- foo
 (gdb) print -object -static-members off -pretty -- foo
 (gdb) print -object -static-members 0 -pretty -- foo
 (gdb) print -o -st 0 -p -- foo

TAB completion is fully supported:

  (gdb) p -[TAB]
  -address         -elements        -pretty          -symbol
  -array           -null-stop       -repeats         -union
  -array-indexes   -object          -static-members  -vtbl

Note that the code is organized such that some of the options and the
"set/show" commands code is shared.  In particular, the "print"
options and the corresponding "set print" commands are defined with
the same structures.  The commands are installed with the
gdb::option::add_setshow_cmds_for_options function.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* compile/compile.c: Include "cli/cli-option.h".
	(compile_print_value): Scope data pointer is now a
	value_print_options pointer; adjust.
	(compile_print_command): Process options.  Scope data pointer is
	now a value_print_options pointer; adjust.
	(_initialize_compile): Update "compile print"'s help to include
	supported options.  Install a completer for "compile print".
	* cp-valprint.c (show_vtblprint, show_objectprint)
	(show_static_field_print): Delete.
	(_initialize_cp_valprint): Don't install "set print
	static-members", "set print vtbl", "set print object" here.
	* printcmd.c: Include "cli/cli-option.h" and
	"common/gdb_optional.h".
	(print_command_parse_format): Rework to fill in a
	value_print_options instead of a format_data.
	(print_value): Change parameter type from format_data pointer to
	value_print_options reference.  Adjust.
	(print_command_1): Process options.  Adjust to pass down a
	value_print_options.
	(print_command_completer): New.
	(_initialize_printcmd): Install print_command_completer as
	handle_brkchars completer for the "print" command.  Update
	"print"'s help to include supported options.
	* valprint.c: Include "cli/cli-option.h".
	(show_vtblprint, show_objectprint, show_static_field_print): Moved
	here from cp-valprint.c.
	(boolean_option_def, uinteger_option_def)
	(value_print_option_defs, make_value_print_options_def_group):
	New.  Use gdb::option::add_setshow_cmds_for_options to install
	"set print elements", "set print null-stop", "set print repeats",
	"set print pretty", "set print union", "set print array", "set
	print address", "set print symbol", "set print array-indexes".
	* valprint.h: Include <string> and "cli/cli-option.h".
	(make_value_print_options_def_group): Declare.
	(print_value): Change parameter type from format_data pointer to
	value_print_options reference.
	(print_command_completer): Declare.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/options.exp: Build executable.
	(test-print): New procedure.
	(top level): Call it, once for "print" and another for "compile
	print".
---
 gdb/compile/compile.c              |  46 +++++--
 gdb/cp-valprint.c                  |  57 --------
 gdb/printcmd.c                     |  94 +++++++++-----
 gdb/testsuite/gdb.base/options.exp | 122 +++++++++++++++++
 gdb/valprint.c                     | 259 +++++++++++++++++++++++++------------
 gdb/valprint.h                     |  20 ++-
 6 files changed, 416 insertions(+), 182 deletions(-)

diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c
index 72920642d16..6693809cf4f 100644
--- a/gdb/compile/compile.c
+++ b/gdb/compile/compile.c
@@ -23,6 +23,7 @@
 #include "command.h"
 #include "cli/cli-script.h"
 #include "cli/cli-utils.h"
+#include "cli/cli-option.h"
 #include "completer.h"
 #include "gdbcmd.h"
 #include "compile.h"
@@ -328,9 +329,9 @@ compile_code_command (const char *arg, int from_tty)
 void
 compile_print_value (struct value *val, void *data_voidp)
 {
-  const struct format_data *fmtp = (const struct format_data *) data_voidp;
+  const value_print_options *print_opts = (value_print_options *) data_voidp;
 
-  print_value (val, fmtp);
+  print_value (val, *print_opts);
 }
 
 /* Handle the input from the 'compile print' command.  The "compile
@@ -342,22 +343,30 @@ static void
 compile_print_command (const char *arg, int from_tty)
 {
   enum compile_i_scope_types scope = COMPILE_I_PRINT_ADDRESS_SCOPE;
-  struct format_data fmt;
+  value_print_options print_opts;
 
   scoped_restore save_async = make_scoped_restore (&current_ui->async, 0);
 
-  /* Passing &FMT as SCOPE_DATA is safe as do_module_cleanup will not
-     touch the stale pointer if compile_object_run has already quit.  */
-  print_command_parse_format (&arg, "compile print", &fmt);
+  get_user_print_options (&print_opts);
+  /* Override global settings with explicit options, if any.  */
+  auto group = make_value_print_options_def_group (&print_opts);
+  gdb::option::process_options
+    (&arg, gdb::option::PROCESS_OPTIONS_REQUIRE_DELIMITER, group);
+
+  print_command_parse_format (&arg, "compile print", &print_opts);
+
+  /* Passing &PRINT_OPTS as SCOPE_DATA is safe as do_module_cleanup
+     will not touch the stale pointer if compile_object_run has
+     already quit.  */
 
   if (arg && *arg)
-    eval_compile_command (NULL, arg, scope, &fmt);
+    eval_compile_command (NULL, arg, scope, &print_opts);
   else
     {
       counted_command_line l = get_command_line (compile_control, "");
 
       l->control_u.compile.scope = scope;
-      l->control_u.compile.scope_data = &fmt;
+      l->control_u.compile.scope_data = &print_opts;
       execute_control_command_untraced (l.get ());
     }
 }
@@ -946,11 +955,19 @@ Usage: compile file [-r|-raw] [FILENAME]\n\
 	       &compile_command_list);
   set_cmd_completer (c, filename_completer);
 
-  add_cmd ("print", class_obscure, compile_print_command,
-	   _("\
+  const auto compile_print_opts = make_value_print_options_def_group (nullptr);
+
+  static const std::string compile_print_help
+    = gdb::option::build_help (N_("\
 Evaluate EXPR by using the compiler and print result.\n\
 \n\
-Usage: compile print[/FMT] [EXPR]\n\
+Usage: compile print [[OPTION]... --] [/FMT] [EXPR]\n\
+\n\
+Options:\n\
+%OPTIONS%\
+Note: because this command accepts arbitrary expressions, if you\n\
+specify any command option, you must use a double dash (\"--\")\n\
+to mark the end of option processing.  E.g.: \"compile print -o -- myobj\".\n\
 \n\
 The expression may be specified on the same line as the command, e.g.:\n\
 \n\
@@ -963,7 +980,12 @@ indicate the end of the expression.\n\
 \n\
 EXPR may be preceded with /FMT, where FMT is a format letter\n\
 but no count or size letter (see \"x\" command)."),
-	   &compile_command_list);
+			       compile_print_opts);
+
+  c = add_cmd ("print", class_obscure, compile_print_command,
+	       compile_print_help.c_str (),
+	       &compile_command_list);
+  set_cmd_completer_handle_brkchars (c, print_command_completer);
 
   add_setshow_boolean_cmd ("compile", class_maintenance, &compile_debug, _("\
 Set compile command debugging."), _("\
diff --git a/gdb/cp-valprint.c b/gdb/cp-valprint.c
index d8d5c06fd1e..5781d7ab561 100644
--- a/gdb/cp-valprint.c
+++ b/gdb/cp-valprint.c
@@ -37,39 +37,6 @@
 #include "typeprint.h"
 #include "common/byte-vector.h"
 
-/* Controls printing of vtbl's.  */
-static void
-show_vtblprint (struct ui_file *file, int from_tty,
-		struct cmd_list_element *c, const char *value)
-{
-  fprintf_filtered (file, _("\
-Printing of C++ virtual function tables is %s.\n"),
-		    value);
-}
-
-/* Controls looking up an object's derived type using what we find in
-   its vtables.  */
-static void
-show_objectprint (struct ui_file *file, int from_tty,
-		  struct cmd_list_element *c,
-		  const char *value)
-{
-  fprintf_filtered (file, _("\
-Printing of object's derived type based on vtable info is %s.\n"),
-		    value);
-}
-
-static void
-show_static_field_print (struct ui_file *file, int from_tty,
-			 struct cmd_list_element *c,
-			 const char *value)
-{
-  fprintf_filtered (file,
-		    _("Printing of C++ static members is %s.\n"),
-		    value);
-}
-
-
 static struct obstack dont_print_vb_obstack;
 static struct obstack dont_print_statmem_obstack;
 static struct obstack dont_print_stat_array_obstack;
@@ -821,30 +788,6 @@ cp_print_class_member (const gdb_byte *valaddr, struct type *type,
 void
 _initialize_cp_valprint (void)
 {
-  add_setshow_boolean_cmd ("static-members", class_support,
-			   &user_print_options.static_field_print, _("\
-Set printing of C++ static members."), _("\
-Show printing of C++ static members."), NULL,
-			   NULL,
-			   show_static_field_print,
-			   &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("vtbl", class_support,
-			   &user_print_options.vtblprint, _("\
-Set printing of C++ virtual function tables."), _("\
-Show printing of C++ virtual function tables."), NULL,
-			   NULL,
-			   show_vtblprint,
-			   &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("object", class_support,
-			   &user_print_options.objectprint, _("\
-Set printing of object's derived type based on vtable info."), _("\
-Show printing of object's derived type based on vtable info."), NULL,
-			   NULL,
-			   show_objectprint,
-			   &setprintlist, &showprintlist);
-
   obstack_begin (&dont_print_stat_array_obstack,
 		 32 * sizeof (struct type *));
   obstack_begin (&dont_print_statmem_obstack,
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index 9e84594fe68..0509360581e 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -45,11 +45,13 @@
 #include "charset.h"
 #include "arch-utils.h"
 #include "cli/cli-utils.h"
+#include "cli/cli-option.h"
 #include "cli/cli-script.h"
 #include "cli/cli-style.h"
 #include "common/format.h"
 #include "source.h"
 #include "common/byte-vector.h"
+#include "common/gdb_optional.h"
 
 /* Last specified output format.  */
 
@@ -1117,40 +1119,41 @@ validate_format (struct format_data fmt, const char *cmdname)
 	   fmt.format, cmdname);
 }
 
-/* Parse print command format string into *FMTP and update *EXPP.
+/* Parse print command format string into *OPTS and update *EXPP.
    CMDNAME should name the current command.  */
 
 void
 print_command_parse_format (const char **expp, const char *cmdname,
-			    struct format_data *fmtp)
+			    value_print_options *opts)
 {
   const char *exp = *expp;
 
   if (exp && *exp == '/')
     {
+      format_data fmt;
+
       exp++;
-      *fmtp = decode_format (&exp, last_format, 0);
-      validate_format (*fmtp, cmdname);
-      last_format = fmtp->format;
+      fmt = decode_format (&exp, last_format, 0);
+      validate_format (fmt, cmdname);
+      last_format = fmt.format;
+
+      opts->format = fmt.format;
+      opts->raw = fmt.raw;
     }
   else
     {
-      fmtp->count = 1;
-      fmtp->format = 0;
-      fmtp->size = 0;
-      fmtp->raw = 0;
+      opts->format = 0;
+      opts->raw = 0;
     }
 
   *expp = exp;
 }
 
-/* Print VAL to console according to *FMTP, including recording it to
-   the history.  */
+/* See valprint.h.  */
 
 void
-print_value (struct value *val, const struct format_data *fmtp)
+print_value (value *val, const value_print_options &opts)
 {
-  struct value_print_options opts;
   int histindex = record_latest_value (val);
 
   annotate_value_history_begin (histindex, value_type (val));
@@ -1159,28 +1162,31 @@ print_value (struct value *val, const struct format_data *fmtp)
 
   annotate_value_history_value ();
 
-  get_formatted_print_options (&opts, fmtp->format);
-  opts.raw = fmtp->raw;
-
-  print_formatted (val, fmtp->size, &opts, gdb_stdout);
+  print_formatted (val, 0, &opts, gdb_stdout);
   printf_filtered ("\n");
 
   annotate_value_history_end ();
 }
 
-/* Evaluate string EXP as an expression in the current language and
-   print the resulting value.  EXP may contain a format specifier as the
-   first argument ("/x myvar" for example, to print myvar in hex).  */
+/* Implementation of the "print" and "call" commands.  */
 
 static void
-print_command_1 (const char *exp, int voidprint)
+print_command_1 (const char *args, int voidprint)
 {
   struct value *val;
-  struct format_data fmt;
+  value_print_options print_opts;
+
+  get_user_print_options (&print_opts);
+  /* Override global settings with explicit options, if any.  */
+  auto group = make_value_print_options_def_group (&print_opts);
+  gdb::option::process_options
+    (&args, gdb::option::PROCESS_OPTIONS_REQUIRE_DELIMITER, group);
+
+  print_command_parse_format (&args, "print", &print_opts);
 
-  print_command_parse_format (&exp, "print", &fmt);
+  const char *exp = args;
 
-  if (exp && *exp)
+  if (exp != nullptr && *exp)
     {
       expression_up expr = parse_expression (exp);
       val = evaluate_expression (expr.get ());
@@ -1190,7 +1196,23 @@ print_command_1 (const char *exp, int voidprint)
 
   if (voidprint || (val && value_type (val) &&
 		    TYPE_CODE (value_type (val)) != TYPE_CODE_VOID))
-    print_value (val, &fmt);
+    print_value (val, print_opts);
+}
+
+/* See valprint.h.  */
+
+void
+print_command_completer (struct cmd_list_element *ignore,
+			 completion_tracker &tracker,
+			 const char *text, const char * /*word*/)
+{
+  const auto group = make_value_print_options_def_group (nullptr);
+  if (gdb::option::complete_options
+      (tracker, &text, gdb::option::PROCESS_OPTIONS_REQUIRE_DELIMITER, group))
+    return;
+
+  const char *word = advance_to_expression_complete_word_point (tracker, text);
+  expression_completer (ignore, tracker, text, word);
 }
 
 static void
@@ -2761,7 +2783,7 @@ Usage: call EXP\n\
 The argument is the function name and arguments, in the notation of the\n\
 current working language.  The result is printed and saved in the value\n\
 history, if it is not void."));
-  set_cmd_completer (c, expression_completer);
+  set_cmd_completer_handle_brkchars (c, print_command_completer);
 
   add_cmd ("variable", class_vars, set_command, _("\
 Evaluate expression EXP and assign result to variable VAR\n\
@@ -2775,9 +2797,18 @@ This may usually be abbreviated to simply \"set\"."),
 	   &setlist);
   add_alias_cmd ("var", "variable", class_vars, 0, &setlist);
 
-  c = add_com ("print", class_vars, print_command, _("\
+  const auto print_opts = make_value_print_options_def_group (nullptr);
+
+  static const std::string print_help = gdb::option::build_help (N_("\
 Print value of expression EXP.\n\
-Usage: print[/FMT] EXP\n\
+Usage: print [[OPTION]... --] [/FMT] [EXP]\n\
+\n\
+Options:\n\
+%OPTIONS%\
+Note: because this command accepts arbitrary expressions, if you\n\
+specify any command option, you must use a double dash (\"--\")\n\
+to mark the end of option processing.  E.g.: \"print -o -- myobj\".\n\
+\n\
 Variables accessible are those of the lexical environment of the selected\n\
 stack frame, plus all those whose scope is global or an entire file.\n\
 \n\
@@ -2797,8 +2828,11 @@ where FOO is stored, etc.  FOO must be an expression whose value\n\
 resides in memory.\n\
 \n\
 EXP may be preceded with /FMT, where FMT is a format letter\n\
-but no count or size letter (see \"x\" command)."));
-  set_cmd_completer (c, expression_completer);
+but no count or size letter (see \"x\" command)."),
+					      print_opts);
+
+  c = add_com ("print", class_vars, print_command, print_help.c_str ());
+  set_cmd_completer_handle_brkchars (c, print_command_completer);
   add_com_alias ("p", "print", class_vars, 1);
   add_com_alias ("inspect", "print", class_vars, 1);
 
diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp
index 1891176dc1d..3b4e7ee189f 100644
--- a/gdb/testsuite/gdb.base/options.exp
+++ b/gdb/testsuite/gdb.base/options.exp
@@ -19,9 +19,18 @@
 
 # The test uses the "maintenance test-options" subcommands to exercise
 # TAB-completion and option processing.
+#
+# It also tests option integration in various commands, including
+# "print" and "compile print".
 
 load_lib completion-support.exp
 
+standard_testfile .c
+
+if {[build_executable "failed to prepare" $testfile $srcfile debug]} {
+    return -1
+}
+
 clean_restart
 
 if { ![readline_is_used] } {
@@ -117,6 +126,111 @@ set all_options {
     "-zuinteger-unlimited"
 }
 
+# Basic option-machinery + "print" command integration tests.
+proc_with_prefix test-print {{prefix ""}} {
+    clean_restart
+
+    # Completing "print" with no argument completes on symbols only,
+    # no options are offered.  Since we haven't loaded any symbols,
+    # the match list should be empty.
+    test_gdb_complete_none "${prefix}print "
+
+    # OTOH, completing at "-" should list all options.
+    test_gdb_complete_multiple "${prefix}print " "-" "" {
+	"-address"
+	"-array"
+	"-array-indexes"
+	"-elements"
+	"-max-depth"
+	"-null-stop"
+	"-object"
+	"-pretty"
+	"-repeats"
+	"-static-members"
+	"-symbol"
+	"-union"
+	"-vtbl"
+    }
+
+    global binfile
+    clean_restart $binfile
+
+    if ![runto_main] {
+	fail "cannot run to main"
+	return
+    }
+
+    # Mix options and format.
+    gdb_test "${prefix}print -pretty -- /x 1" " = 0x1"
+
+    # Smoke test that options actually work.
+    gdb_test "${prefix}print -pretty -- g_s" \
+	[multi_line  \
+	     " = {" \
+	     "  a = 1," \
+	     "  b = 2," \
+	     "  c = 3" \
+	     "}"]
+
+    test_gdb_complete_unique \
+	"${prefix}print xxx" \
+	"${prefix}print xxx1"
+    test_gdb_complete_unique \
+	"${prefix}print -- xxx" \
+	"${prefix}print -- xxx1"
+
+    # Error messages when testing with "compile" are different from
+    # the error messages gdb's internal parser throws.  This procedure
+    # hides the difference.  EXPECTED_RE is only considered when not
+    # testing with "compile".
+    proc test_invalid_expression {cmd expected_re} {
+	upvar prefix prefix
+
+	if {$prefix != "compile "} {
+	    gdb_test $cmd $expected_re
+	} else {
+	    # Error messages depend on compiler version, so we just
+	    # look for the last line indicating a failure.
+	    gdb_test $cmd "Compilation failed\\."
+	}
+    }
+
+    # Check that '-XXX' without a "--" is handled as an
+    # expression.
+    gdb_test "${prefix}print -1" " = -1"
+    test_invalid_expression \
+	"${prefix}print --1" \
+	"Left operand of assignment is not an lvalue\\."
+    test_invalid_expression \
+	"${prefix}print -object" \
+	"No symbol \"object\".*"
+
+    # Test printing with options and no expression.
+    set test "${prefix}print -object --"
+    if {$prefix != "compile "} {
+	# Regular "print" repeats the last history value.
+	gdb_test $test " = -1"
+    } else {
+	# "compile print" starts a multiline expression.
+	gdb_test_multiple $test $test {
+	    -re ">$" {
+		gdb_test "-1\nend" " = -1" \
+		    $test
+	    }
+	}
+    }
+
+    # Check that everything after "-- " is treated as an
+    # expression, not confused with an option.
+    test_invalid_expression \
+	"${prefix}print -- -address" \
+	"No symbol.*"
+    gdb_test "${prefix}print -- -1" " = -1"
+    test_invalid_expression \
+	"${prefix}print -- --1" \
+	"Left operand of assignment is not an lvalue\\."
+}
+
 # Miscellaneous tests.
 proc_with_prefix test-misc {variant} {
     global all_options
@@ -552,3 +666,11 @@ foreach_with_prefix cmd {
     }
     test-enum $cmd
 }
+
+# Run the print integration tests.
+test-print ""
+
+# Same for "compile print".
+if ![skip_compile_feature_tests] {
+    test-print "compile "
+}
diff --git a/gdb/valprint.c b/gdb/valprint.c
index 4c3d67a9ff8..e3197e69192 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -36,6 +36,7 @@
 #include <ctype.h>
 #include <algorithm>
 #include "common/byte-vector.h"
+#include "cli/cli-option.h"
 
 /* Maximum number of wchars returned from wchar_iterate.  */
 #define MAX_WCHARS 4
@@ -3069,7 +3070,181 @@ show_print_raw (const char *args, int from_tty)
   cmd_show_list (showprintrawlist, from_tty, "");
 }
 
+/* Controls printing of vtbl's.  */
+static void
+show_vtblprint (struct ui_file *file, int from_tty,
+		struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("\
+Printing of C++ virtual function tables is %s.\n"),
+		    value);
+}
+
+/* Controls looking up an object's derived type using what we find in
+   its vtables.  */
+static void
+show_objectprint (struct ui_file *file, int from_tty,
+		  struct cmd_list_element *c,
+		  const char *value)
+{
+  fprintf_filtered (file, _("\
+Printing of object's derived type based on vtable info is %s.\n"),
+		    value);
+}
+
+static void
+show_static_field_print (struct ui_file *file, int from_tty,
+			 struct cmd_list_element *c,
+			 const char *value)
+{
+  fprintf_filtered (file,
+		    _("Printing of C++ static members is %s.\n"),
+		    value);
+}
+
 \f
+
+/* A couple typedefs to make writing the options a bit more
+   convenient.  */
+using boolean_option_def
+  = gdb::option::boolean_option_def<value_print_options>;
+using uinteger_option_def
+  = gdb::option::uinteger_option_def<value_print_options>;
+using zuinteger_unlimited_option_def
+  = gdb::option::zuinteger_unlimited_option_def<value_print_options>;
+
+/* Definions of options for the "print" and "compile print"
+   commands.  */
+static const gdb::option::option_def value_print_option_defs[] = {
+
+  boolean_option_def {
+    "address",
+    [] (value_print_options *opt) { return &opt->addressprint; },
+    show_addressprint, /* show_cmd_cb */
+    N_("Set printing of addresses."),
+    N_("Show printing of addresses."),
+    NULL, /* help_doc */
+  },
+
+  boolean_option_def {
+    "array",
+    [] (value_print_options *opt) { return &opt->prettyformat_arrays; },
+    show_prettyformat_arrays, /* show_cmd_cb */
+    N_("Set pretty formatting of arrays."),
+    N_("Show pretty formatting of arrays."),
+    NULL, /* help_doc */
+  },
+
+  boolean_option_def {
+    "array-indexes",
+    [] (value_print_options *opt) { return &opt->print_array_indexes; },
+    show_print_array_indexes, /* show_cmd_cb */
+    N_("Set printing of array indexes."),
+    N_("Show printing of array indexes"),
+    NULL, /* help_doc */
+  },
+
+  uinteger_option_def {
+    "elements",
+    [] (value_print_options *opt) { return &opt->print_max; },
+    show_print_max, /* show_cmd_cb */
+    N_("Set limit on string chars or array elements to print."),
+    N_("Show limit on string chars or array elements to print."),
+    N_("\"unlimited\" causes there to be no limit."),
+  },
+
+  zuinteger_unlimited_option_def {
+    "max-depth",
+    [] (value_print_options *opt) { return &opt->max_depth; },
+    show_print_max_depth, /* show_cmd_cb */
+    N_("Set maximum print depth for nested structures, unions and arrays."),
+    N_("Show maximum print depth for nested structures, unions, and arrays."),
+    N_("When structures, unions, or arrays are nested beyond this depth then they\n\
+will be replaced with either '{...}' or '(...)' depending on the language.\n\
+Use \"unlimited\" to print the complete structure.")
+  },
+
+  boolean_option_def {
+    "null-stop",
+    [] (value_print_options *opt) { return &opt->stop_print_at_null; },
+    show_stop_print_at_null, /* show_cmd_cb */
+    N_("Set printing of char arrays to stop at first null char."),
+    N_("Show printing of char arrays to stop at first null char."),
+    NULL, /* help_doc */
+  },
+
+  boolean_option_def {
+    "object",
+    [] (value_print_options *opt) { return &opt->objectprint; },
+    show_objectprint, /* show_cmd_cb */
+    _("Set printing of C++ virtual function tables."),
+    _("Show printing of C++ virtual function tables."),
+    NULL, /* help_doc */
+  },
+
+  boolean_option_def {
+    "pretty",
+    [] (value_print_options *opt) { return &opt->prettyformat_structs; },
+    show_prettyformat_structs, /* show_cmd_cb */
+    N_("Set pretty formatting of structures."),
+    N_("Show pretty formatting of structures."),
+    NULL, /* help_doc */
+  },
+
+  uinteger_option_def {
+    "repeats",
+    [] (value_print_options *opt) { return &opt->repeat_count_threshold; },
+    show_repeat_count_threshold, /* show_cmd_cb */
+    N_("Set threshold for repeated print elements."),
+    N_("Show threshold for repeated print elements."),
+    N_("\"unlimited\" causes all elements to be individually printed."),
+  },
+
+  boolean_option_def {
+    "static-members",
+    [] (value_print_options *opt) { return &opt->static_field_print; },
+    show_static_field_print, /* show_cmd_cb */
+    N_("Set printing of C++ static members."),
+    N_("Show printing of C++ static members."),
+    NULL, /* help_doc */
+  },
+
+  boolean_option_def {
+    "symbol",
+    [] (value_print_options *opt) { return &opt->symbol_print; },
+    show_symbol_print, /* show_cmd_cb */
+    N_("Set printing of symbol names when printing pointers."),
+    N_("Show printing of symbol names when printing pointers."),
+    NULL, /* help_doc */
+  },
+
+  boolean_option_def {
+    "union",
+    [] (value_print_options *opt) { return &opt->unionprint; },
+    show_unionprint, /* show_cmd_cb */
+    N_("Set printing of unions interior to structures."),
+    N_("Show printing of unions interior to structures."),
+    NULL, /* help_doc */
+  },
+
+  boolean_option_def {
+    "vtbl",
+    [] (value_print_options *opt) { return &opt->vtblprint; },
+    show_vtblprint, /* show_cmd_cb */
+    N_("Set printing of C++ virtual function tables."),
+    N_("Show printing of C++ virtual function tables."),
+    NULL, /* help_doc */
+  },
+};
+
+/* See valprint.h.  */
+
+gdb::option::option_def_group
+make_value_print_options_def_group (value_print_options *opts)
+{
+  return {{value_print_option_defs}, opts};
+}
+
 void
 _initialize_valprint (void)
 {
@@ -3094,71 +3269,9 @@ Generic command for setting what things to print in \"raw\" mode."),
 		  _("Generic command for showing \"print raw\" settings."),
 		  &showprintrawlist, "show print raw ", 0, &showprintlist);
 
-  add_setshow_uinteger_cmd ("elements", no_class,
-			    &user_print_options.print_max, _("\
-Set limit on string chars or array elements to print."), _("\
-Show limit on string chars or array elements to print."), _("\
-\"set print elements unlimited\" causes there to be no limit."),
-			    NULL,
-			    show_print_max,
-			    &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("null-stop", no_class,
-			   &user_print_options.stop_print_at_null, _("\
-Set printing of char arrays to stop at first null char."), _("\
-Show printing of char arrays to stop at first null char."), NULL,
-			   NULL,
-			   show_stop_print_at_null,
-			   &setprintlist, &showprintlist);
-
-  add_setshow_uinteger_cmd ("repeats", no_class,
-			    &user_print_options.repeat_count_threshold, _("\
-Set threshold for repeated print elements."), _("\
-Show threshold for repeated print elements."), _("\
-\"set print repeats unlimited\" causes all elements to be individually printed."),
-			    NULL,
-			    show_repeat_count_threshold,
-			    &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("pretty", class_support,
-			   &user_print_options.prettyformat_structs, _("\
-Set pretty formatting of structures."), _("\
-Show pretty formatting of structures."), NULL,
-			   NULL,
-			   show_prettyformat_structs,
-			   &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("union", class_support,
-			   &user_print_options.unionprint, _("\
-Set printing of unions interior to structures."), _("\
-Show printing of unions interior to structures."), NULL,
-			   NULL,
-			   show_unionprint,
-			   &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("array", class_support,
-			   &user_print_options.prettyformat_arrays, _("\
-Set pretty formatting of arrays."), _("\
-Show pretty formatting of arrays."), NULL,
-			   NULL,
-			   show_prettyformat_arrays,
-			   &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("address", class_support,
-			   &user_print_options.addressprint, _("\
-Set printing of addresses."), _("\
-Show printing of addresses."), NULL,
-			   NULL,
-			   show_addressprint,
-			   &setprintlist, &showprintlist);
-
-  add_setshow_boolean_cmd ("symbol", class_support,
-			   &user_print_options.symbol_print, _("\
-Set printing of symbol names when printing pointers."), _("\
-Show printing of symbol names when printing pointers."),
-			   NULL, NULL,
-			   show_symbol_print,
-			   &setprintlist, &showprintlist);
+  gdb::option::add_setshow_cmds_for_options
+    (class_support, &user_print_options, value_print_option_defs,
+     &setprintlist, &showprintlist);
 
   add_setshow_zuinteger_cmd ("input-radix", class_support, &input_radix_1,
 			     _("\
@@ -3192,20 +3305,4 @@ Without an argument, sets both radices back to the default value of 10."),
 Show the default input and output number radices.\n\
 Use 'show input-radix' or 'show output-radix' to independently show each."),
 	   &showlist);
-
-  add_setshow_boolean_cmd ("array-indexes", class_support,
-                           &user_print_options.print_array_indexes, _("\
-Set printing of array indexes."), _("\
-Show printing of array indexes"), NULL, NULL, show_print_array_indexes,
-                           &setprintlist, &showprintlist);
-
-  add_setshow_zuinteger_unlimited_cmd ("max-depth", class_support,
-                            &user_print_options.max_depth, _("\
-Set maximum print depth for nested structures, unions and arrays."), _("\
-Show maximum print depth for nested structures, unions, and arrays."), _("\
-When structures, unions, or arrays are nested beyond this depth then they\n\
-will be replaced with either '{...}' or '(...)' depending on the language.\n\
-Use 'set print max-depth unlimited' to print the complete structure."),
-				       NULL, show_print_max_depth,
-				       &setprintlist, &showprintlist);
 }
diff --git a/gdb/valprint.h b/gdb/valprint.h
index 0bd3f1966c4..987c534eaf4 100644
--- a/gdb/valprint.h
+++ b/gdb/valprint.h
@@ -20,6 +20,8 @@
 #ifndef VALPRINT_H
 #define VALPRINT_H
 
+#include "cli/cli-option.h"
+
 /* This is used to pass formatting options to various value-printing
    functions.  */
 struct value_print_options
@@ -100,6 +102,11 @@ struct value_print_options
   int finish_print;
 };
 
+/* Create an option_def_group for the value_print options, with OPTS
+   as context.  */
+extern gdb::option::option_def_group make_value_print_options_def_group
+  (value_print_options *opts);
+
 /* The global print options set by the user.  In general this should
    not be directly accessed, except by set/show commands.  Ordinary
    code should call get_user_print_options instead.  */
@@ -233,8 +240,17 @@ struct format_data
   };
 
 extern void print_command_parse_format (const char **expp, const char *cmdname,
-					struct format_data *fmtp);
-extern void print_value (struct value *val, const struct format_data *fmtp);
+					value_print_options *opts);
+
+/* Print VAL to console according to OPTS, including recording it to
+   the history.  */
+extern void print_value (value *val, const value_print_options &opts);
+
+/* Completer for the "print", "call", and "compile print"
+   commands.  */
+extern void print_command_completer (struct cmd_list_element *ignore,
+				     completion_tracker &tracker,
+				     const char *text, const char *word);
 
 /* Given an address ADDR return all the elements needed to print the
    address in a symbolic form.  NAME can be mangled or not depending
-- 
2.14.5

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

* [PATCH v2 10/24] boolean/auto-boolean commands, make "o" ambiguous
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (10 preceding siblings ...)
  2019-05-30 19:54 ` [PATCH v2 21/24] "thread apply 1 -- -" vs "frame apply level 0 -- -" Pedro Alves
@ 2019-05-30 19:54 ` Pedro Alves
  2019-05-30 19:54 ` [PATCH v2 16/24] Make "backtrace" support -OPT options Pedro Alves
                   ` (12 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:54 UTC (permalink / raw)
  To: gdb-patches

We currently accept "o" with boolean/auto-boolean commands, taking it
to mean "on".  But "o" is ambiguous, between "on" and "off".  I can't
imagine why assuming the user wanted to type "on" is a good idea, it
might have been a typo.

This commit makes gdb error out.  We now get:

 (gdb) maint test-settings set boolean o
 "on" or "off" expected.

 (gdb) maint test-settings set auto-boolean o
 "on", "off" or "auto" expected.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* cli/cli-setshow.c (parse_auto_binary_operation)
	(parse_cli_boolean_value): Don't allow "o".

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/settings.exp (test-boolean, test-auto-boolean): Check
	that "o" is ambiguous.
---
 gdb/cli/cli-setshow.c               | 15 ++++++++++-----
 gdb/testsuite/gdb.base/settings.exp | 10 ++++++++--
 2 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 47e50941e3b..082bfe7f580 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -54,18 +54,21 @@ parse_auto_binary_operation (const char *arg)
 
       while (isspace (arg[length - 1]) && length > 0)
 	length--;
-      if (strncmp (arg, "on", length) == 0
+
+      /* Note that "o" is ambiguous.  */
+
+      if ((length == 2 && strncmp (arg, "on", length) == 0)
 	  || strncmp (arg, "1", length) == 0
 	  || strncmp (arg, "yes", length) == 0
 	  || strncmp (arg, "enable", length) == 0)
 	return AUTO_BOOLEAN_TRUE;
-      else if (strncmp (arg, "off", length) == 0
+      else if ((length >= 2 && strncmp (arg, "off", length) == 0)
 	       || strncmp (arg, "0", length) == 0
 	       || strncmp (arg, "no", length) == 0
 	       || strncmp (arg, "disable", length) == 0)
 	return AUTO_BOOLEAN_FALSE;
       else if (strncmp (arg, "auto", length) == 0
-	       || (strncmp (arg, "-1", length) == 0 && length > 1))
+	       || (length > 1 && strncmp (arg, "-1", length) == 0))
 	return AUTO_BOOLEAN_AUTO;
     }
   error (_("\"on\", \"off\" or \"auto\" expected."));
@@ -87,12 +90,14 @@ parse_cli_boolean_value (const char *arg)
   while (arg[length - 1] == ' ' || arg[length - 1] == '\t')
     length--;
 
-  if (strncmp (arg, "on", length) == 0
+  /* Note that "o" is ambiguous.  */
+
+  if ((length == 2 && strncmp (arg, "on", length) == 0)
       || strncmp (arg, "1", length) == 0
       || strncmp (arg, "yes", length) == 0
       || strncmp (arg, "enable", length) == 0)
     return 1;
-  else if (strncmp (arg, "off", length) == 0
+  else if ((length >= 2 && strncmp (arg, "off", length) == 0)
 	   || strncmp (arg, "0", length) == 0
 	   || strncmp (arg, "no", length) == 0
 	   || strncmp (arg, "disable", length) == 0)
diff --git a/gdb/testsuite/gdb.base/settings.exp b/gdb/testsuite/gdb.base/settings.exp
index 2c5e9cc2a04..3c241fa9013 100644
--- a/gdb/testsuite/gdb.base/settings.exp
+++ b/gdb/testsuite/gdb.base/settings.exp
@@ -191,13 +191,16 @@ proc_with_prefix test-boolean {} {
     gdb_test "$set_cmd auto" \
 	"\"on\" or \"off\" expected\\."
 
+    # "o" is ambiguous.
+    gdb_test "$set_cmd o" \
+	"\"on\" or \"off\" expected\\."
+
     # Various valid values.  Test both full value names and
     # abbreviations.
 
     # Note that unlike with auto-bool, empty value implies "on".
     foreach_with_prefix value {
 	""
-	"o"
 	"on"
 	"1"
 	"y"
@@ -275,11 +278,14 @@ proc_with_prefix test-auto-boolean {} {
     gdb_test "$set_cmd on 1" \
 	"\"on\", \"off\" or \"auto\" expected\\."
 
+    # "o" is ambiguous.
+    gdb_test "$set_cmd o" \
+	"\"on\", \"off\" or \"auto\" expected\\."
+
     # Various valid values.  Test both full value names and
     # abbreviations.
 
     foreach_with_prefix value {
-	"o"
 	"on"
 	"1"
 	"y"
-- 
2.14.5

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

* [PATCH v2 05/24] Allow "unlimited" abbreviations
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (8 preceding siblings ...)
  2019-05-30 19:54 ` [PATCH v2 07/24] Remove "show" command completers, "set" command completers for string commands Pedro Alves
@ 2019-05-30 19:54 ` Pedro Alves
  2019-05-30 19:54 ` [PATCH v2 21/24] "thread apply 1 -- -" vs "frame apply level 0 -- -" Pedro Alves
                   ` (14 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:54 UTC (permalink / raw)
  To: gdb-patches

Currently we can abbreviate "on/off/enable/disable/yes/no" in boolean
settings,

  (gdb) set non-stop of
  (gdb) set non-stop en

we can abbreviate the items of enumeration commands,

  (gdb) set print frame-arguments scal
  (gdb) set scheduler-locking rep

but we cannot abbreviate "unlimited" in integer commands.

 (gdb) set print elements u
 No symbol "u" in current context.

This commit fixes that.

Testcases will be in gdb.base/settings.exp and gdb.base/options.exp,
in following patches.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* cli/cli-setshow.c (is_unlimited_literal): Allow abbreviations.
---
 gdb/cli/cli-setshow.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 5b87f905d0d..96d7bf5c3c0 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -132,12 +132,16 @@ deprecated_show_value_hack (struct ui_file *ignore_file,
 static int
 is_unlimited_literal (const char *arg)
 {
-  size_t len = sizeof ("unlimited") - 1;
-
   arg = skip_spaces (arg);
 
-  return (strncmp (arg, "unlimited", len) == 0
-	  && (isspace (arg[len]) || arg[len] == '\0'));
+  const char *p = skip_to_space (arg);
+
+  size_t len = p - arg;
+
+  if (len > 0 && strncmp ("unlimited", arg, len) == 0)
+    return true;
+
+  return false;
 }
 
 
-- 
2.14.5

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

* [PATCH v2 08/24] gdb.base/completion.exp: Fix comment typo
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (5 preceding siblings ...)
  2019-05-30 19:54 ` [PATCH v2 13/24] Make "print" and "compile print" support -OPT options Pedro Alves
@ 2019-05-30 19:54 ` Pedro Alves
  2019-05-30 19:54 ` [PATCH v2 24/24] NEWS and manual changes for command options changes Pedro Alves
                   ` (17 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:54 UTC (permalink / raw)
  To: gdb-patches

Noticed this while writing the following patch.  We cd to $srcdir, not $objdir.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/completion.exp: Fix comment typo.
---
 gdb/testsuite/gdb.base/completion.exp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gdb/testsuite/gdb.base/completion.exp b/gdb/testsuite/gdb.base/completion.exp
index b110ba3c45a..e705f8cdb0e 100644
--- a/gdb/testsuite/gdb.base/completion.exp
+++ b/gdb/testsuite/gdb.base/completion.exp
@@ -665,7 +665,7 @@ gdb_test "complete whatis &values\[0\]->z" \
 # strategy on Tuesdays to get the cursor in the right place, that's
 # not something the testsuite should care about.
 #
-# So, we avoid long lines.  We `cd' to ${objdir} first, and then do
+# So, we avoid long lines.  We `cd' to ${srcdir} first, and then do
 # the completion relative to the current directory.
 
 # ${srcdir} may be a relative path.  We want to make sure we end up
-- 
2.14.5

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

* [PATCH v2 12/24] Introduce generic command options framework
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (12 preceding siblings ...)
  2019-05-30 19:54 ` [PATCH v2 16/24] Make "backtrace" support -OPT options Pedro Alves
@ 2019-05-30 19:54 ` Pedro Alves
  2019-06-03 19:25   ` Tom Tromey
  2019-05-30 19:54 ` [PATCH v2 03/24] Fix TID parser bug Pedro Alves
                   ` (10 subsequent siblings)
  24 siblings, 1 reply; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:54 UTC (permalink / raw)
  To: gdb-patches

This commit adds a generic command options framework, that makes it
easy enough to add '-'-style options to commands in a uniform way,
instead of each command implementing option parsing in its own way.

Options are defined in arrays of option_def objects (for option
definition), and the same options definitions are used for supporting
TAB completion, and also for generating the relevant help fragment of
the "help" command.  See the gdb::options::build_help function, which
returns a string with the result of replacing %OPTIONS% in a template
string with an auto-generated "help" string fragment for all the
passed-in options.

Since most options in GDB are in the form of "-OPT", with a single
dash, this is the format that the framework supports.

I like to think of gdb's "-OPT" as the equivalent to getopt's long
options format ("--OPT"), and gdb's "/" as the equivalent to getopt's
short options format.  getopt's short options format allows mixing
several one-character options, like "ls -als", kind of similar to
gdb's "x /FMT" and "disassemble /MOD", etc.  While with gdb's "-"
options, the option is expected to have a full name, and to be
abbreviatable.  E.g., "watch -location", "break -function main", etc.

This patch only deals with "-" options.  The above comment serves more
to disclose why I don't think we should support mixing several
unrelated options in a single "-" option invocation, like "thread
apply -qcs" instead of "thread apply -q -c -s".

The following patches will add uses of the infrastructure to several
key commands.  Most notably, "print", "compile print", "backtrace",
"frame apply" and "thread apply".  I tried to add options to several
commands in order to make sure the framework didn't leave that many
open holes open.

Options use the same type as set commands -- enum var_types.  So
boolean options are var_boolean, enum options are var_enum, etc.  The
idea is to share code between settings commands and command options.
The "print" options will be based on the "set print" commands, and
their names will be the same.  Actually, their definitions will be the
same too.  There is a function to create "set/show" commands from an
array for option definitions:

 /* Install set/show commands for options defined in OPTIONS.  DATA is
    a pointer to the structure that holds the data associated with the
    OPTIONS array.  */
 extern void add_setshow_cmds_for_options (command_class cmd_class, void *data,
					   gdb::array_view<const option_def> options,
					   struct cmd_list_element **set_list,
					   struct cmd_list_element **show_list);

That will be used by several following patches.

Other features:

 - You can use the "--" delimiter to explicitly indicate end of
   options.  Several existing commands use this token sequence for
   this effect already, so this just standardizes it.

 - You can shorten option names, as long as unambiguous.  Currently,
   some commands allow this (e.g., break -function), while others do
   not (thread apply all -ascending).  As GDB allows abbreviating
   command names and other things, it feels more GDB-ish to allow
   abbreviating option names too, to me.

 - For boolean options, 0/1 stands for off/on, just like with boolean
   "set" commands.

 - For boolean options, "true" is implied, just like with boolean "set
   commands.

These are the option types supported, with a few examples:

 - boolean options (var_boolean).  The option's argument is optional.

   (gdb) print -pretty on -- *obj
   (gdb) print -pretty off -- *obj
   (gdb) print -p -- *obj
   (gdb) print -p 0 -- *obj

 - flag options (like var_boolean, but no option argument (on/off))

   (gdb) thread apply all -s COMMAND

 - enum options  (var_enum)

   (gdb) bt -entry-values compact
   (gdb) bt -e c

 - uinteger options (var_uinteger)

   (gdb) print -elements 100 -- *obj
   (gdb) print -e 100 -- *obj
   (gdb) print -elements unlimited -- *obj
   (gdb) print -e u -- *obj

 - zuinteger-unlimited options (var_zuinteger_unlimited)

   (gdb) print -max-depth 100 -- obj
   (gdb) print -max-depth -1 -- obj
   (gdb) print -max-depth unlimited -- obj

Other var_types could be supported, of course.  These were just the
types that I needed for the commands that I ported over, in the
following patches.

It was interesting (and unfortunate) to find that we need at least 3
different modes to cover the existing commands:

- Commands that require ending options with "--" if you specify any
  option: "print" and "compile print".

- Commands that do not want to require "--", and want to error out if
  you specify an unknown option (i.e., an unknown argument that starts
  with '-'): "compile code" / "compile file".

- Commands that do not want to require "--", and want to process
  unknown options themselves: "bt", because of "bt -COUNT",
  "thread/frame apply", because "-" is a valid command.

The different behavior is encoded in the process_options_mode enum,
passed to process_options/complete_options.

For testing, this patch adds one representative maintenance command
for each of the process_options_mode values, that are used by the
testsuite to exercise the options framework:

 (gdb) maint test-options require-delimiter
 (gdb) maint test-options unknown-is-error
 (gdb) maint test-options unknown-is-operand

and adds another command to help with TAB-completion testing:

 (gdb) maint show test-options-completion-result

See their description at the top of the maint-test-options.c file.

Docs/NEWS are in a patch later in the series.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* Makefile.in (SUBDIR_CLI_SRCS): Add cli/cli-option.c.
	(COMMON_SFILES): Add maint-test-settings.c.
	* cli/cli-decode.c (boolean_enums): New global, factored out from
	...
	(add_setshow_boolean_cmd): ... here.
	* cli/cli-decode.h (boolean_enums): Declare.
	* cli/cli-option.c: New file.
	* cli/cli-option.h: New file.
	* cli/cli-setshow.c (parse_cli_boolean_value(const char **)): New,
	factored out from ...
	(parse_cli_boolean_value(const char *)): ... this.
	(is_unlimited_literal): Change parameter type to pointer to
	pointer.  Adjust and advance ARG pointer.
	(parse_cli_var_uinteger, parse_cli_var_zuinteger_unlimited)
	(parse_cli_var_enum): New, factored out from ...
	(do_set_command): ... this.  Adjust.
	* cli/cli-setshow.h (parse_cli_boolean_value)
	(parse_cli_var_uinteger, parse_cli_var_zuinteger_unlimited)
	(parse_cli_var_enum): Declare.
	* cli/cli-utils.c: Include "cli/cli-option.h".
	(get_ulongest): New.
	* cli/cli-utils.h (get_ulongest): Declare.
	(check_for_argument): New overloads.
	* maint-test-options.c: New file.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/options.c: New file.
	* gdb.base/options.exp: New file.
---
 gdb/Makefile.in                    |   2 +
 gdb/cli/cli-decode.c               |   5 +-
 gdb/cli/cli-decode.h               |   4 +
 gdb/cli/cli-option.c               | 732 +++++++++++++++++++++++++++++++++++++
 gdb/cli/cli-option.h               | 335 +++++++++++++++++
 gdb/cli/cli-setshow.c              | 280 ++++++++------
 gdb/cli/cli-setshow.h              |  27 ++
 gdb/cli/cli-utils.c                |  51 +++
 gdb/cli/cli-utils.h                |  18 +
 gdb/maint-test-options.c           | 460 +++++++++++++++++++++++
 gdb/testsuite/gdb.base/options.c   |  33 ++
 gdb/testsuite/gdb.base/options.exp | 554 ++++++++++++++++++++++++++++
 12 files changed, 2383 insertions(+), 118 deletions(-)
 create mode 100644 gdb/cli/cli-option.c
 create mode 100644 gdb/cli/cli-option.h
 create mode 100644 gdb/maint-test-options.c
 create mode 100644 gdb/testsuite/gdb.base/options.c
 create mode 100644 gdb/testsuite/gdb.base/options.exp

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index e44df54ddea..88ae024bbd8 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -241,6 +241,7 @@ SUBDIR_CLI_SRCS = \
 	cli/cli-dump.c \
 	cli/cli-interp.c \
 	cli/cli-logging.c \
+	cli/cli-option.c \
 	cli/cli-script.c \
 	cli/cli-setshow.c \
 	cli/cli-style.c \
@@ -1062,6 +1063,7 @@ COMMON_SFILES = \
 	macrotab.c \
 	main.c \
 	maint.c \
+	maint-test-options.c \
 	maint-test-settings.c \
 	mdebugread.c \
 	mem-break.c \
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 80158593b38..0370b125615 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -550,6 +550,7 @@ add_setshow_enum_cmd (const char *name,
   set_cmd_context (show, context);
 }
 
+/* See cli-decode.h.  */
 const char * const auto_boolean_enums[] = { "on", "off", "auto", NULL };
 
 /* Add an auto-boolean command named NAME to both the set and show
@@ -577,6 +578,9 @@ add_setshow_auto_boolean_cmd (const char *name,
   c->enums = auto_boolean_enums;
 }
 
+/* See cli-decode.h.  */
+const char * const boolean_enums[] = { "on", "off", NULL };
+
 /* Add element named NAME to both the set and show command LISTs (the
    list for set/show or some sublist thereof).  CLASS is as in
    add_cmd.  VAR is address of the variable which will contain the
@@ -590,7 +594,6 @@ add_setshow_boolean_cmd (const char *name, enum command_class theclass, int *var
 			 struct cmd_list_element **set_list,
 			 struct cmd_list_element **show_list)
 {
-  static const char *boolean_enums[] = { "on", "off", NULL };
   struct cmd_list_element *c;
 
   add_setshow_cmd_full (name, theclass, var_boolean, var,
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index c53683d95c0..13b4d26dd1d 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -261,6 +261,10 @@ extern void not_just_help_class_command (const char *arg, int from_tty);
 
 extern void print_doc_line (struct ui_file *, const char *);
 
+/* The enums of boolean commands.  */
+extern const char * const boolean_enums[];
+
+/* The enums of auto-boolean commands.  */
 extern const char * const auto_boolean_enums[];
 
 /* Verify whether a given cmd_list_element is a user-defined command.
diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c
new file mode 100644
index 00000000000..9793dd5727e
--- /dev/null
+++ b/gdb/cli/cli-option.c
@@ -0,0 +1,732 @@
+/* CLI options framework, for GDB.
+
+   Copyright (C) 2017-2019 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include "defs.h"
+#include "cli/cli-option.h"
+#include "cli/cli-decode.h"
+#include "cli/cli-utils.h"
+#include "cli/cli-setshow.h"
+#include "command.h"
+#include <vector>
+
+namespace gdb {
+namespace option {
+
+/* An option's value.  Which field is active depends on the option's
+   type.  */
+union option_value
+{
+  /* For var_boolean options.  */
+  bool boolean;
+
+  /* For var_uinteger options.  */
+  unsigned int uinteger;
+
+  /* For var_zuinteger_unlimited options.  */
+  int integer;
+
+  /* For var_enum options.  */
+  const char *enumeration;
+};
+
+/* Holds an options definition and its value.  */
+struct option_def_and_value
+{
+  /* The option definition.  */
+  const option_def &option;
+
+  /* A context.  */
+  void *ctx;
+
+  /* The option's value, if any.  */
+  gdb::optional<option_value> value;
+};
+
+/* Info passed around when handling completion.  */
+struct parse_option_completion_info
+{
+  /* The completion word.  */
+  const char *word;
+
+  /* The tracker.  */
+  completion_tracker &tracker;
+};
+
+/* If ARGS starts with "-", look for a "--" delimiter.  If one is
+   found, then interpret everything up until the "--" as command line
+   options.  Otherwise, interpret unknown input as the beginning of
+   the command's operands.  */
+
+static const char *
+find_end_options_delimiter (const char *args)
+{
+  if (args[0] == '-')
+    {
+      const char *p = args;
+
+      p = skip_spaces (p);
+      while (*p)
+	{
+	  if (check_for_argument (&p, "--"))
+	    return p;
+	  else
+	    p = skip_to_space (p);
+	  p = skip_spaces (p);
+	}
+    }
+
+  return nullptr;
+}
+
+/* Complete TEXT/WORD on all options in OPTIONS_GROUP.  */
+
+static void
+complete_on_options (gdb::array_view<const option_def_group> options_group,
+		     completion_tracker &tracker,
+		     const char *text, const char *word)
+{
+  size_t textlen = strlen (text);
+  for (const auto &grp : options_group)
+    for (const auto &opt : grp.options)
+      if (strncmp (opt.name, text, textlen) == 0)
+	{
+	  tracker.add_completion
+	    (make_completion_match_str (opt.name, text, word));
+	}
+}
+
+/* See cli-option.h.  */
+
+void
+complete_on_all_options (completion_tracker &tracker,
+			 gdb::array_view<const option_def_group> options_group)
+{
+  static const char opt[] = "-";
+  complete_on_options (options_group, tracker, opt + 1, opt);
+}
+
+/* Dup STR and return a unique_xmalloc_ptr for the result.  */
+
+static gdb::unique_xmalloc_ptr<char>
+make_unique_xstrdup (const char *str)
+{
+  return gdb::unique_xmalloc_ptr<char> (xstrdup (str));
+}
+
+/* Parse ARGS, guided by OPTIONS_GROUP.  HAVE_DELIMITER is true if the
+   whole ARGS line included the "--" options-terminator delimiter.  */
+
+static gdb::optional<option_def_and_value>
+parse_option (gdb::array_view<const option_def_group> options_group,
+	      process_options_mode mode,
+	      bool have_delimiter,
+	      const char **args,
+	      parse_option_completion_info *completion = nullptr)
+{
+  if (*args == nullptr)
+    return {};
+  else if (**args != '-')
+    {
+      if (have_delimiter)
+	error (_("Unrecognized option at: %s"), *args);
+      return {};
+    }
+  else if (check_for_argument (args, "--"))
+    return {};
+
+  /* Skip the initial '-'.  */
+  const char *arg = *args + 1;
+
+  const char *after = skip_to_space (arg);
+  size_t len = after - arg;
+  const option_def *match = nullptr;
+  void *match_ctx;
+
+  for (const auto &grp : options_group)
+    {
+      for (const auto &o : grp.options)
+	{
+	  if (strncmp (o.name, arg, len) == 0)
+	    {
+	      if (match != nullptr)
+		{
+		  if (completion != nullptr && arg[len] == '\0')
+		    {
+		      complete_on_options (options_group,
+					   completion->tracker,
+					   arg, completion->word);
+		      return {};
+		    }
+
+		  error (_("Ambiguous option at: -%s"), arg);
+		}
+
+	      match = &o;
+	      match_ctx = grp.ctx;
+
+	      if ((isspace (arg[len]) || arg[len] == '\0')
+		  && strlen (o.name) == len)
+		break; /* Exact match.  */
+	    }
+	}
+    }
+
+  if (match == nullptr)
+    {
+      if (have_delimiter || mode != PROCESS_OPTIONS_UNKNOWN_IS_OPERAND)
+	error (_("Unrecognized option at: %s"), *args);
+
+      return {};
+    }
+
+  if (completion != nullptr && arg[len] == '\0')
+    {
+      complete_on_options (options_group, completion->tracker,
+			   arg, completion->word);
+      return {};
+    }
+
+  *args += 1 + len;
+  *args = skip_spaces (*args);
+  if (completion != nullptr)
+    completion->word = *args;
+
+  switch (match->type)
+    {
+    case var_boolean:
+      {
+	if (!match->have_argument)
+	  {
+	    option_value val;
+	    val.boolean = true;
+	    return option_def_and_value {*match, match_ctx, val};
+	  }
+
+	const char *val_str = *args;
+	int res;
+
+	if (**args == '\0' && completion != nullptr)
+	  {
+	    /* Complete on both "on/off" and more options.  */
+
+	    if (mode == PROCESS_OPTIONS_REQUIRE_DELIMITER)
+	      {
+		complete_on_enum (completion->tracker,
+				  boolean_enums, val_str, val_str);
+		complete_on_all_options (completion->tracker, options_group);
+	      }
+	    return option_def_and_value {*match, match_ctx};
+	  }
+	else if (**args == '-')
+	  {
+	    /* Treat:
+		 "cmd -boolean-option -another-opt..."
+	       as:
+		 "cmd -boolean-option on -another-opt..."
+	     */
+	    res = 1;
+	  }
+	else if (**args == '\0')
+	  {
+	    /* Treat:
+		 (1) "cmd -boolean-option "
+	       as:
+		 (1) "cmd -boolean-option on"
+	     */
+	    res = 1;
+	  }
+	else
+	  {
+	    res = parse_cli_boolean_value (args);
+	    if (res < 0)
+	      {
+		const char *end = skip_to_space (*args);
+		if (completion != nullptr)
+		  {
+		    if (*end == '\0')
+		      {
+			complete_on_enum (completion->tracker,
+					  boolean_enums, val_str, val_str);
+			return option_def_and_value {*match, match_ctx};
+		      }
+		  }
+
+		if (have_delimiter)
+		  error (_("Value given for `-%s' is not a boolean: %.*s"),
+			 match->name, (int) (end - val_str), val_str);
+		/* The user didn't separate options from operands
+		   using "--", so treat this unrecognized value as the
+		   start of the operands.  This makes "frame apply all
+		   -past-main CMD" work.  */
+		return option_def_and_value {*match, match_ctx};
+	      }
+	    else if (completion != nullptr && **args == '\0')
+	      {
+		/* While "cmd -boolean [TAB]" only offers "on" and
+		   "off", the boolean option actually accepts "1",
+		   "yes", etc. as boolean values.  We complete on all
+		   of those instead of BOOLEAN_ENUMS here to make
+		   these work:
+
+		    "p -object 1[TAB]" -> "p -object 1 "
+		    "p -object ye[TAB]" -> "p -object yes "
+
+		   Etc.  Note that it's important that the space is
+		   auto-appended.  Otherwise, if we only completed on
+		   on/off here, then it might look to the user like
+		   "1" isn't valid, like:
+		   "p -object 1[TAB]" -> "p -object 1" (i.e., nothing happens).
+		*/
+		static const char *const all_boolean_enums[] = {
+		  "on", "off",
+		  "yes", "no",
+		  "enable", "disable",
+		  "0", "1",
+		  nullptr,
+		};
+		complete_on_enum (completion->tracker, all_boolean_enums,
+				  val_str, val_str);
+		return {};
+	      }
+	  }
+
+	option_value val;
+	val.boolean = res;
+	return option_def_and_value {*match, match_ctx, val};
+      }
+    case var_uinteger:
+    case var_zuinteger_unlimited:
+      {
+	if (completion != nullptr)
+	  {
+	    if (**args == '\0')
+	      {
+		/* Convenience to let the user know what the option
+		   can accept.  Note there's no common prefix between
+		   the strings on purpose, so that readline doesn't do
+		   a partial match.  */
+		completion->tracker.add_completion
+		  (make_unique_xstrdup ("NUMBER"));
+		completion->tracker.add_completion
+		  (make_unique_xstrdup ("unlimited"));
+		return {};
+	      }
+	    else if (startswith ("unlimited", *args))
+	      {
+		completion->tracker.add_completion
+		  (make_unique_xstrdup ("unlimited"));
+		return {};
+	      }
+	  }
+
+	if (match->type == var_zuinteger_unlimited)
+	  {
+	    option_value val;
+	    val.integer = parse_cli_var_zuinteger_unlimited (args, false);
+	    return option_def_and_value {*match, match_ctx, val};
+	  }
+	else
+	  {
+	    option_value val;
+	    val.uinteger = parse_cli_var_uinteger (match->type, args, false);
+	    return option_def_and_value {*match, match_ctx, val};
+	  }
+      }
+    case var_enum:
+      {
+	if (completion != nullptr)
+	  {
+	    const char *after_arg = skip_to_space (*args);
+	    if (*after_arg == '\0')
+	      {
+		complete_on_enum (completion->tracker,
+				  match->enums, *args, *args);
+		*args = after_arg;
+
+		option_value val;
+		val.enumeration = nullptr;
+		return option_def_and_value {*match, match_ctx, val};
+	      }
+	  }
+
+	if (check_for_argument (args, "--"))
+	  {
+	    /* Treat e.g., "backtrace -entry-values --" as if there
+	       was no argument after "-entry-values".  This makes
+	       parse_cli_var_enum throw an error with a suggestion of
+	       what are the valid options.  */
+	    args = nullptr;
+	  }
+
+	option_value val;
+	val.enumeration = parse_cli_var_enum (args, match->enums);
+	return option_def_and_value {*match, match_ctx, val};
+      }
+
+    default:
+      /* Not yet.  */
+      gdb_assert_not_reached (_("option type not supported"));
+    }
+
+  return {};
+}
+
+/* See cli-option.h.  */
+
+bool
+complete_options (completion_tracker &tracker,
+		  const char **args,
+		  process_options_mode mode,
+		  gdb::array_view<const option_def_group> options_group)
+{
+  const char *text = *args;
+
+  tracker.set_use_custom_word_point (true);
+
+  const char *delimiter = find_end_options_delimiter (text);
+  bool have_delimiter = delimiter != nullptr;
+
+  if (text[0] == '-' && (!have_delimiter || *delimiter == '\0'))
+    {
+      parse_option_completion_info completion_info {nullptr, tracker};
+
+      while (1)
+	{
+	  *args = skip_spaces (*args);
+	  completion_info.word = *args;
+
+	  if (strcmp (*args, "-") == 0)
+	    {
+	      complete_on_options (options_group, tracker, *args + 1,
+				   completion_info.word);
+	    }
+	  else if (strcmp (*args, "--") == 0)
+	    {
+	      tracker.add_completion (make_unique_xstrdup (*args));
+	    }
+	  else if (**args == '-')
+	    {
+	      gdb::optional<option_def_and_value> ov
+		= parse_option (options_group, mode, have_delimiter,
+				args, &completion_info);
+	      if (!ov && !tracker.have_completions ())
+		{
+		  tracker.advance_custom_word_point_by (*args - text);
+		  return mode == PROCESS_OPTIONS_REQUIRE_DELIMITER;
+		}
+
+	      if (ov
+		  && ov->option.type == var_boolean
+		  && !ov->value.has_value ())
+		{
+		  /* Looked like a boolean option, but we failed to
+		     parse the value.  If this command requires a
+		     delimiter, this value can't be the start of the
+		     operands, so return true.  Otherwise, if the
+		     command doesn't require a delimiter return false
+		     so that the caller tries to complete on the
+		     operand.  */
+		  tracker.advance_custom_word_point_by (*args - text);
+		  return mode == PROCESS_OPTIONS_REQUIRE_DELIMITER;
+		}
+
+	      /* If we parsed an option with an argument, and reached
+		 the end of the input string with no trailing space,
+		 return true, so that our callers don't try to
+		 complete anything by themselves.  E.g., this makes it
+		 so that with:
+
+		  (gdb) frame apply all -limit 10[TAB]
+
+		 we don't try to complete on command names.  */
+	      if (ov
+		  && !tracker.have_completions ()
+		  && **args == '\0'
+		  && *args > text && !isspace ((*args)[-1]))
+		{
+		  tracker.advance_custom_word_point_by
+		    (*args - text);
+		  return true;
+		}
+	    }
+	  else
+	    {
+	      tracker.advance_custom_word_point_by
+		(completion_info.word - text);
+
+	      /* If the command requires a delimiter, but we haven't
+		 seen one, then return true, so that the caller
+		 doesn't try to complete on whatever follows options,
+		 which for these commands should only be done if
+		 there's a delimiter.  */
+	      if (mode == PROCESS_OPTIONS_REQUIRE_DELIMITER
+		  && !have_delimiter)
+		{
+		  /* If we reached the end of the input string, then
+		     offer all options, since that's all the user can
+		     type (plus "--").  */
+		  if (completion_info.word[0] == '\0')
+		    complete_on_all_options (tracker, options_group);
+		  return true;
+		}
+	      else
+		return false;
+	    }
+
+	  if (tracker.have_completions ())
+	    {
+	      tracker.advance_custom_word_point_by
+		(completion_info.word - text);
+	      return true;
+	    }
+	}
+    }
+  else if (delimiter != nullptr)
+    {
+      tracker.advance_custom_word_point_by (delimiter - text);
+      *args = delimiter;
+      return false;
+    }
+
+  return false;
+}
+
+/* See cli-option.h.  */
+
+bool
+process_options (const char **args,
+		 process_options_mode mode,
+		 gdb::array_view<const option_def_group> options_group)
+{
+  if (*args == nullptr)
+    return false;
+
+  /* If ARGS starts with "-", look for a "--" sequence.  If one is
+     found, then interpret everything up until the "--" as
+     'gdb::option'-style command line options.  Otherwise, interpret
+     ARGS as possibly the command's operands.  */
+  bool have_delimiter = find_end_options_delimiter (*args) != nullptr;
+
+  if (mode == PROCESS_OPTIONS_REQUIRE_DELIMITER && !have_delimiter)
+    return false;
+
+  bool processed_any = false;
+
+  while (1)
+    {
+      *args = skip_spaces (*args);
+
+      auto ov = parse_option (options_group, mode, have_delimiter, args);
+      if (!ov)
+	{
+	  if (processed_any)
+	    return true;
+	  return false;
+	}
+
+      processed_any = true;
+
+      switch (ov->option.type)
+	{
+	case var_boolean:
+	  {
+	    bool value = ov->value.has_value () ? ov->value->boolean : true;
+	    *ov->option.var_address.boolean (ov->option, ov->ctx) = value;
+	  }
+	  break;
+	case var_uinteger:
+	  *ov->option.var_address.uinteger (ov->option, ov->ctx)
+	    = ov->value->uinteger;
+	  break;
+	case var_zuinteger_unlimited:
+	  *ov->option.var_address.integer (ov->option, ov->ctx)
+	    = ov->value->integer;
+	  break;
+	case var_enum:
+	  *ov->option.var_address.enumeration (ov->option, ov->ctx)
+	    = ov->value->enumeration;
+	  break;
+	default:
+	  gdb_assert_not_reached ("unhandled option type");
+	}
+    }
+}
+
+/* Helper for build_help.  Return a fragment of a help string showing
+   OPT's possible values.  Returns NULL if OPT doesn't take an
+   argument.  */
+
+static const char *
+get_val_type_str (const option_def &opt, std::string &buffer)
+{
+  if (!opt.have_argument)
+    return nullptr;
+
+  switch (opt.type)
+    {
+    case var_boolean:
+      return "[on|off]";
+    case var_uinteger:
+    case var_zuinteger_unlimited:
+      return "NUMBER|unlimited";
+    case var_enum:
+      {
+	buffer = "";
+	for (size_t i = 0; opt.enums[i] != nullptr; i++)
+	  {
+	    if (i != 0)
+	      buffer += "|";
+	    buffer += opt.enums[i];
+	  }
+	return buffer.c_str ();
+      }
+    default:
+      return nullptr;
+    }
+}
+
+/* Helper for build_help.  Appends an indented version of DOC into
+   HELP.  */
+
+static void
+append_indented_doc (const char *doc, std::string &help)
+{
+  const char *p = doc;
+  const char *n = strchr (p, '\n');
+
+  while (n != nullptr)
+    {
+      help += "    ";
+      help.append (p, n - p + 1);
+      p = n + 1;
+      n = strchr (p, '\n');
+    }
+  help += "    ";
+  help += p;
+  help += '\n';
+}
+
+/* Fill HELP with an auto-generated "help" string fragment for
+   OPTIONS.  */
+
+static void
+build_help_option (gdb::array_view<const option_def> options,
+		   std::string &help)
+{
+  std::string buffer;
+
+  for (const auto &o : options)
+    {
+      if (o.set_doc == nullptr)
+	continue;
+
+      help += "  -";
+      help += o.name;
+
+      const char *val_type_str = get_val_type_str (o, buffer);
+      if (val_type_str != nullptr)
+	{
+	  help += ' ';
+	  help += val_type_str;
+	}
+      help += "\n";
+      append_indented_doc (o.set_doc, help);
+      if (o.help_doc != nullptr)
+	append_indented_doc (o.help_doc, help);
+      help += '\n';
+    }
+}
+
+/* See cli-option.h.  */
+
+std::string
+build_help (const char *help_tmpl,
+	    gdb::array_view<const option_def_group> options_group)
+{
+  std::string help_str;
+
+  const char *p = strstr (help_tmpl, "%OPTIONS%");
+  help_str.assign (help_tmpl, p);
+
+  for (const auto &grp : options_group)
+    for (const auto &opt : grp.options)
+      build_help_option (opt, help_str);
+
+  p += strlen ("%OPTIONS%");
+  help_str.append (p);
+
+  return help_str;
+}
+
+/* See cli-option.h.  */
+
+void
+add_setshow_cmds_for_options (command_class cmd_class,
+			      void *data,
+			      gdb::array_view<const option_def> options,
+			      struct cmd_list_element **set_list,
+			      struct cmd_list_element **show_list)
+{
+  for (const auto &option : options)
+    {
+      if (option.type == var_boolean)
+	{
+	  add_setshow_boolean_cmd (option.name, cmd_class,
+				   option.var_address.boolean (option, data),
+				   option.set_doc, option.show_doc,
+				   option.help_doc,
+				   nullptr, option.show_cmd_cb,
+				   set_list, show_list);
+	}
+      else if (option.type == var_uinteger)
+	{
+	  add_setshow_uinteger_cmd (option.name, cmd_class,
+				    option.var_address.uinteger (option, data),
+				    option.set_doc, option.show_doc,
+				    option.help_doc,
+				    nullptr, option.show_cmd_cb,
+				    set_list, show_list);
+	}
+      else if (option.type == var_zuinteger_unlimited)
+	{
+	  add_setshow_zuinteger_unlimited_cmd
+	    (option.name, cmd_class,
+	     option.var_address.integer (option, data),
+	     option.set_doc, option.show_doc,
+	     option.help_doc,
+	     nullptr, option.show_cmd_cb,
+	     set_list, show_list);
+	}
+      else if (option.type == var_enum)
+	{
+	  add_setshow_enum_cmd (option.name, cmd_class,
+				option.enums,
+				option.var_address.enumeration (option, data),
+				option.set_doc, option.show_doc,
+				option.help_doc,
+				nullptr, option.show_cmd_cb,
+				set_list, show_list);
+	}
+      else
+	gdb_assert_not_reached (_("option type not handled"));
+    }
+}
+
+} /* namespace option */
+} /* namespace gdb */
diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h
new file mode 100644
index 00000000000..1bfbfce1ce5
--- /dev/null
+++ b/gdb/cli/cli-option.h
@@ -0,0 +1,335 @@
+/* CLI options framework, for GDB.
+
+   Copyright (C) 2017-2019 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef CLI_OPTION_H
+#define CLI_OPTION_H 1
+
+#include "common/gdb_optional.h"
+#include "common/array-view.h"
+#include "completer.h"
+#include <string>
+#include "command.h"
+
+namespace gdb {
+namespace option {
+
+/* A type-erased option definition.  The actual type of the option is
+   stored in the TYPE field.  Client code cannot define objects of
+   this type directly (the ctor is protected).  Instead, one of the
+   wrapper types below that extends this (boolean_option_def,
+   flag_option_def, uinteger_option_def, etc.) should be defined.  */
+struct option_def
+{
+  /* The ctor is protected because you're supposed to construct using
+     one of bool_option_def, etc. below.  */
+protected:
+  typedef void *(erased_get_var_address_ftype) ();
+
+  /* Construct an option.  NAME_ is the option's name.  VAR_TYPE_
+     defines the option's type.  ERASED_GET_VAR_ADDRESS_ is a pointer
+     to a function that returns the option's control variable.
+     SHOW_CMD_CB_ is a pointer to callback for the "show" command that
+     is installed for this option.  SET_DOC_, SHOW_DOC_, HELP_DOC_ are
+     used to create the option's "set/show" commands.  */
+  constexpr option_def (const char *name_,
+			var_types var_type_,
+			erased_get_var_address_ftype *erased_get_var_address_,
+			show_value_ftype *show_cmd_cb_,
+			const char *set_doc_,
+			const char *show_doc_,
+			const char *help_doc_)
+    : name (name_), type (var_type_),
+      erased_get_var_address (erased_get_var_address_),
+      var_address {},
+      show_cmd_cb (show_cmd_cb_),
+      set_doc (set_doc_), show_doc (show_doc_), help_doc (help_doc_)
+  {}
+
+public:
+  /* The option's name.  */
+  const char *name;
+
+  /* The option's type.  */
+  var_types type;
+
+  /* A function that gets the controlling variable's address, type
+     erased.  */
+  erased_get_var_address_ftype *erased_get_var_address;
+
+  /* Get the controlling variable's address.  Each type of variable
+     uses a different union member.  We do this instead of having a
+     single hook that return a "void *", for better type safety.  This
+     way, actual instances of concrete option_def types
+     (boolean_option_def, etc.) fail to compile if you pass in a
+     function with incorrect return type.  CTX here is the aggregate
+     object that groups the option variables from which the callback
+     returns the address of some member.  */
+  union
+    {
+      int *(*boolean) (const option_def &, void *ctx);
+      unsigned int *(*uinteger) (const option_def &, void *ctx);
+      int *(*integer) (const option_def &, void *ctx);
+      const char **(*enumeration) (const option_def &, void *ctx);
+    }
+  var_address;
+
+  /* Pointer to null terminated list of enumerated values (like argv).
+     Only used by var_enum options.  */
+  const char *const *enums = nullptr;
+
+  /* True if the option takes an argument.  */
+  bool have_argument = true;
+
+  /* The "show" callback to use in the associated "show" command.
+     E.g., "show print elements".  */
+  show_value_ftype *show_cmd_cb;
+
+  /* The set/show/help strings.  These are shown in both the help of
+     commands that use the option group this option belongs to (e.g.,
+     "help print"), and in the associated commands (e.g., "set/show
+     print elements", "help set print elements").  */
+  const char *set_doc;
+  const char *show_doc;
+  const char *help_doc;
+
+  /* Convenience method that returns THIS as an option_def.  Useful
+     when you're putting an option_def subclass in an option_def
+     array_view.  */
+  const option_def &def () const
+  {
+    return *this;
+  }
+};
+
+namespace detail
+{
+
+/* Get the address of the option's value, cast to the right type.
+   RetType is the restored type of the variable, and Context is the
+   restored type of the context pointer.  */
+template<typename RetType, typename Context>
+static inline RetType *
+get_var_address (const option_def &option, void *ctx)
+{
+  using unerased_ftype = RetType *(Context *);
+  unerased_ftype *fun = (unerased_ftype *) option.erased_get_var_address;
+  return fun ((Context *) ctx);
+}
+
+/* Convenience identity helper that just returns SELF.  */
+
+template<typename T>
+static T *
+return_self (T *self)
+{
+  return self;
+}
+
+} /* namespace detail */
+
+/* Follows the definitions of the option types that client code should
+   define.  Note that objects of these types are placed in option_def
+   arrays, by design, so they must not have data fields of their
+   own.  */
+
+/* A var_boolean command line option.  */
+
+template<typename Context>
+struct boolean_option_def : option_def
+{
+  boolean_option_def (const char *long_option_,
+		      int *(*get_var_address_cb_) (Context *),
+		      show_value_ftype *show_cmd_cb_,
+		      const char *set_doc_,
+		      const char *show_doc_ = nullptr,
+		      const char *help_doc_ = nullptr)
+    : option_def (long_option_, var_boolean,
+		  (erased_get_var_address_ftype *) get_var_address_cb_,
+		  show_cmd_cb_,
+		  set_doc_, show_doc_, help_doc_)
+  {
+    var_address.boolean = detail::get_var_address<int, Context>;
+  }
+};
+
+/* A flag command line option.  This is a var_boolean option under the
+   hood, but unlike boolean options, flag options don't take an on/off
+   argument.  */
+
+template<typename Context = int>
+struct flag_option_def : boolean_option_def<Context>
+{
+  flag_option_def (const char *long_option_,
+		     int *(*var_address_cb_) (Context *),
+		     const char *set_doc_,
+		     const char *help_doc_ = nullptr)
+    : boolean_option_def<Context> (long_option_,
+				   var_address_cb_,
+				   NULL,
+				   set_doc_, NULL, help_doc_)
+  {
+    this->have_argument = false;
+  }
+
+  flag_option_def (const char *long_option_,
+		     const char *set_doc_,
+		     const char *help_doc_ = nullptr)
+    : boolean_option_def<Context> (long_option_,
+				   gdb::option::detail::return_self,
+				   NULL,
+				   set_doc_, nullptr, help_doc_)
+  {
+    this->have_argument = false;
+  }
+};
+
+/* A var_uinteger command line option.  */
+
+template<typename Context>
+struct uinteger_option_def : option_def
+{
+  uinteger_option_def (const char *long_option_,
+		       unsigned int *(*get_var_address_cb_) (Context *),
+		       show_value_ftype *show_cmd_cb_,
+		       const char *set_doc_,
+		       const char *show_doc_ = nullptr,
+		       const char *help_doc_ = nullptr)
+    : option_def (long_option_, var_uinteger,
+		  (erased_get_var_address_ftype *) get_var_address_cb_,
+		  show_cmd_cb_,
+		  set_doc_, show_doc_, help_doc_)
+  {
+    var_address.uinteger = detail::get_var_address<unsigned int, Context>;
+  }
+};
+
+/* A var_zuinteger_unlimited command line option.  */
+
+template<typename Context>
+struct zuinteger_unlimited_option_def : option_def
+{
+  zuinteger_unlimited_option_def (const char *long_option_,
+				  int *(*get_var_address_cb_) (Context *),
+				  show_value_ftype *show_cmd_cb_,
+				  const char *set_doc_,
+				  const char *show_doc_ = nullptr,
+				  const char *help_doc_ = nullptr)
+    : option_def (long_option_, var_zuinteger_unlimited,
+		  (erased_get_var_address_ftype *) get_var_address_cb_,
+		  show_cmd_cb_,
+		  set_doc_, show_doc_, help_doc_)
+  {
+    var_address.integer = detail::get_var_address<int, Context>;
+  }
+};
+
+/* An var_enum command line option.  */
+
+template<typename Context>
+struct enum_option_def : option_def
+{
+  enum_option_def (const char *long_option_,
+		   const char *const *enumlist,
+		   const char **(*get_var_address_cb_) (Context *),
+		   show_value_ftype *show_cmd_cb_,
+		   const char *set_doc_,
+		   const char *show_doc_ = nullptr,
+		   const char *help_doc_ = nullptr)
+    : option_def (long_option_, var_enum,
+		  (erased_get_var_address_ftype *) get_var_address_cb_,
+		  show_cmd_cb_,
+		  set_doc_, show_doc_, help_doc_)
+  {
+    var_address.enumeration = detail::get_var_address<const char *, Context>;
+    this->enums = enumlist;
+  }
+};
+
+/* A group of options that all share the same context pointer to pass
+   to the options' get-current-value callbacks.  */
+struct option_def_group
+{
+  /* The list of options.  */
+  gdb::array_view<const option_def> options;
+
+  /* The context pointer to pass to the options' get-current-value
+     callbacks.  */
+  void *ctx;
+};
+
+/* Modes of operation for process_options.  */
+enum process_options_mode
+{
+  /* In this mode, options are only processed if we find a "--"
+     delimiter.  Throws an error if unknown options are found.  */
+  PROCESS_OPTIONS_REQUIRE_DELIMITER,
+
+  /* In this mode, a "--" delimiter is not required.  Throws an error
+     if unknown options are found, regardless of whether a delimiter
+     is present.  */
+  PROCESS_OPTIONS_UNKNOWN_IS_ERROR,
+
+  /* In this mode, a "--" delimiter is not required.  If an unknown
+     option is found, assume it is the command's argument/operand.  */
+  PROCESS_OPTIONS_UNKNOWN_IS_OPERAND,
+};
+
+/* Process ARGS, using OPTIONS_GROUP as valid options.  Returns true
+   if the string has been fully parsed and there are no operands to
+   handle by the caller.  Return false if options were parsed, and
+   *ARGS now points at the first operand.  */
+extern bool process_options
+  (const char **args,
+   process_options_mode mode,
+   gdb::array_view<const option_def_group> options_group);
+
+/* Complete ARGS on options listed by OPTIONS_GROUP.  Returns true if
+   the string has been fully parsed and there are no operands to
+   handle by the caller.  Return false if options were parsed, and
+   *ARGS now points at the first operand.  */
+extern bool complete_options
+  (completion_tracker &tracker,
+   const char **args,
+   process_options_mode mode,
+   gdb::array_view<const option_def_group> options_group);
+
+/* Complete on all options listed by OPTIONS_GROUP.  */
+extern void
+  complete_on_all_options (completion_tracker &tracker,
+			   gdb::array_view<const option_def_group> options_group);
+
+/* Return a string with the result of replacing %OPTIONS% in HELP_TMLP
+   with an auto-generated "help" string fragment for all the options
+   in OPTIONS_GROUP.  */
+extern std::string build_help
+  (const char *help_tmpl,
+   gdb::array_view<const option_def_group> options_group);
+
+/* Install set/show commands for options defined in OPTIONS.  DATA is
+   a pointer to the structure that holds the data associated with the
+   OPTIONS array.  */
+extern void add_setshow_cmds_for_options (command_class cmd_class, void *data,
+					  gdb::array_view<const option_def> options,
+					  struct cmd_list_element **set_list,
+					  struct cmd_list_element **show_list);
+
+} /* namespace option */
+} /* namespace gdb */
+
+#endif /* CLI_OPTION_H */
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 082bfe7f580..7de006da21f 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -78,33 +78,48 @@ parse_auto_binary_operation (const char *arg)
 /* See cli-setshow.h.  */
 
 int
-parse_cli_boolean_value (const char *arg)
+parse_cli_boolean_value (const char **arg)
 {
-  int length;
-
-  if (!arg || !*arg)
-    return 1;
+  const char *p = skip_to_space (*arg);
+  size_t length = p - *arg;
 
-  length = strlen (arg);
+  /* Note that "o" is ambiguous.  */
 
-  while (arg[length - 1] == ' ' || arg[length - 1] == '\t')
-    length--;
+  if ((length == 2 && strncmp (*arg, "on", length) == 0)
+      || strncmp (*arg, "1", length) == 0
+      || strncmp (*arg, "yes", length) == 0
+      || strncmp (*arg, "enable", length) == 0)
+    {
+      *arg = skip_spaces (*arg + length);
+      return 1;
+    }
+  else if ((length >= 2 && strncmp (*arg, "off", length) == 0)
+	   || strncmp (*arg, "0", length) == 0
+	   || strncmp (*arg, "no", length) == 0
+	   || strncmp (*arg, "disable", length) == 0)
+    {
+      *arg = skip_spaces (*arg + length);
+      return 0;
+    }
+  else
+    return -1;
+}
 
-  /* Note that "o" is ambiguous.  */
+/* See cli-setshow.h.  */
 
-  if ((length == 2 && strncmp (arg, "on", length) == 0)
-      || strncmp (arg, "1", length) == 0
-      || strncmp (arg, "yes", length) == 0
-      || strncmp (arg, "enable", length) == 0)
+int
+parse_cli_boolean_value (const char *arg)
+{
+  if (!arg || !*arg)
     return 1;
-  else if ((length >= 2 && strncmp (arg, "off", length) == 0)
-	   || strncmp (arg, "0", length) == 0
-	   || strncmp (arg, "no", length) == 0
-	   || strncmp (arg, "disable", length) == 0)
-    return 0;
-  else
+
+  int b = parse_cli_boolean_value (&arg);
+  if (b >= 0 && *arg != '\0')
     return -1;
+
+  return b;
 }
+
 \f
 void
 deprecated_show_value_hack (struct ui_file *ignore_file,
@@ -134,21 +149,136 @@ deprecated_show_value_hack (struct ui_file *ignore_file,
 
 /* Returns true if ARG is "unlimited".  */
 
-static int
-is_unlimited_literal (const char *arg)
+static bool
+is_unlimited_literal (const char **arg)
 {
-  arg = skip_spaces (arg);
+  *arg = skip_spaces (*arg);
 
-  const char *p = skip_to_space (arg);
+  const char *p = skip_to_space (*arg);
 
-  size_t len = p - arg;
+  size_t len = p - *arg;
 
-  if (len > 0 && strncmp ("unlimited", arg, len) == 0)
-    return true;
+  if (len > 0 && strncmp ("unlimited", *arg, len) == 0)
+    {
+      *arg += len;
+      return true;
+    }
 
   return false;
 }
 
+/* See cli-setshow.h.  */
+
+unsigned int
+parse_cli_var_uinteger (var_types var_type, const char **arg,
+			bool expression)
+{
+  LONGEST val;
+
+  if (*arg == nullptr)
+    {
+      if (var_type == var_uinteger)
+	error_no_arg (_("integer to set it to, or \"unlimited\"."));
+      else
+	error_no_arg (_("integer to set it to."));
+    }
+
+  if (var_type == var_uinteger && is_unlimited_literal (arg))
+    val = 0;
+  else if (expression)
+    val = parse_and_eval_long (*arg);
+  else
+    val = get_ulongest (arg);
+
+  if (var_type == var_uinteger && val == 0)
+    val = UINT_MAX;
+  else if (val < 0
+	   /* For var_uinteger, don't let the user set the value
+	      to UINT_MAX directly, as that exposes an
+	      implementation detail to the user interface.  */
+	   || (var_type == var_uinteger && val >= UINT_MAX)
+	   || (var_type == var_zuinteger && val > UINT_MAX))
+    error (_("integer %s out of range"), plongest (val));
+
+  return val;
+}
+
+/* See cli-setshow.h.  */
+
+int
+parse_cli_var_zuinteger_unlimited (const char **arg, bool expression)
+{
+  LONGEST val;
+
+  if (arg == nullptr)
+    error_no_arg (_("integer to set it to, or \"unlimited\"."));
+
+  if (is_unlimited_literal (arg))
+    val = -1;
+  else if (expression)
+    val = parse_and_eval_long (*arg);
+  else
+    val = get_ulongest (arg);
+
+  if (val > INT_MAX)
+    error (_("integer %s out of range"), plongest (val));
+  else if (val < -1)
+    error (_("only -1 is allowed to set as unlimited"));
+
+  return val;
+}
+
+/* See cli-setshow.h.  */
+
+const char *
+parse_cli_var_enum (const char **args, const char *const *enums)
+{
+  /* If no argument was supplied, print an informative error
+     message.  */
+  if (args == NULL || *args == NULL || **args == '\0')
+    {
+      std::string msg;
+
+      for (size_t i = 0; enums[i]; i++)
+	{
+	  if (i != 0)
+	    msg += ", ";
+	  msg += enums[i];
+	}
+      error (_("Requires an argument. Valid arguments are %s."),
+	     msg.c_str ());
+    }
+
+  const char *p = skip_to_space (*args);
+  size_t len = p - *args;
+
+  int nmatches = 0;
+  const char *match = NULL;
+  for (size_t i = 0; enums[i]; i++)
+    if (strncmp (*args, enums[i], len) == 0)
+      {
+	if (enums[i][len] == '\0')
+	  {
+	    match = enums[i];
+	    nmatches = 1;
+	    break; /* Exact match.  */
+	  }
+	else
+	  {
+	    match = enums[i];
+	    nmatches++;
+	  }
+      }
+
+  if (nmatches == 0)
+    error (_("Undefined item: \"%.*s\"."), (int) len, *args);
+
+  if (nmatches > 1)
+    error (_("Ambiguous item \"%.*s\"."), (int) len, *args);
+
+  *args += len;
+  return match;
+}
 
 /* Do a "set" command.  ARG is NULL if no argument, or the
    text of the argument, and FROM_TTY is nonzero if this command is
@@ -295,30 +425,7 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
     case var_uinteger:
     case var_zuinteger:
       {
-	LONGEST val;
-
-	if (arg == NULL)
-	  {
-	    if (c->var_type == var_uinteger)
-	      error_no_arg (_("integer to set it to, or \"unlimited\"."));
-	    else
-	      error_no_arg (_("integer to set it to."));
-	  }
-
-	if (c->var_type == var_uinteger && is_unlimited_literal (arg))
-	  val = 0;
-	else
-	  val = parse_and_eval_long (arg);
-
-	if (c->var_type == var_uinteger && val == 0)
-	  val = UINT_MAX;
-	else if (val < 0
-		 /* For var_uinteger, don't let the user set the value
-		    to UINT_MAX directly, as that exposes an
-		    implementation detail to the user interface.  */
-		 || (c->var_type == var_uinteger && val >= UINT_MAX)
-		 || (c->var_type == var_zuinteger && val > UINT_MAX))
-	  error (_("integer %s out of range"), plongest (val));
+	unsigned int val = parse_cli_var_uinteger (c->var_type, &arg, true);
 
 	if (*(unsigned int *) c->var != val)
 	  {
@@ -341,7 +448,7 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 	      error_no_arg (_("integer to set it to."));
 	  }
 
-	if (c->var_type == var_integer && is_unlimited_literal (arg))
+	if (c->var_type == var_integer && is_unlimited_literal (&arg))
 	  val = 0;
 	else
 	  val = parse_and_eval_long (arg);
@@ -366,59 +473,11 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
       }
     case var_enum:
       {
-	int i;
-	int len;
-	int nmatches;
-	const char *match = NULL;
-	const char *p;
-
-	/* If no argument was supplied, print an informative error
-	   message.  */
-	if (arg == NULL)
-	  {
-	    std::string msg;
-
-	    for (i = 0; c->enums[i]; i++)
-	      {
-		if (i != 0)
-		  msg += ", ";
-		msg += c->enums[i];
-	      }
-	    error (_("Requires an argument. Valid arguments are %s."), 
-		   msg.c_str ());
-	  }
-
-	p = strchr (arg, ' ');
+	const char *end_arg = arg;
+	const char *match = parse_cli_var_enum (&end_arg, c->enums);
 
-	if (p)
-	  len = p - arg;
-	else
-	  len = strlen (arg);
-
-	nmatches = 0;
-	for (i = 0; c->enums[i]; i++)
-	  if (strncmp (arg, c->enums[i], len) == 0)
-	    {
-	      if (c->enums[i][len] == '\0')
-		{
-		  match = c->enums[i];
-		  nmatches = 1;
-		  break; /* Exact match.  */
-		}
-	      else
-		{
-		  match = c->enums[i];
-		  nmatches++;
-		}
-	    }
-
-	if (nmatches <= 0)
-	  error (_("Undefined item: \"%s\"."), arg);
-
-	if (nmatches > 1)
-	  error (_("Ambiguous item \"%s\"."), arg);
-
-	const char *after = skip_spaces (arg + len);
+	int len = end_arg - arg;
+	const char *after = skip_spaces (end_arg);
 	if (*after != '\0')
 	  error (_("Garbage after item \"%.*s\": \"%s\""),
 		 len, arg, after);
@@ -433,20 +492,7 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
       break;
     case var_zuinteger_unlimited:
       {
-	LONGEST val;
-
-	if (arg == NULL)
-	  error_no_arg (_("integer to set it to, or \"unlimited\"."));
-
-	if (is_unlimited_literal (arg))
-	  val = -1;
-	else
-	  val = parse_and_eval_long (arg);
-
-	if (val > INT_MAX)
-	  error (_("integer %s out of range"), plongest (val));
-	else if (val < -1)
-	  error (_("only -1 is allowed to set as unlimited"));
+	int val = parse_cli_var_zuinteger_unlimited (&arg, true);
 
 	if (*(int *) c->var != val)
 	  {
diff --git a/gdb/cli/cli-setshow.h b/gdb/cli/cli-setshow.h
index e4d13c3510d..c00a0989145 100644
--- a/gdb/cli/cli-setshow.h
+++ b/gdb/cli/cli-setshow.h
@@ -23,6 +23,33 @@ struct cmd_list_element;
    Returns 1 for true, 0 for false, and -1 if invalid.  */
 extern int parse_cli_boolean_value (const char *arg);
 
+/* Same as above, but work with a pointer to pointer.  ARG is advanced
+   past a successfully parsed value.  */
+extern int parse_cli_boolean_value (const char **arg);
+
+/* Parse ARG, an option to a var_uinteger or var_zuinteger variable.
+   Either returns the parsed value on success or throws an error.  If
+   EXPRESSION is true, *ARG is parsed as an expression; otherwise, it
+   is parsed with get_ulongest.  It's not possible to parse the
+   integer as an expression when there may be valid input after the
+   integer, such as when parsing command options.  E.g., "print
+   -elements NUMBER -obj --".  In such case, parsing as an expression
+   would parse "-obj --" as part of the expression as well.  */
+extern unsigned int parse_cli_var_uinteger (var_types var_type,
+					    const char **arg,
+					    bool expression);
+
+/* Like parse_cli_var_uinteger, for var_zuinteger_unlimited.  */
+extern int parse_cli_var_zuinteger_unlimited (const char **arg,
+					      bool expression);
+
+/* Parse ARG, an option to a var_enum variable.  ENUM is a
+   null-terminated array of possible values. Either returns the parsed
+   value on success or throws an error.  ARG is advanced past the
+   parsed value.  */
+const char *parse_cli_var_enum (const char **args,
+				const char *const *enums);
+
 extern void do_set_command (const char *arg, int from_tty,
 			    struct cmd_list_element *c);
 extern void do_show_command (const char *arg, int from_tty,
diff --git a/gdb/cli/cli-utils.c b/gdb/cli/cli-utils.c
index 23296cee9c3..306b69e3d8f 100644
--- a/gdb/cli/cli-utils.c
+++ b/gdb/cli/cli-utils.c
@@ -27,6 +27,57 @@ static std::string extract_arg_maybe_quoted (const char **arg);
 
 /* See documentation in cli-utils.h.  */
 
+ULONGEST
+get_ulongest (const char **pp, int trailer)
+{
+  LONGEST retval = 0;	/* default */
+  const char *p = *pp;
+
+  if (*p == '$')
+    {
+      value *val = value_from_history_ref (p, &p);
+
+      if (val != NULL)	/* Value history reference */
+	{
+	  if (TYPE_CODE (value_type (val)) == TYPE_CODE_INT)
+	    retval = value_as_long (val);
+	  else
+	    error (_("History value must have integer type."));
+	}
+      else	/* Convenience variable */
+	{
+	  /* Internal variable.  Make a copy of the name, so we can
+	     null-terminate it to pass to lookup_internalvar().  */
+	  const char *start = ++p;
+	  while (isalnum (*p) || *p == '_')
+	    p++;
+	  std::string varname (start, p - start);
+	  if (!get_internalvar_integer (lookup_internalvar (varname.c_str ()),
+				       &retval))
+	    error (_("Convenience variable $%s does not have integer value."),
+		   varname.c_str ());
+	}
+    }
+  else
+    {
+      retval = strtoulst (p, pp, 0);
+      if (p == *pp)
+	{
+	  /* There is no number here.  (e.g. "cond a == b").  */
+	  error (_("Expected integer at: %s"), p);
+	}
+      p = *pp;
+    }
+
+  if (!(isspace (*p) || *p == '\0' || *p == trailer))
+    error (_("Trailing junk at: %s"), p);
+  p = skip_spaces (p);
+  *pp = p;
+  return retval;
+}
+
+/* See documentation in cli-utils.h.  */
+
 int
 get_number_trailer (const char **pp, int trailer)
 {
diff --git a/gdb/cli/cli-utils.h b/gdb/cli/cli-utils.h
index 9425fb4c09d..41c23561d6f 100644
--- a/gdb/cli/cli-utils.h
+++ b/gdb/cli/cli-utils.h
@@ -39,6 +39,10 @@ extern int get_number (const char **);
 
 extern int get_number (char **);
 
+/* Like get_number_trailer, but works with ULONGEST, and throws on
+   error instead of returning 0.  */
+extern ULONGEST get_ulongest (const char **pp, int trailer = '\0');
+
 /* Extract from ARGS the arguments [-q] [-t TYPEREGEXP] [--] NAMEREGEXP.
 
    The caller is responsible to initialize *QUIET to false, *REGEXP
@@ -193,6 +197,14 @@ extern std::string extract_arg (const char **arg);
    argument.  */
 extern int check_for_argument (const char **str, const char *arg, int arg_len);
 
+/* Same as above, but ARG's length is computed.  */
+
+static inline int
+check_for_argument (const char **str, const char *arg)
+{
+  return check_for_argument (str, arg, strlen (arg));
+}
+
 /* Same, for non-const STR.  */
 
 static inline int
@@ -202,6 +214,12 @@ check_for_argument (char **str, const char *arg, int arg_len)
 			     arg, arg_len);
 }
 
+static inline int
+check_for_argument (char **str, const char *arg)
+{
+  return check_for_argument (str, arg, strlen (arg));
+}
+
 /* A helper function that looks for a set of flags at the start of a
    string.  The possible flags are given as a null terminated string.
    A flag in STR must either be at the end of the string,
diff --git a/gdb/maint-test-options.c b/gdb/maint-test-options.c
new file mode 100644
index 00000000000..e375522bcc7
--- /dev/null
+++ b/gdb/maint-test-options.c
@@ -0,0 +1,460 @@
+/* Maintenance commands for testing the options framework.
+
+   Copyright (C) 2019 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include "defs.h"
+#include "gdbcmd.h"
+#include "cli/cli-option.h"
+
+/* This file defines a couple "maintenance test-options" subcommands
+   to exercise TAB-completion and option processing:
+
+    (gdb) maint test-options require-delimiter
+    (gdb) maint test-options unknown-is-error
+    (gdb) maint test-options unknown-is-operand
+
+   And a third one to help with TAB-completion testing.
+
+    (gdb) maint show test-options-completion-result
+
+   Each of the test-options subcommands exercise
+   gdb::option::process_options with a different enum
+   process_options_mode value.  Examples for command they model:
+
+   - "print" and "compile print", are like "require-delimiter",
+      because they accept random expressions as argument.
+
+   - "backtrace" and "frame/thread apply" are like
+     "unknown-is-operand", because "-" is a valid command.
+
+   - "compile file" and "compile code" are like "unknown-is-error".
+
+   These commands allow exercising all aspects of option processing
+   without having to pick some existing command.  That should be more
+   stable going forward than relying on an existing user command,
+   since if we picked say "print", that command or its options could
+   change in future, and then we'd be left with having to pick some
+   other command or option to exercise some non-command-specific
+   option processing detail.  Also, actual user commands have side
+   effects that we're not interested in when we're focusing on unit
+   testing the options machinery.  BTW, a maintenance command is used
+   as a sort of unit test driver instead of actual "maint selftest"
+   unit tests, since we need to go all the way via via including
+   readline, for proper testing of TAB completion.
+
+   These maintenance commands support options of all the different
+   available kinds of commands (boolean, enum, flag, uinteger):
+
+    (gdb) maint test-options require-delimiter -[TAB]
+    -bool      -enum      -flag      -uinteger   -xx1       -xx2
+
+    (gdb) maint test-options require-delimiter -bool o[TAB]
+    off  on
+    (gdb) maint test-options require-delimiter -enum [TAB]
+    xxx  yyy  zzz
+    (gdb) maint test-options require-delimiter -uinteger [TAB]
+    NUMBER     unlimited
+
+   '-xx1' and '-xx2' are flag options too.  They exist in order to
+   test ambiguous option names, like '-xx'.
+
+  Invoking the commands makes them print out the options parsed:
+
+   (gdb) maint test-options unknown-is-error -flag -enum yyy cmdarg
+   -flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint 0 -zuint-unl 0 -- cmdarg
+
+   (gdb) maint test-options require-delimiter -flag -enum yyy cmdarg
+   -flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint 0  -zuint-unl 0 -- -flag -enum yyy cmdarg
+   (gdb) maint test-options require-delimiter -flag -enum yyy cmdarg --
+   Unrecognized option at: cmdarg --
+   (gdb) maint test-options require-delimiter -flag -enum yyy -- cmdarg
+   -flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint 0 -zuint-unl 0 -- cmdarg
+
+  The "maint show test-options-completion-result" command exists in
+  order to do something similar for completion:
+
+   (gdb) maint test-options unknown-is-error -flag -b 0 -enum yyy OPERAND[TAB]
+   (gdb) maint show test-options-completion-result
+   0 OPERAND
+
+   (gdb) maint test-options unknown-is-error -flag -b 0 -enum yyy[TAB]
+   (gdb) maint show test-options-completion-result
+   1
+
+   (gdb) maint test-options require-dash -unknown[TAB]
+   (gdb) maint show test-options-completion-result
+   1
+
+  Here, "1" means the completion function processed the whole input
+  line, and that the command shouldn't do anything with the arguments,
+  since there are no operands.  While "0" indicates that there are
+  operands after options.  The text after "0" is the operands.
+
+  This level of detail is particularly important because getting the
+  completion function's entry point to return back to the caller the
+  right pointer into the operand is quite tricky in several scenarios.  */
+
+/* Enum values for the "maintenance test-options" commands.  */
+const char test_options_enum_values_xxx[] = "xxx";
+const char test_options_enum_values_yyy[] = "yyy";
+const char test_options_enum_values_zzz[] = "zzz";
+static const char *const test_options_enum_values_choices[] =
+{
+  test_options_enum_values_xxx,
+  test_options_enum_values_yyy,
+  test_options_enum_values_zzz,
+  NULL
+};
+
+/* Option data for the "maintenance test-options" commands.  */
+
+struct test_options_opts
+{
+  int flag_opt = 0;
+  int xx1_opt = 0;
+  int xx2_opt = 0;
+  int boolean_opt = 0;
+  const char *enum_opt = test_options_enum_values_xxx;
+  unsigned int uint_opt = 0;
+  int zuint_unl_opt = 0;
+};
+
+/* Option definitions for the "maintenance test-options" commands.  */
+
+static const gdb::option::option_def test_options_option_defs[] = {
+
+  /* A flag option.  */
+  gdb::option::flag_option_def<test_options_opts> {
+    "flag",
+    [] (test_options_opts *opts) { return &opts->flag_opt; },
+    N_("A flag option."),
+  },
+
+  /* A couple flags with similar names, for "ambiguous option names"
+     testing.  */
+  gdb::option::flag_option_def<test_options_opts> {
+    "xx1",
+    [] (test_options_opts *opts) { return &opts->xx1_opt; },
+    N_("A flag option."),
+  },
+  gdb::option::flag_option_def<test_options_opts> {
+    "xx2",
+    [] (test_options_opts *opts) { return &opts->xx2_opt; },
+    N_("A flag option."),
+  },
+
+  /* A boolean option.  */
+  gdb::option::boolean_option_def<test_options_opts> {
+    "bool",
+    [] (test_options_opts *opts) { return &opts->boolean_opt; },
+    nullptr, /* show_cmd_cb */
+    N_("A boolean option."),
+  },
+
+  /* An enum option.  */
+  gdb::option::enum_option_def<test_options_opts> {
+    "enum",
+    test_options_enum_values_choices,
+    [] (test_options_opts *opts) { return &opts->enum_opt; },
+    nullptr, /* show_cmd_cb */
+    N_("An enum option."),
+  },
+
+  /* A uinteger option.  */
+  gdb::option::uinteger_option_def<test_options_opts> {
+    "uinteger",
+    [] (test_options_opts *opts) { return &opts->uint_opt; },
+    nullptr, /* show_cmd_cb */
+    N_("A uinteger option."),
+    nullptr, /* show_doc */
+    N_("A help doc that spawns\nmultiple lines."),
+  },
+
+  /* A zuinteger_unlimited option.  */
+  gdb::option::zuinteger_unlimited_option_def<test_options_opts> {
+    "zuinteger-unlimited",
+    [] (test_options_opts *opts) { return &opts->zuint_unl_opt; },
+    nullptr, /* show_cmd_cb */
+    N_("A zuinteger-unlimited option."),
+    nullptr, /* show_doc */
+    nullptr, /* help_doc */
+  },
+};
+
+/* Create an option_def_group for the test_options_opts options, with
+   OPTS as context.  */
+
+static inline gdb::option::option_def_group
+make_test_options_options_def_group (test_options_opts *opts)
+{
+  return {{test_options_option_defs}, opts};
+}
+
+/* Implementation of the "maintenance test-options
+   require-delimiter/unknown-is-error/unknown-is-operand" commands.
+   Each of the commands maps to a different enum process_options_mode
+   enumerator.  The test strategy is simply processing the options in
+   a number of scenarios, and printing back the parsed result.  */
+
+static void
+maintenance_test_options_command_mode (const char *args,
+				       gdb::option::process_options_mode mode)
+{
+  test_options_opts opts;
+
+  gdb::option::process_options (&args, mode,
+				make_test_options_options_def_group (&opts));
+
+  if (args == nullptr)
+    args = "";
+  else
+    args = skip_spaces (args);
+
+  printf_unfiltered (_("-flag %d -xx1 %d -xx2 %d -bool %d "
+		       "-enum %s -uint %s -zuint-unl %s -- %s\n"),
+		     opts.flag_opt,
+		     opts.xx1_opt,
+		     opts.xx2_opt,
+		     opts.boolean_opt,
+		     opts.enum_opt,
+		     (opts.uint_opt == UINT_MAX
+		      ? "unlimited"
+		      : pulongest (opts.uint_opt)),
+		     (opts.zuint_unl_opt == -1
+		      ? "unlimited"
+		      : plongest (opts.zuint_unl_opt)),
+		     args);
+}
+
+/* Variables used by the "maintenance show
+   test-options-completion-result" command.  These variables are
+   stored by the completer of the "maint test-options"
+   subcommands.  */
+
+/* The result of gdb::option::complete_options.  */
+static int maintenance_test_options_command_completion_result;
+/* The text at the word point after gdb::option::complete_options
+   returns.  */
+static std::string maintenance_test_options_command_completion_text;
+
+/* The "maintenance show test-options-completion-result" command.  */
+
+static void
+maintenance_show_test_options_completion_result
+  (struct ui_file *file, int from_tty,
+   struct cmd_list_element *c, const char *value)
+{
+  if (maintenance_test_options_command_completion_result)
+    fprintf_filtered (file, "1\n");
+  else
+    fprintf_filtered
+      (file, _("0 %s\n"),
+       maintenance_test_options_command_completion_text.c_str ());
+}
+
+/* Implementation of completer for the "maintenance test-options
+   require-delimiter/unknown-is-error/unknown-is-operand" commands.
+   Each of the commands maps to a different enum process_options_mode
+   enumerator.  */
+
+static void
+maintenance_test_options_completer_mode (completion_tracker &tracker,
+					 const char *text,
+					 gdb::option::process_options_mode mode)
+{
+  try
+    {
+      maintenance_test_options_command_completion_result
+	= gdb::option::complete_options
+	   (tracker, &text, mode,
+	    make_test_options_options_def_group (nullptr));
+      maintenance_test_options_command_completion_text = text;
+    }
+  catch (const gdb_exception_error &ex)
+    {
+      maintenance_test_options_command_completion_result = 1;
+      throw;
+    }
+}
+
+/* Implementation of the "maintenance test-options require-delimiter"
+   command.  */
+
+static void
+maintenance_test_options_require_delimiter_command (const char *args,
+						    int from_tty)
+{
+  maintenance_test_options_command_mode
+    (args, gdb::option::PROCESS_OPTIONS_REQUIRE_DELIMITER);
+}
+
+/* Implementation of the "maintenance test-options
+   unknown-is-error" command.  */
+
+static void
+maintenance_test_options_unknown_is_error_command (const char *args,
+						   int from_tty)
+{
+  maintenance_test_options_command_mode
+    (args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR);
+}
+
+/* Implementation of the "maintenance test-options
+   unknown-is-operand" command.  */
+
+static void
+maintenance_test_options_unknown_is_operand_command (const char *args,
+						     int from_tty)
+{
+  maintenance_test_options_command_mode
+    (args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND);
+}
+
+/* Completer for the "maintenance test-options require-delimiter"
+   command.  */
+
+static void
+maintenance_test_options_require_delimiter_command_completer
+  (cmd_list_element *ignore, completion_tracker &tracker,
+   const char *text, const char *word)
+{
+  maintenance_test_options_completer_mode
+    (tracker, text, gdb::option::PROCESS_OPTIONS_REQUIRE_DELIMITER);
+}
+
+/* Completer for the "maintenance test-options unknown-is-error"
+   command.  */
+
+static void
+maintenance_test_options_unknown_is_error_command_completer
+  (cmd_list_element *ignore, completion_tracker &tracker,
+   const char *text, const char *word)
+{
+  maintenance_test_options_completer_mode
+    (tracker, text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR);
+}
+
+/* Completer for the "maintenance test-options unknown-is-operand"
+   command.  */
+
+static void
+maintenance_test_options_unknown_is_operand_command_completer
+  (cmd_list_element *ignore, completion_tracker &tracker,
+   const char *text, const char *word)
+{
+  maintenance_test_options_completer_mode
+    (tracker, text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND);
+}
+
+/* Command list for maint test-options.  */
+struct cmd_list_element *maintenance_test_options_list;
+
+/* The "maintenance test-options" prefix command.  */
+
+static void
+maintenance_test_options_command (const char *arg, int from_tty)
+{
+  printf_unfiltered
+    (_("\"maintenance test-options\" must be followed "
+       "by the name of a subcommand.\n"));
+  help_list (maintenance_test_options_list, "maintenance test-options ",
+	     all_commands, gdb_stdout);
+}
+
+\f
+void
+_initialize_maint_test_options ()
+{
+  cmd_list_element *cmd;
+
+  add_prefix_cmd ("test-options", no_class, maintenance_test_options_command,
+		  _("\
+Generic command for testing the options infrastructure."),
+		  &maintenance_test_options_list,
+		  "maintenance test-options ", 0,
+		  &maintenancelist);
+
+  const auto def_group = make_test_options_options_def_group (nullptr);
+
+  static const std::string help_require_delim_str
+    = gdb::option::build_help (_("\
+Command used for testing options processing.\n\
+Usage: maint test-options require-delimiter [[OPTION]... --] [OPERAND]...\n\
+\n\
+Options:\n\
+\n\
+%OPTIONS%\n\
+If you specify any command option, you must use a double dash (\"--\")\n\
+to mark the end of option processing."),
+			       def_group);
+
+  static const std::string help_unknown_is_error_str
+    = gdb::option::build_help (_("\
+Command used for testing options processing.\n\
+Usage: maint test-options unknown-is-error [OPTION]... [OPERAND]...\n\
+\n\
+Options:\n\
+\n\
+%OPTIONS%"),
+			       def_group);
+
+  static const std::string help_unknown_is_operand_str
+    = gdb::option::build_help (_("\
+Command used for testing options processing.\n\
+Usage: maint test-options unknown-is-operand [OPTION]... [OPERAND]...\n\
+\n\
+Options:\n\
+\n\
+%OPTIONS%"),
+			       def_group);
+
+  cmd = add_cmd ("require-delimiter", class_maintenance,
+		 maintenance_test_options_require_delimiter_command,
+		 help_require_delim_str.c_str (),
+		 &maintenance_test_options_list);
+  set_cmd_completer_handle_brkchars
+    (cmd, maintenance_test_options_require_delimiter_command_completer);
+
+  cmd = add_cmd ("unknown-is-error", class_maintenance,
+		 maintenance_test_options_unknown_is_error_command,
+		 help_unknown_is_error_str.c_str (),
+		 &maintenance_test_options_list);
+  set_cmd_completer_handle_brkchars
+    (cmd, maintenance_test_options_unknown_is_error_command_completer);
+
+  cmd = add_cmd ("unknown-is-operand", class_maintenance,
+		 maintenance_test_options_unknown_is_operand_command,
+		 help_unknown_is_operand_str.c_str (),
+		 &maintenance_test_options_list);
+  set_cmd_completer_handle_brkchars
+    (cmd, maintenance_test_options_unknown_is_operand_command_completer);
+
+  add_setshow_zinteger_cmd ("test-options-completion-result", class_maintenance,
+			    &maintenance_test_options_command_completion_result,
+			    _("\
+Set maintenance test-options completion result."), _("\
+Show maintenance test-options completion result."), _("\
+Show the results of completing\n\
+\"maint test-options require-delimiter\",\n\
+\"maint test-options unknown-is-error\", or\n\
+\"maint test-options unknown-is-operand\"."),
+			    NULL,
+			    maintenance_show_test_options_completion_result,
+			    &maintenance_set_cmdlist,
+			    &maintenance_show_cmdlist);
+}
diff --git a/gdb/testsuite/gdb.base/options.c b/gdb/testsuite/gdb.base/options.c
new file mode 100644
index 00000000000..fd19922b54e
--- /dev/null
+++ b/gdb/testsuite/gdb.base/options.c
@@ -0,0 +1,33 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019 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/>.  */
+
+int xxx1= 123;
+
+struct S
+{
+  int a;
+  int b;
+  int c;
+};
+
+struct S g_s = {1, 2, 3};
+
+int
+main ()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp
new file mode 100644
index 00000000000..1891176dc1d
--- /dev/null
+++ b/gdb/testsuite/gdb.base/options.exp
@@ -0,0 +1,554 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2019 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/>.
+
+# Test the gdb::option framework.
+
+# The test uses the "maintenance test-options" subcommands to exercise
+# TAB-completion and option processing.
+
+load_lib completion-support.exp
+
+clean_restart
+
+if { ![readline_is_used] } {
+    untested "no tab completion support without readline"
+    return -1
+}
+
+# Check the completion result, as returned by the "maintenance show
+# test-options-completion-result" command.  TEST is used as test name.
+proc check_completion_result {expected test} {
+    gdb_test "maintenance show test-options-completion-result" \
+	"$expected" \
+	"$test: res=$expected"
+}
+
+# Like test_gdb_complete_unique, but the expected output is expected
+# to be the input line.  I.e., the line is already complete.  We're
+# just checking whether GDB recognizes the option and auto-appends a
+# space.
+proc test_completer_recognizes {res input_line} {
+    set expected_re [string_to_regexp $input_line]
+    test_gdb_complete_unique $input_line $expected_re
+    check_completion_result $res $input_line
+}
+
+# Wrapper around test_gdb_complete_multiple that also checks the
+# completion result is RES.
+proc res_test_gdb_complete_multiple {res cmd_prefix completion_word args} {
+    test_gdb_complete_multiple $cmd_prefix $completion_word {*}$args
+    check_completion_result $res "$cmd_prefix$completion_word"
+}
+
+# Wrapper around test_gdb_complete_none that also checks the
+# completion result is RES.
+proc res_test_gdb_complete_none { res input_line } {
+    test_gdb_complete_none $input_line
+    check_completion_result $res "$input_line"
+}
+
+# Wrapper around test_gdb_complete_unique that also checks the
+# completion result is RES.
+proc res_test_gdb_complete_unique { res input_line args} {
+    test_gdb_complete_unique $input_line {*}$args
+    check_completion_result $res "$input_line"
+}
+
+# Make a full command name from VARIANT.  VARIANT is either
+# "require-delimiter", "unknown-is-error" or "unknown-is-operand".
+proc make_cmd {variant} {
+    return "maint test-options $variant"
+}
+
+# Return a string for the expected result of running "maint
+# test-options xxx", with no flag/option set.  OPERAND is the expected
+# operand.
+proc expect_none {operand} {
+    return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint 0 -zuint-unl 0 -- $operand"
+}
+
+# Return a string for the expected result of running "maint
+# test-options xxx", with -flag set.  OPERAND is the expected operand.
+proc expect_flag {operand} {
+    return "-flag 1 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint 0 -zuint-unl 0 -- $operand"
+}
+
+# Return a string for the expected result of running "maint
+# test-options xxx", with -bool set.  OPERAND is the expected operand.
+proc expect_bool {operand} {
+    return "-flag 0 -xx1 0 -xx2 0 -bool 1 -enum xxx -uint 0 -zuint-unl 0 -- $operand"
+}
+
+# Return a string for the expected result of running "maint
+# test-options xxx", with one of the integer options set to $VAL.
+# OPTION determines which option to expect set.  OPERAND is the
+# expected operand.
+proc expect_integer {option val operand} {
+    if {$option == "uinteger"} {
+	return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint $val -zuint-unl 0 -- $operand"
+    } elseif {$option == "zuinteger-unlimited"} {
+	return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint 0 -zuint-unl $val -- $operand"
+    } else {
+	error "unsupported option: $option"
+    }
+}
+
+set all_options {
+    "-bool"
+    "-enum"
+    "-flag"
+    "-uinteger"
+    "-xx1"
+    "-xx2"
+    "-zuinteger-unlimited"
+}
+
+# Miscellaneous tests.
+proc_with_prefix test-misc {variant} {
+    global all_options
+
+    set cmd [make_cmd $variant]
+
+    # Call test command with no arguments at all.
+    gdb_test "$cmd" [expect_none ""]
+
+    # Now with a single dash.
+    if {$variant == "require-delimiter"} {
+	gdb_test "$cmd -" [expect_none "-"]
+    } else {
+	gdb_test "$cmd -" "Ambiguous option at: -"
+    }
+
+    # Completing at "-" should list all options.
+    res_test_gdb_complete_multiple "1" "$cmd " "-" "" $all_options
+
+    # Now with a double dash.
+    gdb_test "$cmd --" [expect_none ""]
+
+    # "--" is recognized by options completer, gdb auto-appends a
+    # space.
+    test_completer_recognizes 1 "$cmd --"
+
+    # Now with a double dash, plus a dash as operand.
+    gdb_test "$cmd -- -" [expect_none "-"]
+    res_test_gdb_complete_none "0 -" "$cmd -- -"
+
+    # Completing an unambiguous option just appends an empty space.
+    test_completer_recognizes 1 "$cmd -flag"
+
+    # Try running an ambiguous option.
+    if {$variant == "require-delimiter"} {
+	gdb_test "$cmd -xx" [expect_none "-xx"]
+    } else {
+	gdb_test "$cmd -xx" "Ambiguous option at: -xx"
+    }
+
+    # Check that options are not case insensitive.
+    gdb_test "$cmd -flag --" [expect_flag ""]
+
+    # Check how the different modes behave on unknown option, with a
+    # delimiter.
+    gdb_test "$cmd -FLAG --" \
+	"Unrecognized option at: -FLAG --"
+
+    # Check how the different modes behave on unknown option, without
+    # a delimiter.
+    if {$variant == "unknown-is-error"} {
+	gdb_test "$cmd -FLAG" \
+	    "Unrecognized option at: -FLAG"
+    } else {
+	gdb_test "$cmd -FLAG" [expect_none "-FLAG"]
+    }
+
+    # Test parsing stops at a negative integer.
+    gdb_test "$cmd -1 --" \
+	"Unrecognized option at: -1 --"
+    gdb_test "$cmd -2 --" \
+	"Unrecognized option at: -2 --"
+}
+
+# Flag option tests.
+proc_with_prefix test-flag {variant} {
+    global all_options
+
+    set cmd [make_cmd $variant]
+
+    # Completing a flag just appends a space.
+    test_completer_recognizes 1 "$cmd -flag"
+
+    # Add a dash, and all options should be shown.
+    test_gdb_complete_multiple "$cmd  -flag " "-" "" $all_options
+
+    # Basic smoke tests of accepted / not accepted values.
+
+    # Check all the different variants a bool option may be specified.
+    if {$variant == "require-delimiter"} {
+	gdb_test "$cmd -flag 999" [expect_none "-flag 999"]
+    } else {
+	gdb_test "$cmd -flag 999" [expect_flag "999"]
+    }
+    gdb_test "$cmd -flag -- 999" [expect_flag "999"]
+
+    # If the "--" separator is present, then GDB errors out if the
+    # flag option is passed some value -- check that too.
+    gdb_test "$cmd -flag xxx 999 --" "Unrecognized option at: xxx 999 --"
+    gdb_test "$cmd -flag o 999 --" "Unrecognized option at: o 999 --"
+    gdb_test "$cmd -flag 1 999 --" "Unrecognized option at: 1 999 --"
+
+    # Extract twice the same flag, separated by one space.
+    gdb_test "$cmd -flag -flag -- non flags args" \
+	[expect_flag "non flags args"]
+
+    # Extract twice the same flag, separated by one space.
+    gdb_test "$cmd -xx1     -xx2 -xx1  -xx2 -xx1    -- non flags args" \
+	"-flag 0 -xx1 1 -xx2 1 -bool 0 -enum xxx -uint 0 -zuint-unl 0 -- non flags args"
+
+    # Extract 2 known flags in front of unknown flags.
+    gdb_test "$cmd -xx1 -xx2 -a -b -c -xx1 --" \
+	"Unrecognized option at: -a -b -c -xx1 --"
+
+    # Check that combined flags are not recognised.
+    gdb_test "$cmd -xx1 -xx1xx2 -xx1 --" \
+	"Unrecognized option at: -xx1xx2 -xx1 --"
+
+    # Make sure the completer don't confuse a flag option with a
+    # boolean option.  Specifically, "o" should not complete to
+    # "on/off".
+
+    if {$variant == "require-delimiter"} {
+	res_test_gdb_complete_none "1" "$cmd -flag o"
+
+	gdb_test "$cmd -flag o" [expect_none "-flag o"]
+    } else {
+	res_test_gdb_complete_none "0 o" "$cmd -flag o"
+
+	gdb_test "$cmd -flag o" [expect_flag "o"]
+    }
+}
+
+# Boolean option tests.
+proc_with_prefix test-boolean {variant} {
+    global all_options
+
+    set cmd [make_cmd $variant]
+
+    # Boolean option's values are optional -- "on" is implied.  Check
+    # that:
+    #
+    # - For require-delimiter commands, completing after a boolean
+    #   option lists all other options, plus "on/off".  This is
+    #   because operands won't be processed until we see a "--"
+    #   delimiter.
+    #
+    # - For !require-delimiter commands, completing after a boolean
+    #   option completes as an operand, since that will tend to be
+    #   more common than typing "on/off".
+    #   E.g., "frame apply all -past-main COMMAND".
+
+    if {$variant == "require-delimiter"} {
+	res_test_gdb_complete_multiple 1 "$cmd -bool " "" "" {
+	    "-bool"
+	    "-enum"
+	    "-flag"
+	    "-uinteger"
+	    "-xx1"
+	    "-xx2"
+	    "-zuinteger-unlimited"
+	    "off"
+	    "on"
+	}
+    } else {
+	res_test_gdb_complete_none "0 " "$cmd -bool "
+    }
+
+    # Add another dash, and "on/off" are no longer offered:
+    res_test_gdb_complete_multiple 1 "$cmd -bool " "-" ""  $all_options
+
+    # Basic smoke tests of accepted / not accepted values.
+
+    # The command accepts all of "1/0/enable/disable/yes/no" too, even
+    # though like the "set" command, we don't offer those as
+    # completion candidates if you complete right after the boolean
+    # command's name, like:
+    #
+    #  (gdb) maint test-options require-delimiter -bool [TAB]
+    #  off        on
+    #
+    # However, the completer does recognize them if you start typing
+    # the boolean value.
+    foreach value {"0" "1"} {
+	test_completer_recognizes 1 "$cmd -bool $value"
+    }
+    foreach value {"of" "off"} {
+	res_test_gdb_complete_unique 1 \
+	    "$cmd -bool $value" \
+	    "$cmd -bool off"
+    }
+    foreach value {"y" "ye" "yes"} {
+	res_test_gdb_complete_unique 1 \
+	    "$cmd -bool $value" \
+	    "$cmd -bool yes"
+    }
+    foreach value {"n" "no"} {
+	res_test_gdb_complete_unique 1 \
+	    "$cmd -bool $value" \
+	    "$cmd -bool no"
+    }
+    foreach value {
+	"e"
+	"en"
+	"ena"
+	"enab"
+	"enabl"
+	"enable"
+    } {
+	res_test_gdb_complete_unique 1 \
+	    "$cmd -bool $value" \
+	    "$cmd -bool enable"
+    }
+    foreach value {
+	"d"
+	"di"
+	"dis"
+	"disa"
+	"disab"
+	"disabl"
+	"disable"
+    } {
+	res_test_gdb_complete_unique 1 \
+	    "$cmd -bool $value" \
+	    "$cmd -bool disable"
+    }
+
+    if {$variant == "require-delimiter"} {
+	res_test_gdb_complete_none "1" "$cmd -bool xxx"
+    } else {
+	res_test_gdb_complete_none "0 xxx" "$cmd -bool xxx"
+    }
+
+    # The command accepts abbreviations of "enable/disable/yes/no",
+    # even though we don't offer those for completion.
+    foreach value {
+	"1"
+	"y" "ye" "yes"
+	"e"
+	"en"
+	"ena"
+	"enab"
+	"enabl"
+	"enable"} {
+	gdb_test "$cmd -bool $value --" [expect_bool ""]
+    }
+    foreach value {
+	"0"
+	"of" "off"
+	"n" "no"
+	"d"
+	"di"
+	"dis"
+	"disa"
+	"disab"
+	"disabl"
+	"disable"} {
+	gdb_test "$cmd -bool $value --" [expect_none ""]
+    }
+
+    if {$variant == "require-delimiter"} {
+	gdb_test "$cmd -bool 999" [expect_none "-bool 999"]
+    } else {
+	gdb_test "$cmd -bool 999" [expect_bool "999"]
+    }
+    gdb_test "$cmd -bool -- 999" [expect_bool "999"]
+
+    # Since "on" is implied after a boolean option, for
+    # !require-delimiter commands, anything that is not
+    # yes/no/1/0/on/off/enable/disable should be considered as the raw
+    # input after the last option.  Also check "o", which might look
+    # like "on" or "off", but it's treated the same.
+
+    foreach arg {"xxx" "o"} {
+	if {$variant == "require-delimiter"} {
+	    gdb_test "$cmd -bool $arg" [expect_none "-bool $arg"]
+	} else {
+	    gdb_test "$cmd -bool $arg" [expect_bool "$arg"]
+	}
+    }
+    # Also try -1.  "unknown-is-error" commands error out saying that
+    # that's not a valid option.
+    if {$variant == "require-delimiter"} {
+	gdb_test "$cmd -bool -1" \
+	     [expect_none "-bool -1"]
+    } elseif {$variant == "unknown-is-error"} {
+	gdb_test "$cmd -bool -1" \
+	    "Unrecognized option at: -1"
+    } else {
+	gdb_test "$cmd -bool -1" [expect_bool "-1"]
+    }
+
+    # OTOH, if the "--" separator is present, then GDB errors out if
+    # the boolean option is passed an invalid value -- check that too.
+    gdb_test "$cmd -bool -1 999 --" \
+	"Unrecognized option at: -1 999 --"
+    gdb_test "$cmd -bool xxx 999 --" \
+	"Value given for `-bool' is not a boolean: xxx"
+    gdb_test "$cmd -bool o 999 --" \
+	"Value given for `-bool' is not a boolean: o"
+
+    # Completing after a boolean option + "o" does list "on/off",
+    # though.
+    if {$variant == "require-delimiter"} {
+	res_test_gdb_complete_multiple 1 "$cmd -bool " "o" "" {
+	    "off"
+	    "on"
+	}
+    } else {
+	res_test_gdb_complete_multiple "0 o" "$cmd -bool " "o" "" {
+	    "off"
+	    "on"
+	}
+    }
+}
+
+# Uinteger option tests.  OPTION is which integer option we're
+# testing.  Can be "uinteger" or "zuinteger-unlimited".
+proc_with_prefix test-uinteger {variant option} {
+    global all_options
+
+    set cmd "[make_cmd $variant] -$option"
+
+    # Test completing a uinteger option:
+    res_test_gdb_complete_multiple 1 "$cmd " "" "" {
+	"NUMBER"
+	"unlimited"
+    }
+
+    # NUMBER above is just a placeholder, make sure we don't complete
+    # it as a valid option.
+    res_test_gdb_complete_none 1 "$cmd NU"
+
+    # "unlimited" is valid though.
+    res_test_gdb_complete_unique 1 \
+	"$cmd u" \
+	"$cmd unlimited"
+
+    # Basic smoke test of accepted / not accepted values.
+    gdb_test "$cmd 1 -- 999" [expect_integer $option "1" "999"]
+    gdb_test "$cmd unlimited -- 999" \
+	[expect_integer $option "unlimited" "999"]
+    if {$option == "zuinteger-unlimited"} {
+	gdb_test "$cmd -1 --" [expect_integer $option "unlimited" ""]
+	gdb_test "$cmd 0 --" [expect_integer $option "0" ""]
+    } else {
+	gdb_test "$cmd -1 --" "integer -1 out of range"
+	gdb_test "$cmd 0 --" [expect_integer $option "unlimited" ""]
+    }
+    gdb_test "$cmd xxx --" \
+	"Expected integer at: xxx --"
+    gdb_test "$cmd unlimitedx --" \
+	"Expected integer at: unlimitedx --"
+
+    # Don't offer completions until we're past the
+    # -uinteger/-zuinteger-unlimited argument.
+    res_test_gdb_complete_none 1 "$cmd 1"
+
+    # A number of invalid values.
+    foreach value {"x" "x " "1a" "1a " "1-" "1- " "unlimitedx"} {
+	res_test_gdb_complete_none 1 "$cmd $value"
+    }
+
+    # Try "-1".
+    if {$option == "uinteger"} {
+	# -1 is invalid uinteger.
+	foreach value {"-1" "-1 "} {
+	    res_test_gdb_complete_none 1 "$cmd $value"
+	}
+    } else {
+	# -1 is valid for zuinteger-unlimited.
+	res_test_gdb_complete_none 1 "$cmd -1"
+	if {$variant == "require-delimiter"} {
+	    res_test_gdb_complete_multiple 1 "$cmd -1 " "" "-" $all_options
+	} else {
+	    res_test_gdb_complete_none "0 " "$cmd -1 "
+	}
+    }
+
+    # Check that after a fully parsed option:
+    #
+    #  - for require-delimiter commands, completion offers all
+    #    options.
+    #
+    #  - for !require-delimiter commands, completion offers nothing
+    #    and returns false.
+    if {$variant == "require-delimiter"} {
+	res_test_gdb_complete_multiple 1 "$cmd 1 " "" "-" $all_options
+    } else {
+	res_test_gdb_complete_none "0 " "$cmd 1 "
+    }
+
+    # Test completing non-option arguments after "-uinteger 1 ".
+    foreach operand {"x" "x " "1a" "1a " "1-" "1- "} {
+	if {$variant == "require-delimiter"} {
+	    res_test_gdb_complete_none 1 "$cmd 1 $operand"
+	} else {
+	    res_test_gdb_complete_none "0 $operand" "$cmd 1 $operand"
+	}
+    }
+    # These look like options, but they aren't.
+    foreach operand {"-1" "-1 "} {
+	if {$variant == "unknown-is-operand"} {
+	    res_test_gdb_complete_none "0 $operand" "$cmd 1 $operand"
+	} else {
+	    res_test_gdb_complete_none 1 "$cmd 1 $operand"
+	}
+    }
+}
+
+# Enum option tests.
+proc_with_prefix test-enum {variant} {
+    set cmd [make_cmd $variant]
+
+    res_test_gdb_complete_multiple 1 "$cmd -enum " "" "" {
+	"xxx"
+	"yyy"
+	"zzz"
+    }
+
+    # Check that "-" where a value is expected does not show the
+    # command's options.  I.e., an enum's value is not optional.
+    # Check both completion and running the command.
+    res_test_gdb_complete_none 1 "$cmd -enum -"
+    gdb_test "$cmd -enum --"\
+	"Requires an argument. Valid arguments are xxx, yyy, zzz\\."
+
+    # Try passing an undefined item to an enum option.
+    gdb_test "$cmd -enum www --" "Undefined item: \"www\"."
+}
+
+# Run the options framework tests first.
+foreach_with_prefix cmd {
+    "require-delimiter"
+    "unknown-is-error"
+    "unknown-is-operand"
+} {
+    test-misc $cmd
+    test-flag $cmd
+    test-boolean $cmd
+    foreach subcmd {"uinteger" "zuinteger-unlimited" } {
+	test-uinteger $cmd $subcmd
+    }
+    test-enum $cmd
+}
-- 
2.14.5

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

* [PATCH v2 19/24] Introduce complete_command
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (15 preceding siblings ...)
  2019-05-30 19:59 ` [PATCH v2 22/24] Make "thread apply" use the gdb::option framework Pedro Alves
@ 2019-05-30 19:59 ` Pedro Alves
  2019-06-03 19:28   ` Tom Tromey
  2019-05-30 20:01 ` [PATCH v2 20/24] Make "frame apply" support -OPT options Pedro Alves
                   ` (7 subsequent siblings)
  24 siblings, 1 reply; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:59 UTC (permalink / raw)
  To: gdb-patches

This adds a completion helper routine that makes it possible for a
command that takes another command as argument, such as "frame apply
all COMMAND" as "thread apply all COMMAND", to complete on COMMAND,
and have the completion machinery recurse and complete COMMAND as if
you tried to complete "(gdb) COMMAND".  I.e., we'll be able to
complete like this, for example:

 (gdb) thread apply all -[TAB]
 -c           -ascending   -q           -s
 (gdb) thread apply all -ascending frame apply all -[TAB]
 -c           -limit       -past-entry  -past-main   -q           -s
 (gdb) thread apply all -ascending frame apply all -past-main print -[TAB]
 -address         -elements        -pretty          -symbol
 -array           -null-stop       -repeats         -union
 -array-indexes   -object          -static-members  -vtbl
 (gdb) thread apply all -ascending frame apply all -past-main print glo[TAB]
 global1         global2

Above, the completer function understands that "thread apply all" is a
command, and then parses "-ascending" successfully and understand that
the rest of the string is "thread apply all"'s operand.  And then, the
process repeats for the "frame apply" command, and on and on.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* completer.c (complete_command): New.
	(gdb_completion_word_break_characters_throw): Add assertion.
	* completer.h (complete_command): Declare.
---
 gdb/completer.c | 36 ++++++++++++++++++++++++++++++++++++
 gdb/completer.h | 11 +++++++++++
 2 files changed, 47 insertions(+)

diff --git a/gdb/completer.c b/gdb/completer.c
index 4e92e499b3a..f875295f6eb 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -423,6 +423,39 @@ completion_tracker::completes_to_completion_word (const char *word)
   return false;
 }
 
+/* See completer.h.  */
+
+void
+complete_command (completion_tracker &tracker, const char *text)
+{
+  /* Must be called from a custom-word-point completer.  */
+  gdb_assert (tracker.use_custom_word_point ());
+
+  /* Disable the custom word point temporarily, because we want to
+     probe whether the command we're completing itself uses a custom
+     word point.  */
+  tracker.set_use_custom_word_point (false);
+  size_t save_custom_word_point = tracker.custom_word_point ();
+
+  int quote_char = '\0';
+  const char *word = completion_find_completion_word (tracker, text,
+						      &quote_char);
+
+  if (tracker.use_custom_word_point ())
+    {
+      /* The command we're completing uses a custom word point, so the
+	 tracker already contains the matches.  We're done.  */
+      return;
+    }
+
+  /* Restore the custom word point settings.  */
+  tracker.set_custom_word_point (save_custom_word_point);
+  tracker.set_use_custom_word_point (true);
+
+  /* Run the handle_completions completer phase.  */
+  complete_line (tracker, word, text, strlen (text));
+}
+
 /* Complete on linespecs, which might be of two possible forms:
 
        file:line
@@ -1905,6 +1938,9 @@ gdb_completion_word_break_characters_throw ()
     {
       gdb_assert (tracker.custom_word_point () > 0);
       rl_point = tracker.custom_word_point () - 1;
+
+      gdb_assert (rl_point >= 0 && rl_point < strlen (rl_line_buffer));
+
       gdb_custom_word_point_brkchars[0] = rl_line_buffer[rl_point];
       rl_completer_word_break_characters = gdb_custom_word_point_brkchars;
       rl_completer_quote_characters = NULL;
diff --git a/gdb/completer.h b/gdb/completer.h
index 58fe84f4fee..1b7176f031a 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -611,6 +611,17 @@ extern completion_list complete_source_filenames (const char *text);
 extern void complete_expression (completion_tracker &tracker,
 				 const char *text, const char *word);
 
+/* Called by custom word point completers that want to recurse into
+   the completion machinery to complete a command.  Used to complete
+   COMMAND in "thread apply all COMMAND", for example.  Note that
+   unlike command_completer, this fully recurses into the proper
+   completer for COMMAND, so that e.g.,
+
+     (gdb) thread apply all print -[TAB]
+
+   does the right thing and show the print options.  */
+extern void complete_command (completion_tracker &tracker, const char *text);
+
 extern const char *skip_quoted_chars (const char *, const char *,
 				      const char *);
 
-- 
2.14.5

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

* [PATCH v2 22/24] Make "thread apply" use the gdb::option framework
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (14 preceding siblings ...)
  2019-05-30 19:54 ` [PATCH v2 03/24] Fix TID parser bug Pedro Alves
@ 2019-05-30 19:59 ` Pedro Alves
  2019-05-30 19:59 ` [PATCH v2 19/24] Introduce complete_command Pedro Alves
                   ` (8 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 19:59 UTC (permalink / raw)
  To: gdb-patches

Similarly to the "frame apply" patch, this makes the "thread apply"
family of commands -- "thread apply TID", "thread apply all" and
"taas" use the gdb::option framework for '-'-style options.

No new options are added, but there are some user-visible changes:

- Can now abbreviate and complete "-ascending"

- We now have a completer for "thread apply" commands

  Can now complete options ("thread apply all -[TAB]"), and also,
  'thread apply all COMMAND[TAB]' now does what you'd expect, by
  making use of the new complete_command routine.

- "help" output tweaked with auto-generated option descriptions:

   ~~~
   Usage: thread apply all [OPTION]... COMMAND
   Prints per-inferior thread number and target system's thread id
   followed by COMMAND output.

   By default, an error raised during the execution of COMMAND
   aborts "thread apply".

   Options:
     -ascending
       Call COMMAND for all threads in ascending order.
       The default is descending order.

     -q
       Disables printing the thread information.

     -c
       Print any error raised by COMMAND and continue.

     -s
       Silently ignore any errors or empty output produced by COMMAND.
   ~~~

  The "By default ..." sentence is new as well.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* thread.c: Include "cli/cli-option.h".
	(tp_array_compar_ascending): Global.
	(tp_array_compar): Delete function.
	(tp_array_compar_ascending, tp_array_compar_descending): New
	functions.
	(ascending_option_def, qcs_flag_option_def)
	(thr_qcs_flags_option_defs)
	(make_thread_apply_all_options_def_group)
	(make_thread_apply_options_def_group): New.
	(thread_apply_all_command): Use gdb::option::process_options.
	(thread_apply_command_completer)
	(thread_apply_all_command_completer): New.
	(thread_apply_command): Use gdb::option::process_options.
	(_initialize_thread): Delete THREAD_APPLY_FLAGS_HELP, replace it
	with a new THREAD_APPLY_OPTION_HELP.  Use gdb::option::build_help
	to generate help text of "thread apply".  Adjust "taas"'s help.
	* tid-parse.c (tid_range_parser::in_thread_range): New method.
	* tid-parse.h (tid_range_parser::in_thread_range): New method.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/options.exp (test-thread-apply): New.
	(top level): Call it.
---
 gdb/testsuite/gdb.base/options.exp |  95 +++++++++++++-
 gdb/thread.c                       | 258 ++++++++++++++++++++++++++++---------
 gdb/tid-parse.c                    |   6 +
 gdb/tid-parse.h                    |   3 +
 4 files changed, 301 insertions(+), 61 deletions(-)

diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp
index 195bbb168ae..4570c2a93d4 100644
--- a/gdb/testsuite/gdb.base/options.exp
+++ b/gdb/testsuite/gdb.base/options.exp
@@ -28,6 +28,8 @@
 #  - frame apply
 #  - faas
 #  - tfaas
+#  - thread apply
+#  - taas
 
 load_lib completion-support.exp
 
@@ -371,6 +373,87 @@ proc_with_prefix test-frame-apply {} {
     }
 }
 
+# Basic option-machinery + "thread apply" command integration tests.
+proc_with_prefix test-thread-apply {} {
+
+    test_gdb_complete_unique "thread apply all" "thread apply all"
+    test_gdb_complete_unique "taas" "taas"
+
+    gdb_test "thread apply 1-" \
+	"inverted range"
+    test_gdb_complete_none "frame apply level 1-"
+
+    foreach cmd {
+	"thread apply all"
+	"thread apply 1"
+	"taas"
+    } {
+	test_gdb_completion_offers_commands "$cmd "
+
+	# taas is silent on command error by design.  This procedure
+	# hides the difference.  EXPECTED_RE is only considered when
+	# not testing with "taas".
+	proc test_invalid_cmd {cmd arg expected_re} {
+	    if {$cmd != "taas"} {
+		gdb_test "$cmd$arg" $expected_re
+	    } else {
+		gdb_test_no_output "$cmd$arg"
+	    }
+	}
+
+	gdb_test "$cmd -" "Ambiguous option at: -"
+
+	if {$cmd != "thread apply 1"} {
+	    test_gdb_complete_multiple "$cmd " "-" "" {
+		"-ascending"
+		"-c"
+		"-q"
+		"-s"
+	    }
+	} else {
+	    # "-ascending" only works with "all".
+	    test_gdb_complete_multiple "$cmd " "-" "" {
+		"-c"
+		"-q"
+		"-s"
+	    }
+	}
+
+	if {$cmd == "thread apply all" || $cmd == "taas"} {
+	    set errmsg \
+		"Please specify a command at the end of 'thread apply all'"
+	} elseif {$cmd == "thread apply 1"} {
+	    set errmsg \
+		"Please specify a command following the thread ID list"
+	} else {
+	    error "unexpected cmd: $cmd"
+	}
+
+	with_test_prefix "no-trailing-space" {
+	    gdb_test "$cmd --" $errmsg
+	    test_gdb_complete_unique "$cmd --" "$cmd --"
+	}
+
+	with_test_prefix "trailing-space" {
+	    gdb_test "$cmd -- " $errmsg
+	    test_gdb_completion_offers_commands "$cmd -- "
+	}
+
+	# '-' is a valid TUI command.
+	test_invalid_cmd "$cmd" " -- -" \
+	    "Cannot enable the TUI when output is not a terminal"
+	test_gdb_complete_unique \
+	    "$cmd -- -" \
+	    "$cmd -- -"
+
+	test_invalid_cmd $cmd " -foo" \
+	    "Undefined command: \"-foo\".  Try \"help\"\\."
+	test_gdb_complete_none "$cmd -foo"
+
+	test_gdb_completion_offers_commands "$cmd -c "
+    }
+}
+
 # Miscellaneous tests.
 proc_with_prefix test-misc {variant} {
     global all_options
@@ -808,14 +891,17 @@ foreach_with_prefix cmd {
 }
 
 # Run the print integration tests, both as "standalone", and under
-# "frame apply".  The latter checks that the "frame apply ... COMMAND"
-# commands recurse the completion machinery for COMMAND completion
-# correctly.
+# "frame/thread apply".  The latter checks that the "frame/thread
+# apply ... COMMAND" commands recurse the completion machinery for
+# COMMAND completion correctly.
 foreach prefix {
     ""
     "frame apply all "
     "frame apply 1 "
     "frame apply level 0 "
+    "thread apply all "
+    "thread apply 1 "
+    "thread apply 1 frame apply 1 "
 } {
     test-print $prefix
 }
@@ -832,3 +918,6 @@ test-backtrace
 
 # Basic "frame apply" integration tests.
 test-frame-apply
+
+# Basic "thread apply" integration tests.
+test-thread-apply
diff --git a/gdb/thread.c b/gdb/thread.c
index ea87f51c6e6..d3362090244 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -39,6 +39,7 @@
 #include "observable.h"
 #include "annotate.h"
 #include "cli/cli-decode.h"
+#include "cli/cli-option.h"
 #include "gdb_regex.h"
 #include "cli/cli-utils.h"
 #include "thread-fsm.h"
@@ -1426,30 +1427,30 @@ print_thread_id (struct thread_info *thr)
   return s;
 }
 
-/* If true, tp_array_compar should sort in ascending order, otherwise
-   in descending order.  */
+/* Sort an array of struct thread_info pointers by thread ID (first by
+   inferior number, and then by per-inferior thread number).  Sorts in
+   ascending order.  */
 
-static bool tp_array_compar_ascending;
+static bool
+tp_array_compar_ascending (const thread_info *a, const thread_info *b)
+{
+  if (a->inf->num != b->inf->num)
+    return a->inf->num < b->inf->num;
+
+  return (a->per_inf_num < b->per_inf_num);
+}
 
-/* Sort an array for struct thread_info pointers by thread ID (first
-   by inferior number, and then by per-inferior thread number).  The
-   order is determined by TP_ARRAY_COMPAR_ASCENDING.  */
+/* Sort an array of struct thread_info pointers by thread ID (first by
+   inferior number, and then by per-inferior thread number).  Sorts in
+   descending order.  */
 
 static bool
-tp_array_compar (const thread_info *a, const thread_info *b)
+tp_array_compar_descending (const thread_info *a, const thread_info *b)
 {
   if (a->inf->num != b->inf->num)
-    {
-      if (tp_array_compar_ascending)
-	return a->inf->num < b->inf->num;
-      else
-	return a->inf->num > b->inf->num;
-    }
+    return a->inf->num > b->inf->num;
 
-  if (tp_array_compar_ascending)
-    return (a->per_inf_num < b->per_inf_num);
-  else
-    return (a->per_inf_num > b->per_inf_num);
+  return (a->per_inf_num > b->per_inf_num);
 }
 
 /* Switch to thread THR and execute CMD.
@@ -1490,6 +1491,60 @@ thr_try_catch_cmd (thread_info *thr, const char *cmd, int from_tty,
     }
 }
 
+/* Option definition of "thread apply"'s "-ascending" option.  */
+
+static const gdb::option::flag_option_def<> ascending_option_def = {
+  "ascending",
+  N_("\
+Call COMMAND for all threads in ascending order.\n\
+The default is descending order."),
+};
+
+/* The qcs command line flags for the "thread apply" commands.  Keep
+   this in sync with the "frame apply" commands.  */
+
+using qcs_flag_option_def
+  = gdb::option::flag_option_def<qcs_flags>;
+
+static const gdb::option::option_def thr_qcs_flags_option_defs[] = {
+  qcs_flag_option_def {
+    "q", [] (qcs_flags *opt) { return &opt->quiet; },
+    N_("Disables printing the thread information."),
+  },
+
+  qcs_flag_option_def {
+    "c", [] (qcs_flags *opt) { return &opt->cont; },
+    N_("Print any error raised by COMMAND and continue."),
+  },
+
+  qcs_flag_option_def {
+    "s", [] (qcs_flags *opt) { return &opt->silent; },
+    N_("Silently ignore any errors or empty output produced by COMMAND."),
+  },
+};
+
+/* Create an option_def_group for the "thread apply all" options, with
+   ASCENDING and FLAGS as context.  */
+
+static inline std::array<gdb::option::option_def_group, 2>
+make_thread_apply_all_options_def_group (int *ascending,
+					 qcs_flags *flags)
+{
+  return {{
+    { ascending_option_def.def (), ascending},
+    { thr_qcs_flags_option_defs, flags },
+  }};
+}
+
+/* Create an option_def_group for the "thread apply" options, with
+   FLAGS as context.  */
+
+static inline gdb::option::option_def_group
+make_thread_apply_options_def_group (qcs_flags *flags)
+{
+  return {thr_qcs_flags_option_defs, flags};
+}
+
 /* Apply a GDB command to a list of threads.  List syntax is a whitespace
    separated list of numbers, or ranges, or the keyword `all'.  Ranges consist
    of two numbers separated by a hyphen.  Examples:
@@ -1501,24 +1556,15 @@ thr_try_catch_cmd (thread_info *thr, const char *cmd, int from_tty,
 static void
 thread_apply_all_command (const char *cmd, int from_tty)
 {
+  int ascending = false;
   qcs_flags flags;
 
-  tp_array_compar_ascending = false;
+  auto group = make_thread_apply_all_options_def_group (&ascending,
+							&flags);
+  gdb::option::process_options
+    (&cmd, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group);
 
-  while (cmd != NULL)
-    {
-      if (check_for_argument (&cmd, "-ascending", strlen ("-ascending")))
-	{
-	  cmd = skip_spaces (cmd);
-	  tp_array_compar_ascending = true;
-	  continue;
-	}
-
-      if (parse_flags_qcs ("thread apply all", &cmd, &flags))
-	continue;
-
-      break;
-    }
+  validate_flags_qcs ("thread apply all", &flags);
 
   if (cmd == NULL || *cmd == '\000')
     error (_("Please specify a command at the end of 'thread apply all'"));
@@ -1544,7 +1590,10 @@ thread_apply_all_command (const char *cmd, int from_tty)
 	 exit.  */
       scoped_inc_dec_ref inc_dec_ref (thr_list_cpy);
 
-      std::sort (thr_list_cpy.begin (), thr_list_cpy.end (), tp_array_compar);
+      auto *sorter = (ascending
+		      ? tp_array_compar_ascending
+		      : tp_array_compar_descending);
+      std::sort (thr_list_cpy.begin (), thr_list_cpy.end (), sorter);
 
       scoped_restore_current_thread restore_thread;
 
@@ -1554,6 +1603,81 @@ thread_apply_all_command (const char *cmd, int from_tty)
     }
 }
 
+/* Completer for "thread apply [ID list]".  */
+
+static void
+thread_apply_command_completer (cmd_list_element *ignore,
+				completion_tracker &tracker,
+				const char *text, const char * /*word*/)
+{
+  /* Don't leave this to complete_options because there's an early
+     return below.  */
+  tracker.set_use_custom_word_point (true);
+
+  tid_range_parser parser;
+  parser.init (text, current_inferior ()->num);
+
+  try
+    {
+      while (!parser.finished ())
+	{
+	  int inf_num, thr_start, thr_end;
+
+	  if (!parser.get_tid_range (&inf_num, &thr_start, &thr_end))
+	    break;
+
+	  if (parser.in_star_range () || parser.in_thread_range ())
+	    parser.skip_range ();
+	}
+    }
+  catch (const gdb_exception_error &ex)
+    {
+      /* get_tid_range throws if it parses a negative number, for
+	 example.  But a seemingly negative number may be the start of
+	 an option instead.  */
+    }
+
+  const char *cmd = parser.cur_tok ();
+
+  if (cmd == text)
+    {
+      /* No thread ID list yet.  */
+      return;
+    }
+
+  /* Check if we're past a valid thread ID list already.  */
+  if (parser.finished ()
+      && cmd > text && !isspace (cmd[-1]))
+    return;
+
+  /* We're past the thread ID list, advance word point.  */
+  tracker.advance_custom_word_point_by (cmd - text);
+  text = cmd;
+
+  const auto group = make_thread_apply_options_def_group (nullptr);
+  if (gdb::option::complete_options
+      (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
+    return;
+
+  complete_command (tracker, text);
+}
+
+/* Completer for "thread apply all".  */
+
+static void
+thread_apply_all_command_completer (cmd_list_element *ignore,
+				    completion_tracker &tracker,
+				    const char *text, const char *word)
+{
+  const auto group = make_thread_apply_all_options_def_group (nullptr,
+							      nullptr);
+  if (gdb::option::complete_options
+      (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
+    return;
+
+  complete_command (tracker, text);
+}
+
 /* Implementation of the "thread apply" command.  */
 
 static void
@@ -1577,8 +1701,11 @@ thread_apply_command (const char *tidlist, int from_tty)
 
   cmd = parser.cur_tok ();
 
-  while (parse_flags_qcs ("thread apply", &cmd, &flags))
-    ;
+  auto group = make_thread_apply_options_def_group (&flags);
+  gdb::option::process_options
+    (&cmd, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group);
+
+  validate_flags_qcs ("thread apply", &flags);
 
   if (*cmd == '\0')
     error (_("Please specify a command following the thread ID list"));
@@ -1953,37 +2080,52 @@ Use this command to switch between threads.\n\
 The new thread ID must be currently known."),
 		  &thread_cmd_list, "thread ", 1, &cmdlist);
 
-#define THREAD_APPLY_FLAGS_HELP "\
+#define THREAD_APPLY_OPTION_HELP "\
 Prints per-inferior thread number and target system's thread id\n\
 followed by COMMAND output.\n\
-FLAG arguments are -q (quiet), -c (continue), -s (silent).\n\
-Flag -q disables printing the thread information.\n\
-By default, if a COMMAND raises an error, thread apply is aborted.\n\
-Flag -c indicates to print the error and continue.\n\
-Flag -s indicates to silently ignore a COMMAND that raises an error\n\
-or produces no output."
-
-  add_prefix_cmd ("apply", class_run, thread_apply_command,
-		  _("Apply a command to a list of threads.\n\
-Usage: thread apply ID... [FLAG]... COMMAND\n\
+\n\
+By default, an error raised during the execution of COMMAND\n\
+aborts \"thread apply\".\n\
+\n\
+Options:\n\
+%OPTIONS%"
+
+  const auto thread_apply_opts = make_thread_apply_options_def_group (nullptr);
+
+  static std::string thread_apply_help = gdb::option::build_help (N_("\
+Apply a command to a list of threads.\n\
+Usage: thread apply ID... [OPTION]... COMMAND\n\
 ID is a space-separated list of IDs of threads to apply COMMAND on.\n"
-THREAD_APPLY_FLAGS_HELP),
-		  &thread_apply_list, "thread apply ", 1, &thread_cmd_list);
+THREAD_APPLY_OPTION_HELP),
+			       thread_apply_opts);
+
+  c = add_prefix_cmd ("apply", class_run, thread_apply_command,
+		      thread_apply_help.c_str (),
+		      &thread_apply_list, "thread apply ", 1,
+		      &thread_cmd_list);
+  set_cmd_completer_handle_brkchars (c, thread_apply_command_completer);
 
-  add_cmd ("all", class_run, thread_apply_all_command,
-	   _("\
+  const auto thread_apply_all_opts
+    = make_thread_apply_all_options_def_group (nullptr, nullptr);
+
+  static std::string thread_apply_all_help = gdb::option::build_help (N_("\
 Apply a command to all threads.\n\
 \n\
-Usage: thread apply all [-ascending] [FLAG]... COMMAND\n\
--ascending: Call COMMAND for all threads in ascending order.\n\
-            The default is descending order.\n"
-THREAD_APPLY_FLAGS_HELP),
-	   &thread_apply_list);
+Usage: thread apply all [OPTION]... COMMAND\n"
+THREAD_APPLY_OPTION_HELP),
+			       thread_apply_all_opts);
+
+  c = add_cmd ("all", class_run, thread_apply_all_command,
+	       thread_apply_all_help.c_str (),
+	       &thread_apply_list);
+  set_cmd_completer_handle_brkchars (c, thread_apply_all_command_completer);
 
-  add_com ("taas", class_run, taas_command, _("\
+  c = add_com ("taas", class_run, taas_command, _("\
 Apply a command to all threads (ignoring errors and empty output).\n\
-Usage: taas COMMAND\n\
-shortcut for 'thread apply all -s COMMAND'"));
+Usage: taas [OPTION]... COMMAND\n\
+shortcut for 'thread apply all -s [OPTION]... COMMAND'\n\
+See \"help thread apply all\" for available options."));
+  set_cmd_completer_handle_brkchars (c, thread_apply_all_command_completer);
 
   c = add_com ("tfaas", class_run, tfaas_command, _("\
 Apply a command to all frames of all threads (ignoring errors and empty output).\n\
diff --git a/gdb/tid-parse.c b/gdb/tid-parse.c
index 07d7d2c3b2a..6ad8b1f4aff 100644
--- a/gdb/tid-parse.c
+++ b/gdb/tid-parse.c
@@ -307,6 +307,12 @@ tid_range_parser::in_star_range () const
   return m_state == STATE_STAR_RANGE;
 }
 
+bool
+tid_range_parser::in_thread_range () const
+{
+  return m_state == STATE_THREAD_RANGE;
+}
+
 /* See tid-parse.h.  */
 
 int
diff --git a/gdb/tid-parse.h b/gdb/tid-parse.h
index bc70b2d4e4c..da8ead13653 100644
--- a/gdb/tid-parse.h
+++ b/gdb/tid-parse.h
@@ -114,6 +114,9 @@ public:
      range.  */
   bool in_star_range () const;
 
+  /* Returns true if processing a thread range (e.g., 1.2-3).  */
+  bool in_thread_range () const;
+
   /* Returns true if parsing has completed.  */
   bool finished () const;
 
-- 
2.14.5

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

* [PATCH v2 20/24] Make "frame apply" support -OPT options
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (16 preceding siblings ...)
  2019-05-30 19:59 ` [PATCH v2 19/24] Introduce complete_command Pedro Alves
@ 2019-05-30 20:01 ` Pedro Alves
  2019-05-30 20:01 ` [PATCH v2 14/24] Migrate rest of compile commands to new options framework Pedro Alves
                   ` (6 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 20:01 UTC (permalink / raw)
  To: gdb-patches

This adds support for '-'-style options to the "frame apply" family of
commands -- "frame apply COUNT", "frame apply level", "frame apply
all", "faas" and "tfaas".

The -q/-c/-s flags were already supported, -past-main/-past-entry is
new:

~~~
(gdb) help frame apply all
Apply a command to all frames.

Usage: frame apply all [OPTION]... COMMAND
Prints the frame location information followed by COMMAND output.

By default, an error raised during the execution of COMMAND
aborts "frame apply".

Options:
  -q
    Disables printing the frame location information.

  -c
    Print any error raised by COMMAND and continue.

  -s
    Silently ignore any errors or empty output produced by COMMAND.

  -past-main [on|off]
    Set whether backtraces should continue past "main".
    Normally the caller of "main" is not of interest, so GDB will terminate
    the backtrace at "main".  Set this if you need to see the rest
    of the stack trace.

  -past-entry [on|off]
    Set whether backtraces should continue past the entry point of a program.
    Normally there are no callers beyond the entry point of a program, so GDB
    will terminate the backtrace there.  Set this if you need to see
    the rest of the stack trace.
~~~

TAB completion of options is now supported.  Also, TAB completion of
COMMAND in "frame apply all COMMAND" does the right thing now, making
use of complete_command, added by the previous patch.  E.g.:

 (gdb) thread apply all -ascending frame apply all -past-main print -[TAB]
 -address         -elements        -pretty          -symbol
 -array           -null-stop       -repeats         -union
 -array-indexes   -object          -static-members  -vtbl
 (gdb) thread apply all -ascending frame apply all -past-main print glo[TAB]
 global1         global2

The change to tfaas_command is necessary because otherwise you get
this:

 (gdb) tfaas --
 Unrecognized option at: frame apply all -s --

That's because the above is equivalent to:

 (gdb) thread apply all -s frame apply all -s --

and the "--" instructs "thread apply" to consider everything up to
"--" as its command options.  And from that view, "frame" is an
invalid option.

The change makes tfaas be equivalent to:

 (gdb) thread apply all -s -- frame apply all -s --

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* cli/cli-utils.c (parse_flags_qcs): Use validate_flags_qcs.
	(validate_flags_qcs): New.
	* cli/cli-utils.h (struct qcs_flags): Change field types to int.
	(validate_flags_qcs): Declare.
	* stack.c (qcs_flag_option_def, fr_qcs_flags_option_defs): New.
	(make_frame_apply_options_def_group): New.
	(frame_apply_command_count): Process options with
	gdb::option::process_options.
	(frame_apply_completer): New.
	(frame_apply_level_completer, frame_apply_all_completer)
	(frame_apply_completer): New.
	(_initialize_stack): Update help of "frame apply", "frame apply
	level", "frame apply all" and "faas" to mention supported options
	and install command completers.
	* stack.h (frame_apply_all_completer): Declare.
	* thread.c: Include "stack.h".
	(tfaas_command): Add "--".
	(_initialize_thread): Update help "tfaas" to mention supported
	options and install command completer.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/options.exp (test-frame-apply): New.
	(top level): Test print commands with different "frame apply"
	prefixes.
---
 gdb/cli/cli-utils.c                |  14 ++-
 gdb/cli/cli-utils.h                |  14 ++-
 gdb/stack.c                        | 222 +++++++++++++++++++++++++++++++------
 gdb/stack.h                        |   5 +
 gdb/testsuite/gdb.base/options.exp |  97 +++++++++++++++-
 gdb/thread.c                       |  12 +-
 6 files changed, 320 insertions(+), 44 deletions(-)

diff --git a/gdb/cli/cli-utils.c b/gdb/cli/cli-utils.c
index 306b69e3d8f..30d4091a0d1 100644
--- a/gdb/cli/cli-utils.c
+++ b/gdb/cli/cli-utils.c
@@ -573,8 +573,18 @@ parse_flags_qcs (const char *which_command, const char **str,
       gdb_assert_not_reached ("int qcs flag out of bound");
     }
 
-  if (flags->cont && flags->silent)
-    error (_("%s: -c and -s are mutually exclusive"), which_command);
+  validate_flags_qcs (which_command, flags);
 
   return true;
 }
+
+/* See documentation in cli-utils.h.  */
+
+void
+validate_flags_qcs (const char *which_command, qcs_flags *flags)
+{
+  if (flags->cont && flags->silent)
+    error (_("%s: -c and -s are mutually exclusive"), which_command);
+}
+
+/* See documentation in cli-utils.h.  */
diff --git a/gdb/cli/cli-utils.h b/gdb/cli/cli-utils.h
index 41c23561d6f..e6b877d5ab7 100644
--- a/gdb/cli/cli-utils.h
+++ b/gdb/cli/cli-utils.h
@@ -229,13 +229,14 @@ check_for_argument (char **str, const char *arg)
    such that FLAGS [N - 1] is the valid found flag.  */
 extern int parse_flags (const char **str, const char *flags);
 
-/* qcs_flags struct regroups the flags parsed by parse_flags_qcs.  */
+/* qcs_flags struct groups the -q, -c, and -s flags parsed by "thread
+   apply" and "frame apply" commands */
 
 struct qcs_flags
 {
-  bool quiet = false;
-  bool cont = false;
-  bool silent = false;
+  int quiet = false;
+  int cont = false;
+  int silent = false;
 };
 
 /* A helper function that uses parse_flags to handle the flags qcs :
@@ -259,4 +260,9 @@ struct qcs_flags
 extern bool parse_flags_qcs (const char *which_command, const char **str,
 			     qcs_flags *flags);
 
+/* Validate FLAGS.  Throws an error if both FLAGS->CONT and
+   FLAGS->SILENT are true.  WHICH_COMMAND is included in the error
+   message.  */
+extern void validate_flags_qcs (const char *which_command, qcs_flags *flags);
+
 #endif /* CLI_CLI_UTILS_H */
diff --git a/gdb/stack.c b/gdb/stack.c
index 1cb1fc9c38c..a994889af73 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -2823,6 +2823,42 @@ func_command (const char *arg, int from_tty)
     }
 }
 
+/* The qcs command line flags for the "frame apply" commands.  Keep
+   this in sync with the "thread apply" commands.  */
+
+using qcs_flag_option_def
+  = gdb::option::flag_option_def<qcs_flags>;
+
+static const gdb::option::option_def fr_qcs_flags_option_defs[] = {
+  qcs_flag_option_def {
+    "q", [] (qcs_flags *opt) { return &opt->quiet; },
+    N_("Disables printing the frame location information."),
+  },
+
+  qcs_flag_option_def {
+    "c", [] (qcs_flags *opt) { return &opt->cont; },
+    N_("Print any error raised by COMMAND and continue."),
+  },
+
+  qcs_flag_option_def {
+    "s", [] (qcs_flags *opt) { return &opt->silent; },
+    N_("Silently ignore any errors or empty output produced by COMMAND."),
+  },
+};
+
+/* Create an option_def_group array for all the "frame apply" options,
+   with FLAGS and SET_BT_OPTS as context.  */
+
+static inline std::array<gdb::option::option_def_group, 2>
+make_frame_apply_options_def_group (qcs_flags *flags,
+				    set_backtrace_options *set_bt_opts)
+{
+  return {{
+    { {fr_qcs_flags_option_defs}, flags },
+    { {set_backtrace_option_defs}, set_bt_opts },
+  }};
+}
+
 /* Apply a GDB command to all stack frames, or a set of identified frames,
    or innermost COUNT frames.
    With a negative COUNT, apply command on outermost -COUNT frames.
@@ -2852,10 +2888,13 @@ frame_apply_command_count (const char *which_command,
 			   struct frame_info *trailing, int count)
 {
   qcs_flags flags;
-  struct frame_info *fi;
+  set_backtrace_options set_bt_opts = user_set_backtrace_options;
 
-  while (cmd != NULL && parse_flags_qcs (which_command, &cmd, &flags))
-    ;
+  auto group = make_frame_apply_options_def_group (&flags, &set_bt_opts);
+  gdb::option::process_options
+    (&cmd, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group);
+
+  validate_flags_qcs (which_command, &flags);
 
   if (cmd == NULL || *cmd == '\0')
     error (_("Please specify a command to apply on the selected frames"));
@@ -2866,7 +2905,12 @@ frame_apply_command_count (const char *which_command,
      these also.  */
   scoped_restore_current_thread restore_thread;
 
-  for (fi = trailing; fi && count--; fi = get_prev_frame (fi))
+  /* These options are handled quite deep in the unwind machinery, so
+     we get to pass them down by swapping globals.  */
+  scoped_restore restore_set_backtrace_options
+    = make_scoped_restore (&user_set_backtrace_options, set_bt_opts);
+
+  for (frame_info *fi = trailing; fi && count--; fi = get_prev_frame (fi))
     {
       QUIT;
 
@@ -2909,6 +2953,104 @@ frame_apply_command_count (const char *which_command,
     }
 }
 
+/* Completer for the "frame apply ..." commands.  */
+
+static void
+frame_apply_completer (completion_tracker &tracker, const char *text)
+{
+  const auto group = make_frame_apply_options_def_group (nullptr, nullptr);
+  if (gdb::option::complete_options
+      (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
+    return;
+
+  complete_command (tracker, text);
+}
+
+/* Completer for the "frame apply" commands.  */
+
+static void
+frame_apply_level_cmd_completer (struct cmd_list_element *ignore,
+				 completion_tracker &tracker,
+				 const char *text, const char */*word*/)
+{
+  /* Do this explicitly because there's an early return below.  */
+  tracker.set_use_custom_word_point (true);
+
+  number_or_range_parser levels (text);
+
+  /* Skip the LEVEL list to find the options and command args.  */
+  try
+    {
+      while (!levels.finished ())
+	{
+	  /* Call for effect.  */
+	  levels.get_number ();
+
+	  if (levels.in_range ())
+	    levels.skip_range ();
+	}
+    }
+  catch (const gdb_exception_error &ex)
+    {
+      /* get_number throws if it parses a negative number, for
+	 example.  But a seemingly negative number may be the start of
+	 an option instead.  */
+    }
+
+  const char *cmd = levels.cur_tok ();
+
+  if (cmd == text)
+    {
+      /* No level list yet.  */
+      return;
+    }
+
+  /* Check if we're past a valid LEVEL already.  */
+  if (levels.finished ()
+      && cmd > text && !isspace (cmd[-1]))
+    return;
+
+  /* We're past LEVELs, advance word point.  */
+  tracker.advance_custom_word_point_by (cmd - text);
+  text = cmd;
+
+  frame_apply_completer (tracker, text);
+}
+
+/* Completer for the "frame apply all" command.  */
+
+void
+frame_apply_all_cmd_completer (struct cmd_list_element *ignore,
+			       completion_tracker &tracker,
+			       const char *text, const char */*word*/)
+{
+  frame_apply_completer (tracker, text);
+}
+
+/* Completer for the "frame apply COUNT" command.  */
+
+static void
+frame_apply_cmd_completer (struct cmd_list_element *ignore,
+			   completion_tracker &tracker,
+			   const char *text, const char */*word*/)
+{
+  const char *cmd = text;
+
+  int count = get_number_trailer (&cmd, 0);
+  if (count == 0)
+    return;
+
+  /* Check if we're past a valid COUNT already.  */
+  if (cmd > text && !isspace (cmd[-1]))
+    return;
+
+  /* We're past COUNT, advance word point.  */
+  tracker.advance_custom_word_point_by (cmd - text);
+  text = cmd;
+
+  frame_apply_completer (tracker, text);
+}
+
 /* Implementation of the "frame apply level" command.  */
 
 static void
@@ -3095,44 +3237,62 @@ A single numerical argument specifies the frame to select."),
 
   add_com_alias ("f", "frame", class_stack, 1);
 
-#define FRAME_APPLY_FLAGS_HELP "\
+#define FRAME_APPLY_OPTION_HELP "\
 Prints the frame location information followed by COMMAND output.\n\
-FLAG arguments are -q (quiet), -c (continue), -s (silent).\n\
-Flag -q disables printing the frame location information.\n\
-By default, if a COMMAND raises an error, frame apply is aborted.\n\
-Flag -c indicates to print the error and continue.\n\
-Flag -s indicates to silently ignore a COMMAND that raises an error\n\
-or produces no output."
-
-  add_prefix_cmd ("apply", class_stack, frame_apply_command,
-		  _("Apply a command to a number of frames.\n\
-Usage: frame apply COUNT [FLAG]... COMMAND\n\
+\n\
+By default, an error raised during the execution of COMMAND\n\
+aborts \"frame apply\".\n\
+\n\
+Options:\n\
+%OPTIONS%"
+
+  const auto frame_apply_opts
+    = make_frame_apply_options_def_group (nullptr, nullptr);
+
+  static std::string frame_apply_cmd_help = gdb::option::build_help (N_("\
+Apply a command to a number of frames.\n\
+Usage: frame apply COUNT [OPTION]... COMMAND\n\
 With a negative COUNT argument, applies the command on outermost -COUNT frames.\n"
-FRAME_APPLY_FLAGS_HELP),
-		  &frame_apply_cmd_list, "frame apply ", 1, &frame_cmd_list);
+				  FRAME_APPLY_OPTION_HELP),
+			       frame_apply_opts);
 
-  add_cmd ("all", class_stack, frame_apply_all_command,
-	   _("\
+  cmd = add_prefix_cmd ("apply", class_stack, frame_apply_command,
+			frame_apply_cmd_help.c_str (),
+			&frame_apply_cmd_list, "frame apply ", 1,
+			&frame_cmd_list);
+  set_cmd_completer_handle_brkchars (cmd, frame_apply_cmd_completer);
+
+  static std::string frame_apply_all_cmd_help = gdb::option::build_help (N_("\
 Apply a command to all frames.\n\
 \n\
-Usage: frame apply all [FLAG]... COMMAND\n"
-FRAME_APPLY_FLAGS_HELP),
-	   &frame_apply_cmd_list);
+Usage: frame apply all [OPTION]... COMMAND\n"
+				  FRAME_APPLY_OPTION_HELP),
+			       frame_apply_opts);
 
-  add_cmd ("level", class_stack, frame_apply_level_command,
-	   _("\
+  cmd = add_cmd ("all", class_stack, frame_apply_all_command,
+		 frame_apply_all_cmd_help.c_str (),
+		 &frame_apply_cmd_list);
+  set_cmd_completer_handle_brkchars (cmd, frame_apply_all_cmd_completer);
+
+  static std::string frame_apply_level_cmd_help = gdb::option::build_help (N_("\
 Apply a command to a list of frames.\n\
 \n\
-Usage: frame apply level LEVEL... [FLAG]... COMMAND\n\
-ID is a space-separated list of LEVELs of frames to apply COMMAND on.\n"
-FRAME_APPLY_FLAGS_HELP),
+Usage: frame apply level LEVEL... [OPTION]... COMMAND\n\
+LEVEL is a space-separated list of levels of frames to apply COMMAND on.\n"
+				  FRAME_APPLY_OPTION_HELP),
+			       frame_apply_opts);
+
+  cmd = add_cmd ("level", class_stack, frame_apply_level_command,
+	   frame_apply_level_cmd_help.c_str (),
 	   &frame_apply_cmd_list);
+  set_cmd_completer_handle_brkchars (cmd, frame_apply_level_cmd_completer);
 
-  add_com ("faas", class_stack, faas_command, _("\
+  cmd = add_com ("faas", class_stack, faas_command, _("\
 Apply a command to all frames (ignoring errors and empty output).\n\
-Usage: faas COMMAND\n\
-shortcut for 'frame apply all -s COMMAND'"));
-
+Usage: faas [OPTION]... COMMAND\n\
+shortcut for 'frame apply all -s [OPTION]... COMMAND'\n\
+See \"help frame apply all\" for available options."));
+  set_cmd_completer_handle_brkchars (cmd, frame_apply_all_cmd_completer);
 
   add_prefix_cmd ("frame", class_stack,
 		  &frame_cmd.base_command, _("\
diff --git a/gdb/stack.h b/gdb/stack.h
index 6c6caa913e2..9ac77c06179 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -52,4 +52,9 @@ struct symtab* get_last_displayed_symtab (void);
 int get_last_displayed_line (void);
 symtab_and_line get_last_displayed_sal ();
 
+/* Completer for the "frame apply all" command.  */
+void frame_apply_all_cmd_completer (struct cmd_list_element *ignore,
+				    completion_tracker &tracker,
+				    const char *text, const char */*word*/);
+
 #endif /* #ifndef STACK_H */
diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp
index 5a35074054e..195bbb168ae 100644
--- a/gdb/testsuite/gdb.base/options.exp
+++ b/gdb/testsuite/gdb.base/options.exp
@@ -25,6 +25,9 @@
 #  - print
 #  - compile print
 #  - backtrace
+#  - frame apply
+#  - faas
+#  - tfaas
 
 load_lib completion-support.exp
 
@@ -295,6 +298,79 @@ proc_with_prefix test-backtrace {} {
 	"backtrace (1 + xxx1"
 }
 
+# Basic option-machinery + "frame apply" command integration tests.
+proc_with_prefix test-frame-apply {} {
+    test_gdb_complete_unique "frame apply all" "frame apply all"
+
+    gdb_test "frame apply level 0-" \
+	"Please specify a command to apply on the selected frames"
+    test_gdb_complete_none "frame apply level 0-"
+
+    foreach cmd {
+	"frame apply all"
+	"frame apply 1"
+	"frame apply level 0"
+	"faas"
+	"tfaas"
+    } {
+	test_gdb_completion_offers_commands "$cmd "
+
+	# tfaas is silent on command error by design.  This procedure
+	# hides that aspect.  EXPECTED_RE is only considered when not
+	# testing with "faas"/"tfaas".
+	proc test_error_cmd {cmd arg expected_re} {
+	    if {$cmd == "tfaas"} {
+		gdb_test_no_output "$cmd$arg"
+	    } else {
+		gdb_test "$cmd$arg" $expected_re
+	    }
+	}
+	# Same, but for tests where both "faas" and "tfaas" are
+	# expected to be silent.
+	proc test_error_cmd2 {cmd arg expected_re} {
+	    if {$cmd == "tfaas" || $cmd == "faas"} {
+		gdb_test_no_output "$cmd$arg"
+	    } else {
+		gdb_test "$cmd$arg" $expected_re
+	    }
+	}
+
+	test_error_cmd $cmd " -" "Ambiguous option at: -"
+	test_gdb_complete_multiple "$cmd " "-" "" {
+	    "-c"
+	    "-past-entry"
+	    "-past-main"
+	    "-q"
+	    "-s"
+	}
+
+	with_test_prefix "no-trailing-space" {
+	    test_error_cmd $cmd " --" \
+		"Please specify a command to apply on the selected frames"
+	    test_gdb_complete_unique "$cmd --" "$cmd --"
+	}
+
+	with_test_prefix "trailing-space" {
+	    test_error_cmd $cmd " -- " \
+		"Please specify a command to apply on the selected frames"
+	    test_gdb_completion_offers_commands "$cmd -- "
+	}
+
+	# '-' is a valid TUI command.
+	test_error_cmd2 $cmd " -- -" \
+	    "Cannot enable the TUI when output is not a terminal"
+	test_gdb_complete_unique \
+	    "$cmd -- -" \
+	    "$cmd -- -"
+
+	test_error_cmd2 $cmd " -foo" \
+	    "Undefined command: \"-foo\".  Try \"help\"\\."
+	test_gdb_complete_none "$cmd -foo"
+
+	test_gdb_completion_offers_commands "$cmd -s "
+    }
+}
+
 # Miscellaneous tests.
 proc_with_prefix test-misc {variant} {
     global all_options
@@ -731,13 +807,28 @@ foreach_with_prefix cmd {
     test-enum $cmd
 }
 
-# Run the print integration tests.
-test-print ""
+# Run the print integration tests, both as "standalone", and under
+# "frame apply".  The latter checks that the "frame apply ... COMMAND"
+# commands recurse the completion machinery for COMMAND completion
+# correctly.
+foreach prefix {
+    ""
+    "frame apply all "
+    "frame apply 1 "
+    "frame apply level 0 "
+} {
+    test-print $prefix
+}
 
-# Same for "compile print".
+# Same for "compile print".  Not really a wrapper prefix command like
+# "frame apply", but similar enough that we test pretty much the same
+# things.
 if ![skip_compile_feature_tests] {
     test-print "compile "
 }
 
 # Basic "backtrace" integration tests.
 test-backtrace
+
+# Basic "frame apply" integration tests.
+test-frame-apply
diff --git a/gdb/thread.c b/gdb/thread.c
index a84dbf9fa1e..24906fa7d60 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -46,6 +46,7 @@
 #include <algorithm>
 #include "common/gdb_optional.h"
 #include "inline-frame.h"
+#include "stack.h"
 
 /* Definition of struct thread_info exported to gdbthread.h.  */
 
@@ -1653,7 +1654,7 @@ static void
 tfaas_command (const char *cmd, int from_tty)
 {
   std::string expanded
-    = std::string ("thread apply all -s frame apply all -s ") + cmd;
+    = std::string ("thread apply all -s -- frame apply all -s ") + cmd;
   execute_command (expanded.c_str (), from_tty);
 }
 
@@ -1938,6 +1939,7 @@ void
 _initialize_thread (void)
 {
   static struct cmd_list_element *thread_apply_list = NULL;
+  cmd_list_element *c;
 
   add_info ("threads", info_threads_command,
 	    _("Display currently known threads.\n\
@@ -1983,10 +1985,12 @@ Apply a command to all threads (ignoring errors and empty output).\n\
 Usage: taas COMMAND\n\
 shortcut for 'thread apply all -s COMMAND'"));
 
-  add_com ("tfaas", class_run, tfaas_command, _("\
+  c = add_com ("tfaas", class_run, tfaas_command, _("\
 Apply a command to all frames of all threads (ignoring errors and empty output).\n\
-Usage: tfaas COMMAND\n\
-shortcut for 'thread apply all -s frame apply all -s COMMAND'"));
+Usage: tfaas [OPTION]... COMMAND\n\
+shortcut for 'thread apply all -s -- frame apply all -s [OPTION]... COMMAND'\n\
+See \"help frame apply all\" for available options."));
+  set_cmd_completer_handle_brkchars (c, frame_apply_all_cmd_completer);
 
   add_cmd ("name", class_run, thread_name_command,
 	   _("Set the current thread's name.\n\
-- 
2.14.5

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

* [PATCH v2 14/24] Migrate rest of compile commands to new options framework
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (17 preceding siblings ...)
  2019-05-30 20:01 ` [PATCH v2 20/24] Make "frame apply" support -OPT options Pedro Alves
@ 2019-05-30 20:01 ` Pedro Alves
  2019-05-30 20:02 ` [PATCH v2 23/24] Delete parse_flags/parse_flags_qcs Pedro Alves
                   ` (5 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 20:01 UTC (permalink / raw)
  To: gdb-patches

As I was in the neighbourhood, I converted the other "compile"
subcommands to the new options framework too.  Specifically, "compile
code" and "compile file".

The user-visible changes are:

  - All abbreviations of "-raw" are accepted now, instead of just -r.
    Obviously that means "-ra" is now accepted.

  - Option completion now works.

  - "compile file" did not have a completer yet, and now it knows to
    complete on filenames.

  - You couldn't use "compile file" with a file named "-something".
    You can now, with "compile file -- -something".

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* compile/compile.c (struct compile_options): New.
	(compile_flag_option_def, compile_command_option_defs)
	(make_compile_options_def_group): New.
	(compile_file_command): Handle options with
	gdb::option::process_options.
	(compile_file_command_completer): New function.
	(compile_code_command): Handle options with
	gdb::option::process_options.
	(compile_code_command_completer): New function.
	(_initialize_compiler): Install completers for "compile code" and
	"compile file".  Mention available options in "compile code" and
	"compile code"'s help.
	* completer.c (advance_to_completion_word): New, factored out from
	...
	(advance_to_expression_complete_word_point): ... this.
	(advance_to_filename_complete_word_point): New.
	* completer.h (advance_to_filename_complete_word_point): New
	declaration.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.compile/compile.exp: Adjust expected output to option
	processing changes.
---
 gdb/compile/compile.c                 | 169 ++++++++++++++++++++++------------
 gdb/completer.c                       |  34 +++++--
 gdb/completer.h                       |   7 +-
 gdb/testsuite/gdb.compile/compile.exp |  15 ++-
 4 files changed, 155 insertions(+), 70 deletions(-)

diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c
index 6693809cf4f..b467254fe83 100644
--- a/gdb/compile/compile.c
+++ b/gdb/compile/compile.c
@@ -236,19 +236,34 @@ show_compile_debug (struct ui_file *file, int from_tty,
 
 \f
 
-/* Check *ARG for a "-raw" or "-r" argument.  Return 0 if not seen.
-   Return 1 if seen and update *ARG.  */
+/* Options for the compile command.  */
 
-static int
-check_raw_argument (const char **arg)
+struct compile_options
 {
-  *arg = skip_spaces (*arg);
+  /* For -raw.  */
+  int raw = false;
+};
+
+using compile_flag_option_def
+  = gdb::option::flag_option_def<compile_options>;
+
+static const gdb::option::option_def compile_command_option_defs[] = {
+
+  compile_flag_option_def {
+    "raw",
+    [] (compile_options *opts) { return &opts->raw; },
+    N_("Suppress automatic 'void _gdb_expr () { CODE }' wrapping."),
+  },
+
+};
 
-  if (arg != NULL
-      && (check_for_argument (arg, "-raw", sizeof ("-raw") - 1)
-	  || check_for_argument (arg, "-r", sizeof ("-r") - 1)))
-      return 1;
-  return 0;
+/* Create an option_def_group for the "compile" command's options,
+   with OPTS as context.  */
+
+static gdb::option::option_def_group
+make_compile_options_def_group (compile_options *opts)
+{
+  return {compile_command_option_defs, opts};
 }
 
 /* Handle the input from the 'compile file' command.  The "compile
@@ -256,65 +271,74 @@ check_raw_argument (const char **arg)
    that may contain calls to the GCC compiler.  */
 
 static void
-compile_file_command (const char *arg, int from_tty)
+compile_file_command (const char *args, int from_tty)
 {
-  enum compile_i_scope_types scope = COMPILE_I_SIMPLE_SCOPE;
-
   scoped_restore save_async = make_scoped_restore (&current_ui->async, 0);
 
-  /* Check the user did not just <enter> after command.  */
-  if (arg == NULL)
-    error (_("You must provide a filename for this command."));
+  /* Check if a -raw option is provided.  */
 
-  /* Check if a raw (-r|-raw) argument is provided.  */
-  if (arg != NULL && check_raw_argument (&arg))
-    {
-      scope = COMPILE_I_RAW_SCOPE;
-      arg = skip_spaces (arg);
-    }
+  compile_options options;
 
-  /* After processing arguments, check there is a filename at the end
-     of the command.  */
-  if (arg[0] == '\0')
-    error (_("You must provide a filename with the raw option set."));
+  const gdb::option::option_def_group group
+    = make_compile_options_def_group (&options);
+  gdb::option::process_options
+    (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR,
+     group);
+
+  enum compile_i_scope_types scope
+    = options.raw ? COMPILE_I_RAW_SCOPE : COMPILE_I_SIMPLE_SCOPE;
+
+  args = skip_spaces (args);
 
-  if (arg[0] == '-')
-    error (_("Unknown argument specified."));
+  /* After processing options, check whether we have a filename.  */
+  if (args == nullptr || args[0] == '\0')
+    error (_("You must provide a filename for this command."));
 
-  arg = skip_spaces (arg);
-  gdb::unique_xmalloc_ptr<char> abspath = gdb_abspath (arg);
+  args = skip_spaces (args);
+  gdb::unique_xmalloc_ptr<char> abspath = gdb_abspath (args);
   std::string buffer = string_printf ("#include \"%s\"\n", abspath.get ());
   eval_compile_command (NULL, buffer.c_str (), scope, NULL);
 }
 
+/* Completer for the "compile file" command.  */
+
+static void
+compile_file_command_completer (struct cmd_list_element *ignore,
+				completion_tracker &tracker,
+				const char *text, const char *word)
+{
+  const gdb::option::option_def_group group
+    = make_compile_options_def_group (nullptr);
+  if (gdb::option::complete_options
+      (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, group))
+    return;
+
+  word = advance_to_filename_complete_word_point (tracker, text);
+  filename_completer (ignore, tracker, text, word);
+}
+
 /* Handle the input from the 'compile code' command.  The
    "compile code" command is used to evaluate an expression that may
    contain calls to the GCC compiler.  The language expected in this
    compile command is the language currently set in GDB.  */
 
 static void
-compile_code_command (const char *arg, int from_tty)
+compile_code_command (const char *args, int from_tty)
 {
-  enum compile_i_scope_types scope = COMPILE_I_SIMPLE_SCOPE;
-
   scoped_restore save_async = make_scoped_restore (&current_ui->async, 0);
 
-  if (arg != NULL && check_raw_argument (&arg))
-    {
-      scope = COMPILE_I_RAW_SCOPE;
-      arg = skip_spaces (arg);
-    }
+  compile_options options;
 
-  arg = skip_spaces (arg);
+  const gdb::option::option_def_group group
+    = make_compile_options_def_group (&options);
+  gdb::option::process_options
+    (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, group);
 
-  if (arg != NULL && !check_for_argument (&arg, "--", sizeof ("--") - 1))
-    {
-      if (arg[0] == '-')
-	error (_("Unknown argument specified."));
-    }
+  enum compile_i_scope_types scope
+    = options.raw ? COMPILE_I_RAW_SCOPE : COMPILE_I_SIMPLE_SCOPE;
 
-  if (arg && *arg)
-    eval_compile_command (NULL, arg, scope, NULL);
+  if (args && *args)
+    eval_compile_command (NULL, args, scope, NULL);
   else
     {
       counted_command_line l = get_command_line (compile_control, "");
@@ -324,6 +348,23 @@ compile_code_command (const char *arg, int from_tty)
     }
 }
 
+/* Completer for the "compile code" command.  */
+
+static void
+compile_code_command_completer (struct cmd_list_element *ignore,
+				completion_tracker &tracker,
+				const char *text, const char *word)
+{
+  const gdb::option::option_def_group group
+    = make_compile_options_def_group (nullptr);
+  if (gdb::option::complete_options
+      (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, group))
+    return;
+
+  word = advance_to_expression_complete_word_point (tracker, text);
+  symbol_completer (ignore, tracker, text, word);
+}
+
 /* Callback for compile_print_command.  */
 
 void
@@ -927,15 +968,16 @@ Command to compile source code and inject it into the inferior."),
 		  &compile_command_list, "compile ", 1, &cmdlist);
   add_com_alias ("expression", "compile", class_obscure, 0);
 
-  add_cmd ("code", class_obscure, compile_code_command,
-	   _("\
+  const auto compile_opts = make_compile_options_def_group (nullptr);
+
+  static const std::string compile_code_help
+    = gdb::option::build_help (N_("\
 Compile, inject, and execute code.\n\
 \n\
-Usage: compile code [-r|-raw] [--] [CODE]\n\
--r|-raw: Suppress automatic 'void _gdb_expr () { CODE }' wrapping.\n\
---: Do not parse any options beyond this delimiter.  All text to the\n\
-    right will be treated as source code.\n\
+Usage: compile code [OPTION]... [CODE]\n\
 \n\
+Options:\n\
+%OPTIONS%\n\
 The source code may be specified as a simple one line expression, e.g.:\n\
 \n\
     compile code printf(\"Hello world\\n\");\n\
@@ -944,16 +986,27 @@ Alternatively, you can type a multiline expression by invoking\n\
 this command with no argument.  GDB will then prompt for the\n\
 expression interactively; type a line containing \"end\" to\n\
 indicate the end of the expression."),
-	   &compile_command_list);
+			       compile_opts);
 
-  c = add_cmd ("file", class_obscure, compile_file_command,
-	       _("\
+  c = add_cmd ("code", class_obscure, compile_code_command,
+	       compile_code_help.c_str (),
+	       &compile_command_list);
+  set_cmd_completer_handle_brkchars (c, compile_code_command_completer);
+
+static const std::string compile_file_help
+    = gdb::option::build_help (N_("\
 Evaluate a file containing source code.\n\
 \n\
-Usage: compile file [-r|-raw] [FILENAME]\n\
--r|-raw: Suppress automatic 'void _gdb_expr () { CODE }' wrapping."),
+Usage: compile file [OPTION].. [FILENAME]\n\
+\n\
+Options:\n\
+%OPTIONS%"),
+			       compile_opts);
+
+  c = add_cmd ("file", class_obscure, compile_file_command,
+	       compile_file_help.c_str (),
 	       &compile_command_list);
-  set_cmd_completer (c, filename_completer);
+  set_cmd_completer_handle_brkchars (c, compile_file_command_completer);
 
   const auto compile_print_opts = make_value_print_options_def_group (nullptr);
 
diff --git a/gdb/completer.c b/gdb/completer.c
index b2768ede769..4e92e499b3a 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -352,16 +352,18 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info,
   return line_buffer + point;
 }
 
-/* See completer.h.  */
+/* Find the completion word point for TEXT, emulating the algorithm
+   readline uses to find the word point, using WORD_BREAK_CHARACTERS
+   as word break characters.  */
 
-const char *
-advance_to_expression_complete_word_point (completion_tracker &tracker,
-					   const char *text)
+static const char *
+advance_to_completion_word (completion_tracker &tracker,
+			    const char *word_break_characters,
+			    const char *text)
 {
   gdb_rl_completion_word_info info;
 
-  info.word_break_characters
-    = current_language->la_word_break_characters ();
+  info.word_break_characters = word_break_characters;
   info.quote_characters = gdb_completer_quote_characters;
   info.basic_quote_characters = rl_basic_quote_characters;
 
@@ -382,6 +384,26 @@ advance_to_expression_complete_word_point (completion_tracker &tracker,
 
 /* See completer.h.  */
 
+const char *
+advance_to_expression_complete_word_point (completion_tracker &tracker,
+					   const char *text)
+{
+  const char *brk_chars = current_language->la_word_break_characters ();
+  return advance_to_completion_word (tracker, brk_chars, text);
+}
+
+/* See completer.h.  */
+
+const char *
+advance_to_filename_complete_word_point (completion_tracker &tracker,
+					 const char *text)
+{
+  const char *brk_chars = gdb_completer_file_name_break_characters;
+  return advance_to_completion_word (tracker, brk_chars, text);
+}
+
+/* See completer.h.  */
+
 bool
 completion_tracker::completes_to_completion_word (const char *word)
 {
diff --git a/gdb/completer.h b/gdb/completer.h
index 27371b63a57..58fe84f4fee 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -532,10 +532,15 @@ extern const char *completion_find_completion_word (completion_tracker &tracker,
    completion word point for TEXT, emulating the algorithm readline
    uses to find the word point, using the current language's word
    break characters.  */
-
 const char *advance_to_expression_complete_word_point
   (completion_tracker &tracker, const char *text);
 
+/* Assuming TEXT is an filename, find the completion word point for
+   TEXT, emulating the algorithm readline uses to find the word
+   point.  */
+extern const char *advance_to_filename_complete_word_point
+  (completion_tracker &tracker, const char *text);
+
 extern char **gdb_rl_attempted_completion_function (const char *text,
 						    int start, int end);
 
diff --git a/gdb/testsuite/gdb.compile/compile.exp b/gdb/testsuite/gdb.compile/compile.exp
index b4e88470e40..20a6a72386c 100644
--- a/gdb/testsuite/gdb.compile/compile.exp
+++ b/gdb/testsuite/gdb.compile/compile.exp
@@ -325,7 +325,7 @@ gdb_test_no_output "set debug compile off"
 gdb_test "show debug compile" "Compile debugging is .*"
 gdb_test "show compile-args" \
     "Compile command command-line arguments are .*"
-gdb_test "compile code -z" "Unknown argument.*"
+gdb_test "compile code -z" "Unrecognized option at: -z"
 
 gdb_test "set lang rust" \
     "Warning: the current language does not match this frame."
@@ -340,12 +340,17 @@ gdb_test "compile file" \
     "You must provide a filename for this command.*" \
     "Test compile file without a filename"
 gdb_test "compile file -r" \
-    "You must provide a filename with the raw option set.*" \
+    "You must provide a filename for this command.*" \
     "Test compile file and raw option without a filename"
 gdb_test "compile file -z" \
-    "Unknown argument.*" \
-    "test compile file with unknown argument"
-
+    "Unrecognized option at: -z" \
+    "test compile file with unknown option"
+gdb_test "compile file -z --" \
+    "Unrecognized option at: -z --" \
+    "test compile file with unknown option plus --"
+gdb_test "compile file -raw -- -raw" \
+    "/-raw: No such file or directory.*" \
+    "test compile file \"-raw\" file"
 
 # LOC_CONST tests.
 
-- 
2.14.5

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

* [PATCH v2 09/24] New set/show testing framework (gdb.base/settings.exp)
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (19 preceding siblings ...)
  2019-05-30 20:02 ` [PATCH v2 23/24] Delete parse_flags/parse_flags_qcs Pedro Alves
@ 2019-05-30 20:02 ` Pedro Alves
  2019-05-30 20:03 ` [PATCH v2 04/24] Make check_for_argument skip whitespace after arg itself Pedro Alves
                   ` (3 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 20:02 UTC (permalink / raw)
  To: gdb-patches

This commit adds new representative commands for all types of settings
commands supported by gdb (enum var_types), and then uses them to
exercise settings parsing and completion.

  (gdb) maint test-settings s[TAB]
  set   show

  (gdb) maint test-settings set [TAB]
  auto-boolean         integer              uinteger
  boolean              optional-filename    zinteger
  enum                 string               zuinteger
  filename             string-noescape      zuinteger-unlimited

  (gdb) maint test-settings set enum [TAB]
  xxx  yyy  zzz

  etc.

This is basically unit testing, except that it goes fully via GDB.  It
must be done this way in order to exercise TAB completion properly,
which must go via readline.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* Makefile.in (COMMON_SFILES): Add maint-test-settings.c.
	* NEWS: Mention maint test-settings KIND.
	* maint-test-settings.c: New file.

gdb/doc/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.texinfo (Maintenance Commands): Document "maint
	test-settings" commands.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/settings.c: New file.
	* gdb.base/settings.exp: New file.
---
 gdb/doc/gdb.texinfo                 |   6 +
 gdb/NEWS                            |   4 +
 gdb/Makefile.in                     |   1 +
 gdb/maint-test-settings.c           | 257 +++++++++++++++++
 gdb/testsuite/gdb.base/settings.c   |  23 ++
 gdb/testsuite/gdb.base/settings.exp | 536 ++++++++++++++++++++++++++++++++++++
 6 files changed, 827 insertions(+)
 create mode 100644 gdb/maint-test-settings.c
 create mode 100644 gdb/testsuite/gdb.base/settings.c
 create mode 100644 gdb/testsuite/gdb.base/settings.exp

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f2d8710d7dc..72dd826d373 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -37093,6 +37093,12 @@ If section was not specified, the section in which the symbol was found
 is also printed.  For dynamically linked executables, the name of
 executable or shared library containing the symbol is printed as well.
 
+@kindex maint test-settings
+@item maint test-settings set @var{kind}
+@itemx maint test-settings show @var{kind}
+These are representative commands for each @var{kind} of setting type
+@value{GDBN} supports.  They are used by the testsuite for exercising
+the settings infrastructure.
 @end table
 
 The following command is useful for non-interactive invocations of
diff --git a/gdb/NEWS b/gdb/NEWS
index ab582c036dc..4b5bd07d0af 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -57,6 +57,10 @@ set logging debugredirect [on|off]
   By default, GDB debug output will go to both the terminal and the logfile.
   Set if you want debug output to go only to the log file.
 
+maint test-settings KIND
+  A set of commands used by the testsuite for exercising the settings
+  infrastructure.
+
 * New MI commands
 
 -complete
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 0f495783600..e44df54ddea 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1062,6 +1062,7 @@ COMMON_SFILES = \
 	macrotab.c \
 	main.c \
 	maint.c \
+	maint-test-settings.c \
 	mdebugread.c \
 	mem-break.c \
 	memattr.c \
diff --git a/gdb/maint-test-settings.c b/gdb/maint-test-settings.c
new file mode 100644
index 00000000000..fa13519eb96
--- /dev/null
+++ b/gdb/maint-test-settings.c
@@ -0,0 +1,257 @@
+/* Support for GDB maintenance commands.
+
+   Copyright (C) 2019 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+
+#include "defs.h"
+#include "gdbcmd.h"
+
+/* Command list for "maint test-settings".  */
+static cmd_list_element *maintenance_test_settings_list;
+
+/* Command list for "maint test-settings set/show".  */
+static cmd_list_element *maintenance_test_settings_set_list;
+static cmd_list_element *maintenance_test_settings_show_list;
+
+/* The "maintenance test-options" prefix command.  */
+
+static void
+maintenance_test_settings_cmd (const char *arg, int from_tty)
+{
+  printf_unfiltered
+    (_("\"maintenance test-settings\" must be followed "
+       "by the name of a subcommand.\n"));
+  help_list (maintenance_test_settings_list, "maintenance test-settings ",
+	     all_commands, gdb_stdout);
+}
+
+/* The "maintenance test-options set" prefix command.  */
+
+static void
+maintenance_test_settings_set_cmd (const char *args, int from_tty)
+{
+  printf_unfiltered (_("\"maintenance test-settings set\" must be followed "
+		       "by the name of a set command.\n"));
+  help_list (maintenance_test_settings_set_list,
+	     "maintenance test-settings set ",
+	     all_commands, gdb_stdout);
+}
+
+/* The "maintenance test-options show" prefix command.  */
+
+static void
+maintenance_test_settings_show_cmd (const char *args, int from_tty)
+{
+  cmd_show_list (maintenance_test_settings_show_list, from_tty, "");
+}
+
+/* Control variables for all the "maintenance test-options set/show
+   xxx" commands.  */
+
+static int maintenance_test_settings_boolean;
+
+static auto_boolean maintenance_test_settings_auto_boolean = AUTO_BOOLEAN_AUTO;
+
+static unsigned int maintenance_test_settings_uinteger;
+
+static int maintenance_test_settings_integer;
+
+static int maintenance_test_settings_zinteger;
+
+static unsigned int maintenance_test_settings_zuinteger;
+
+static int maintenance_test_settings_zuinteger_unlimited;
+
+static char *maintenance_test_settings_string;
+
+static char *maintenance_test_settings_string_noescape;
+
+static char *maintenance_test_settings_optional_filename;
+
+static char *maintenance_test_settings_filename;
+
+static const char *maintenance_test_settings_enum;
+
+/* Enum values for the "maintenance test-settings set/show boolean"
+   commands.  */
+static const char *const maintenance_test_settings_enums[] = {
+  "xxx", "yyy", "zzz", nullptr
+};
+
+/* The "maintenance test-options show xxx" commands.  */
+
+static void
+maintenance_test_settings_show_value_cmd
+  (struct ui_file *file, int from_tty,
+   struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, (("%s\n")), value);
+}
+
+\f
+void
+_initialize_maint_test_settings (void)
+{
+  add_prefix_cmd ("test-settings", no_class,
+		  maintenance_test_settings_cmd,
+		  _("\
+Generic command for testing the settings infrastructure."),
+		  &maintenance_test_settings_list,
+		  "maintenance test-settings ", 0,
+		  &maintenancelist);
+
+  add_prefix_cmd ("set", class_maintenance,
+		  maintenance_test_settings_set_cmd, _("\
+Set GDB internal variables used for set/show command infrastructure testing."),
+		  &maintenance_test_settings_set_list,
+		  "maintenance test-settings set ",
+		  0/*allow-unknown*/,
+		  &maintenance_test_settings_list);
+
+  add_prefix_cmd ("show", class_maintenance,
+		  maintenance_test_settings_show_cmd, _("\
+Show GDB internal variables used for set/show command infrastructure testing."),
+		  &maintenance_test_settings_show_list,
+		  "maintenance test-settings show ",
+		  0/*allow-unknown*/,
+		  &maintenance_test_settings_list);
+
+  add_setshow_boolean_cmd ("boolean", class_maintenance,
+			   &maintenance_test_settings_boolean, _("\
+command used for internal testing"), _("\
+command used for internal testing"),
+			   nullptr, /* help_doc */
+			   nullptr, /* set_cmd */
+			   maintenance_test_settings_show_value_cmd,
+			   &maintenance_test_settings_set_list,
+			   &maintenance_test_settings_show_list);
+
+  add_setshow_auto_boolean_cmd ("auto-boolean", class_maintenance,
+				&maintenance_test_settings_auto_boolean, _("\
+command used for internal testing"), _("\
+command used for internal testing"),
+				nullptr, /* help_doc */
+				nullptr, /* set_cmd */
+				maintenance_test_settings_show_value_cmd,
+				&maintenance_test_settings_set_list,
+				&maintenance_test_settings_show_list);
+
+  add_setshow_uinteger_cmd ("uinteger", class_maintenance,
+			   &maintenance_test_settings_uinteger, _("\
+command used for internal testing"), _("\
+command used for internal testing"),
+			   nullptr, /* help_doc */
+			   nullptr, /* set_cmd */
+			   maintenance_test_settings_show_value_cmd,
+			   &maintenance_test_settings_set_list,
+			   &maintenance_test_settings_show_list);
+
+  add_setshow_integer_cmd ("integer", class_maintenance,
+			   &maintenance_test_settings_integer, _("\
+command used for internal testing"), _("\
+command used for internal testing"),
+			   nullptr, /* help_doc */
+			   nullptr, /* set_cmd */
+			   maintenance_test_settings_show_value_cmd,
+			   &maintenance_test_settings_set_list,
+			   &maintenance_test_settings_show_list);
+
+  add_setshow_string_cmd ("string", class_maintenance,
+     &maintenance_test_settings_string, _("\
+command used for internal testing"), _("\
+command used for internal testing"),
+     nullptr, /* help_doc */
+     nullptr, /* set_cmd */
+     maintenance_test_settings_show_value_cmd,
+     &maintenance_test_settings_set_list,
+     &maintenance_test_settings_show_list);
+
+  add_setshow_string_noescape_cmd
+    ("string-noescape", class_maintenance,
+     &maintenance_test_settings_string_noescape, _("\
+command used for internal testing"), _("\
+command used for internal testing"),
+     nullptr, /* help_doc */
+     nullptr, /* set_cmd */
+     maintenance_test_settings_show_value_cmd,
+     &maintenance_test_settings_set_list,
+     &maintenance_test_settings_show_list);
+
+  add_setshow_optional_filename_cmd
+    ("optional-filename", class_maintenance,
+     &maintenance_test_settings_optional_filename, _("\
+command used for internal testing"), _("\
+command used for internal testing"),
+     nullptr, /* help_doc */
+     nullptr, /* set_cmd */
+     maintenance_test_settings_show_value_cmd,
+     &maintenance_test_settings_set_list,
+     &maintenance_test_settings_show_list);
+
+  add_setshow_filename_cmd ("filename", class_maintenance,
+			    &maintenance_test_settings_filename, _("\
+command used for internal testing"), _("\
+command used for internal testing"),
+			    nullptr, /* help_doc */
+			    nullptr, /* set_cmd */
+			    maintenance_test_settings_show_value_cmd,
+			    &maintenance_test_settings_set_list,
+			    &maintenance_test_settings_show_list);
+
+  add_setshow_zinteger_cmd ("zinteger", class_maintenance,
+			    &maintenance_test_settings_zinteger, _("\
+command used for internal testing"), _("\
+command used for internal testing"),
+			    nullptr, /* help_doc */
+			    nullptr, /* set_cmd */
+			    maintenance_test_settings_show_value_cmd,
+			    &maintenance_test_settings_set_list,
+			    &maintenance_test_settings_show_list);
+
+  add_setshow_zuinteger_cmd ("zuinteger", class_maintenance,
+			     &maintenance_test_settings_zuinteger, _("\
+command used for internal testing"), _("\
+command used for internal testing"),
+			     nullptr, /* help_doc */
+			     nullptr, /* set_cmd */
+			     maintenance_test_settings_show_value_cmd,
+			     &maintenance_test_settings_set_list,
+			     &maintenance_test_settings_show_list);
+
+  add_setshow_zuinteger_unlimited_cmd
+    ("zuinteger-unlimited", class_maintenance,
+     &maintenance_test_settings_zuinteger_unlimited, _("\
+command used for internal testing"), _("\
+command used for internal testing"),
+     nullptr, /* help_doc */
+     nullptr, /* set_cmd */
+     maintenance_test_settings_show_value_cmd,
+     &maintenance_test_settings_set_list,
+     &maintenance_test_settings_show_list);
+
+  add_setshow_enum_cmd ("enum", class_maintenance,
+			maintenance_test_settings_enums,
+			&maintenance_test_settings_enum, _("\
+command used for internal testing"), _("\
+command used for internal testing"),
+			nullptr, /* help_doc */
+			nullptr, /* set_cmd */
+			maintenance_test_settings_show_value_cmd,
+			&maintenance_test_settings_set_list,
+			&maintenance_test_settings_show_list);
+}
diff --git a/gdb/testsuite/gdb.base/settings.c b/gdb/testsuite/gdb.base/settings.c
new file mode 100644
index 00000000000..fd22b58603d
--- /dev/null
+++ b/gdb/testsuite/gdb.base/settings.c
@@ -0,0 +1,23 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019 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/>.  */
+
+int xxx1= 123;
+
+int
+main ()
+{
+}
diff --git a/gdb/testsuite/gdb.base/settings.exp b/gdb/testsuite/gdb.base/settings.exp
new file mode 100644
index 00000000000..2c5e9cc2a04
--- /dev/null
+++ b/gdb/testsuite/gdb.base/settings.exp
@@ -0,0 +1,536 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2019 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/>.
+
+# Test the set/show commands framework.  The test uses the
+# "maintenance test-settings set/show xxx" subcommands to exercise
+# TAB-completion and setting processing.
+
+load_lib completion-support.exp
+
+standard_testfile .c
+
+if {[build_executable "failed to prepare" $testfile $srcfile debug]} {
+    return -1
+}
+
+clean_restart
+
+if { ![readline_is_used] } {
+    untested "no tab completion support without readline"
+    return -1
+}
+
+# Test the show command SHOW_CMD.  EXPECTED_RE is the expected output.
+# This procedure exists in order to make it easier to make the test
+# name/message unique, since we test the "show" commands many times.
+# EXPECTED_RE is made part of the test name.
+proc show_setting {show_cmd expected_re} {
+    gdb_test "$show_cmd" $expected_re "$show_cmd: $expected_re"
+}
+
+# var_Xinteger tests.  VARIANT determines which command/variant to
+# exercise.
+proc test-integer {variant} {
+    set set_cmd "maint test-settings set $variant"
+    set show_cmd "maint test-settings show $variant"
+
+    # A bogus value.
+    gdb_test "$set_cmd bogus" \
+	"No symbol table is loaded\\.  Use the \"file\" command\\."
+
+    # Seemingly-valid but not quite valid value.
+    gdb_test "$set_cmd 1a" \
+	"Invalid number \"1a\"\\."
+
+    # Valid value followed by garbage.
+    gdb_test "$set_cmd 1 1" \
+	"A syntax error in expression, near `1'\\."
+
+    # Valid value followed by garbage.
+    gdb_test "$set_cmd 1 x" \
+	"A syntax error in expression, near `x'\\."
+
+    if {$variant == "zuinteger-unlimited"} {
+	# -1 means unlimited.  Other negative values are rejected.  -1
+	# -is tested further below, along the "unlimited" tests.
+	gdb_test "$set_cmd -2" "only -1 is allowed to set as unlimited"
+    } elseif {$variant == "uinteger" || $variant == "zuinteger"} {
+	# Negative values are not accepted.
+	gdb_test "$set_cmd -1" "integer -1 out of range"
+	gdb_test "$set_cmd -2" "integer -2 out of range"
+    } else {
+	# Negative values are not accepted.
+	gdb_test_no_output "$set_cmd -1"
+	show_setting "$show_cmd" "-1"
+	gdb_test_no_output "$set_cmd -2"
+	show_setting "$show_cmd" "-2"
+    }
+
+    # Regular integer is accepted.
+    gdb_test_no_output "$set_cmd 999"
+    show_setting "$show_cmd" "999"
+
+    if {$variant == "zinteger" || $variant == "zuinteger"} {
+	# 0 means 0.
+	gdb_test_no_output "$set_cmd 0"
+	show_setting "$show_cmd" "0"
+    } else {
+	# Either 0 or -1 mean unlimited.  Test both the number and
+	# "unlimited".  For the latter, test both full name and
+	# abbreviations.
+
+	if {$variant == "zuinteger-unlimited"} {
+	    gdb_test_no_output "$set_cmd -1"
+	} else {
+	    gdb_test_no_output "$set_cmd 0"
+	}
+	show_setting "$show_cmd" "unlimited"
+
+	foreach_with_prefix value {
+	    "u"
+	    "un"
+	    "unl"
+	    "unli"
+	    "unlim"
+	    "unlimi"
+	    "unlimit"
+	    "unlimite"
+	    "unlimited"
+	} {
+	    # Alternate between integer and unlimited, to make sure the
+	    # setting really took effect.
+	    gdb_test_no_output "$set_cmd 1"
+	    show_setting "$show_cmd" "1"
+
+	    gdb_test_no_output "$set_cmd $value"
+	    show_setting "$show_cmd" "unlimited"
+	}
+    }
+
+    if {$variant == "zuinteger"} {
+	test_gdb_complete_multiple "maint test-settings set " "zuinteger" "" {
+	    "zuinteger"
+	    "zuinteger-unlimited"
+	}
+    } else {
+	test_gdb_complete_unique \
+	    "$set_cmd" \
+	    "$set_cmd"
+    }
+
+    if {$variant == "zinteger" || $variant == "zuinteger"} {
+	test_gdb_complete_none \
+	    "$set_cmd " \
+    } else {
+	test_gdb_complete_unique \
+	    "$set_cmd " \
+	    "$set_cmd unlimited"
+    }
+
+    test_gdb_complete_none "$set_cmd unlimited "
+    test_gdb_complete_none "$set_cmd unlimited u"
+    test_gdb_complete_none "$set_cmd unlimited 1"
+    test_gdb_complete_none "$set_cmd x"
+    test_gdb_complete_none "$set_cmd x "
+    test_gdb_complete_none "$set_cmd -1"
+    test_gdb_complete_none "$set_cmd -1 "
+    test_gdb_complete_none "$set_cmd 1 "
+
+    # Check show command completion.
+    if {$variant == "zuinteger"} {
+	test_gdb_complete_multiple "maintenance test-settings show " "zuinteger" "" {
+	    "zuinteger"
+	    "zuinteger-unlimited"
+	}
+    } else {
+	test_gdb_complete_unique \
+	    "$show_cmd" \
+	    "$show_cmd"
+    }
+    test_gdb_complete_none "$show_cmd "
+}
+
+# boolean tests.
+proc_with_prefix test-boolean {} {
+    # Use these variables to make sure we don't call the wrong command
+    # by mistake.
+    set set_cmd "maint test-settings set boolean"
+    set show_cmd "maint test-settings show boolean"
+
+    # A bogus value.
+    gdb_test "$set_cmd bogus" \
+	"\"on\" or \"off\" expected\\."
+
+    # Seemingly-valid but not quite valid value.
+    gdb_test "$set_cmd on1" \
+	"\"on\" or \"off\" expected\\."
+
+    # Valid value followed by garbage.
+    gdb_test "$set_cmd on 1" \
+	"\"on\" or \"off\" expected\\."
+
+    # Unlike auto-bool settings, "-1" is not accepted.
+    gdb_test "$set_cmd -1" \
+	"\"on\" or \"off\" expected\\."
+
+    # Nor "auto".
+    gdb_test "$set_cmd auto" \
+	"\"on\" or \"off\" expected\\."
+
+    # Various valid values.  Test both full value names and
+    # abbreviations.
+
+    # Note that unlike with auto-bool, empty value implies "on".
+    foreach_with_prefix value {
+	""
+	"o"
+	"on"
+	"1"
+	"y"
+	"ye"
+	"yes"
+	"e"
+	"en"
+	"ena"
+	"enab"
+	"enabl"
+	"enable"
+    } {
+	gdb_test_no_output "$set_cmd off"
+	show_setting "$show_cmd" "off"
+
+	gdb_test_no_output "$set_cmd $value"
+	show_setting "$show_cmd" "on"
+    }
+
+    foreach_with_prefix value {
+	"of"
+	"off"
+	"0"
+	"n"
+	"no"
+	"d"
+	"di"
+	"dis"
+	"disa"
+	"disab"
+	"disabl"
+	"disable"
+    } {
+	gdb_test_no_output "$set_cmd on"
+	show_setting "$show_cmd" "on"
+
+	gdb_test_no_output "$set_cmd $value"
+	show_setting "$show_cmd" "off"
+    }
+
+    test_gdb_complete_multiple "$set_cmd " "" "o" {
+	"off"
+	"on"
+    }
+
+    test_gdb_complete_unique \
+	"$set_cmd of" \
+	"$set_cmd off"
+
+    test_gdb_complete_none "$set_cmd x"
+
+    # Check that the show command doesn't complete anything.
+    test_gdb_complete_unique \
+	"$show_cmd" \
+	"$show_cmd"
+    test_gdb_complete_none "$show_cmd "
+}
+
+# auto-boolean tests.
+proc_with_prefix test-auto-boolean {} {
+    # Use these variables to make sure we don't call the wrong command
+    # by mistake.
+    set set_cmd "maint test-settings set auto-boolean"
+    set show_cmd "maint test-settings show auto-boolean"
+
+    # A bogus value.
+    gdb_test "$set_cmd bogus" \
+	"\"on\", \"off\" or \"auto\" expected\\."
+
+    # Seemingly-valid but not quite valid value.
+    gdb_test "$set_cmd on1" \
+	"\"on\", \"off\" or \"auto\" expected\\."
+
+    # Valid value followed by garbage.
+    gdb_test "$set_cmd on 1" \
+	"\"on\", \"off\" or \"auto\" expected\\."
+
+    # Various valid values.  Test both full value names and
+    # abbreviations.
+
+    foreach_with_prefix value {
+	"o"
+	"on"
+	"1"
+	"y"
+	"ye"
+	"yes"
+	"e"
+	"en"
+	"ena"
+	"enab"
+	"enabl"
+	"enable"
+    } {
+	gdb_test_no_output "$set_cmd off"
+	show_setting "$show_cmd" "off"
+
+	gdb_test_no_output "$set_cmd $value"
+	show_setting "$show_cmd" "on"
+    }
+
+    foreach_with_prefix value {
+	"of"
+	"off"
+	"0"
+	"n"
+	"no"
+	"d"
+	"di"
+	"dis"
+	"disa"
+	"disab"
+	"disabl"
+	"disable"
+    } {
+	gdb_test_no_output "$set_cmd on"
+	show_setting "$show_cmd" "on"
+
+	gdb_test_no_output "$set_cmd $value"
+	show_setting "$show_cmd" "off"
+    }
+
+    foreach_with_prefix value {
+	"a"
+	"au"
+	"aut"
+	"auto"
+	"-1"
+    } {
+	gdb_test_no_output "$set_cmd on"
+	show_setting "$show_cmd" "on"
+
+	gdb_test_no_output "$set_cmd $value"
+	show_setting "$show_cmd" "auto"
+    }
+
+    # "-" is not accepted as abbreviation of "-1".
+    gdb_test "$set_cmd -" \
+	"\"on\", \"off\" or \"auto\" expected\\."
+
+    test_gdb_complete_multiple "$set_cmd " "" "" {
+	"auto"
+	"off"
+	"on"
+    }
+
+    test_gdb_complete_unique \
+	"$set_cmd of" \
+	"$set_cmd off"
+
+    test_gdb_complete_none "$set_cmd x"
+
+    # Check that the show command doesn't complete anything.
+    test_gdb_complete_unique \
+	"$show_cmd" \
+	"$show_cmd"
+    test_gdb_complete_none "$show_cmd "
+}
+
+# Enum option tests.
+proc_with_prefix test-enum {} {
+    # Use these variables to make sure we don't call the wrong command
+    # by mistake.
+    set set_cmd "maint test-settings set enum"
+    set show_cmd "maint test-settings show enum"
+
+    # Missing value.
+    gdb_test "$set_cmd" \
+	"Requires an argument\\. Valid arguments are xxx, yyy, zzz\\."
+
+    # A bogus value.
+    gdb_test "$set_cmd bogus" \
+	"Undefined item: \"bogus\"."
+
+    # Seemingly-valid but not quite valid value.
+    gdb_test "$set_cmd xxx1" \
+	"Undefined item: \"xxx1\"."
+
+    # Valid value followed by garbage.
+    gdb_test "$set_cmd xxx 1" \
+	"Garbage after item \"xxx\": \"1\""
+    # Valid value followed by garbage, with extra spaces.
+    gdb_test "$set_cmd xxx      1" \
+	"Garbage after item \"xxx\": \"1\""
+    # Abbreviated value followed by garbage.
+    gdb_test "$set_cmd xx 1" \
+	"Garbage after item \"xx\": \"1\""
+
+    # Various valid values.  Test both full value names and
+    # abbreviations.
+    gdb_test_no_output "$set_cmd x"
+    show_setting "$show_cmd" "xxx"
+    gdb_test_no_output "$set_cmd yy"
+    show_setting "$show_cmd" "yyy"
+    gdb_test_no_output "$set_cmd zzz"
+    show_setting "$show_cmd" "zzz"
+
+    test_gdb_complete_multiple "$set_cmd " "" "" {
+	"xxx"
+	"yyy"
+	"zzz"
+    }
+
+    test_gdb_complete_unique \
+	"$set_cmd x" \
+	"$set_cmd xxx"
+
+    test_gdb_complete_none "$set_cmd a"
+
+    # Check that the show command doesn't complete anything.
+    test_gdb_complete_unique \
+	"$show_cmd" \
+	"$show_cmd"
+    test_gdb_complete_none "$show_cmd "
+}
+
+# string settings tests.
+proc test-string {variant} {
+    global gdb_prompt
+    global srcfile binfile
+
+    # Load symbols for the completion test below.
+    clean_restart $binfile
+
+    # Use these variables to make sure we don't call the wrong command
+    # by mistake.
+    set set_cmd "maint test-settings set $variant"
+    set show_cmd "maint test-settings show $variant"
+
+    # Empty string.  Also checks that gdb doesn't crash if we haven't
+    # set the string yet.
+    gdb_test "$show_cmd" "^$show_cmd\r\n" "$show_cmd: empty first time"
+
+    # A string value.
+    gdb_test_no_output "$set_cmd hello world"
+    show_setting "$show_cmd" "hello world"
+
+    # A quoted string value.
+    if {$variant == "string"} {
+	gdb_test_no_output "$set_cmd \"hello world\""
+	show_setting "$show_cmd" "\\\\\"hello world\\\\\""
+    } else {
+	gdb_test_no_output "$set_cmd \"hello world\""
+	show_setting "$show_cmd" "\"hello world\""
+    }
+
+    # Test clearing the string.
+    with_test_prefix "clear string" {
+	if {$variant == "filename"} {
+	    gdb_test "$set_cmd" \
+		"Argument required \\(filename to set it to\\.\\)\\."
+
+	    # Check the value didn't change.
+	    show_setting "$show_cmd" "\"hello world\""
+	} else {
+	    gdb_test_no_output "$set_cmd"
+	    gdb_test "$show_cmd" \
+		"^$show_cmd\r\n" "$show_cmd: empty second time"
+	}
+    }
+
+    # Test completion.
+    if {$variant == "string" || $variant == "string-noescape" } {
+	# Make sure GDB doesn't try to complete on symbols, which
+	# doesn't make any sense.
+	test_gdb_complete_none "$set_cmd "
+    } else {
+	# Complete on filename.
+
+	# See comments in gdb.base/completion.exp.
+
+	# We `cd' to ${srcdir}, and then do the completion relative to
+	# the current directory.
+
+	# ${srcdir} may be a relative path.  We want to make sure we
+	# end up in the right directory - so make sure we know where
+	# it is.
+	global srcdir
+	set mydir [pwd]
+	cd ${srcdir}
+	set fullsrcdir [pwd]
+	cd ${mydir}
+
+	gdb_test "cd ${fullsrcdir}" \
+	    "Working directory [string_to_regexp ${fullsrcdir}].*" \
+	    "cd to \${srcdir}"
+
+	set unique_file ../testsuite/gdb.base/comp-dir/subdir/dummy
+
+	test_gdb_complete_unique \
+	    "$set_cmd ${unique_file}" \
+	    "$set_cmd ${unique_file}.txt"
+
+	test_gdb_complete_none "$set_cmd ${unique_file}.abc"
+    }
+
+    # Check show command completion.
+    if {$variant == "string"} {
+	test_gdb_complete_multiple "maint test-settings show " "string" "" {
+	    "string"
+	    "string-noescape"
+	}
+    } else {
+	test_gdb_complete_unique \
+	    "$show_cmd" \
+	    "$show_cmd"
+    }
+    test_gdb_complete_none "$show_cmd "
+}
+
+foreach variant {
+    uinteger
+    integer
+    zinteger
+    zuinteger
+    zuinteger-unlimited
+} {
+    with_test_prefix "test-integer $variant" {
+	test-integer $variant
+    }
+}
+
+test-boolean
+test-auto-boolean
+test-enum
+
+foreach variant {
+    string
+    string-noescape
+    optional-filename
+    filename
+} {
+    with_test_prefix "test-string $variant" {
+	test-string $variant
+    }
+}
-- 
2.14.5

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

* [PATCH v2 23/24] Delete parse_flags/parse_flags_qcs
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (18 preceding siblings ...)
  2019-05-30 20:01 ` [PATCH v2 14/24] Migrate rest of compile commands to new options framework Pedro Alves
@ 2019-05-30 20:02 ` Pedro Alves
  2019-05-30 20:02 ` [PATCH v2 09/24] New set/show testing framework (gdb.base/settings.exp) Pedro Alves
                   ` (4 subsequent siblings)
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 20:02 UTC (permalink / raw)
  To: gdb-patches

Now that "thread/frame apply" have been converted to the gdb::option
framework, these functions are no longer used.

For a while, I thought about keeping the unit tests, by making a local
version of parse_flags_qcs in the unit tests file.  But all that would
really test that is used by GDB itself, is the validate_flags_qcs
function.  So in the end, I went through all the unit tests, and
converted any that wasn't already covered to gdb.base/options.exp
tests.  And those have all already been added in previous patches.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* cli/cli-utils.c (parse_flags, parse_flags_qcs): Delete.
	* cli/cli-utils.h (parse_flags, parse_flags_qcs): Delete.
	* unittests/cli-utils-selftests.c (test_parse_flags)
	(test_parse_flags_qcs): Delete.
	(test_cli_utils): Don't call deleted functions.
---
 gdb/cli/cli-utils.c                 |  56 ---------------
 gdb/cli/cli-utils.h                 |  30 --------
 gdb/unittests/cli-utils-selftests.c | 133 ------------------------------------
 3 files changed, 219 deletions(-)

diff --git a/gdb/cli/cli-utils.c b/gdb/cli/cli-utils.c
index 30d4091a0d1..f5d47aeffbc 100644
--- a/gdb/cli/cli-utils.c
+++ b/gdb/cli/cli-utils.c
@@ -524,62 +524,6 @@ check_for_argument (const char **str, const char *arg, int arg_len)
 
 /* See documentation in cli-utils.h.  */
 
-int
-parse_flags (const char **str, const char *flags)
-{
-  const char *p = skip_spaces (*str);
-
-  if (p[0] == '-'
-      && isalpha (p[1])
-      && (p[2] == '\0' || isspace (p[2])))
-    {
-      const char pf = p[1];
-      const char *f = flags;
-
-      while (*f != '\0')
-	{
-	  if (*f == pf)
-	    {
-	      *str = skip_spaces (p + 2);
-	      return f - flags + 1;
-	    }
-	  f++;
-	}
-    }
-
-  return 0;
-}
-
-/* See documentation in cli-utils.h.  */
-
-bool
-parse_flags_qcs (const char *which_command, const char **str,
-		 qcs_flags *flags)
-{
-  switch (parse_flags (str, "qcs"))
-    {
-    case 0:
-      return false;
-    case 1:
-      flags->quiet = true;
-      break;
-    case 2:
-      flags->cont = true;
-      break;
-    case 3:
-      flags->silent = true;
-      break;
-    default:
-      gdb_assert_not_reached ("int qcs flag out of bound");
-    }
-
-  validate_flags_qcs (which_command, flags);
-
-  return true;
-}
-
-/* See documentation in cli-utils.h.  */
-
 void
 validate_flags_qcs (const char *which_command, qcs_flags *flags)
 {
diff --git a/gdb/cli/cli-utils.h b/gdb/cli/cli-utils.h
index e6b877d5ab7..c2a0f374a6e 100644
--- a/gdb/cli/cli-utils.h
+++ b/gdb/cli/cli-utils.h
@@ -220,15 +220,6 @@ check_for_argument (char **str, const char *arg)
   return check_for_argument (str, arg, strlen (arg));
 }
 
-/* A helper function that looks for a set of flags at the start of a
-   string.  The possible flags are given as a null terminated string.
-   A flag in STR must either be at the end of the string,
-   or be followed by whitespace.
-   Returns 0 if no valid flag is found at the start of STR.
-   Otherwise updates *STR, and returns N (which is > 0),
-   such that FLAGS [N - 1] is the valid found flag.  */
-extern int parse_flags (const char **str, const char *flags);
-
 /* qcs_flags struct groups the -q, -c, and -s flags parsed by "thread
    apply" and "frame apply" commands */
 
@@ -239,27 +230,6 @@ struct qcs_flags
   int silent = false;
 };
 
-/* A helper function that uses parse_flags to handle the flags qcs :
-     A flag -q sets FLAGS->QUIET to true.
-     A flag -c sets FLAGS->CONT to true.
-     A flag -s sets FLAGS->SILENT to true.
-
-   The caller is responsible to initialize *FLAGS to false before the (first)
-   call to parse_flags_qcs.
-   parse_flags_qcs can then be called iteratively to search for more
-   valid flags, as part of a 'main parsing loop' searching for -q/-c/-s
-   flags together with other flags and options.
-
-   Returns true and updates *STR and one of FLAGS->QUIET, FLAGS->CONT,
-   FLAGS->SILENT if it finds a valid flag.
-   Returns false if no valid flag is found at the beginning of STR.
-
-   Throws an error if a flag is found such that both FLAGS->CONT and
-   FLAGS->SILENT are true.  */
-
-extern bool parse_flags_qcs (const char *which_command, const char **str,
-			     qcs_flags *flags);
-
 /* Validate FLAGS.  Throws an error if both FLAGS->CONT and
    FLAGS->SILENT are true.  WHICH_COMMAND is included in the error
    message.  */
diff --git a/gdb/unittests/cli-utils-selftests.c b/gdb/unittests/cli-utils-selftests.c
index 99b98bf8cd1..a251a8e58f8 100644
--- a/gdb/unittests/cli-utils-selftests.c
+++ b/gdb/unittests/cli-utils-selftests.c
@@ -101,143 +101,10 @@ test_number_or_range_parser ()
   }
 }
 
-static void
-test_parse_flags ()
-{
-  const char *flags = "abc";
-  const char *non_flags_args = "non flags args";
-
-  /* Extract twice the same flag, separated by one space.  */
-  {
-    const char *t1 = "-a -a non flags args";
-
-    SELF_CHECK (parse_flags (&t1, flags) == 1);
-    SELF_CHECK (parse_flags (&t1, flags) == 1);
-    SELF_CHECK (strcmp (t1, non_flags_args) == 0);
-  }
-
-  /* Extract some flags, separated by one or more spaces.  */
-  {
-    const char *t2 = "-c     -b -c  -b -c    non flags args";
-
-    SELF_CHECK (parse_flags (&t2, flags) == 3);
-    SELF_CHECK (parse_flags (&t2, flags) == 2);
-    SELF_CHECK (parse_flags (&t2, flags) == 3);
-    SELF_CHECK (parse_flags (&t2, flags) == 2);
-    SELF_CHECK (parse_flags (&t2, flags) == 3);
-    SELF_CHECK (strcmp (t2, non_flags_args) == 0);
-  }
-
-  /* Check behaviour where there is no flag to extract.  */
-  {
-    const char *t3 = non_flags_args;
-
-    SELF_CHECK (parse_flags (&t3, flags) == 0);
-    SELF_CHECK (strcmp (t3, non_flags_args) == 0);
-  }
-
-  /* Extract 2 known flags in front of unknown flags.  */
-  {
-    const char *t4 = "-c -b -x -y -z -c";
-
-    SELF_CHECK (parse_flags (&t4, flags) == 3);
-    SELF_CHECK (parse_flags (&t4, flags) == 2);
-    SELF_CHECK (strcmp (t4, "-x -y -z -c") == 0);
-    SELF_CHECK (parse_flags (&t4, flags) == 0);
-    SELF_CHECK (strcmp (t4, "-x -y -z -c") == 0);
-  }
-
-  /* Check combined flags are not recognised.  */
-  {
-    const char *t5 = "-c -cb -c";
-
-    SELF_CHECK (parse_flags (&t5, flags) == 3);
-    SELF_CHECK (parse_flags (&t5, flags) == 0);
-    SELF_CHECK (strcmp (t5, "-cb -c") == 0);
-  }
-}
-
-static void
-test_parse_flags_qcs ()
-{
-  const char *non_flags_args = "non flags args";
-
-  /* Test parsing of 2 flags out of the known 3.  */
-  {
-    const char *t1 = "-q -s    non flags args";
-    qcs_flags flags;
-
-    SELF_CHECK (parse_flags_qcs ("test_parse_flags_qcs.t1.q",
-				 &t1,
-				 &flags) == 1);
-    SELF_CHECK (flags.quiet && !flags.cont && !flags.silent);
-    SELF_CHECK (parse_flags_qcs ("test_parse_flags_qcs.t1.s",
-				 &t1,
-				 &flags) == 1);
-    SELF_CHECK (flags.quiet && !flags.cont && flags.silent);
-    SELF_CHECK (strcmp (t1, non_flags_args) == 0);
-  }
-
-  /* Test parsing when there is no flag.  */
-  {
-    const char *t2 = "non flags args";
-    qcs_flags flags;
-
-    SELF_CHECK (parse_flags_qcs ("test_parse_flags_qcs.t2",
-				 &t2,
-				 &flags) == 0);
-    SELF_CHECK (!flags.quiet && !flags.cont && !flags.silent);
-    SELF_CHECK (strcmp (t2, non_flags_args) == 0);
-  }
-
-  /* Test parsing stops at a negative integer.  */
-  {
-    const char *t3 = "-123 non flags args";
-    const char *orig_t3 = t3;
-    qcs_flags flags;
-
-    SELF_CHECK (parse_flags_qcs ("test_parse_flags_qcs.t3",
-				 &t3,
-				 &flags) == 0);
-    SELF_CHECK (!flags.quiet && !flags.cont && !flags.silent);
-    SELF_CHECK (strcmp (t3, orig_t3) == 0);
-  }
-
-  /* Test mutual exclusion between -c and -s.  */
-  {
-    const char *t4 = "-c -s non flags args";
-    qcs_flags flags;
-
-    try
-      {
-	SELF_CHECK (parse_flags_qcs ("test_parse_flags_qcs.t4.cs",
-				     &t4,
-				     &flags) == 1);
-
-	(void) parse_flags_qcs ("test_parse_flags_qcs.t4.cs",
-				&t4,
-				&flags);
-	SELF_CHECK (false);
-      }
-    catch (const gdb_exception_error &ex)
-      {
-	SELF_CHECK (ex.reason == RETURN_ERROR);
-	SELF_CHECK (ex.error == GENERIC_ERROR);
-	SELF_CHECK
-	  (strcmp (ex.what (),
-		   "test_parse_flags_qcs.t4.cs: "
-		   "-c and -s are mutually exclusive") == 0);
-      }
-  }
-
-}
-
 static void
 test_cli_utils ()
 {
   selftests::cli_utils::test_number_or_range_parser ();
-  selftests::cli_utils::test_parse_flags ();
-  selftests::cli_utils::test_parse_flags_qcs ();
 }
 
 }
-- 
2.14.5

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

* [PATCH v2 18/24] lib/completion-support.exp: Add test_gdb_completion_offers_commands
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (22 preceding siblings ...)
  2019-05-30 20:03 ` [PATCH v2 17/24] "backtrace full/no-filters/hide" completer Pedro Alves
@ 2019-05-30 20:03 ` Pedro Alves
  2019-06-03 19:33 ` [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Tom Tromey
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 20:03 UTC (permalink / raw)
  To: gdb-patches

This adds a procedure to the collection of completion-testing
routines, that allows checking whether completion offers all commands
as completion candidates.  This will be used for testing completing
"frame apply all [TAB]", "thread apply all [TAB]", etc.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

        * lib/completion-support.exp (test_gdb_complete_tab_multiple)
	(test_gdb_complete_cmd_multiple, test_gdb_complete_multiple): Add
	'max_completions' parameter and handle it.
	(test_gdb_completion_offers_commands): New.
---
 gdb/testsuite/lib/completion-support.exp | 66 +++++++++++++++++++++++++++-----
 1 file changed, 57 insertions(+), 9 deletions(-)

diff --git a/gdb/testsuite/lib/completion-support.exp b/gdb/testsuite/lib/completion-support.exp
index 3e498d3c631..97fed18b055 100644
--- a/gdb/testsuite/lib/completion-support.exp
+++ b/gdb/testsuite/lib/completion-support.exp
@@ -119,10 +119,11 @@ proc test_gdb_complete_tab_unique { input_line complete_line_re append_char_re }
 
 # Test that completing INPUT_LINE with TAB completes to "INPUT_LINE +
 # ADD_COMPLETED_LINE" and that it displays the completion matches in
-# COMPLETION_LIST.
+# COMPLETION_LIST.  If MAX_COMPLETIONS then we expect the completion
+# to hit the max-completions limit.
 
 proc test_gdb_complete_tab_multiple { input_line add_completed_line \
-					  completion_list } {
+					  completion_list {max_completions 0}} {
     global gdb_prompt
 
     set input_line_re [string_to_regexp $input_line]
@@ -130,6 +131,12 @@ proc test_gdb_complete_tab_multiple { input_line add_completed_line \
 
     set expected_re [make_tab_completion_list_re $completion_list]
 
+    if {$max_completions} {
+	append expected_re "\r\n"
+	append expected_re \
+	    "\\*\\*\\* List may be truncated, max-completions reached\\. \\*\\*\\*"
+    }
+
     set test "tab complete \"$input_line\""
     send_gdb "$input_line\t"
     gdb_test_multiple "" "$test (first tab)" {
@@ -179,12 +186,20 @@ proc test_gdb_complete_cmd_unique { input_line complete_line_re } {
 
 # Test that completing "CMD_PREFIX + COMPLETION_WORD" with the
 # complete command displays the COMPLETION_LIST completion list.  Each
-# entry in the list should be prefixed by CMD_PREFIX.
+# entry in the list should be prefixed by CMD_PREFIX.  If
+# MAX_COMPLETIONS then we expect the completion to hit the
+# max-completions limit.
 
-proc test_gdb_complete_cmd_multiple { cmd_prefix completion_word completion_list {start_quote_char ""} {end_quote_char ""} } {
+proc test_gdb_complete_cmd_multiple { cmd_prefix completion_word completion_list {start_quote_char ""} {end_quote_char ""} {max_completions 0}} {
     global gdb_prompt
 
     set expected_re [make_cmd_completion_list_re $cmd_prefix $completion_list $start_quote_char $end_quote_char]
+
+    if {$max_completions} {
+	append expected_re \
+	    "$cmd_prefix \\*\\*\\* List may be truncated, max-completions reached\\. \\*\\*\\*.*\r\n"
+    }
+
     set cmd_re [string_to_regexp "complete $cmd_prefix$completion_word"]
     set test "cmd complete \"$cmd_prefix$completion_word\""
     gdb_test_multiple "complete $cmd_prefix$completion_word" $test {
@@ -255,11 +270,15 @@ proc test_gdb_complete_unique { input_line complete_line {append_char " "} {max_
 # Test that completing "CMD_PREFIX + COMPLETION_WORD" adds
 # ADD_COMPLETED_LINE to the input line, and that it displays
 # COMPLETION_LIST as completion match list.  COMPLETION_WORD is the
-# completion word.
-
-proc test_gdb_complete_multiple { cmd_prefix completion_word add_completed_line completion_list {start_quote_char ""} {end_quote_char ""}} {
-    test_gdb_complete_tab_multiple "$cmd_prefix$completion_word" $add_completed_line $completion_list
-    test_gdb_complete_cmd_multiple $cmd_prefix $completion_word $completion_list $start_quote_char $end_quote_char
+# completion word.  If MAX_COMPLETIONS then we expect the completion
+# to hit the max-completions limit.
+
+proc test_gdb_complete_multiple {
+  cmd_prefix completion_word add_completed_line completion_list
+  {start_quote_char ""} {end_quote_char ""} {max_completions 0}
+} {
+    test_gdb_complete_tab_multiple "$cmd_prefix$completion_word" $add_completed_line $completion_list $max_completions
+    test_gdb_complete_cmd_multiple $cmd_prefix $completion_word $completion_list $start_quote_char $end_quote_char $max_completions
 }
 
 # Test that all the substring prefixes of INPUT from [0..START) to
@@ -481,3 +500,32 @@ proc foreach_location_labels { sources functions labels body_linespec body_expli
 	    }
 	}
 }
+
+# Check that completion of INPUT_LINE results in GDB completing on all
+# command names.
+proc test_gdb_completion_offers_commands {input_line} {
+    global gdb_prompt
+
+    # There are two many commands to usefully check here.  So we force
+    # max-completions to 2, and check if those 2 come out.
+
+    # Save current max-completions.
+    set max_completions 0
+    set test "show max-completions"
+    gdb_test_multiple $test $test {
+	-re "Maximum number of completion candidates is (.*)\\.\r\n$gdb_prompt $" {
+	    set max_completions $expect_out(1,string)
+	}
+    }
+
+    # Force showing two commands.
+    gdb_test_no_output "set max-completions 2" ""
+
+    test_gdb_complete_multiple $input_line "" "" {
+	"!"
+	"+"
+    } "" "" 1
+
+    # Restore.
+    gdb_test_no_output "set max-completions $max_completions" ""
+}
-- 
2.14.5

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

* [PATCH v2 17/24] "backtrace full/no-filters/hide" completer
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (21 preceding siblings ...)
  2019-05-30 20:03 ` [PATCH v2 04/24] Make check_for_argument skip whitespace after arg itself Pedro Alves
@ 2019-05-30 20:03 ` Pedro Alves
  2019-05-30 20:03 ` [PATCH v2 18/24] lib/completion-support.exp: Add test_gdb_completion_offers_commands Pedro Alves
  2019-06-03 19:33 ` [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Tom Tromey
  24 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 20:03 UTC (permalink / raw)
  To: gdb-patches

"backtrace"'s completer now completes on command options:

 (gdb) bt -[TAB]
 -entry-values         -full                 -no-filters           -past-main
 -frame-arguments      -hide                 -past-entry           -raw-frame-arguments

But it doesn't know how to complete on qualifiers:

 (gdb) bt fu[TAB]
 funlockfile       futimens          futimes.c
 funlockfile.c     futimens.c        futimesat
 futex-internal.h  futimes           futimesat.c

This commit fixes that:

 (gdb) bt fu[TAB]ll
 (gdb) bt n[TAB]o-filters
 (gdb) bt h[TAB]ide

I considered teaching the gdb::option framework to handle non-'-'
options, but decided it wasn't worth it for this special case, and I'd
rather not make it easy to add new qualifier-like options.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* stack.c (parse_backtrace_qualifiers): New.
	(backtrace_command): Use it.
	(backtrace_command_completer): Complete on qualifiers.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/options.exp (test-backtrace): Test completing qualifiers.
---
 gdb/stack.c                        | 84 ++++++++++++++++++++++++++++----------
 gdb/testsuite/gdb.base/options.exp | 11 +++++
 2 files changed, 73 insertions(+), 22 deletions(-)

diff --git a/gdb/stack.c b/gdb/stack.c
index 5e878d3c887..1cb1fc9c38c 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -2027,6 +2027,45 @@ make_backtrace_options_def_group (frame_print_options *fp_opts,
   }};
 }
 
+/* Parse the backtrace command's qualifiers.  Returns ARG advanced
+   past the qualifiers, if any.  BT_CMD_OPTS, if not null, is used to
+   store the parsed qualifiers.  */
+
+static const char *
+parse_backtrace_qualifiers (const char *arg,
+			    backtrace_cmd_options *bt_cmd_opts = nullptr)
+{
+  while (true)
+    {
+      const char *save_arg = arg;
+      std::string this_arg = extract_arg (&arg);
+
+      if (this_arg.empty ())
+	return arg;
+
+      if (subset_compare (this_arg.c_str (), "no-filters"))
+	{
+	  if (bt_cmd_opts != nullptr)
+	    bt_cmd_opts->no_filters = true;
+	}
+      else if (subset_compare (this_arg.c_str (), "full"))
+	{
+	  if (bt_cmd_opts != nullptr)
+	    bt_cmd_opts->full = true;
+	}
+      else if (subset_compare (this_arg.c_str (), "hide"))
+	{
+	  if (bt_cmd_opts != nullptr)
+	    bt_cmd_opts->hide = true;
+	}
+      else
+	{
+	  /* Not a recognized qualifier, so stop.  */
+	  return save_arg;
+	}
+    }
+}
+
 static void
 backtrace_command (const char *arg, int from_tty)
 {
@@ -2043,28 +2082,7 @@ backtrace_command (const char *arg, int from_tty)
      compatibility.  */
   if (arg != NULL)
     {
-      while (true)
-	{
-	  const char *save_arg = arg;
-	  std::string this_arg = extract_arg (&arg);
-
-	  if (this_arg.empty ())
-	    break;
-
-	  if (subset_compare (this_arg.c_str (), "no-filters"))
-	    bt_cmd_opts.no_filters = true;
-	  else if (subset_compare (this_arg.c_str (), "full"))
-	    bt_cmd_opts.full = true;
-	  else if (subset_compare (this_arg.c_str (), "hide"))
-	    bt_cmd_opts.hide = true;
-	  else
-	    {
-	      /* Not a recognized argument, so stop.  */
-	      arg = save_arg;
-	      break;
-	    }
-	}
-
+      arg = parse_backtrace_qualifiers (arg, &bt_cmd_opts);
       if (*arg == '\0')
 	arg = NULL;
     }
@@ -2090,6 +2108,28 @@ backtrace_command_completer (struct cmd_list_element *ignore,
       (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
     return;
 
+  if (*text != '\0')
+    {
+      const char *p = skip_to_space (text);
+      if (*p == '\0')
+	{
+	  static const char *const backtrace_cmd_qualifier_choices[] = {
+	    "full", "no-filters", "hide", nullptr,
+	  };
+	  complete_on_enum (tracker, backtrace_cmd_qualifier_choices,
+			    text, text);
+
+	  if (tracker.have_completions ())
+	    return;
+	}
+      else
+	{
+	  const char *cmd = parse_backtrace_qualifiers (text);
+	  tracker.advance_custom_word_point_by (cmd - text);
+	  text = cmd;
+	}
+    }
+
   const char *word = advance_to_expression_complete_word_point (tracker, text);
   expression_completer (ignore, tracker, text, word);
 }
diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp
index 17573460b4d..5a35074054e 100644
--- a/gdb/testsuite/gdb.base/options.exp
+++ b/gdb/testsuite/gdb.base/options.exp
@@ -256,6 +256,17 @@ proc_with_prefix test-backtrace {} {
 	"-raw-frame-arguments"
     }
 
+    # Test that we complete the qualifiers, if there's any.
+    test_gdb_complete_unique \
+	"backtrace ful" \
+	"backtrace full"
+    test_gdb_complete_unique \
+	"backtrace hid" \
+	"backtrace hide"
+    test_gdb_complete_unique \
+	"backtrace no-fil" \
+	"backtrace no-filters"
+
     global binfile
     clean_restart $binfile
 
-- 
2.14.5

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

* [PATCH v2 04/24] Make check_for_argument skip whitespace after arg itself
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (20 preceding siblings ...)
  2019-05-30 20:02 ` [PATCH v2 09/24] New set/show testing framework (gdb.base/settings.exp) Pedro Alves
@ 2019-05-30 20:03 ` Pedro Alves
  2019-06-03 18:49   ` Tom Tromey
  2019-05-30 20:03 ` [PATCH v2 17/24] "backtrace full/no-filters/hide" completer Pedro Alves
                   ` (2 subsequent siblings)
  24 siblings, 1 reply; 43+ messages in thread
From: Pedro Alves @ 2019-05-30 20:03 UTC (permalink / raw)
  To: gdb-patches

Basically every caller of check_for_argument needs to skip space after
the argument.  This patch makes check_for_argument do it itself.

Suggested by Philippe Waroquiers

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves <palves@redhat.com>

	* ax-gdb.c (agent_command_1): Remove skip_spaces call.
	* breakpoint.c (watch_maybe_just_location): Remove skip_spaces
	call.
	* cli/cli-utils.c (extract_info_print_args): Remove skip_spaces
	calls.
	(check_for_argument): Skip spaces after argument.
---
 gdb/ax-gdb.c        | 2 --
 gdb/breakpoint.c    | 5 +----
 gdb/cli/cli-utils.c | 3 +--
 gdb/cli/cli-utils.h | 4 +++-
 4 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c
index 778e89515b6..5872bec4777 100644
--- a/gdb/ax-gdb.c
+++ b/gdb/ax-gdb.c
@@ -2634,8 +2634,6 @@ agent_command_1 (const char *exp, int eval)
     {
       struct linespec_result canonical;
 
-      exp = skip_spaces (exp);
-
       event_location_up location
 	= new_linespec_location (&exp, symbol_name_match_type::WILD);
       decode_line_full (location.get (), DECODE_LINE_FUNFIRSTLINE, NULL,
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 054bb1b13da..b85f7eedac8 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -10899,10 +10899,7 @@ watch_maybe_just_location (const char *arg, int accessflag, int from_tty)
   if (arg
       && (check_for_argument (&arg, "-location", sizeof ("-location") - 1)
 	  || check_for_argument (&arg, "-l", sizeof ("-l") - 1)))
-    {
-      arg = skip_spaces (arg);
-      just_location = 1;
-    }
+    just_location = 1;
 
   watch_command_1 (arg, accessflag, from_tty, just_location, 0);
 }
diff --git a/gdb/cli/cli-utils.c b/gdb/cli/cli-utils.c
index c8b07f0b6bc..a24fe9278c7 100644
--- a/gdb/cli/cli-utils.c
+++ b/gdb/cli/cli-utils.c
@@ -139,7 +139,6 @@ extract_info_print_args (const char **args,
   /* Check for NAMEREGEXP or -- NAMEREGEXP.  */
   if (**args != '-' || check_for_argument (args, "--", 2))
     {
-      *args = skip_spaces (*args);
       *regexp = *args;
       *args = NULL;
       return true;
@@ -155,7 +154,6 @@ extract_info_print_args (const char **args,
   if (check_for_argument (args, "-q", 2))
     {
       *quiet = true;
-      *args = skip_spaces (*args);
       return true;
     }
 
@@ -459,6 +457,7 @@ check_for_argument (const char **str, const char *arg, int arg_len)
       && ((*str)[arg_len] == '\0' || isspace ((*str)[arg_len])))
     {
       *str += arg_len;
+      *str = skip_spaces (*str);
       return 1;
     }
   return 0;
diff --git a/gdb/cli/cli-utils.h b/gdb/cli/cli-utils.h
index d0161db0916..9425fb4c09d 100644
--- a/gdb/cli/cli-utils.h
+++ b/gdb/cli/cli-utils.h
@@ -188,7 +188,9 @@ extern std::string extract_arg (const char **arg);
 /* A helper function that looks for an argument at the start of a
    string.  The argument must also either be at the end of the string,
    or be followed by whitespace.  Returns 1 if it finds the argument,
-   0 otherwise.  If the argument is found, it updates *STR.  */
+   0 otherwise.  If the argument is found, it updates *STR to point
+   past the argument and past any whitespace following the
+   argument.  */
 extern int check_for_argument (const char **str, const char *arg, int arg_len);
 
 /* Same, for non-const STR.  */
-- 
2.14.5

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

* Re: [PATCH v2 15/24] "set print raw frame-arguments" -> "set print raw-frame-arguments"
  2019-05-30 19:53 ` [PATCH v2 15/24] "set print raw frame-arguments" -> "set print raw-frame-arguments" Pedro Alves
@ 2019-05-31  5:48   ` Eli Zaretskii
  0 siblings, 0 replies; 43+ messages in thread
From: Eli Zaretskii @ 2019-05-31  5:48 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

> From: Pedro Alves <palves@redhat.com>
> Date: Thu, 30 May 2019 20:53:24 +0100
> 
> gdb/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* NEWS (New commands): Mention set/show print raw-frame-arguments,
> 	and that "set/show print raw frame-arguments" are now deprecated.
> 
> 	* cli/cli-decode.c (add_setshow_boolean_cmd): Now returns the
> 	command.
> 	* command.h (add_setshow_boolean_cmd): Return cmd_list_element *.
> 	* stack.c (_initialize_stack): Install "set/show print
> 	raw-frame-arguments", and deprecate "set/show print raw
> 	frame-arguments".
> 	* valprint.c (_initialize_valprint): Deprecate "set/show print
> 	raw".
> 
> gdb/doc/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* gdb.texinfo (Print Settings): Document "set/show print
> 	raw-frame-arguments" instead of "set/show print raw
> 	frame-arguments".
> 
> gdb/testsuite/ChangeLog:
> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> 	* gdb.guile/scm-frame-args.exp: Use "set print
> 	raw-frame-arguments" instead of "set print raw frame-arguments".
> 	* gdb.python/py-frame-args.exp: Likewise.

The documentation parts LGTM, thanks.

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

* Re: [PATCH v2 04/24] Make check_for_argument skip whitespace after arg itself
  2019-05-30 20:03 ` [PATCH v2 04/24] Make check_for_argument skip whitespace after arg itself Pedro Alves
@ 2019-06-03 18:49   ` Tom Tromey
  2019-06-04 22:21     ` Pedro Alves
  0 siblings, 1 reply; 43+ messages in thread
From: Tom Tromey @ 2019-06-03 18:49 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

Pedro> Basically every caller of check_for_argument needs to skip space after
Pedro> the argument.  This patch makes check_for_argument do it itself.

Pedro> Suggested by Philippe Waroquiers

Philippe's recent help-styling series added a new instance of this, so
whoever lands their series last ought to be sure to go fix that up.

thanks,
Tom

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

* Re: [PATCH v2 06/24] Fix "set enum-command value garbage"
  2019-05-30 19:53 ` [PATCH v2 06/24] Fix "set enum-command value garbage" Pedro Alves
@ 2019-06-03 18:51   ` Tom Tromey
  2019-06-04 21:52     ` Pedro Alves
  0 siblings, 1 reply; 43+ messages in thread
From: Tom Tromey @ 2019-06-03 18:51 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

Pedro>  (gdb) set print entry-values compact foo
Pedro>   Garbage after item "compact": "foo"

In the crucial junk-vs-garbage wording war, junk is winning 9 to 2, so
perhaps its dominance should be continued.

I'm not totally sold on either of these phrasings but at the same time I
don't recall anybody ever complaining about them.

Tom

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

* Re: [PATCH v2 07/24] Remove "show" command completers, "set" command completers for string commands
  2019-05-30 19:54 ` [PATCH v2 07/24] Remove "show" command completers, "set" command completers for string commands Pedro Alves
@ 2019-06-03 18:55   ` Tom Tromey
  2019-06-04 21:54     ` Pedro Alves
  0 siblings, 1 reply; 43+ messages in thread
From: Tom Tromey @ 2019-06-03 18:55 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

Pedro> I wonder whether we should instead make the default be no completer.

I think so.  The current default seems wrong in many situations, and
expensive to invoke besides.

One thing bash does is bind certain keys to specific completers, so for
example M-! will complete on command names, no matter the context.
Perhaps gdb could provide something like this as well, for the odd case
where you really want to complete on a symbol- or file-name in an
unusual context.

I'm ok with this patch in the interim.

Tom

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

* Re: [PATCH v2 13/24] Make "print" and "compile print" support -OPT options
  2019-05-30 19:54 ` [PATCH v2 13/24] Make "print" and "compile print" support -OPT options Pedro Alves
@ 2019-06-03 19:20   ` Tom Tromey
  2019-06-04 21:59     ` Pedro Alves
  0 siblings, 1 reply; 43+ messages in thread
From: Tom Tromey @ 2019-06-03 19:20 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

Pedro> This patch adds support for "print -option optval --", etc.
Pedro> Likewise for "compile print".

Pedro> We'll get:

Pedro> (gdb) help print
Pedro> Print value of expression EXP.
Pedro> Usage: print [[OPTION]... --] [/FMT] [EXP]

Pedro> Options:
Pedro>   -address [on|off]
Pedro>     Set printing of addresses.

Pedro>   -array [on|off]
Pedro>     Set pretty formatting of arrays.
[...]
Pedro> -null-stop [on|off]
Pedro>     Set printing of char arrays to stop at first null char.

I assume -null-stop is out-dented due to a cut-and-paste error with the
email, but I wanted to double-check that there isn't a bug here.


Pedro> I want to highlight the comment above about "--".
[...]

I think this approach makes sense, and it addresses the one thing that
made me hesitate about this idea.

Tom

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

* Re: [PATCH v2 12/24] Introduce generic command options framework
  2019-05-30 19:54 ` [PATCH v2 12/24] Introduce generic command options framework Pedro Alves
@ 2019-06-03 19:25   ` Tom Tromey
  2019-06-04 18:46     ` [PATCH] Introduce and use make_unique_xstrdup (was: Re: [PATCH v2 12/24] Introduce generic command options framework) Pedro Alves
  2019-06-04 21:37     ` [PATCH v2 12/24] Introduce generic command options framework Pedro Alves
  0 siblings, 2 replies; 43+ messages in thread
From: Tom Tromey @ 2019-06-03 19:25 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

Pedro> This commit adds a generic command options framework, that makes it
Pedro> easy enough to add '-'-style options to commands in a uniform way,
Pedro> instead of each command implementing option parsing in its own way.

This all seems reasonable to me.  Thanks for taking this on.

Pedro> +/* Dup STR and return a unique_xmalloc_ptr for the result.  */
Pedro> +
Pedro> +static gdb::unique_xmalloc_ptr<char>
Pedro> +make_unique_xstrdup (const char *str)
Pedro> +{
Pedro> +  return gdb::unique_xmalloc_ptr<char> (xstrdup (str));
Pedro> +}

I could 25 spots that could use this, so how about putting it into a
header somewhere?  I am happy to convert all the existing uses.

Pedro> +  if (len > 0 && strncmp ("unlimited", *arg, len) == 0)

I should have mentioned this in an earlier patch, but won't this also
match things like "unlimitedjunk"?

Tom

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

* Re: [PATCH v2 19/24] Introduce complete_command
  2019-05-30 19:59 ` [PATCH v2 19/24] Introduce complete_command Pedro Alves
@ 2019-06-03 19:28   ` Tom Tromey
  2019-06-04 22:10     ` Pedro Alves
  0 siblings, 1 reply; 43+ messages in thread
From: Tom Tromey @ 2019-06-03 19:28 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

Pedro> This adds a completion helper routine that makes it possible for a
Pedro> command that takes another command as argument, such as "frame apply
Pedro> all COMMAND" as "thread apply all COMMAND", to complete on COMMAND,
Pedro> and have the completion machinery recurse and complete COMMAND as if
Pedro> you tried to complete "(gdb) COMMAND".  I.e., we'll be able to
Pedro> complete like this, for example:

Pedro> 	* completer.c (complete_command): New.

I think that the function should have a different name.  Traditionally,
*_command is the name of the command "*" in gdb, and there's already a
function called complete_command.  Overloading makes this not matter too
much, but I also think it's best to reserve overloading for functions
that are semantically similar.

Tom

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

* Re: [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options
  2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
                   ` (23 preceding siblings ...)
  2019-05-30 20:03 ` [PATCH v2 18/24] lib/completion-support.exp: Add test_gdb_completion_offers_commands Pedro Alves
@ 2019-06-03 19:33 ` Tom Tromey
  24 siblings, 0 replies; 43+ messages in thread
From: Tom Tromey @ 2019-06-03 19:33 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

Pedro> Here's v2 of this patch series, which I believe addresses all comments
Pedro> against v1 so far.

I've read through the series and sent my comments.  It all looks good to
me, and I'm looking forward to it going in.  Thanks once again.

Tom

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

* [PATCH] Introduce and use make_unique_xstrdup (was: Re: [PATCH v2 12/24] Introduce generic command options framework)
  2019-06-03 19:25   ` Tom Tromey
@ 2019-06-04 18:46     ` Pedro Alves
  2019-06-04 18:59       ` [PATCH] Introduce and use make_unique_xstrdup Tom Tromey
  2019-06-04 21:37     ` [PATCH v2 12/24] Introduce generic command options framework Pedro Alves
  1 sibling, 1 reply; 43+ messages in thread
From: Pedro Alves @ 2019-06-04 18:46 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 6/3/19 8:16 PM, Tom Tromey wrote:

> Pedro> +/* Dup STR and return a unique_xmalloc_ptr for the result.  */
> Pedro> +
> Pedro> +static gdb::unique_xmalloc_ptr<char>
> Pedro> +make_unique_xstrdup (const char *str)
> Pedro> +{
> Pedro> +  return gdb::unique_xmalloc_ptr<char> (xstrdup (str));
> Pedro> +}
> 
> I could 25 spots that could use this, so how about putting it into a
> header somewhere?  I am happy to convert all the existing uses.

Yeah.  I added a bunch of those spots myself, and been meaning to do
this.

How about this?

From 366c62a676e47c7303622c811c646ea1b24eae31 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Tue, 4 Jun 2019 18:38:55 +0100
Subject: [PATCH] Introduce and use make_unique_xstrdup

Adds an utility function to make it shorter to write the common case
of wrapping an xstrdup with a unique_xmalloc_ptr, and uses it
throughout.

Note: I tried to put this in common/common-utils.h near skip_spaces,
etc. but that is included in common/common-defs.h before
common/gdb_unique_ptr.h is included, so it would fail to compile
because gdb::unique_xmalloc_ptr isn't defined at that point yet.  I
tried moving the gdb_unique_ptr.h inclusion before common-utils.h, but
that doesn't work because gdb_unique_ptr.h depends on common-utils.h
for xfree.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* common/gdb_unique_ptr.h (make_unique_xstrdup): New.

	* ada-lang.c (catch_ada_completer): Use make_unique_xstrdup.
	* breakpoint.c (condition_completer): Likewise.
	* cli/cli-dump.c (scan_expression): Likewise.
	* common/filestuff.c (mkdir_recursive): Likewise.
	* common/gdb_tilde_expand.c (gdb_tilde_expand_up)
	* common/pathstuff.c (gdb_realpath, gdb_realpath_keepfile)
	(gdb_abspath): Likewise.
	* compile/compile-cplus-types.c
	(compile_cplus_instance::decl_name): Likewise.
	* completer.c (complete_explicit_location):
	(signal_completer, reg_or_group_completer_1): Likewise.
	* cp-support.c (cp_remove_params_if_any): Likewise.
	* fbsd-tdep.c (fbsd_core_vnode_path): Likewise.
	* guile/scm-safe-call.c (gdbscm_safe_eval_string): Likewise.
	* infcmd.c (strip_bg_char): Likewise.
	* linespec.c (copy_token_string): Likewise.
	* mi/mi-main.c (output_cores): Likewise.
	* psymtab.c (psymtab_search_name):
	* symfile.c (test_set_ext_lang_command): Likewise.
	* target.c (target_fileio_read_stralloc): Likewise.
	* tui/tui-regs.c (tui_reggroup_completer): Likewise.
	* value.c (complete_internalvar): Likewise.

gdb/gdbserver/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* server.c (captured_main): Use make_unique_xstrdup.
---
 gdb/ada-lang.c                    |  3 +--
 gdb/breakpoint.c                  |  5 +----
 gdb/cli/cli-dump.c                |  2 +-
 gdb/common/filestuff.c            |  2 +-
 gdb/common/gdb_tilde_expand.c     |  2 +-
 gdb/common/gdb_unique_ptr.h       |  8 ++++++++
 gdb/common/pathstuff.c            |  8 ++++----
 gdb/compile/compile-cplus-types.c |  2 +-
 gdb/completer.c                   | 19 ++++---------------
 gdb/cp-support.c                  |  2 +-
 gdb/fbsd-tdep.c                   |  2 +-
 gdb/gdbserver/server.c            |  2 +-
 gdb/guile/scm-safe-call.c         |  2 +-
 gdb/infcmd.c                      |  2 +-
 gdb/linespec.c                    |  2 +-
 gdb/mi/mi-main.c                  |  2 +-
 gdb/psymtab.c                     |  2 +-
 gdb/symfile.c                     |  2 +-
 gdb/target.c                      |  2 +-
 gdb/tui/tui-regs.c                |  2 +-
 gdb/value.c                       |  6 +-----
 21 files changed, 34 insertions(+), 45 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 99c099aa07d..22c51fbf082 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -13195,8 +13195,7 @@ catch_ada_completer (struct cmd_list_element *cmd, completion_tracker &tracker,
   for (const ada_exc_info &info : exceptions)
     {
       if (startswith (info.name, word))
-	tracker.add_completion
-	  (gdb::unique_xmalloc_ptr<char> (xstrdup (info.name)));
+	tracker.add_completion (make_unique_xstrdup (info.name));
     }
 }
 
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index b85f7eedac8..08083645e1e 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -941,10 +941,7 @@ condition_completer (struct cmd_list_element *cmd,
 	  xsnprintf (number, sizeof (number), "%d", b->number);
 
 	  if (strncmp (number, text, len) == 0)
-	    {
-	      gdb::unique_xmalloc_ptr<char> copy (xstrdup (number));
-	      tracker.add_completion (std::move (copy));
-	    }
+	    tracker.add_completion (make_unique_xstrdup (number));
 	}
 
       return;
diff --git a/gdb/cli/cli-dump.c b/gdb/cli/cli-dump.c
index 8f0d8bfa8cb..621bc9373ff 100644
--- a/gdb/cli/cli-dump.c
+++ b/gdb/cli/cli-dump.c
@@ -37,7 +37,7 @@ static gdb::unique_xmalloc_ptr<char>
 scan_expression (const char **cmd, const char *def)
 {
   if ((*cmd) == NULL || (**cmd) == '\0')
-    return gdb::unique_xmalloc_ptr<char> (xstrdup (def));
+    return make_unique_xstrdup (def);
   else
     {
       char *exp;
diff --git a/gdb/common/filestuff.c b/gdb/common/filestuff.c
index 1ca62482a7e..c7b8c694055 100644
--- a/gdb/common/filestuff.c
+++ b/gdb/common/filestuff.c
@@ -462,7 +462,7 @@ is_regular_file (const char *name, int *errno_ptr)
 bool
 mkdir_recursive (const char *dir)
 {
-  gdb::unique_xmalloc_ptr<char> holder (xstrdup (dir));
+  auto holder = make_unique_xstrdup (dir);
   char * const start = holder.get ();
   char *component_start = start;
   char *component_end = start;
diff --git a/gdb/common/gdb_tilde_expand.c b/gdb/common/gdb_tilde_expand.c
index fc338ff6ecb..326df8f6aa9 100644
--- a/gdb/common/gdb_tilde_expand.c
+++ b/gdb/common/gdb_tilde_expand.c
@@ -91,5 +91,5 @@ gdb_tilde_expand_up (const char *dir)
   gdb_assert (glob.pathc () > 0);
   /* "glob" may return more than one match to the path provided by the
      user, but we are only interested in the first match.  */
-  return gdb::unique_xmalloc_ptr<char> (xstrdup (glob.pathv ()[0]));
+  return make_unique_xstrdup (glob.pathv ()[0]);
 }
diff --git a/gdb/common/gdb_unique_ptr.h b/gdb/common/gdb_unique_ptr.h
index a4be2bb7963..67a5f265353 100644
--- a/gdb/common/gdb_unique_ptr.h
+++ b/gdb/common/gdb_unique_ptr.h
@@ -56,4 +56,12 @@ struct noop_deleter
 
 } /* namespace gdb */
 
+/* Dup STR and return a unique_xmalloc_ptr for the result.  */
+
+static inline gdb::unique_xmalloc_ptr<char>
+make_unique_xstrdup (const char *str)
+{
+  return gdb::unique_xmalloc_ptr<char> (xstrdup (str));
+}
+
 #endif /* COMMON_GDB_UNIQUE_PTR_H */
diff --git a/gdb/common/pathstuff.c b/gdb/common/pathstuff.c
index 2b1669a5b99..e0e048d6654 100644
--- a/gdb/common/pathstuff.c
+++ b/gdb/common/pathstuff.c
@@ -65,7 +65,7 @@ gdb_realpath (const char *filename)
        we might not be able to display the original casing in a given
        path.  */
     if (len > 0 && len < MAX_PATH)
-      return gdb::unique_xmalloc_ptr<char> (xstrdup (buf));
+      return make_unique_xstrdup (buf);
   }
 #else
   {
@@ -77,7 +77,7 @@ gdb_realpath (const char *filename)
 #endif
 
   /* This system is a lost cause, just dup the buffer.  */
-  return gdb::unique_xmalloc_ptr<char> (xstrdup (filename));
+  return make_unique_xstrdup (filename);
 }
 
 /* See common/pathstuff.h.  */
@@ -92,7 +92,7 @@ gdb_realpath_keepfile (const char *filename)
   /* Extract the basename of filename, and return immediately
      a copy of filename if it does not contain any directory prefix.  */
   if (base_name == filename)
-    return gdb::unique_xmalloc_ptr<char> (xstrdup (filename));
+    return make_unique_xstrdup (filename);
 
   dir_name = (char *) alloca ((size_t) (base_name - filename + 2));
   /* Allocate enough space to store the dir_name + plus one extra
@@ -135,7 +135,7 @@ gdb_abspath (const char *path)
     return gdb_tilde_expand_up (path);
 
   if (IS_ABSOLUTE_PATH (path))
-    return gdb::unique_xmalloc_ptr<char> (xstrdup (path));
+    return make_unique_xstrdup (path);
 
   /* Beware the // my son, the Emacs barfs, the botch that catch...  */
   return gdb::unique_xmalloc_ptr<char>
diff --git a/gdb/compile/compile-cplus-types.c b/gdb/compile/compile-cplus-types.c
index 692393085e2..d7aef265cf8 100644
--- a/gdb/compile/compile-cplus-types.c
+++ b/gdb/compile/compile-cplus-types.c
@@ -65,7 +65,7 @@ compile_cplus_instance::decl_name (const char *natural)
   if (name != nullptr)
     return name;
 
-  return gdb::unique_xmalloc_ptr<char> (xstrdup (natural));
+  return make_unique_xstrdup (natural);
 }
 
 /* Get the access flag for the NUM'th field of TYPE.  */
diff --git a/gdb/completer.c b/gdb/completer.c
index b2768ede769..5dd9a99f2a2 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -805,9 +805,7 @@ complete_explicit_location (completion_tracker &tracker,
 		   before: "b -function 'not_loaded_function_yet()'"
 		   after:  "b -function 'not_loaded_function_yet()' "
 	      */
-	      gdb::unique_xmalloc_ptr<char> text_copy
-		(xstrdup (text));
-	      tracker.add_completion (std::move (text_copy));
+	      tracker.add_completion (make_unique_xstrdup (text));
 	    }
 	  else if (quoted_arg_end[1] == ' ')
 	    {
@@ -1733,10 +1731,7 @@ signal_completer (struct cmd_list_element *ignore,
 	continue;
 
       if (strncasecmp (signame, word, len) == 0)
-	{
-	  gdb::unique_xmalloc_ptr<char> copy (xstrdup (signame));
-	  tracker.add_completion (std::move (copy));
-	}
+	tracker.add_completion (make_unique_xstrdup (signame));
     }
 }
 
@@ -1775,10 +1770,7 @@ reg_or_group_completer_1 (completion_tracker &tracker,
 	   i++)
 	{
 	  if (*name != '\0' && strncmp (word, name, len) == 0)
-	    {
-	      gdb::unique_xmalloc_ptr<char> copy (xstrdup (name));
-	      tracker.add_completion (std::move (copy));
-	    }
+	    tracker.add_completion (make_unique_xstrdup (name));
 	}
     }
 
@@ -1792,10 +1784,7 @@ reg_or_group_completer_1 (completion_tracker &tracker,
 	{
 	  name = reggroup_name (group);
 	  if (strncmp (word, name, len) == 0)
-	    {
-	      gdb::unique_xmalloc_ptr<char> copy (xstrdup (name));
-	      tracker.add_completion (std::move (copy));
-	    }
+	    tracker.add_completion (make_unique_xstrdup (name));
 	}
     }
 }
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 4afb79a4ea9..9ba6ee3ede5 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -908,7 +908,7 @@ cp_remove_params_if_any (const char *demangled_name, bool completion_mode)
      we're completing / matching everything, avoid returning NULL
      which would make callers interpret the result as an error.  */
   if (demangled_name[0] == '\0' && completion_mode)
-    return gdb::unique_xmalloc_ptr<char> (xstrdup (""));
+    return make_unique_xstrdup ("");
 
   gdb::unique_xmalloc_ptr<char> without_params
     = cp_remove_params_1 (demangled_name, false);
diff --git a/gdb/fbsd-tdep.c b/gdb/fbsd-tdep.c
index cc7c2b7ab2f..e81c6c99bff 100644
--- a/gdb/fbsd-tdep.c
+++ b/gdb/fbsd-tdep.c
@@ -1266,7 +1266,7 @@ fbsd_core_vnode_path (struct gdbarch *gdbarch, int fd)
 	  && bfd_get_signed_32 (core_bfd, descdata + KF_FD) == fd)
 	{
 	  char *path = (char *) descdata + KF_PATH;
-	  return gdb::unique_xmalloc_ptr<char> (xstrdup (path));
+	  return make_unique_xstrdup (path);
 	}
 
       descdata += structsize;
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index fae5b2433cf..5e93a58fd6b 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -3802,7 +3802,7 @@ captured_main (int argc, char *argv[])
       int i, n;
 
       n = argc - (next_arg - argv);
-      program_path.set (gdb::unique_xmalloc_ptr<char> (xstrdup (next_arg[0])));
+      program_path.set (make_unique_xstrdup (next_arg[0]));
       for (i = 1; i < n; i++)
 	program_args.push_back (xstrdup (next_arg[i]));
       program_args.push_back (NULL);
diff --git a/gdb/guile/scm-safe-call.c b/gdb/guile/scm-safe-call.c
index 86e3bbc356b..14eace5f9cd 100644
--- a/gdb/guile/scm-safe-call.c
+++ b/gdb/guile/scm-safe-call.c
@@ -404,7 +404,7 @@ gdbscm_safe_eval_string (const char *string, int display_result)
   result = gdbscm_with_guile (scscm_eval_scheme_string, (void *) &data);
 
   if (result != NULL)
-    return gdb::unique_xmalloc_ptr<char> (xstrdup (result));
+    return make_unique_xstrdup (result);
   return NULL;
 }
 \f
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 1dfbe2329b7..00b55c828ab 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -421,7 +421,7 @@ strip_bg_char (const char *args, int *bg_char_p)
     }
 
   *bg_char_p = 0;
-  return gdb::unique_xmalloc_ptr<char> (xstrdup (args));
+  return make_unique_xstrdup (args);
 }
 
 /* Common actions to take after creating any sort of inferior, by any
diff --git a/gdb/linespec.c b/gdb/linespec.c
index f418e03b774..f0afe1b4ca1 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -555,7 +555,7 @@ copy_token_string (linespec_token token)
   const char *str, *s;
 
   if (token.type == LSTOKEN_KEYWORD)
-    return gdb::unique_xmalloc_ptr<char> (xstrdup (LS_TOKEN_KEYWORD (token)));
+    return make_unique_xstrdup (LS_TOKEN_KEYWORD (token));
 
   str = LS_TOKEN_STOKEN (token).ptr;
   s = remove_trailing_whitespace (str, str + LS_TOKEN_STOKEN (token).length);
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 4921c13528e..13c310d494c 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -695,7 +695,7 @@ static void
 output_cores (struct ui_out *uiout, const char *field_name, const char *xcores)
 {
   ui_out_emit_list list_emitter (uiout, field_name);
-  gdb::unique_xmalloc_ptr<char> cores (xstrdup (xcores));
+  auto cores = make_unique_xstrdup (xcores);
   char *p = cores.get ();
 
   for (p = strtok (p, ","); p;  p = strtok (NULL, ","))
diff --git a/gdb/psymtab.c b/gdb/psymtab.c
index 78a46997ca8..6cc7566580a 100644
--- a/gdb/psymtab.c
+++ b/gdb/psymtab.c
@@ -647,7 +647,7 @@ psymtab_search_name (const char *name)
       break;
     }
 
-  return gdb::unique_xmalloc_ptr<char> (xstrdup (name));
+  return make_unique_xstrdup (name);
 }
 
 /* Look, in partial_symtab PST, for symbol whose natural name is NAME.
diff --git a/gdb/symfile.c b/gdb/symfile.c
index af99da18f7a..26762d289ca 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -3877,7 +3877,7 @@ test_set_ext_lang_command ()
   SELF_CHECK (lang == language_unknown);
 
   /* Test adding a new extension using the CLI command.  */
-  gdb::unique_xmalloc_ptr<char> args_holder (xstrdup (".hello rust"));
+  auto args_holder = make_unique_xstrdup (".hello rust");
   ext_args = args_holder.get ();
   set_ext_lang_command (NULL, 1, NULL);
 
diff --git a/gdb/target.c b/gdb/target.c
index 752e62b022f..1c04095fd63 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3106,7 +3106,7 @@ target_fileio_read_stralloc (struct inferior *inf, const char *filename)
     return gdb::unique_xmalloc_ptr<char> (nullptr);
 
   if (transferred == 0)
-    return gdb::unique_xmalloc_ptr<char> (xstrdup (""));
+    return make_unique_xstrdup ("");
 
   bufstr[transferred] = 0;
 
diff --git a/gdb/tui/tui-regs.c b/gdb/tui/tui-regs.c
index 91b40f5d072..e6aeedd9231 100644
--- a/gdb/tui/tui-regs.c
+++ b/gdb/tui/tui-regs.c
@@ -676,7 +676,7 @@ tui_reggroup_completer (struct cmd_list_element *ignore,
   for (tmp = extra; *tmp != NULL; ++tmp)
     {
       if (strncmp (word, *tmp, len) == 0)
-	tracker.add_completion (gdb::unique_xmalloc_ptr<char> (xstrdup (*tmp)));
+	tracker.add_completion (make_unique_xstrdup (*tmp));
     }
 }
 
diff --git a/gdb/value.c b/gdb/value.c
index dad9f07b68e..71030efed07 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -2014,11 +2014,7 @@ complete_internalvar (completion_tracker &tracker, const char *name)
 
   for (var = internalvars; var; var = var->next)
     if (strncmp (var->name, name, len) == 0)
-      {
-	gdb::unique_xmalloc_ptr<char> copy (xstrdup (var->name));
-
-	tracker.add_completion (std::move (copy));
-      }
+      tracker.add_completion (make_unique_xstrdup (var->name));
 }
 
 /* Create an internal variable with name NAME and with a void value.
-- 
2.14.5

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

* Re: [PATCH] Introduce and use make_unique_xstrdup
  2019-06-04 18:46     ` [PATCH] Introduce and use make_unique_xstrdup (was: Re: [PATCH v2 12/24] Introduce generic command options framework) Pedro Alves
@ 2019-06-04 18:59       ` Tom Tromey
  2019-06-04 21:51         ` Pedro Alves
  0 siblings, 1 reply; 43+ messages in thread
From: Tom Tromey @ 2019-06-04 18:59 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Tom Tromey, gdb-patches

>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

Pedro> On 6/3/19 8:16 PM, Tom Tromey wrote:
Pedro> +/* Dup STR and return a unique_xmalloc_ptr for the result.  */
Pedro> +
Pedro> +static gdb::unique_xmalloc_ptr<char>
Pedro> +make_unique_xstrdup (const char *str)
Pedro> +{
Pedro> +  return gdb::unique_xmalloc_ptr<char> (xstrdup (str));
Pedro> +}
>> 
>> I could 25 spots that could use this, so how about putting it into a
>> header somewhere?  I am happy to convert all the existing uses.

Pedro> Yeah.  I added a bunch of those spots myself, and been meaning to do
Pedro> this.

Pedro> How about this?

Looks good to me, thanks!

Tom

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

* Re: [PATCH v2 12/24] Introduce generic command options framework
  2019-06-03 19:25   ` Tom Tromey
  2019-06-04 18:46     ` [PATCH] Introduce and use make_unique_xstrdup (was: Re: [PATCH v2 12/24] Introduce generic command options framework) Pedro Alves
@ 2019-06-04 21:37     ` Pedro Alves
  1 sibling, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-06-04 21:37 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 6/3/19 8:16 PM, Tom Tromey wrote:
> 
> Pedro> +  if (len > 0 && strncmp ("unlimited", *arg, len) == 0)
> 
> I should have mentioned this in an earlier patch, but won't this also
> match things like "unlimitedjunk"?

It won't -- the code reads:

 static int
 is_unlimited_literal (const char *arg)
 {
   arg = skip_spaces (arg);

   const char *p = skip_to_space (arg);

   size_t len = p - arg;

   if (len > 0 && strncmp ("unlimited", arg, len) == 0)
     return true;

   return false;
 }

So for "unlimitedjunk", LEN will be 13, which is longer than
strlen ("unlimited"), so the strncmp returns != 0.

Thanks for bringing this up -- I missed adding a test for that
case.  I'm adding the below to the new settings.exp testcase.

Also, while writing the test I realized that we don't error
out with:

  (gdb) maint test-settings set uinteger unlimited junk

I wrote a follow up patch on top of the series to fix that.

Thanks,
Pedro Alves

diff --git c/gdb/testsuite/gdb.base/settings.exp w/gdb/testsuite/gdb.base/settings.exp
index 36c80086a3d..c62957181e2 100644
--- c/gdb/testsuite/gdb.base/settings.exp
+++ w/gdb/testsuite/gdb.base/settings.exp
@@ -141,7 +141,10 @@ proc test-integer {variant} {
            "$set_cmd unlimited"
     }
 
+    gdb_test "$set_cmd unlimitedu" "No symbol table is loaded.*"
+
     test_gdb_complete_none "$set_cmd unlimited "
+    test_gdb_complete_none "$set_cmd unlimitedu"

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

* Re: [PATCH] Introduce and use make_unique_xstrdup
  2019-06-04 18:59       ` [PATCH] Introduce and use make_unique_xstrdup Tom Tromey
@ 2019-06-04 21:51         ` Pedro Alves
  0 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-06-04 21:51 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 6/4/19 7:59 PM, Tom Tromey wrote:
>>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:
> 
> Pedro> On 6/3/19 8:16 PM, Tom Tromey wrote:
> Pedro> +/* Dup STR and return a unique_xmalloc_ptr for the result.  */
> Pedro> +
> Pedro> +static gdb::unique_xmalloc_ptr<char>
> Pedro> +make_unique_xstrdup (const char *str)
> Pedro> +{
> Pedro> +  return gdb::unique_xmalloc_ptr<char> (xstrdup (str));
> Pedro> +}
>>>
>>> I could 25 spots that could use this, so how about putting it into a
>>> header somewhere?  I am happy to convert all the existing uses.
> 
> Pedro> Yeah.  I added a bunch of those spots myself, and been meaning to do
> Pedro> this.
> 
> Pedro> How about this?
> 
> Looks good to me, thanks!

I've pushed this one in.

Thanks,
Pedro Alves

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

* Re: [PATCH v2 06/24] Fix "set enum-command value garbage"
  2019-06-03 18:51   ` Tom Tromey
@ 2019-06-04 21:52     ` Pedro Alves
  0 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-06-04 21:52 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 6/3/19 7:51 PM, Tom Tromey wrote:
>>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:
> 
> Pedro>  (gdb) set print entry-values compact foo
> Pedro>   Garbage after item "compact": "foo"
> 
> In the crucial junk-vs-garbage wording war, junk is winning 9 to 2, so
> perhaps its dominance should be continued.
> 
> I'm not totally sold on either of these phrasings but at the same time I
> don't recall anybody ever complaining about them.

Ahaha, yeah..  Just following the trend here.  I've changed them to
say "junk", and adjusted the testcase as well.

Thanks,
Pedro Alves

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

* Re: [PATCH v2 07/24] Remove "show" command completers, "set" command completers for string commands
  2019-06-03 18:55   ` Tom Tromey
@ 2019-06-04 21:54     ` Pedro Alves
  0 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-06-04 21:54 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 6/3/19 7:55 PM, Tom Tromey wrote:
>>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:
> 
> Pedro> I wonder whether we should instead make the default be no completer.
> 
> I think so.  The current default seems wrong in many situations, and
> expensive to invoke besides.
> 
> One thing bash does is bind certain keys to specific completers, so for
> example M-! will complete on command names, no matter the context.
> Perhaps gdb could provide something like this as well, for the odd case
> where you really want to complete on a symbol- or file-name in an
> unusual context.

That's an interesting idea.

> 
> I'm ok with this patch in the interim.
> 
> Tom
> 

Thanks,
Pedro Alves

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

* Re: [PATCH v2 13/24] Make "print" and "compile print" support -OPT options
  2019-06-03 19:20   ` Tom Tromey
@ 2019-06-04 21:59     ` Pedro Alves
  0 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-06-04 21:59 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 6/3/19 8:20 PM, Tom Tromey wrote:
>>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:
> 
> Pedro> This patch adds support for "print -option optval --", etc.
> Pedro> Likewise for "compile print".
> 
> Pedro> We'll get:
> 
> Pedro> (gdb) help print
> Pedro> Print value of expression EXP.
> Pedro> Usage: print [[OPTION]... --] [/FMT] [EXP]
> 
> Pedro> Options:
> Pedro>   -address [on|off]
> Pedro>     Set printing of addresses.
> 
> Pedro>   -array [on|off]
> Pedro>     Set pretty formatting of arrays.
> [...]
> Pedro> -null-stop [on|off]
> Pedro>     Set printing of char arrays to stop at first null char.
> 
> I assume -null-stop is out-dented due to a cut-and-paste error with the
> email, but I wanted to double-check that there isn't a bug here.

Indeed, there's no bug here.  Noticed this in the v1 submission but forgot
to fix it for v2...  Sorry about that.

> 
> 
> Pedro> I want to highlight the comment above about "--".
> [...]
> 
> I think this approach makes sense, and it addresses the one thing that
> made me hesitate about this idea.

Awesome, that's great to hear.

Thanks,
Pedro Alves

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

* Re: [PATCH v2 19/24] Introduce complete_command
  2019-06-03 19:28   ` Tom Tromey
@ 2019-06-04 22:10     ` Pedro Alves
  2019-06-05  1:53       ` Tom Tromey
  0 siblings, 1 reply; 43+ messages in thread
From: Pedro Alves @ 2019-06-04 22:10 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 6/3/19 8:28 PM, Tom Tromey wrote:
>>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:
> 
> Pedro> This adds a completion helper routine that makes it possible for a
> Pedro> command that takes another command as argument, such as "frame apply
> Pedro> all COMMAND" as "thread apply all COMMAND", to complete on COMMAND,
> Pedro> and have the completion machinery recurse and complete COMMAND as if
> Pedro> you tried to complete "(gdb) COMMAND".  I.e., we'll be able to
> Pedro> complete like this, for example:
> 
> Pedro> 	* completer.c (complete_command): New.
> 
> I think that the function should have a different name.  Traditionally,
> *_command is the name of the command "*" in gdb, and there's already a
> function called complete_command.  Overloading makes this not matter too
> much, but I also think it's best to reserve overloading for functions
> that are semantically similar.

Hmmm.  Somehow I forgot the "complete" command.  Bizarre, since the
approach here is actually stolen from the same trick that I had
applied to make the "complete" command complete command arguments
correctly a couple years ago.  Eh.

The name I had chosen as following the complete_xxx pattern in the
completer.h functions -- complete_expression, complete_symbol,
complete_source_filenames, complete_files_symbols, etc.

Not sure what to call this.  It ultimately call complete_line, and it's
used to complete nested commands so, maybe

  complete_nested_command_line 

?

Thanks,
Pedro Alves

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

* Re: [PATCH v2 04/24] Make check_for_argument skip whitespace after arg itself
  2019-06-03 18:49   ` Tom Tromey
@ 2019-06-04 22:21     ` Pedro Alves
  0 siblings, 0 replies; 43+ messages in thread
From: Pedro Alves @ 2019-06-04 22:21 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 6/3/19 7:49 PM, Tom Tromey wrote:
>>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:
> 
> Pedro> Basically every caller of check_for_argument needs to skip space after
> Pedro> the argument.  This patch makes check_for_argument do it itself.
> 
> Pedro> Suggested by Philippe Waroquiers
> 
> Philippe's recent help-styling series added a new instance of this, so
> whoever lands their series last ought to be sure to go fix that up.

His series is in, so I made this change:

diff --git i/gdb/cli/cli-cmds.c w/gdb/cli/cli-cmds.c
index 09f932c2d21..4e33e3e3b72 100644
--- i/gdb/cli/cli-cmds.c
+++ w/gdb/cli/cli-cmds.c
@@ -1381,9 +1381,6 @@ apropos_command (const char *arg, int from_tty)
 {
   bool verbose = arg && check_for_argument (&arg, "-v", 2);
 
-  if (verbose)
-    arg = skip_spaces (arg);
-
   if (arg == NULL || *arg == '\0')
     error (_("REGEXP string is empty"));

Thanks,
Pedro Alves

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

* Re: [PATCH v2 19/24] Introduce complete_command
  2019-06-04 22:10     ` Pedro Alves
@ 2019-06-05  1:53       ` Tom Tromey
  0 siblings, 0 replies; 43+ messages in thread
From: Tom Tromey @ 2019-06-05  1:53 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Tom Tromey, gdb-patches

>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

Pedro> Not sure what to call this.  It ultimately call complete_line, and it's
Pedro> used to complete nested commands so, maybe

Pedro>   complete_nested_command_line 

Pedro> ?

Works for me.

Tom

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

end of thread, other threads:[~2019-06-05  1:53 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-30 19:53 [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Pedro Alves
2019-05-30 19:53 ` [PATCH v2 15/24] "set print raw frame-arguments" -> "set print raw-frame-arguments" Pedro Alves
2019-05-31  5:48   ` Eli Zaretskii
2019-05-30 19:53 ` [PATCH v2 11/24] number_or_range_parser::get_number, don't treat "1 -" as a range Pedro Alves
2019-05-30 19:53 ` [PATCH v2 01/24] Fix latent bug in custom word point completion handling Pedro Alves
2019-05-30 19:53 ` [PATCH v2 02/24] Fix latent bug with custom word point completers Pedro Alves
2019-05-30 19:53 ` [PATCH v2 06/24] Fix "set enum-command value garbage" Pedro Alves
2019-06-03 18:51   ` Tom Tromey
2019-06-04 21:52     ` Pedro Alves
2019-05-30 19:54 ` [PATCH v2 13/24] Make "print" and "compile print" support -OPT options Pedro Alves
2019-06-03 19:20   ` Tom Tromey
2019-06-04 21:59     ` Pedro Alves
2019-05-30 19:54 ` [PATCH v2 08/24] gdb.base/completion.exp: Fix comment typo Pedro Alves
2019-05-30 19:54 ` [PATCH v2 24/24] NEWS and manual changes for command options changes Pedro Alves
2019-05-30 19:54 ` [PATCH v2 07/24] Remove "show" command completers, "set" command completers for string commands Pedro Alves
2019-06-03 18:55   ` Tom Tromey
2019-06-04 21:54     ` Pedro Alves
2019-05-30 19:54 ` [PATCH v2 05/24] Allow "unlimited" abbreviations Pedro Alves
2019-05-30 19:54 ` [PATCH v2 21/24] "thread apply 1 -- -" vs "frame apply level 0 -- -" Pedro Alves
2019-05-30 19:54 ` [PATCH v2 10/24] boolean/auto-boolean commands, make "o" ambiguous Pedro Alves
2019-05-30 19:54 ` [PATCH v2 16/24] Make "backtrace" support -OPT options Pedro Alves
2019-05-30 19:54 ` [PATCH v2 12/24] Introduce generic command options framework Pedro Alves
2019-06-03 19:25   ` Tom Tromey
2019-06-04 18:46     ` [PATCH] Introduce and use make_unique_xstrdup (was: Re: [PATCH v2 12/24] Introduce generic command options framework) Pedro Alves
2019-06-04 18:59       ` [PATCH] Introduce and use make_unique_xstrdup Tom Tromey
2019-06-04 21:51         ` Pedro Alves
2019-06-04 21:37     ` [PATCH v2 12/24] Introduce generic command options framework Pedro Alves
2019-05-30 19:54 ` [PATCH v2 03/24] Fix TID parser bug Pedro Alves
2019-05-30 19:59 ` [PATCH v2 22/24] Make "thread apply" use the gdb::option framework Pedro Alves
2019-05-30 19:59 ` [PATCH v2 19/24] Introduce complete_command Pedro Alves
2019-06-03 19:28   ` Tom Tromey
2019-06-04 22:10     ` Pedro Alves
2019-06-05  1:53       ` Tom Tromey
2019-05-30 20:01 ` [PATCH v2 20/24] Make "frame apply" support -OPT options Pedro Alves
2019-05-30 20:01 ` [PATCH v2 14/24] Migrate rest of compile commands to new options framework Pedro Alves
2019-05-30 20:02 ` [PATCH v2 23/24] Delete parse_flags/parse_flags_qcs Pedro Alves
2019-05-30 20:02 ` [PATCH v2 09/24] New set/show testing framework (gdb.base/settings.exp) Pedro Alves
2019-05-30 20:03 ` [PATCH v2 04/24] Make check_for_argument skip whitespace after arg itself Pedro Alves
2019-06-03 18:49   ` Tom Tromey
2019-06-04 22:21     ` Pedro Alves
2019-05-30 20:03 ` [PATCH v2 17/24] "backtrace full/no-filters/hide" completer Pedro Alves
2019-05-30 20:03 ` [PATCH v2 18/24] lib/completion-support.exp: Add test_gdb_completion_offers_commands Pedro Alves
2019-06-03 19:33 ` [PATCH v2 00/24] gdb::option framework, "print -OPT", other cmd options Tom Tromey

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).