public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v8 0/6] gdb: split array and string limiting options
@ 2022-11-24 11:21 Maciej W. Rozycki
  2022-11-24 11:21 ` [PATCH v8 1/6] GDB: Fix documentation for `theclass' parameters in cli-decode.c Maciej W. Rozycki
                   ` (7 more replies)
  0 siblings, 8 replies; 32+ messages in thread
From: Maciej W. Rozycki @ 2022-11-24 11:21 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Simon Marchi, Tom Tromey, Simon Sobisch

Hi,

 Here is v8 of the series to split array and string limiting options.

 Since no consensus was reached as to fixing PARAM_ZUINTEGER_UNLIMITED 
API for the `unlimited' setting I have dropped what used to be change 1/4 
in v7 and adjusted what used to be change 2/4 accordingly.  I have also 
moved extra literals out of the `erased_args' structure as requested.

 While updating inline documentation in the course of the latter action I 
have noticed that it has gone out of sync WRT actual code for numerous 
functions in cli-decode.c.  So as not to mix up additions with fixes I 
have made preparatory changes 1/6 through 3/6 that address the problems 
with documentation first and then changes 4/6 through 6/6 respectively map 
to changes 2/4 through 4/4 from v7.

 I have regression-tested the changes with a native `x86_64-linux-gnu' 
configuration.

 See individual change descriptions for details, including updates made.

 OK to apply?

  Maciej

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

* [PATCH v8 1/6] GDB: Fix documentation for `theclass' parameters in cli-decode.c
  2022-11-24 11:21 [PATCH v8 0/6] gdb: split array and string limiting options Maciej W. Rozycki
@ 2022-11-24 11:21 ` Maciej W. Rozycki
  2023-01-12 18:36   ` Simon Marchi
  2022-11-24 11:22 ` [PATCH v8 2/6] GDB: Add missing inline documentation for `add_setshow_cmd_full' Maciej W. Rozycki
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Maciej W. Rozycki @ 2022-11-24 11:21 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Simon Marchi, Tom Tromey, Simon Sobisch

Rename CLASS to THECLASS in the documentation for `theclass' parameters 
throughout cli-decode.c, complementing commit fe978cb071b4 ("C++ keyword 
cleanliness, mostly auto-generated").
---
New change in v8.
---
 gdb/cli/cli-decode.c |   22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

gdb-cli-decode-doc-theclass.diff
Index: src/gdb/cli/cli-decode.c
===================================================================
--- src.orig/gdb/cli/cli-decode.c
+++ src/gdb/cli/cli-decode.c
@@ -161,7 +161,7 @@ cmd_list_element::command_components ()
 
 /* Add element named NAME.
    Space for NAME and DOC must be allocated by the caller.
-   CLASS is the top level category into which commands are broken down
+   THECLASS is the top level category into which commands are broken down
    for "help" purposes.
    FUN should be the function to execute the command;
    it will get a character string as argument, with leading
@@ -494,7 +494,7 @@ empty_func (const char *args, int from_t
 /* Add element named NAME to command list LIST (the list for set/show
    or some sublist thereof).
    TYPE is set_cmd or show_cmd.
-   CLASS is as in add_cmd.
+   THECLASS is as in add_cmd.
    VAR_TYPE is the kind of thing we are setting.
    VAR is address of the variable being controlled by this command.
    SET_SETTING_FUNC is a pointer to an optional function callback used to set
@@ -525,7 +525,7 @@ add_set_or_show_cmd (const char *name,
 }
 
 /* Add element named NAME to both the command SET_LIST and SHOW_LIST.
-   CLASS is as in add_cmd.  VAR_TYPE is the kind of thing we are
+   THECLASS is as in add_cmd.  VAR_TYPE is the kind of thing we are
    setting.  VAR is address of the variable being controlled by this
    command.  If nullptr is given as VAR, then both SET_SETTING_FUNC and
    GET_SETTING_FUNC must be provided. SET_SETTING_FUNC and GET_SETTING_FUNC are
@@ -610,7 +610,7 @@ add_setshow_cmd_full (const char *name,
 }
 
 /* Add element named NAME to command list LIST (the list for set or
-   some sublist thereof).  CLASS is as in add_cmd.  ENUMLIST is a list
+   some sublist thereof).  THECLASS is as in add_cmd.  ENUMLIST is a list
    of strings which may follow NAME.  VAR is address of the variable
    which will contain the matching string (from ENUMLIST).  */
 
@@ -674,7 +674,7 @@ add_setshow_enum_cmd (const char *name,
 const char * const auto_boolean_enums[] = { "on", "off", "auto", NULL };
 
 /* Add an auto-boolean command named NAME to both the set and show
-   command list lists.  CLASS is as in add_cmd.  VAR is address of the
+   command list lists.  THECLASS is as in add_cmd.  VAR is address of the
    variable which will contain the value.  DOC is the documentation
    string.  FUNC is the corresponding callback.  */
 
@@ -730,7 +730,7 @@ add_setshow_auto_boolean_cmd (const char
 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
+   list for set/show or some sublist thereof).  THECLASS 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.
    Returns the new command element.  */
@@ -995,7 +995,7 @@ integer_unlimited_completer (struct cmd_
 }
 
 /* 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
+   list for set/show or some sublist thereof).  THECLASS 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.  This
    function is only used in Python API.  Please don't use it elsewhere.  */
@@ -1045,7 +1045,7 @@ add_setshow_integer_cmd (const char *nam
 }
 
 /* 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
+   list for set/show or some sublist thereof).  THECLASS 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.  */
 
@@ -1095,7 +1095,7 @@ add_setshow_uinteger_cmd (const char *na
 }
 
 /* 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
+   list for set/show or some sublist thereof).  THECLASS 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.  */
 
@@ -1182,7 +1182,7 @@ add_setshow_zuinteger_unlimited_cmd (con
 }
 
 /* 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
+   list for set/show or some sublist thereof).  THECLASS 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.  */
 
@@ -1638,7 +1638,7 @@ help_cmd (const char *command, struct ui
  *
  * LIST is the list.
  * CMDTYPE is the prefix to use in the title string.
- * CLASS is the class with which to list the nodes of this list (see
+ * THECLASS is the class with which to list the nodes of this list (see
  * documentation for help_cmd_list below),  As usual, ALL_COMMANDS for
  * everything, ALL_CLASSES for just classes, and non-negative for only things
  * in a specific class.

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

* [PATCH v8 2/6] GDB: Add missing inline documentation for `add_setshow_cmd_full'
  2022-11-24 11:21 [PATCH v8 0/6] gdb: split array and string limiting options Maciej W. Rozycki
  2022-11-24 11:21 ` [PATCH v8 1/6] GDB: Fix documentation for `theclass' parameters in cli-decode.c Maciej W. Rozycki
@ 2022-11-24 11:22 ` Maciej W. Rozycki
  2023-01-12 18:40   ` Simon Marchi
  2022-11-24 11:22 ` [PATCH v8 3/6] GDB: Add references to erased args in cli-decode.c Maciej W. Rozycki
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Maciej W. Rozycki @ 2022-11-24 11:22 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Simon Marchi, Tom Tromey, Simon Sobisch

Complement commit 1d7fe7f01b93 ("gdb: Introduce setting construct 
within cmd_list_element") and add missing description for 
`add_setshow_cmd_full'.
---
New change in v8, split off from 2/4 in v7.
---
 gdb/cli/cli-decode.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)

gdb-setshow-cmd-full-doc.diff
Index: src/gdb/cli/cli-decode.c
===================================================================
--- src.orig/gdb/cli/cli-decode.c
+++ src/gdb/cli/cli-decode.c
@@ -580,6 +580,18 @@ add_setshow_cmd_full_erased (const char
   return {set, show};
 }
 
+/* Add element named NAME to both the command SET_LIST and SHOW_LIST.
+   THECLASS is as in add_cmd.  VAR_TYPE is the kind of thing we are
+   setting.  VAR is address of the variable being controlled by this
+   command.  If nullptr is given as VAR, then both SET_SETTING_FUNC and
+   GET_SETTING_FUNC must be provided.  SET_SETTING_FUNC and GET_SETTING_FUNC
+   are callbacks used to access and modify the underlying property,
+   whatever its storage is.  SET_FUNC and SHOW_FUNC are the callback
+   functions (if non-NULL).  SET_DOC, SHOW_DOC and HELP_DOC are the
+   documentation strings.
+
+   Return the newly created set and show commands.  */
+
 template<typename T>
 static set_show_commands
 add_setshow_cmd_full (const char *name,

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

* [PATCH v8 3/6] GDB: Add references to erased args in cli-decode.c
  2022-11-24 11:21 [PATCH v8 0/6] gdb: split array and string limiting options Maciej W. Rozycki
  2022-11-24 11:21 ` [PATCH v8 1/6] GDB: Fix documentation for `theclass' parameters in cli-decode.c Maciej W. Rozycki
  2022-11-24 11:22 ` [PATCH v8 2/6] GDB: Add missing inline documentation for `add_setshow_cmd_full' Maciej W. Rozycki
@ 2022-11-24 11:22 ` Maciej W. Rozycki
  2023-01-12 18:46   ` Simon Marchi
  2022-11-24 11:22 ` [PATCH v8 4/6] GDB: Allow arbitrary keywords in integer set commands Maciej W. Rozycki
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Maciej W. Rozycki @ 2022-11-24 11:22 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Simon Marchi, Tom Tromey, Simon Sobisch

Complement commit 1d7fe7f01b93 ("gdb: Introduce setting construct within 
cmd_list_element") and commit 702991711a91 ("gdb: Have setter and getter 
callbacks for settings") and update inline documentation accordingly for 
`add_set_or_show_cmd' and `add_setshow_cmd_full_erased', documenting the 
`args' parameter and removing references to `var', `set_setting_func' 
and `get_setting_func'.
---
New change in v8.
---
 gdb/cli/cli-decode.c |   17 ++++++-----------
 1 file changed, 6 insertions(+), 11 deletions(-)

gdb-setshow-cmd-erased-args-doc.diff
Index: src/gdb/cli/cli-decode.c
===================================================================
--- src.orig/gdb/cli/cli-decode.c
+++ src/gdb/cli/cli-decode.c
@@ -496,11 +496,8 @@ empty_func (const char *args, int from_t
    TYPE is set_cmd or show_cmd.
    THECLASS is as in add_cmd.
    VAR_TYPE is the kind of thing we are setting.
-   VAR is address of the variable being controlled by this command.
-   SET_SETTING_FUNC is a pointer to an optional function callback used to set
-   the setting value.
-   GET_SETTING_FUNC is a pointer to an optional function callback used to get
-   the setting value.
+   ARGS is a pre-validated type-erased reference to the variable being
+   controlled by this command.
    DOC is the documentation string.  */
 
 static struct cmd_list_element *
@@ -526,12 +523,10 @@ add_set_or_show_cmd (const char *name,
 
 /* Add element named NAME to both the command SET_LIST and SHOW_LIST.
    THECLASS is as in add_cmd.  VAR_TYPE is the kind of thing we are
-   setting.  VAR is address of the variable being controlled by this
-   command.  If nullptr is given as VAR, then both SET_SETTING_FUNC and
-   GET_SETTING_FUNC must be provided. SET_SETTING_FUNC and GET_SETTING_FUNC are
-   callbacks used to access and modify the underlying property, whatever its
-   storage is.  SET_FUNC and SHOW_FUNC are the callback functions (if non-NULL).
-   SET_DOC, SHOW_DOC and HELP_DOC are the documentation strings.
+   setting.  ARGS is a pre-validated type-erased reference to the
+   variable being controlled by this command.  SET_FUNC and SHOW_FUNC
+   are the callback functions (if non-NULL).  SET_DOC, SHOW_DOC and
+   HELP_DOC are the documentation strings.
 
    Return the newly created set and show commands.  */
 

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

* [PATCH v8 4/6] GDB: Allow arbitrary keywords in integer set commands
  2022-11-24 11:21 [PATCH v8 0/6] gdb: split array and string limiting options Maciej W. Rozycki
                   ` (2 preceding siblings ...)
  2022-11-24 11:22 ` [PATCH v8 3/6] GDB: Add references to erased args in cli-decode.c Maciej W. Rozycki
@ 2022-11-24 11:22 ` Maciej W. Rozycki
  2022-11-24 11:57   ` Eli Zaretskii
  2023-01-12 20:48   ` Simon Marchi
  2022-11-24 11:22 ` [PATCH v8 5/6] GDB: Add a character string limiting option Maciej W. Rozycki
                   ` (3 subsequent siblings)
  7 siblings, 2 replies; 32+ messages in thread
From: Maciej W. Rozycki @ 2022-11-24 11:22 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Simon Marchi, Tom Tromey, Simon Sobisch

Rather than just `unlimited' allow the integer set commands (or command 
options) to define arbitrary keywords for the user to use, removing 
hardcoded arrangements for the `unlimited' keyword.

Remove the confusingly named `var_zinteger', `var_zuinteger' and 
`var_zuinteger_unlimited' `set'/`show' command variable types redefining 
them in terms of `var_uinteger', `var_integer' and `var_pinteger', which 
have the range of [0;UINT_MAX], [INT_MIN;INT_MAX], and [0;INT_MAX] each.

Following existing practice `var_pinteger' allows extra negative values 
to be used, however unlike `var_zuinteger_unlimited' any number of such 
values can be defined rather than just `-1'.

The "p" in `var_pinteger' stands for "positive", for the lack of a more 
appropriate unambiguous letter, even though 0 obviously is not positive; 
"n" would be confusing as to whether it stands for "non-negative" or 
"negative".

Add a new structure, `literal_def', the entries of which define extra 
keywords allowed for a command and numerical values they correspond to.  
Those values are not verified against the basic range supported by the 
underlying variable type, allowing extra values to be allowed outside 
that range, which may or may not be individually made visible to the 
user.  An optional value translation is possible with the structure to 
follow the existing practice for some commands where user-entered 0 is 
internally translated to UINT_MAX or INT_MAX.  Such translation can now 
be arbitrary.  Literals defined by this structure are automatically used 
for completion as necessary.

So for example:

const literal_def integer_unlimited_literals[] =
  {
    { "unlimited", INT_MAX, 0 },
    { nullptr }
  };

defines an extra `unlimited' keyword and a user-visible 0 value, both of 
which get translated to INT_MAX for the setting to be used with.

Similarly:

const literal_def zuinteger_unlimited_literals[] =
  {
    { "unlimited", -1, -1 },
    { nullptr }
  };

defines the same keyword and a corresponding user-visible -1 value that 
is used for the requested setting.  If the last member were omitted (or 
set to `{}') here, then only the keyword would be allowed for the user 
to enter and while -1 would still be used internally trying to enter it 
as a part of a command would result in an "integer -1 out of range" 
error.

Use said error message in all cases (citing the invalid value requested) 
replacing "only -1 is allowed to set as unlimited" previously used for 
`var_zuinteger_unlimited' settings only rather than propagating it to 
`var_pinteger' type.  It could only be used for the specific case where 
a single extra `unlimited' keyword was defined standing for -1 and the 
use of numeric equivalents is discouraged anyway as it is for historical 
reasons only that they expose GDB internals, confusingly different 
across variable types.  Similarly update the "must be >= -1" Guile error 
message.

Redefine Guile and Python parameter types in terms of the new variable 
types and interpret extra keywords as Scheme keywords and Python strings 
used to communicate corresponding parameter values.  Do not add a new
PARAM_INTEGER Guile parameter type, however do handle the `var_integer' 
variable type now, permitting existing parameters defined by GDB proper, 
such as `listsize', to be accessed from Scheme code.

With these changes in place it should be trivial for a Scheme or Python 
programmer to expand the syntax of the `make-parameter' command and the 
`gdb.Parameter' class initializer to have arbitrary extra literals along 
with their internal representation supplied.

Update the testsuite accordingly.
---
Changes from v7:

- Handle the special case of returning -1 for the value of `unlimited' 
  with `var_pinteger' parameters in Python code.

- Update Python documentation accordingly, including `unlimited' case 
  descriptions from what used to be in 1/4.

- Pass extra literals outside erased args with `add_set_or_show_cmd' and 
  up the call chain.

No change from v6.

Changes from v5:

- Add a translation layer from Guile and Python parameter types to new
  GDB variable types and remove `var_zuinteger', `var_uinteger', and 
  `var_zuinteger_unlimited' variable types altogether now.

- Add an optional `extra_literals' initialiser to the `setting' class
  constructor.

- Remove the "only -1 is allowed to set as unlimited" error message
  altogether now rather than propagating it to `var_pinteger' type.

- Make the `val' member of `struct literal_def' optional and remove the
  `allow' member; simplify processing accordingly.

- Rename `zuinteger_unlimited_literals' to `pinteger_unlimited_literals',
  making the names of all `*_unlimited_literals' arrays consistent with 
  the corresponding `var_*' variable types.

- Rename `struct integer_option_def' to `struct pinteger_option_def',
  observing it's come from `struct zuinteger_unlimited_option_def' and 
  what used to be the `var_zuinteger_unlimited' now has `var_pinteger' 
  semantics.

- Update the names of test flags used by `maint test-options' accordingly.

- Add constructor variants to `struct uinteger_option_def' and `struct
  pinteger_option_def' that allow one to skip the `extra_literals' 
  initialiser altogether rather than having to pass in `nullptr'.

- Update Python documentation mentioning the use of literal `unlimited'
  with the respective parameter types.

No change from v4.

New change in v4.
---
 gdb/cli/cli-cmds.c                        |   59 ++---
 gdb/cli/cli-decode.c                      |  321 +++++++++++++++++++++++-------
 gdb/cli/cli-option.c                      |  117 +++++++---
 gdb/cli/cli-option.h                      |   54 +++--
 gdb/cli/cli-setshow.c                     |  245 ++++++++++------------
 gdb/cli/cli-setshow.h                     |   20 -
 gdb/command.h                             |  110 +++++++---
 gdb/doc/python.texi                       |   28 +-
 gdb/guile/scm-param.c                     |  319 +++++++++++++++++++----------
 gdb/maint-test-options.c                  |   44 ++--
 gdb/python/py-param.c                     |  286 ++++++++++++++++----------
 gdb/python/python.c                       |   52 +++-
 gdb/testsuite/gdb.base/max-value-size.exp |    2 
 gdb/testsuite/gdb.base/options.exp        |   47 ++--
 gdb/testsuite/gdb.base/settings.exp       |    2 
 gdb/testsuite/gdb.base/with.exp           |    2 
 gdb/testsuite/gdb.guile/scm-parameter.exp |   23 --
 gdb/testsuite/gdb.python/py-parameter.exp |   15 +
 gdb/valprint.c                            |    9 
 19 files changed, 1133 insertions(+), 622 deletions(-)

gdb-setshow-cmd-extra-literals.diff
Index: src/gdb/cli/cli-cmds.c
===================================================================
--- src.orig/gdb/cli/cli-cmds.c
+++ src/gdb/cli/cli-cmds.c
@@ -2213,22 +2213,40 @@ value_from_setting (const setting &var,
 {
   switch (var.type ())
     {
+    case var_uinteger:
     case var_integer:
-      if (var.get<int> () == INT_MAX)
-	return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				   0);
-      else
-	return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				   var.get<int> ());
-    case var_zinteger:
-      return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				 var.get<int> ());
+    case var_pinteger:
+      {
+	LONGEST value
+	  = (var.type () == var_uinteger
+	     ? static_cast<LONGEST> (var.get<unsigned int> ())
+	     : static_cast<LONGEST> (var.get<int> ()));
+
+	if (var.extra_literals () != nullptr)
+	  for (const literal_def *l = var.extra_literals ();
+	       l->literal != nullptr;
+	       l++)
+	    if (value == l->use)
+	      {
+		if (l->val.has_value ())
+		  value = *l->val;
+		else
+		  return allocate_value (builtin_type (gdbarch)->builtin_void);
+		break;
+	      }
+
+	if (var.type () == var_uinteger)
+	  return
+	    value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int,
+				 static_cast<const unsigned int> (value));
+	else
+	  return
+	    value_from_longest (builtin_type (gdbarch)->builtin_int,
+				static_cast<const int> (value));
+      }
     case var_boolean:
       return value_from_longest (builtin_type (gdbarch)->builtin_int,
 				 var.get<bool> () ? 1 : 0);
-    case var_zuinteger_unlimited:
-      return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				 var.get<int> ());
     case var_auto_boolean:
       {
 	int val;
@@ -2250,17 +2268,6 @@ value_from_setting (const setting &var,
 	return value_from_longest (builtin_type (gdbarch)->builtin_int,
 				   val);
       }
-    case var_uinteger:
-      if (var.get<unsigned int> () == UINT_MAX)
-	return value_from_ulongest
-	  (builtin_type (gdbarch)->builtin_unsigned_int, 0);
-      else
-	return value_from_ulongest
-	  (builtin_type (gdbarch)->builtin_unsigned_int,
-	   var.get<unsigned int> ());
-    case var_zuinteger:
-      return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int,
-				  var.get<unsigned int> ());
     case var_string:
     case var_string_noescape:
     case var_optional_filename:
@@ -2330,13 +2337,11 @@ str_value_from_setting (const setting &v
 {
   switch (var.type ())
     {
+    case var_uinteger:
     case var_integer:
-    case var_zinteger:
+    case var_pinteger:
     case var_boolean:
-    case var_zuinteger_unlimited:
     case var_auto_boolean:
-    case var_uinteger:
-    case var_zuinteger:
       {
 	std::string cmd_val = get_setshow_command_value_string (var);
 
Index: src/gdb/cli/cli-decode.c
===================================================================
--- src.orig/gdb/cli/cli-decode.c
+++ src/gdb/cli/cli-decode.c
@@ -496,6 +496,8 @@ empty_func (const char *args, int from_t
    TYPE is set_cmd or show_cmd.
    THECLASS is as in add_cmd.
    VAR_TYPE is the kind of thing we are setting.
+   EXTRA_LITERALS if non-NULL define extra literals to be accepted in lieu of
+   a number for integer variables.
    ARGS is a pre-validated type-erased reference to the variable being
    controlled by this command.
    DOC is the documentation string.  */
@@ -505,6 +507,7 @@ add_set_or_show_cmd (const char *name,
 		     enum cmd_types type,
 		     enum command_class theclass,
 		     var_types var_type,
+		     const literal_def *extra_literals,
 		     const setting::erased_args &arg,
 		     const char *doc,
 		     struct cmd_list_element **list)
@@ -513,7 +516,7 @@ add_set_or_show_cmd (const char *name,
 
   gdb_assert (type == set_cmd || type == show_cmd);
   c->type = type;
-  c->var.emplace (var_type, arg);
+  c->var.emplace (var_type, extra_literals, arg);
 
   /* This needs to be something besides NULL so that this isn't
      treated as a help class.  */
@@ -523,10 +526,12 @@ add_set_or_show_cmd (const char *name,
 
 /* Add element named NAME to both the command SET_LIST and SHOW_LIST.
    THECLASS is as in add_cmd.  VAR_TYPE is the kind of thing we are
-   setting.  ARGS is a pre-validated type-erased reference to the
-   variable being controlled by this command.  SET_FUNC and SHOW_FUNC
-   are the callback functions (if non-NULL).  SET_DOC, SHOW_DOC and
-   HELP_DOC are the documentation strings.
+   setting.  EXTRA_LITERALS if non-NULL define extra literals to be
+   accepted in lieu of a number for integer variables.  ARGS is a
+   pre-validated type-erased reference to the variable being controlled
+   by this command.  SET_FUNC and SHOW_FUNC are the callback functions
+   (if non-NULL).  SET_DOC, SHOW_DOC and HELP_DOC are the documentation
+   strings.
 
    Return the newly created set and show commands.  */
 
@@ -534,6 +539,7 @@ static set_show_commands
 add_setshow_cmd_full_erased (const char *name,
 			     enum command_class theclass,
 			     var_types var_type,
+			     const literal_def *extra_literals,
 			     const setting::erased_args &args,
 			     const char *set_doc, const char *show_doc,
 			     const char *help_doc,
@@ -557,14 +563,16 @@ add_setshow_cmd_full_erased (const char
       full_set_doc = make_unique_xstrdup (set_doc);
       full_show_doc = make_unique_xstrdup (show_doc);
     }
-  set = add_set_or_show_cmd (name, set_cmd, theclass, var_type, args,
+  set = add_set_or_show_cmd (name, set_cmd, theclass, var_type,
+			     extra_literals, args,
 			     full_set_doc.release (), set_list);
   set->doc_allocated = 1;
 
   if (set_func != NULL)
     set->func = set_func;
 
-  show = add_set_or_show_cmd (name, show_cmd, theclass, var_type, args,
+  show = add_set_or_show_cmd (name, show_cmd, theclass, var_type,
+			      extra_literals, args,
 			      full_show_doc.release (), show_list);
   show->doc_allocated = 1;
   show->show_value_func = show_func;
@@ -575,14 +583,41 @@ add_setshow_cmd_full_erased (const char
   return {set, show};
 }
 
+/* Completes on integer commands that support extra literals.  */
+
+static void
+integer_literals_completer (struct cmd_list_element *c,
+			    completion_tracker &tracker,
+			    const char *text, const char *word)
+{
+  const literal_def *extra_literals = c->var->extra_literals ();
+
+  if (*text == '\0')
+    {
+      tracker.add_completion (make_unique_xstrdup ("NUMBER"));
+      for (const literal_def *l = extra_literals;
+	   l->literal != nullptr;
+	   l++)
+	tracker.add_completion (make_unique_xstrdup (l->literal));
+    }
+  else
+    for (const literal_def *l = extra_literals;
+	 l->literal != nullptr;
+	 l++)
+      if (startswith (l->literal, text))
+	tracker.add_completion (make_unique_xstrdup (l->literal));
+}
+
 /* Add element named NAME to both the command SET_LIST and SHOW_LIST.
    THECLASS is as in add_cmd.  VAR_TYPE is the kind of thing we are
    setting.  VAR is address of the variable being controlled by this
-   command.  If nullptr is given as VAR, then both SET_SETTING_FUNC and
-   GET_SETTING_FUNC must be provided.  SET_SETTING_FUNC and GET_SETTING_FUNC
-   are callbacks used to access and modify the underlying property,
-   whatever its storage is.  SET_FUNC and SHOW_FUNC are the callback
-   functions (if non-NULL).  SET_DOC, SHOW_DOC and HELP_DOC are the
+   command.  EXTRA_LITERALS if non-NULL define extra literals to be
+   accepted in lieu of a number for integer variables.  If nullptr is
+   given as VAR, then both SET_SETTING_FUNC and GET_SETTING_FUNC must
+   be provided.  SET_SETTING_FUNC and GET_SETTING_FUNC are callbacks
+   used to access and modify the underlying property, whatever its
+   storage is.  SET_FUNC and SHOW_FUNC are the callback functions
+   (if non-NULL).  SET_DOC, SHOW_DOC and HELP_DOC are the
    documentation strings.
 
    Return the newly created set and show commands.  */
@@ -592,6 +627,7 @@ static set_show_commands
 add_setshow_cmd_full (const char *name,
 		      enum command_class theclass,
 		      var_types var_type, T *var,
+		      const literal_def *extra_literals,
 		      const char *set_doc, const char *show_doc,
 		      const char *help_doc,
 		      typename setting_func_types<T>::set set_setting_func,
@@ -604,16 +640,43 @@ add_setshow_cmd_full (const char *name,
   auto erased_args
     = setting::erase_args (var_type, var,
 			   set_setting_func, get_setting_func);
+  auto cmds = add_setshow_cmd_full_erased (name,
+					   theclass,
+					   var_type, extra_literals,
+					   erased_args,
+					   set_doc, show_doc,
+					   help_doc,
+					   set_func,
+					   show_func,
+					   set_list,
+					   show_list);
 
-  return add_setshow_cmd_full_erased (name,
-				      theclass,
-				      var_type, erased_args,
-				      set_doc, show_doc,
-				      help_doc,
-				      set_func,
-				      show_func,
-				      set_list,
-				      show_list);
+  if (extra_literals != nullptr)
+    set_cmd_completer (cmds.set, integer_literals_completer);
+
+  return cmds;
+}
+
+/* Same as above but omitting EXTRA_LITERALS.  */
+
+template<typename T>
+static set_show_commands
+add_setshow_cmd_full (const char *name,
+		      enum command_class theclass,
+		      var_types var_type, T *var,
+		      const char *set_doc, const char *show_doc,
+		      const char *help_doc,
+		      typename setting_func_types<T>::set set_setting_func,
+		      typename setting_func_types<T>::get get_setting_func,
+		      cmd_func_ftype *set_func,
+		      show_value_ftype *show_func,
+		      struct cmd_list_element **set_list,
+		      struct cmd_list_element **show_list)
+{
+  return add_setshow_cmd_full (name, theclass, var_type, var, nullptr,
+			       set_doc, show_doc, help_doc,
+			       set_setting_func, get_setting_func,
+			       set_func, show_func, set_list, show_list);
 }
 
 /* Add element named NAME to command list LIST (the list for set or
@@ -982,25 +1045,6 @@ add_setshow_optional_filename_cmd (const
   return cmds;
 }
 
-/* Completes on literal "unlimited".  Used by integer commands that
-   support a special "unlimited" value.  */
-
-static void
-integer_unlimited_completer (struct cmd_list_element *ignore,
-			     completion_tracker &tracker,
-			     const char *text, const char *word)
-{
-  static const char * const keywords[] =
-    {
-      "unlimited",
-      NULL,
-    };
-
-  if (*text == '\0')
-    tracker.add_completion (make_unique_xstrdup ("NUMBER"));
-  complete_on_enum (tracker, keywords, text, word);
-}
-
 /* Add element named NAME to both the set and show command LISTs (the
    list for set/show or some sublist thereof).  THECLASS is as in
    add_cmd.  VAR is address of the variable which will contain the
@@ -1009,6 +1053,55 @@ integer_unlimited_completer (struct cmd_
 
 set_show_commands
 add_setshow_integer_cmd (const char *name, enum command_class theclass,
+			 int *var, const literal_def *extra_literals,
+			 const char *set_doc, const char *show_doc,
+			 const char *help_doc,
+			 cmd_func_ftype *set_func,
+			 show_value_ftype *show_func,
+			 struct cmd_list_element **set_list,
+			 struct cmd_list_element **show_list)
+{
+  set_show_commands commands
+    = add_setshow_cmd_full<int> (name, theclass, var_integer, var,
+				 extra_literals, set_doc, show_doc,
+				 help_doc, nullptr, nullptr, set_func,
+				 show_func, set_list, show_list);
+  return commands;
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_integer_cmd (const char *name, command_class theclass,
+			 const literal_def *extra_literals,
+			 const char *set_doc, const char *show_doc,
+			 const char *help_doc,
+			 setting_func_types<int>::set set_func,
+			 setting_func_types<int>::get get_func,
+			 show_value_ftype *show_func,
+			 cmd_list_element **set_list,
+			 cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<int> (name, theclass, var_integer, nullptr,
+					 extra_literals, set_doc, show_doc,
+					 help_doc, set_func, get_func, nullptr,
+					 show_func, set_list, show_list);
+  return cmds;
+}
+
+/* Accept `unlimited' or 0, translated internally to INT_MAX.  */
+const literal_def integer_unlimited_literals[] =
+  {
+    { "unlimited", INT_MAX, 0 },
+    { nullptr }
+  };
+
+/* Same as above but using `integer_unlimited_literals', with a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_integer_cmd (const char *name, enum command_class theclass,
 			 int *var,
 			 const char *set_doc, const char *show_doc,
 			 const char *help_doc,
@@ -1019,12 +1112,10 @@ add_setshow_integer_cmd (const char *nam
 {
   set_show_commands commands
     = add_setshow_cmd_full<int> (name, theclass, var_integer, var,
+				 integer_unlimited_literals,
 				 set_doc, show_doc, help_doc,
 				 nullptr, nullptr, set_func,
 				 show_func, set_list, show_list);
-
-  set_cmd_completer (commands.set, integer_unlimited_completer);
-
   return commands;
 }
 
@@ -1042,12 +1133,54 @@ add_setshow_integer_cmd (const char *nam
 			 cmd_list_element **show_list)
 {
   auto cmds = add_setshow_cmd_full<int> (name, theclass, var_integer, nullptr,
+					 integer_unlimited_literals,
 					 set_doc, show_doc, help_doc, set_func,
 					 get_func, nullptr, show_func, set_list,
 					 show_list);
+  return cmds;
+}
 
-  set_cmd_completer (cmds.set, integer_unlimited_completer);
+/* 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.  */
+
+set_show_commands
+add_setshow_pinteger_cmd (const char *name, enum command_class theclass,
+			  int *var, const literal_def *extra_literals,
+			  const char *set_doc, const char *show_doc,
+			  const char *help_doc,
+			  cmd_func_ftype *set_func,
+			  show_value_ftype *show_func,
+			  struct cmd_list_element **set_list,
+			  struct cmd_list_element **show_list)
+{
+  set_show_commands commands
+    = add_setshow_cmd_full<int> (name, theclass, var_pinteger, var,
+				 extra_literals, set_doc, show_doc,
+				 help_doc, nullptr, nullptr, set_func,
+				 show_func, set_list, show_list);
+  return commands;
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
 
+set_show_commands
+add_setshow_pinteger_cmd (const char *name, command_class theclass,
+			  const literal_def *extra_literals,
+			  const char *set_doc, const char *show_doc,
+			  const char *help_doc,
+			  setting_func_types<int>::set set_func,
+			  setting_func_types<int>::get get_func,
+			  show_value_ftype *show_func,
+			  cmd_list_element **set_list,
+			  cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<int> (name, theclass, var_pinteger, nullptr,
+					 extra_literals, set_doc, show_doc,
+					 help_doc, set_func, get_func, nullptr,
+					 show_func, set_list, show_list);
   return cmds;
 }
 
@@ -1058,7 +1191,7 @@ add_setshow_integer_cmd (const char *nam
 
 set_show_commands
 add_setshow_uinteger_cmd (const char *name, enum command_class theclass,
-			  unsigned int *var,
+			  unsigned int *var, const literal_def *extra_literals,
 			  const char *set_doc, const char *show_doc,
 			  const char *help_doc,
 			  cmd_func_ftype *set_func,
@@ -1068,12 +1201,9 @@ add_setshow_uinteger_cmd (const char *na
 {
   set_show_commands commands
     = add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger, var,
-					  set_doc, show_doc, help_doc,
-					  nullptr, nullptr, set_func,
+					  extra_literals, set_doc, show_doc,
+					  help_doc, nullptr, nullptr, set_func,
 					  show_func, set_list, show_list);
-
-  set_cmd_completer (commands.set, integer_unlimited_completer);
-
   return commands;
 }
 
@@ -1082,6 +1212,7 @@ add_setshow_uinteger_cmd (const char *na
 
 set_show_commands
 add_setshow_uinteger_cmd (const char *name, command_class theclass,
+			  const literal_def *extra_literals,
 			  const char *set_doc, const char *show_doc,
 			  const char *help_doc,
 			  setting_func_types<unsigned int>::set set_func,
@@ -1091,13 +1222,63 @@ add_setshow_uinteger_cmd (const char *na
 			  cmd_list_element **show_list)
 {
   auto cmds = add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger,
-						  nullptr, set_doc, show_doc,
-						  help_doc, set_func, get_func,
-						  nullptr, show_func, set_list,
+						  nullptr, extra_literals,
+						  set_doc, show_doc, help_doc,
+						  set_func, get_func, nullptr,
+						  show_func, set_list,
 						  show_list);
+  return cmds;
+}
 
-  set_cmd_completer (cmds.set, integer_unlimited_completer);
+/* Accept `unlimited' or 0, translated internally to UINT_MAX.  */
+const literal_def uinteger_unlimited_literals[] =
+  {
+    { "unlimited", UINT_MAX, 0 },
+    { nullptr }
+  };
+
+/* Same as above but using `uinteger_unlimited_literals', with a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_uinteger_cmd (const char *name, enum command_class theclass,
+			  unsigned int *var,
+			  const char *set_doc, const char *show_doc,
+			  const char *help_doc,
+			  cmd_func_ftype *set_func,
+			  show_value_ftype *show_func,
+			  struct cmd_list_element **set_list,
+			  struct cmd_list_element **show_list)
+{
+  set_show_commands commands
+    = add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger, var,
+					  uinteger_unlimited_literals,
+					  set_doc, show_doc, help_doc, nullptr,
+					  nullptr, set_func, show_func,
+					  set_list, show_list);
+  return commands;
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
 
+set_show_commands
+add_setshow_uinteger_cmd (const char *name, command_class theclass,
+			  const char *set_doc, const char *show_doc,
+			  const char *help_doc,
+			  setting_func_types<unsigned int>::set set_func,
+			  setting_func_types<unsigned int>::get get_func,
+			  show_value_ftype *show_func,
+			  cmd_list_element **set_list,
+			  cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger,
+						  nullptr,
+						  uinteger_unlimited_literals,
+						  set_doc, show_doc, help_doc,
+						  set_func, get_func, nullptr,
+						  show_func, set_list,
+						  show_list);
   return cmds;
 }
 
@@ -1116,7 +1297,7 @@ add_setshow_zinteger_cmd (const char *na
 			  struct cmd_list_element **set_list,
 			  struct cmd_list_element **show_list)
 {
-  return add_setshow_cmd_full<int> (name, theclass, var_zinteger, var,
+  return add_setshow_cmd_full<int> (name, theclass, var_integer, var,
 				    set_doc, show_doc, help_doc,
 				    nullptr, nullptr, set_func,
 				    show_func, set_list, show_list);
@@ -1135,12 +1316,22 @@ add_setshow_zinteger_cmd (const char *na
 			  cmd_list_element **set_list,
 			  cmd_list_element **show_list)
 {
-  return add_setshow_cmd_full<int> (name, theclass, var_zinteger, nullptr,
+  return add_setshow_cmd_full<int> (name, theclass, var_integer, nullptr,
 				    set_doc, show_doc, help_doc, set_func,
 				    get_func, nullptr, show_func, set_list,
 				    show_list);
 }
 
+/* Accept `unlimited' or -1, using -1 internally.  */
+const literal_def pinteger_unlimited_literals[] =
+  {
+    { "unlimited", -1, -1 },
+    { nullptr }
+  };
+
+/* Same as above but using `pinteger_unlimited_literals', with a pointer
+   to a global storage buffer.  */
+
 set_show_commands
 add_setshow_zuinteger_unlimited_cmd (const char *name,
 				     enum command_class theclass,
@@ -1154,13 +1345,11 @@ add_setshow_zuinteger_unlimited_cmd (con
 				     struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full<int> (name, theclass, var_zuinteger_unlimited, var,
+    = add_setshow_cmd_full<int> (name, theclass, var_pinteger, var,
+				 pinteger_unlimited_literals,
 				 set_doc, show_doc, help_doc, nullptr,
 				 nullptr, set_func, show_func, set_list,
 				 show_list);
-
-  set_cmd_completer (commands.set, integer_unlimited_completer);
-
   return commands;
 }
 
@@ -1178,13 +1367,11 @@ add_setshow_zuinteger_unlimited_cmd (con
 				     cmd_list_element **show_list)
 {
   auto cmds
-    = add_setshow_cmd_full<int> (name, theclass, var_zuinteger_unlimited,
-				 nullptr, set_doc, show_doc, help_doc, set_func,
+    = add_setshow_cmd_full<int> (name, theclass, var_pinteger, nullptr,
+				 pinteger_unlimited_literals,
+				 set_doc, show_doc, help_doc, set_func,
 				 get_func, nullptr, show_func, set_list,
 				 show_list);
-
-  set_cmd_completer (cmds.set, integer_unlimited_completer);
-
   return cmds;
 }
 
@@ -1203,7 +1390,7 @@ add_setshow_zuinteger_cmd (const char *n
 			   struct cmd_list_element **set_list,
 			   struct cmd_list_element **show_list)
 {
-  return add_setshow_cmd_full<unsigned int> (name, theclass, var_zuinteger,
+  return add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger,
 					     var, set_doc, show_doc, help_doc,
 					     nullptr, nullptr, set_func,
 					     show_func, set_list, show_list);
@@ -1222,7 +1409,7 @@ add_setshow_zuinteger_cmd (const char *n
 			   cmd_list_element **set_list,
 			   cmd_list_element **show_list)
 {
-  return add_setshow_cmd_full<unsigned int> (name, theclass, var_zuinteger,
+  return add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger,
 					     nullptr, set_doc, show_doc,
 					     help_doc, set_func, get_func,
 					     nullptr, show_func, set_list,
Index: src/gdb/cli/cli-option.c
===================================================================
--- src.orig/gdb/cli/cli-option.c
+++ src/gdb/cli/cli-option.c
@@ -38,7 +38,7 @@ union option_value
   /* For var_uinteger options.  */
   unsigned int uinteger;
 
-  /* For var_zuinteger_unlimited options.  */
+  /* For var_integer and var_pinteger options.  */
   int integer;
 
   /* For var_enum options.  */
@@ -356,42 +356,55 @@ parse_option (gdb::array_view<const opti
 	return option_def_and_value {*match, match_ctx, val};
       }
     case var_uinteger:
-    case var_zuinteger_unlimited:
+    case var_integer:
+    case var_pinteger:
       {
 	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))
+	    if (match->extra_literals != nullptr)
 	      {
-		completion->tracker.add_completion
-		  (make_unique_xstrdup ("unlimited"));
-		return {};
+		/* Convenience to let the user know what the option can
+		   accept.  Make sure there's no common prefix between
+		   "NUMBER" and all the strings when adding new ones,
+		   so that readline doesn't do a partial match.  */
+		if (**args == '\0')
+		  {
+		    completion->tracker.add_completion
+		      (make_unique_xstrdup ("NUMBER"));
+		    for (const literal_def *l = match->extra_literals;
+			 l->literal != nullptr;
+			 l++)
+		      completion->tracker.add_completion
+			(make_unique_xstrdup (l->literal));
+		    return {};
+		  }
+		else
+		  {
+		    bool completions = false;
+		    for (const literal_def *l = match->extra_literals;
+			 l->literal != nullptr;
+			 l++)
+		      if (startswith (l->literal, *args))
+			{
+			  completion->tracker.add_completion
+			    (make_unique_xstrdup (l->literal));
+			  completions = true;
+			}
+		    if (completions)
+		      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};
-	  }
+	LONGEST v = parse_cli_var_integer (match->type,
+					   match->extra_literals,
+					   args, false);
+	option_value val;
+	if (match->type == var_uinteger)
+	  val.uinteger = v;
 	else
-	  {
-	    option_value val;
-	    val.uinteger = parse_cli_var_uinteger (match->type, args, false);
-	    return option_def_and_value {*match, match_ctx, val};
-	  }
+	  val.integer = v;
+	return option_def_and_value {*match, match_ctx, val};
       }
     case var_enum:
       {
@@ -593,7 +606,8 @@ save_option_value_in_ctx (gdb::optional<
       *ov->option.var_address.uinteger (ov->option, ov->ctx)
 	= ov->value->uinteger;
       break;
-    case var_zuinteger_unlimited:
+    case var_integer:
+    case var_pinteger:
       *ov->option.var_address.integer (ov->option, ov->ctx)
 	= ov->value->integer;
       break;
@@ -664,8 +678,20 @@ get_val_type_str (const option_def &opt,
     case var_boolean:
       return "[on|off]";
     case var_uinteger:
-    case var_zuinteger_unlimited:
-      return "NUMBER|unlimited";
+    case var_integer:
+    case var_pinteger:
+      {
+	buffer = "NUMBER";
+	if (opt.extra_literals != nullptr)
+	  for (const literal_def *l = opt.extra_literals;
+	       l->literal != nullptr;
+	       l++)
+	    {
+	      buffer += '|';
+	      buffer += l->literal;
+	    }
+	return buffer.c_str ();
+      }
     case var_enum:
       {
 	buffer = "";
@@ -789,20 +815,31 @@ add_setshow_cmds_for_options (command_cl
 	{
 	  add_setshow_uinteger_cmd (option.name, cmd_class,
 				    option.var_address.uinteger (option, data),
+				    option.extra_literals,
 				    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)
+      else if (option.type == var_integer)
 	{
-	  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);
+	  add_setshow_integer_cmd (option.name, cmd_class,
+				   option.var_address.integer (option, data),
+				   option.extra_literals,
+				   option.set_doc, option.show_doc,
+				   option.help_doc,
+				   nullptr, option.show_cmd_cb,
+				   set_list, show_list);
+	}
+      else if (option.type == var_pinteger)
+	{
+	  add_setshow_pinteger_cmd (option.name, cmd_class,
+				    option.var_address.integer (option, data),
+				    option.extra_literals,
+				    option.set_doc, option.show_doc,
+				    option.help_doc,
+				    nullptr, option.show_cmd_cb,
+				    set_list, show_list);
 	}
       else if (option.type == var_enum)
 	{
Index: src/gdb/cli/cli-option.h
===================================================================
--- src.orig/gdb/cli/cli-option.h
+++ src/gdb/cli/cli-option.h
@@ -49,12 +49,13 @@ struct option_def
      used to create the option's "set/show" commands.  */
   constexpr option_def (const char *name_,
 			var_types var_type_,
+			const literal_def *extra_literals_,
 			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_),
+    : name (name_), type (var_type_), extra_literals (extra_literals_),
       erased_get_var_address (erased_get_var_address_),
       var_address {},
       show_cmd_cb (show_cmd_cb_),
@@ -68,6 +69,9 @@ struct option_def
   /* The option's type.  */
   var_types type;
 
+  /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
+  const literal_def *extra_literals;
+
   /* A function that gets the controlling variable's address, type
      erased.  */
   erased_get_var_address_ftype *erased_get_var_address;
@@ -160,7 +164,7 @@ struct boolean_option_def : option_def
 		      const char *set_doc_,
 		      const char *show_doc_ = nullptr,
 		      const char *help_doc_ = nullptr)
-    : option_def (long_option_, var_boolean,
+    : option_def (long_option_, var_boolean, nullptr,
 		  (erased_get_var_address_ftype *) get_var_address_cb_,
 		  show_cmd_cb_,
 		  set_doc_, show_doc_, help_doc_)
@@ -207,37 +211,59 @@ struct uinteger_option_def : option_def
 {
   uinteger_option_def (const char *long_option_,
 		       unsigned int *(*get_var_address_cb_) (Context *),
+		       const literal_def *extra_literals_,
 		       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,
+    : option_def (long_option_, var_uinteger, extra_literals_,
 		  (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>;
   }
+
+  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)
+    : uinteger_option_def (long_option_, get_var_address_cb_, nullptr,
+			   show_cmd_cb_, set_doc_, show_doc_, help_doc_)
+  { /* Nothing.  */ }
 };
 
-/* A var_zuinteger_unlimited command line option.  */
+/* A var_pinteger command line option.  */
 
 template<typename Context>
-struct zuinteger_unlimited_option_def : option_def
+struct pinteger_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,
+  pinteger_option_def (const char *long_option_,
+		       int *(*get_var_address_cb_) (Context *),
+		       const literal_def *extra_literals_,
+		       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_pinteger, extra_literals_,
 		  (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>;
   }
+
+  pinteger_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)
+    : pinteger_option_def (long_option_, get_var_address_cb_, nullptr,
+			   show_cmd_cb_, set_doc_, show_doc_, help_doc_)
+  { /* Nothing.  */ }
 };
 
 /* An var_enum command line option.  */
@@ -252,7 +278,7 @@ struct enum_option_def : option_def
 		   const char *set_doc_,
 		   const char *show_doc_ = nullptr,
 		   const char *help_doc_ = nullptr)
-    : option_def (long_option_, var_enum,
+    : option_def (long_option_, var_enum, nullptr,
 		  (erased_get_var_address_ftype *) get_var_address_cb_,
 		  show_cmd_cb_,
 		  set_doc_, show_doc_, help_doc_)
@@ -273,7 +299,7 @@ struct string_option_def : option_def
 		     const char *set_doc_,
 		     const char *show_doc_ = nullptr,
 		     const char *help_doc_ = nullptr)
-    : option_def (long_option_, var_string,
+    : option_def (long_option_, var_string, nullptr,
 		  (erased_get_var_address_ftype *) get_var_address_cb_,
 		  show_cmd_cb_,
 		  set_doc_, show_doc_, help_doc_)
Index: src/gdb/cli/cli-setshow.c
===================================================================
--- src.orig/gdb/cli/cli-setshow.c
+++ src/gdb/cli/cli-setshow.c
@@ -149,10 +149,11 @@ deprecated_show_value_hack (struct ui_fi
     }
 }
 
-/* Returns true if ARG is "unlimited".  */
+/* Returns true and the value in VAL if ARG is an accepted literal.  */
 
 static bool
-is_unlimited_literal (const char **arg, bool expression)
+get_literal_val (LONGEST &val, const literal_def *extra_literals,
+		 const char **arg, bool expression)
 {
   *arg = skip_spaces (*arg);
 
@@ -162,85 +163,104 @@ is_unlimited_literal (const char **arg,
 
   size_t len = p - *arg;
 
-  if (len > 0 && strncmp ("unlimited", *arg, len) == 0)
-    {
-      *arg += len;
-
-      /* If parsing an expression (i.e., parsing for a "set" command),
-	 anything after "unlimited" is junk.  For options, anything
-	 after "unlimited" might be a command argument or another
-	 option.  */
-      if (expression)
+  if (len > 0 && extra_literals != nullptr)
+    for (const literal_def *l = extra_literals;
+	 l->literal != nullptr;
+	 l++)
+      if (strncmp (l->literal, *arg, len) == 0)
 	{
-	  const char *after = skip_spaces (*arg);
-	  if (*after != '\0')
-	    error (_("Junk after \"%.*s\": %s"),
-		   (int) len, unl_start, after);
-	}
+	  *arg += len;
 
-      return true;
-    }
+	  /* If parsing an expression (i.e., parsing for a "set" command),
+	     anything after the literal is junk.  For options, anything
+	     after the literal might be a command argument or another
+	     option.  */
+	  if (expression)
+	    {
+	      const char *after = skip_spaces (*arg);
+	      if (*after != '\0')
+		error (_("Junk after \"%.*s\": %s"),
+		       (int) len, unl_start, after);
+	    }
+
+	  val = l->use;
+	  return true;
+	}
 
   return false;
 }
 
 /* See cli-setshow.h.  */
 
-unsigned int
-parse_cli_var_uinteger (var_types var_type, const char **arg,
-			bool expression)
+LONGEST
+parse_cli_var_integer (var_types var_type, const literal_def *extra_literals,
+		       const char **arg, bool expression)
 {
   LONGEST val;
 
   if (*arg == nullptr || **arg == '\0')
     {
-      if (var_type == var_uinteger)
-	error_no_arg (_("integer to set it to, or \"unlimited\""));
-      else
+      if (extra_literals == nullptr)
 	error_no_arg (_("integer to set it to"));
-    }
-
-  if (var_type == var_uinteger && is_unlimited_literal (arg, expression))
-    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.  */
+      else
+	{
+	  std::string buffer = "";
+	  size_t count = 0;
 
-int
-parse_cli_var_zuinteger_unlimited (const char **arg, bool expression)
-{
-  LONGEST val;
+	  for (const literal_def *l = extra_literals;
+	       l->literal != nullptr;
+	       l++, count++)
+	    {
+	      if (count != 0)
+		buffer += ", ";
+	      buffer = buffer + '"' + l->literal + '"';
+	    }
+	  if (count > 1)
+	    error_no_arg
+	      (string_printf (_("integer to set it to, or one of: %s"),
+			      buffer.c_str ()).c_str ());
+	  else
+	    error_no_arg
+	      (string_printf (_("integer to set it to, or %s"),
+			      buffer.c_str ()).c_str ());
+	}
+    }
 
-  if (*arg == nullptr || **arg == '\0')
-    error_no_arg (_("integer to set it to, or \"unlimited\""));
+  if (!get_literal_val (val, extra_literals, arg, expression))
+    {
+      if (expression)
+	val = parse_and_eval_long (*arg);
+      else
+	val = get_ulongest (arg);
 
-  if (is_unlimited_literal (arg, expression))
-    val = -1;
-  else if (expression)
-    val = parse_and_eval_long (*arg);
-  else
-    val = get_ulongest (arg);
+      enum tribool allowed = TRIBOOL_UNKNOWN;
+      if (extra_literals != nullptr)
+	{
+	  for (const literal_def *l = extra_literals;
+	       l->literal != nullptr;
+	       l++)
+	    if (l->val.has_value () && val == *l->val)
+	      {
+		allowed = TRIBOOL_TRUE;
+		val = l->use;
+		break;
+	      }
+	    else if (val == l->use)
+	      allowed = TRIBOOL_FALSE;
+	}
 
-  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"));
+      if (allowed == TRIBOOL_UNKNOWN)
+	{
+	  if (val > UINT_MAX || val < INT_MIN
+	      || (var_type == var_uinteger && val < 0)
+	      || (var_type == var_integer && val > INT_MAX)
+	      || (var_type == var_pinteger && val < 0)
+	      || (var_type == var_pinteger && val > INT_MAX))
+	    allowed = TRIBOOL_FALSE;
+	}
+      if (allowed == TRIBOOL_FALSE)
+	error (_("integer %s out of range"), plongest (val));
+    }
 
   return val;
 }
@@ -405,41 +425,18 @@ do_set_command (const char *arg, int fro
       option_changed = c->var->set<enum auto_boolean> (parse_auto_binary_operation (arg));
       break;
     case var_uinteger:
-    case var_zuinteger:
       option_changed
-	= c->var->set<unsigned int> (parse_cli_var_uinteger (c->var->type (),
-							     &arg, true));
+	= c->var->set<unsigned int> (parse_cli_var_integer (c->var->type (),
+							    c->var->
+							    extra_literals (),
+							    &arg, true));
       break;
     case var_integer:
-    case var_zinteger:
-      {
-	LONGEST val;
-
-	if (*arg == '\0')
-	  {
-	    if (c->var->type () == var_integer)
-	      error_no_arg (_("integer to set it to, or \"unlimited\""));
-	    else
-	      error_no_arg (_("integer to set it to"));
-	  }
-
-	if (c->var->type () == var_integer && is_unlimited_literal (&arg, true))
-	  val = 0;
-	else
-	  val = parse_and_eval_long (arg);
-
-	if (val == 0 && c->var->type () == var_integer)
-	  val = INT_MAX;
-	else if (val < INT_MIN
-		 /* For var_integer, don't let the user set the value
-		    to INT_MAX directly, as that exposes an
-		    implementation detail to the user interface.  */
-		 || (c->var->type () == var_integer && val >= INT_MAX)
-		 || (c->var->type () == var_zinteger && val > INT_MAX))
-	  error (_("integer %s out of range"), plongest (val));
-
-	option_changed = c->var->set<int> (val);
-      }
+    case var_pinteger:
+      option_changed
+	= c->var->set<int> (parse_cli_var_integer (c->var->type (),
+						   c->var->extra_literals (),
+						   &arg, true));
       break;
     case var_enum:
       {
@@ -454,10 +451,6 @@ do_set_command (const char *arg, int fro
 	option_changed = c->var->set<const char *> (match);
       }
       break;
-    case var_zuinteger_unlimited:
-      option_changed = c->var->set<int>
-	(parse_cli_var_zuinteger_unlimited (&arg, true));
-      break;
     default:
       error (_("gdb internal error: bad var_type in do_setshow_command"));
     }
@@ -551,7 +544,6 @@ do_set_command (const char *arg, int fro
 	  }
 	  break;
 	case var_uinteger:
-	case var_zuinteger:
 	  {
 	    char s[64];
 
@@ -560,8 +552,7 @@ do_set_command (const char *arg, int fro
 	  }
 	  break;
 	case var_integer:
-	case var_zinteger:
-	case var_zuinteger_unlimited:
+	case var_pinteger:
 	  {
 	    char s[64];
 
@@ -623,36 +614,32 @@ get_setshow_command_value_string (const
 	}
       break;
     case var_uinteger:
-    case var_zuinteger:
-      {
-	const unsigned int value = var.get<unsigned int> ();
-
-	if (var.type () == var_uinteger
-	    && value == UINT_MAX)
-	  stb.puts ("unlimited");
-	else
-	  stb.printf ("%u", value);
-      }
-      break;
     case var_integer:
-    case var_zinteger:
+    case var_pinteger:
       {
-	const int value = var.get<int> ();
+	bool printed = false;
+	const LONGEST value
+	  = (var.type () == var_uinteger
+	     ? static_cast<LONGEST> (var.get<unsigned int> ())
+	     : static_cast<LONGEST> (var.get<int> ()));
 
-	if (var.type () == var_integer
-	    && value == INT_MAX)
-	  stb.puts ("unlimited");
-	else
-	  stb.printf ("%d", value);
-      }
-      break;
-    case var_zuinteger_unlimited:
-      {
-	const int value = var.get<int> ();
-	if (value == -1)
-	  stb.puts ("unlimited");
-	else
-	  stb.printf ("%d", value);
+	if (var.extra_literals () != nullptr)
+	  for (const literal_def *l = var.extra_literals ();
+	       l->literal != nullptr;
+	       l++)
+	    if (value == l->use)
+	      {
+		stb.puts (l->literal);
+		printed = true;
+		break;
+	      }
+	if (!printed)
+	  {
+	    if (var.type () == var_uinteger)
+	      stb.printf ("%u", static_cast<const unsigned int> (value));
+	    else
+	      stb.printf ("%d", static_cast<const int> (value));
+	  }
       }
       break;
     default:
Index: src/gdb/cli/cli-setshow.h
===================================================================
--- src.orig/gdb/cli/cli-setshow.h
+++ src/gdb/cli/cli-setshow.h
@@ -29,21 +29,19 @@ extern int parse_cli_boolean_value (cons
    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
+/* Parse ARG, an option to a var_uinteger, var_integer or var_pinteger
+   variable.  Return the parsed value on success or throw an error.  If
+   EXTRA_LITERALS is non-null, then interpret those literals accordingly.
+   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);
+extern LONGEST parse_cli_var_integer (var_types var_type,
+				      const literal_def *extra_literals,
+				      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
Index: src/gdb/command.h
===================================================================
--- src.orig/gdb/command.h
+++ src/gdb/command.h
@@ -84,16 +84,18 @@ enum var_types
        value.  */
     var_auto_boolean,
 
-    /* Unsigned Integer.  *VAR is an unsigned int.  The user can type
-       0 to mean "unlimited", which is stored in *VAR as UINT_MAX.  */
+    /* Unsigned Integer.  *VAR is an unsigned int.  In the Guile and Python
+       APIs 0 means unlimited, which is stored in *VAR as UINT_MAX.  */
     var_uinteger,
 
-    /* Like var_uinteger but signed.  *VAR is an int.  The user can
-       type 0 to mean "unlimited", which is stored in *VAR as
-       INT_MAX.  The only remaining use of it is the Python API.
-       Don't use it elsewhere.  */
+    /* Like var_uinteger but signed.  *VAR is an int.  In the Guile and
+       Python APIs 0 means unlimited, which is stored in *VAR as INT_MAX.  */
     var_integer,
 
+    /* Like var_integer but negative numbers are not allowed,
+       except for special values.  *VAR is an int.  */
+    var_pinteger,
+
     /* String which the user enters with escapes (e.g. the user types
        \n and it is a real newline in the stored string).
        *VAR is a std::string, "" if the string is empty.  */
@@ -106,22 +108,25 @@ enum var_types
     var_optional_filename,
     /* String which stores a filename.  (*VAR) is a std::string.  */
     var_filename,
-    /* ZeroableInteger.  *VAR is an int.  Like var_integer except
-       that zero really means zero.  */
-    var_zinteger,
-    /* ZeroableUnsignedInteger.  *VAR is an unsigned int.  Zero really
-       means zero.  */
-    var_zuinteger,
-    /* ZeroableUnsignedInteger with unlimited value.  *VAR is an int,
-       but its range is [0, INT_MAX].  -1 stands for unlimited and
-       other negative numbers are not allowed.  */
-    var_zuinteger_unlimited,
     /* Enumerated type.  Can only have one of the specified values.
        *VAR is a char pointer to the name of the element that we
        find.  */
     var_enum
   };
 
+/* A structure describing an extra literal accepted and shown in place
+   of a number.  */
+struct literal_def
+  {
+    /* The literal to define, e.g. "unlimited".  */
+    const char *literal;
+    /* The number to substitute internally for LITERAL or VAL;
+       the use of this number is not allowed (unless the same as VAL).  */
+    LONGEST use;
+    /* An optional number accepted that stands for the literal.  */
+    gdb::optional<LONGEST> val;
+  };
+
 /* Return true if a setting of type VAR_TYPE is backed with type T.
 
    This function is left without definition intentionally.  This template is
@@ -152,15 +157,14 @@ inline bool var_type_uses<enum auto_bool
 template<>
 inline bool var_type_uses<unsigned int> (var_types t)
 {
-  return (t == var_uinteger || t == var_zinteger || t == var_zuinteger);
+  return t == var_uinteger;
 }
 
 /* Return true if a setting of type T is backed by an int variable.  */
 template<>
 inline bool var_type_uses<int> (var_types t)
 {
-  return (t == var_integer || t == var_zinteger
-	  || t == var_zuinteger_unlimited);
+  return t == var_integer || t == var_pinteger;
 }
 
 /* Return true if a setting of type T is backed by a std::string variable.  */
@@ -218,8 +222,8 @@ struct setting
 
      Type T must match the var type VAR_TYPE (see VAR_TYPE_USES).  */
   template<typename T>
-  setting (var_types var_type, T *var)
-    : m_var_type (var_type), m_var (var)
+  setting (var_types var_type, T *var, const void *extra_literals = nullptr)
+    : m_var_type (var_type), m_var (var), m_extra_literals (extra_literals)
   {
     gdb_assert (var != nullptr);
     gdb_assert (var_type_uses<T> (var_type));
@@ -258,12 +262,14 @@ struct setting
     };
   }
 
-  /* Create a setting backed by pre-validated type-erased args.
-     ERASED_VAR's fields' real types must match the var type VAR_TYPE
-     (see VAR_TYPE_USES).  */
-  setting (var_types var_type, const erased_args &args)
+  /* Create a setting backed by pre-validated type-erased args and using
+     EXTRA_LITERALS.  ERASED_VAR's fields' real types must match the var
+     type VAR_TYPE (see VAR_TYPE_USES).  */
+  setting (var_types var_type, const literal_def *extra_literals,
+	   const erased_args &args)
     : m_var_type (var_type),
       m_var (args.var),
+      m_extra_literals (extra_literals),
       m_getter (args.getter),
       m_setter (args.setter)
   {
@@ -294,6 +300,10 @@ struct setting
   var_types type () const
   { return m_var_type; }
 
+  /* Access any extra literals accepted.  */
+  const literal_def *extra_literals () const
+  { return static_cast<const literal_def *> (m_extra_literals); }
+
   /* Return the current value.
 
      The template parameter T is the type of the variable used to store the
@@ -356,6 +366,9 @@ struct setting
      non-nullptr.  */
   void *m_var = nullptr;
 
+  /* Any extra literals accepted.  */
+  const void *m_extra_literals = nullptr;
+
   /* Pointer to a user provided getter.  */
   erased_func m_getter = nullptr;
 
@@ -651,6 +664,11 @@ typedef void (show_value_ftype) (struct
    instead print the value out directly.  */
 extern show_value_ftype deprecated_show_value_hack;
 
+/* Various sets of extra literals accepted.  */
+extern const literal_def integer_unlimited_literals[];
+extern const literal_def uinteger_unlimited_literals[];
+extern const literal_def pinteger_unlimited_literals[];
+
 extern set_show_commands add_setshow_enum_cmd
   (const char *name, command_class theclass, const char *const *enumlist,
    const char **var, const char *set_doc, const char *show_doc,
@@ -747,6 +765,20 @@ extern set_show_commands add_setshow_opt
    cmd_list_element **show_list);
 
 extern set_show_commands add_setshow_integer_cmd
+  (const char *name, command_class theclass, int *var,
+   const literal_def *extra_literals, const char *set_doc,
+   const char *show_doc, const char *help_doc, cmd_func_ftype *set_func,
+   show_value_ftype *show_func, cmd_list_element **set_list,
+   cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_integer_cmd
+  (const char *name, command_class theclass, const literal_def *extra_literals,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   setting_func_types<int>::set set_func,
+   setting_func_types<int>::get get_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_integer_cmd
   (const char *name, command_class theclass, int *var, const char *set_doc,
    const char *show_doc, const char *help_doc, cmd_func_ftype *set_func,
    show_value_ftype *show_func, cmd_list_element **set_list,
@@ -759,6 +791,34 @@ extern set_show_commands add_setshow_int
    setting_func_types<int>::get get_func, show_value_ftype *show_func,
    cmd_list_element **set_list, cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_pinteger_cmd
+  (const char *name, command_class theclass, int *var,
+   const literal_def *extra_literals, const char *set_doc,
+   const char *show_doc, const char *help_doc, cmd_func_ftype *set_func,
+   show_value_ftype *show_func, cmd_list_element **set_list,
+   cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_pinteger_cmd
+  (const char *name, command_class theclass, const literal_def *extra_literals,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   setting_func_types<int>::set set_func,
+   setting_func_types<int>::get get_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_uinteger_cmd
+  (const char *name, command_class theclass, unsigned int *var,
+   const literal_def *extra_literals,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   cmd_func_ftype *set_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_uinteger_cmd
+  (const char *name, command_class theclass, const literal_def *extra_literals,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   setting_func_types<unsigned int>::set set_func,
+   setting_func_types<unsigned int>::get get_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_uinteger_cmd
   (const char *name, command_class theclass, unsigned int *var,
    const char *set_doc, const char *show_doc, const char *help_doc,
Index: src/gdb/doc/python.texi
===================================================================
--- src.orig/gdb/doc/python.texi
+++ src/gdb/doc/python.texi
@@ -4611,14 +4611,18 @@ Python, true and false are represented u
 @findex PARAM_UINTEGER
 @findex gdb.PARAM_UINTEGER
 @item gdb.PARAM_UINTEGER
-The value is an unsigned integer.  The value of 0 should be
-interpreted to mean ``unlimited''.
+The value is an unsigned integer.  The value of @code{None} should be
+interpreted to mean ``unlimited'' (literal @code{'unlimited'} can also
+be used to set that value), and the value of 0 is reserved and should
+not be used.
 
 @findex PARAM_INTEGER
 @findex gdb.PARAM_INTEGER
 @item gdb.PARAM_INTEGER
-The value is a signed integer.  The value of 0 should be interpreted
-to mean ``unlimited''.
+The value is a signed integer.  The value of @code{None} should be
+interpreted to mean ``unlimited'' (literal @code{'unlimited'} can also
+be used to set that value), and the value of 0 is reserved and should
+not be used.
 
 @findex PARAM_STRING
 @findex gdb.PARAM_STRING
@@ -4648,21 +4652,23 @@ The value is a filename.  This is just l
 @findex PARAM_ZINTEGER
 @findex gdb.PARAM_ZINTEGER
 @item gdb.PARAM_ZINTEGER
-The value is an integer.  This is like @code{PARAM_INTEGER}, except 0
-is interpreted as itself.
+The value is a signed integer.  This is like @code{PARAM_INTEGER},
+except that 0 is allowed and the value of @code{None} is not supported.
 
 @findex PARAM_ZUINTEGER
 @findex gdb.PARAM_ZUINTEGER
 @item gdb.PARAM_ZUINTEGER
-The value is an unsigned integer.  This is like @code{PARAM_INTEGER},
-except 0 is interpreted as itself, and the value cannot be negative.
+The value is an unsigned integer.  This is like @code{PARAM_UINTEGER},
+except that 0 is allowed and the value of @code{None} is not supported.
 
 @findex PARAM_ZUINTEGER_UNLIMITED
 @findex gdb.PARAM_ZUINTEGER_UNLIMITED
 @item gdb.PARAM_ZUINTEGER_UNLIMITED
-The value is a signed integer.  This is like @code{PARAM_ZUINTEGER},
-except the special value -1 should be interpreted to mean
-``unlimited''.  Other negative values are not allowed.
+The value is a signed integer.  This is like @code{PARAM_INTEGER}
+including that the value of @code{None} should be interpreted to mean
+``unlimited'' (literal @code{'unlimited'} can also be used to set that
+value), except that 0 is allowed, and the value cannot be negative,
+except the special value -1 is returned for the setting of ``unlimited''.
 
 @findex PARAM_ENUM
 @findex gdb.PARAM_ENUM
Index: src/gdb/guile/scm-param.c
===================================================================
--- src.orig/gdb/guile/scm-param.c
+++ src/gdb/guile/scm-param.c
@@ -75,9 +75,15 @@ struct param_smob
   /* One of the COMMAND_* constants.  */
   enum command_class cmd_class;
 
+  /* Guile parameter type name.  */
+  const char *pname;
+
   /* The type of the parameter.  */
   enum var_types type;
 
+  /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
+  const literal_def *extra_literals;
+
   /* The docs for the parameter.  */
   char *set_doc;
   char *show_doc;
@@ -110,6 +116,50 @@ struct param_smob
   SCM containing_scm;
 };
 
+/* Guile parameter types as in PARAMETER_TYPES later on.  */
+
+typedef enum param_types
+  {
+    param_boolean,
+    param_auto_boolean,
+    param_zinteger,
+    param_uinteger,
+    param_zuinteger,
+    param_zuinteger_unlimited,
+    param_string,
+    param_string_noescape,
+    param_optional_filename,
+    param_filename,
+    param_enum,
+  }
+param_types;
+
+/* Translation from Guile parameters to GDB variable types.  Keep in the
+   same order as PARAM_TYPES due to C++'s lack of designated initializers.  */
+
+static const struct
+  {
+    /* The type of the parameter.  */
+    enum var_types type;
+
+    /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
+    const literal_def *extra_literals;
+  }
+param_to_var[] =
+  {
+    { var_boolean },
+    { var_auto_boolean },
+    { var_integer },
+    { var_uinteger, uinteger_unlimited_literals },
+    { var_uinteger },
+    { var_pinteger, pinteger_unlimited_literals },
+    { var_string },
+    { var_string_noescape },
+    { var_optional_filename },
+    { var_filename },
+    { var_enum }
+  };
+
 /* Wraps a setting around an existing param_smob.  This abstraction
    is used to manipulate the value in S->VALUE in a type safe manner using
    the setting interface.  */
@@ -117,18 +167,20 @@ struct param_smob
 static setting
 make_setting (param_smob *s)
 {
-  if (var_type_uses<bool> (s->type))
-    return setting (s->type, &s->value.boolval);
-  else if (var_type_uses<int> (s->type))
-    return setting (s->type, &s->value.intval);
-  else if (var_type_uses<auto_boolean> (s->type))
-    return setting (s->type, &s->value.autoboolval);
-  else if (var_type_uses<unsigned int> (s->type))
-    return setting (s->type, &s->value.uintval);
-  else if (var_type_uses<std::string> (s->type))
-    return setting (s->type, s->value.stringval);
-  else if (var_type_uses<const char *> (s->type))
-    return setting (s->type, &s->value.cstringval);
+  enum var_types type = s->type;
+
+  if (var_type_uses<bool> (type))
+    return setting (type, &s->value.boolval);
+  else if (var_type_uses<int> (type))
+    return setting (type, &s->value.intval, s->extra_literals);
+  else if (var_type_uses<auto_boolean> (type))
+    return setting (type, &s->value.autoboolval);
+  else if (var_type_uses<unsigned int> (type))
+    return setting (type, &s->value.uintval, s->extra_literals);
+  else if (var_type_uses<std::string> (type))
+    return setting (type, s->value.stringval);
+  else if (var_type_uses<const char *> (type))
+    return setting (type, &s->value.cstringval);
   else
     gdb_assert_not_reached ("unhandled var type");
 }
@@ -149,10 +201,9 @@ static SCM set_doc_keyword;
 static SCM show_doc_keyword;
 static SCM initial_value_keyword;
 static SCM auto_keyword;
-static SCM unlimited_keyword;
 
 static int pascm_is_valid (param_smob *);
-static const char *pascm_param_type_name (enum var_types type);
+static const char *pascm_param_type_name (enum param_types type);
 static SCM pascm_param_value (const setting &var, int arg_pos,
 			      const char *func_name);
 \f
@@ -171,7 +222,7 @@ pascm_print_param_smob (SCM self, SCM po
   if (! pascm_is_valid (p_smob))
     scm_puts (" {invalid}", port);
 
-  gdbscm_printf (port, " %s ", pascm_param_type_name (p_smob->type));
+  gdbscm_printf (port, " %s ", p_smob->pname);
 
   value = pascm_param_value (make_setting (p_smob), GDBSCM_ARG_NONE, NULL);
   scm_display (value, port);
@@ -369,7 +420,9 @@ pascm_show_func (struct ui_file *file, i
    function.  */
 
 static set_show_commands
-add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
+add_setshow_generic (enum var_types param_type,
+		     const literal_def *extra_literals,
+		     enum command_class cmd_class,
 		     char *cmd_name, param_smob *self,
 		     char *set_doc, char *show_doc, char *help_doc,
 		     cmd_func_ftype *set_func,
@@ -398,32 +451,26 @@ add_setshow_generic (enum var_types para
 
     case var_uinteger:
       commands = add_setshow_uinteger_cmd (cmd_name, cmd_class,
-					   &self->value.uintval, set_doc,
-					   show_doc, help_doc, set_func,
-					   show_func, set_list, show_list);
-      break;
-
-    case var_zinteger:
-      commands = add_setshow_zinteger_cmd (cmd_name, cmd_class,
-					   &self->value.intval, set_doc,
+					   &self->value.uintval,
+					   extra_literals, set_doc,
 					   show_doc, help_doc, set_func,
 					   show_func, set_list, show_list);
       break;
 
-    case var_zuinteger:
-      commands = add_setshow_zuinteger_cmd (cmd_name, cmd_class,
-					    &self->value.uintval, set_doc,
-					    show_doc, help_doc, set_func,
-					    show_func, set_list, show_list);
+    case var_integer:
+      commands = add_setshow_integer_cmd (cmd_name, cmd_class,
+					  &self->value.intval,
+					  extra_literals, set_doc,
+					  show_doc, help_doc, set_func,
+					  show_func, set_list, show_list);
       break;
 
-    case var_zuinteger_unlimited:
-      commands = add_setshow_zuinteger_unlimited_cmd (cmd_name, cmd_class,
-						      &self->value.intval,
-						      set_doc, show_doc,
-						      help_doc, set_func,
-						      show_func, set_list,
-						      show_list);
+    case var_pinteger:
+      commands = add_setshow_pinteger_cmd (cmd_name, cmd_class,
+					   &self->value.intval,
+					   extra_literals, set_doc,
+					   show_doc, help_doc, set_func,
+					   show_func, set_list, show_list);
       break;
 
     case var_string:
@@ -532,19 +579,17 @@ compute_enum_list (SCM enum_values_scm,
 
 static const scheme_integer_constant parameter_types[] =
 {
-  /* Note: var_integer is deprecated, and intentionally does not
-     appear here.  */
-  { "PARAM_BOOLEAN", var_boolean }, /* ARI: var_boolean */
-  { "PARAM_AUTO_BOOLEAN", var_auto_boolean },
-  { "PARAM_ZINTEGER", var_zinteger },
-  { "PARAM_UINTEGER", var_uinteger },
-  { "PARAM_ZUINTEGER", var_zuinteger },
-  { "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited },
-  { "PARAM_STRING", var_string },
-  { "PARAM_STRING_NOESCAPE", var_string_noescape },
-  { "PARAM_OPTIONAL_FILENAME", var_optional_filename },
-  { "PARAM_FILENAME", var_filename },
-  { "PARAM_ENUM", var_enum },
+  { "PARAM_BOOLEAN", param_boolean }, /* ARI: param_boolean */
+  { "PARAM_AUTO_BOOLEAN", param_auto_boolean },
+  { "PARAM_ZINTEGER", param_zinteger },
+  { "PARAM_UINTEGER", param_uinteger },
+  { "PARAM_ZUINTEGER", param_zuinteger },
+  { "PARAM_ZUINTEGER_UNLIMITED", param_zuinteger_unlimited },
+  { "PARAM_STRING", param_string },
+  { "PARAM_STRING_NOESCAPE", param_string_noescape },
+  { "PARAM_OPTIONAL_FILENAME", param_optional_filename },
+  { "PARAM_FILENAME", param_filename },
+  { "PARAM_ENUM", param_enum },
 
   END_INTEGER_CONSTANTS
 };
@@ -568,7 +613,7 @@ pascm_valid_parameter_type_p (int param_
 /* Return PARAM_TYPE as a string.  */
 
 static const char *
-pascm_param_type_name (enum var_types param_type)
+pascm_param_type_name (enum param_types param_type)
 {
   int i;
 
@@ -588,10 +633,6 @@ pascm_param_type_name (enum var_types pa
 static SCM
 pascm_param_value (const setting &var, int arg_pos, const char *func_name)
 {
-  /* Note: We *could* support var_integer here in case someone is trying to get
-     the value of a Python-created parameter (which is the only place that
-     still supports var_integer).  To further discourage its use we do not.  */
-
   switch (var.type ())
     {
     case var_string:
@@ -631,20 +672,29 @@ pascm_param_value (const setting &var, i
 	  return auto_keyword;
       }
 
-    case var_zuinteger_unlimited:
-      if (var.get<int> () == -1)
-	return unlimited_keyword;
-      gdb_assert (var.get<int> () >= 0);
-      /* Fall through.  */
-    case var_zinteger:
-      return scm_from_int (var.get<int> ());
-
     case var_uinteger:
-      if (var.get<unsigned int> ()== UINT_MAX)
-	return unlimited_keyword;
-      /* Fall through.  */
-    case var_zuinteger:
-      return scm_from_uint (var.get<unsigned int> ());
+    case var_integer:
+    case var_pinteger:
+      {
+	LONGEST value
+	  = (var.type () == var_uinteger
+	     ? static_cast<LONGEST> (var.get<unsigned int> ())
+	     : static_cast<LONGEST> (var.get<int> ()));
+
+	if (var.extra_literals () != nullptr)
+	  for (const literal_def *l = var.extra_literals ();
+	       l->literal != nullptr;
+	       l++)
+	    if (value == l->use)
+	      return scm_from_latin1_keyword (l->literal);
+	if (var.type () == var_pinteger)
+	  gdb_assert (value >= 0);
+
+	if (var.type () == var_uinteger)
+	  return scm_from_uint (static_cast<const unsigned int> (value));
+	else
+	  return scm_from_int (static_cast<const int> (value));
+      }
 
     default:
       break;
@@ -735,53 +785,91 @@ pascm_set_param_value_x (param_smob *p_s
 	var.set<enum auto_boolean> (AUTO_BOOLEAN_FALSE);
       break;
 
-    case var_zinteger:
+    case var_integer:
     case var_uinteger:
-    case var_zuinteger:
-    case var_zuinteger_unlimited:
-      if (var.type () == var_uinteger
-	  || var.type () == var_zuinteger_unlimited)
-	{
-	  SCM_ASSERT_TYPE (scm_is_integer (value)
-			   || scm_is_eq (value, unlimited_keyword),
-			   value, arg_pos, func_name,
-			   _("integer or #:unlimited"));
-	  if (scm_is_eq (value, unlimited_keyword))
+    case var_pinteger:
+      {
+	const literal_def *extra_literals = p_smob->extra_literals;
+	enum tribool allowed = TRIBOOL_UNKNOWN;
+	enum var_types var_type = var.type ();
+	bool integer = scm_is_integer (value);
+	bool keyword = scm_is_keyword (value);
+	std::string buffer = "";
+	size_t count = 0;
+	LONGEST val;
+
+	if (extra_literals != nullptr)
+	  for (const literal_def *l = extra_literals;
+	       l->literal != nullptr;
+	       l++, count++)
 	    {
-	      if (var.type () == var_uinteger)
-		var.set<unsigned int> (UINT_MAX);
-	      else
-		var.set<int> (-1);
-	      break;
+	      if (count != 0)
+		buffer += ", ";
+	      buffer = buffer + "#:" + l->literal;
+	      if (keyword
+		  && allowed == TRIBOOL_UNKNOWN
+		  && scm_is_eq (value,
+				scm_from_latin1_keyword (l->literal)))
+		{
+		  val = l->use;
+		  allowed = TRIBOOL_TRUE;
+		}
 	    }
-	}
-      else
-	{
-	  SCM_ASSERT_TYPE (scm_is_integer (value), value, arg_pos, func_name,
-			   _("integer"));
-	}
 
-      if (var.type () == var_uinteger
-	  || var.type () == var_zuinteger)
-	{
-	  unsigned int u = scm_to_uint (value);
+	if (allowed == TRIBOOL_UNKNOWN)
+	  {
+	    if (extra_literals == nullptr)
+	      SCM_ASSERT_TYPE (integer, value, arg_pos, func_name,
+			       _("integer"));
+	    else if (count > 1)
+	      SCM_ASSERT_TYPE (integer, value, arg_pos, func_name,
+			       string_printf (_("integer or one of: %s"),
+					      buffer.c_str ()).c_str ());
+	    else
+	      SCM_ASSERT_TYPE (integer, value, arg_pos, func_name,
+			       string_printf (_("integer or %s"),
+					      buffer.c_str ()).c_str ());
 
-	  if (var.type () == var_uinteger && u == 0)
-	    u = UINT_MAX;
-	  var.set<unsigned int> (u);
-	}
-      else
-	{
-	  int i = scm_to_int (value);
+	    val = (var_type == var_uinteger
+		   ? static_cast<LONGEST> (scm_to_uint (value))
+		   : static_cast<LONGEST> (scm_to_int (value)));
 
-	  if (var.type () == var_zuinteger_unlimited && i < -1)
-	    {
-	      gdbscm_out_of_range_error (func_name, arg_pos, value,
-					 _("must be >= -1"));
+	    if (extra_literals != nullptr)
+	      for (const literal_def *l = extra_literals;
+		   l->literal != nullptr;
+		   l++)
+		{
+		  if (l->val.has_value () && val == *l->val)
+		    {
+		      allowed = TRIBOOL_TRUE;
+		      val = l->use;
+		      break;
+		    }
+		  else if (val == l->use)
+		    allowed = TRIBOOL_FALSE;
+		}
 	    }
-	  var.set<int> (i);
-	}
-      break;
+
+	if (allowed == TRIBOOL_UNKNOWN)
+	  {
+	    if (val > UINT_MAX || val < INT_MIN
+		|| (var_type == var_uinteger && val < 0)
+		|| (var_type == var_integer && val > INT_MAX)
+		|| (var_type == var_pinteger && val < 0)
+		|| (var_type == var_pinteger && val > INT_MAX))
+	      allowed = TRIBOOL_FALSE;
+	  }
+	if (allowed == TRIBOOL_FALSE)
+	  gdbscm_out_of_range_error (func_name, arg_pos, value,
+				     _("integer out of range"));
+
+	if (var_type == var_uinteger)
+	  var.set<unsigned int> (static_cast<const unsigned int> (val));
+	else
+	  var.set<int> (static_cast<const int> (val));
+
+	break;
+      }
 
     default:
       gdb_assert_not_reached ("bad parameter type");
@@ -858,7 +946,7 @@ gdbscm_make_parameter (SCM name_scm, SCM
   char *s;
   char *name;
   int cmd_class = no_class;
-  int param_type = var_boolean; /* ARI: var_boolean */
+  int param_type = param_boolean; /* ARI: param_boolean */
   SCM enum_list_scm = SCM_BOOL_F;
   SCM set_func = SCM_BOOL_F, show_func = SCM_BOOL_F;
   char *doc = NULL, *set_doc = NULL, *show_doc = NULL;
@@ -913,12 +1001,12 @@ gdbscm_make_parameter (SCM name_scm, SCM
 				 scm_from_int (param_type),
 				 _("invalid parameter type argument"));
     }
-  if (enum_list_arg_pos > 0 && param_type != var_enum)
+  if (enum_list_arg_pos > 0 && param_type != param_enum)
     {
       gdbscm_misc_error (FUNC_NAME, enum_list_arg_pos, enum_list_scm,
 		_("#:enum-values can only be provided with PARAM_ENUM"));
     }
-  if (enum_list_arg_pos < 0 && param_type == var_enum)
+  if (enum_list_arg_pos < 0 && param_type == param_enum)
     {
       gdbscm_misc_error (FUNC_NAME, GDBSCM_ARG_NONE, SCM_BOOL_F,
 			 _("PARAM_ENUM requires an enum-values argument"));
@@ -933,7 +1021,7 @@ gdbscm_make_parameter (SCM name_scm, SCM
       SCM_ASSERT_TYPE (gdbscm_is_procedure (show_func), show_func,
 		       show_func_arg_pos, FUNC_NAME, _("procedure"));
     }
-  if (param_type == var_enum)
+  if (param_type == param_enum)
     {
       /* Note: enum_list lives in GC space, so we don't have to worry about
 	 freeing it if we later throw an exception.  */
@@ -950,7 +1038,10 @@ gdbscm_make_parameter (SCM name_scm, SCM
      freeing them if we throw an exception.  */
   p_smob->name = name;
   p_smob->cmd_class = (enum command_class) cmd_class;
-  p_smob->type = (enum var_types) param_type;
+  p_smob->pname
+    = pascm_param_type_name (static_cast<enum param_types> (param_type));
+  p_smob->type = param_to_var[param_type].type;
+  p_smob->extra_literals = param_to_var[param_type].extra_literals;
   p_smob->doc = doc;
   p_smob->set_doc = set_doc;
   p_smob->show_doc = show_doc;
@@ -1031,7 +1122,8 @@ gdbscm_register_parameter_x (SCM self)
   try
     {
       p_smob->commands = add_setshow_generic
-	(p_smob->type, p_smob->cmd_class, p_smob->cmd_name, p_smob,
+	(p_smob->type, p_smob->extra_literals,
+	 p_smob->cmd_class, p_smob->cmd_name, p_smob,
 	 p_smob->set_doc, p_smob->show_doc, p_smob->doc,
 	 (gdbscm_is_procedure (p_smob->set_func) ? pascm_set_func : NULL),
 	 (gdbscm_is_procedure (p_smob->show_func) ? pascm_show_func : NULL),
@@ -1203,5 +1295,4 @@ gdbscm_initialize_parameters (void)
   show_doc_keyword = scm_from_latin1_keyword ("show-doc");
   initial_value_keyword = scm_from_latin1_keyword ("initial-value");
   auto_keyword = scm_from_latin1_keyword ("auto");
-  unlimited_keyword = scm_from_latin1_keyword ("unlimited");
 }
Index: src/gdb/maint-test-options.c
===================================================================
--- src.orig/gdb/maint-test-options.c
+++ src/gdb/maint-test-options.c
@@ -61,13 +61,15 @@
    available kinds of commands (boolean, enum, flag, string, uinteger):
 
     (gdb) maint test-options require-delimiter -[TAB]
-    -bool      -enum      -flag      -string     -uinteger   -xx1       -xx2
+    -bool                -pinteger-unlimited  -xx1
+    -enum                -string              -xx2
+    -flag                -uinteger-unlimited
 
     (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]
+    (gdb) maint test-options require-delimiter -uinteger-unlimited [TAB]
     NUMBER     unlimited
 
    '-xx1' and '-xx2' are flag options too.  They exist in order to
@@ -76,14 +78,14 @@
   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
+   -flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint-unl 0 -pint-unl 0 -string '' -- 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
+   -flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0 -string '' -- -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
+   -flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint-unl 0 -pint-unl 0 -string '' -- cmdarg
 
   The "maint show test-options-completion-result" command exists in
   order to do something similar for completion:
@@ -131,8 +133,8 @@ struct test_options_opts
   bool xx2_opt = false;
   bool boolean_opt = false;
   const char *enum_opt = test_options_enum_values_xxx;
-  unsigned int uint_opt = 0;
-  int zuint_unl_opt = 0;
+  unsigned int uint_unl_opt = 0;
+  int pint_unl_opt = 0;
   std::string string_opt;
 
   test_options_opts () = default;
@@ -145,18 +147,18 @@ struct test_options_opts
   {
     gdb_printf (file,
 		_("-flag %d -xx1 %d -xx2 %d -bool %d "
-		  "-enum %s -uint %s -zuint-unl %s -string '%s' -- %s\n"),
+		  "-enum %s -uint-unl %s -pint-unl %s -string '%s' -- %s\n"),
 		flag_opt,
 		xx1_opt,
 		xx2_opt,
 		boolean_opt,
 		enum_opt,
-		(uint_opt == UINT_MAX
+		(uint_unl_opt == UINT_MAX
 		 ? "unlimited"
-		 : pulongest (uint_opt)),
-		(zuint_unl_opt == -1
+		 : pulongest (uint_unl_opt)),
+		(pint_unl_opt == -1
 		 ? "unlimited"
-		 : plongest (zuint_unl_opt)),
+		 : plongest (pint_unl_opt)),
 		string_opt.c_str (),
 		args);
   }
@@ -203,22 +205,24 @@ static const gdb::option::option_def tes
     N_("An enum option."),
   },
 
-  /* A uinteger option.  */
+  /* A uinteger + "unlimited" option.  */
   gdb::option::uinteger_option_def<test_options_opts> {
-    "uinteger",
-    [] (test_options_opts *opts) { return &opts->uint_opt; },
+    "uinteger-unlimited",
+    [] (test_options_opts *opts) { return &opts->uint_unl_opt; },
+    uinteger_unlimited_literals,
     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; },
+  /* A pinteger + "unlimited" option.  */
+  gdb::option::pinteger_option_def<test_options_opts> {
+    "pinteger-unlimited",
+    [] (test_options_opts *opts) { return &opts->pint_unl_opt; },
+    pinteger_unlimited_literals,
     nullptr, /* show_cmd_cb */
-    N_("A zuinteger-unlimited option."),
+    N_("A pinteger-unlimited option."),
     nullptr, /* show_doc */
     nullptr, /* help_doc */
   },
Index: src/gdb/python/py-param.c
===================================================================
--- src.orig/gdb/python/py-param.c
+++ src/gdb/python/py-param.c
@@ -28,24 +28,70 @@
 #include "language.h"
 #include "arch-utils.h"
 
+/* Python parameter types as in PARM_CONSTANTS below.  */
+
+typedef enum param_types
+  {
+    param_boolean,
+    param_auto_boolean,
+    param_uinteger,
+    param_integer,
+    param_string,
+    param_string_noescape,
+    param_optional_filename,
+    param_filename,
+    param_zinteger,
+    param_zuinteger,
+    param_zuinteger_unlimited,
+    param_enum,
+  }
+param_types;
+
+/* Translation from Python parameters to GDB variable types.  Keep in the
+   same order as PARAM_TYPES due to C++'s lack of designated initializers.  */
+
+static const struct
+  {
+    /* The type of the parameter.  */
+    enum var_types type;
+
+    /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
+    const literal_def *extra_literals;
+  }
+param_to_var[] =
+  {
+    { var_boolean },
+    { var_auto_boolean },
+    { var_uinteger, uinteger_unlimited_literals },
+    { var_integer, integer_unlimited_literals },
+    { var_string },
+    { var_string_noescape },
+    { var_optional_filename },
+    { var_filename },
+    { var_integer },
+    { var_uinteger },
+    { var_pinteger, pinteger_unlimited_literals },
+    { var_enum }
+  };
+
 /* Parameter constants and their values.  */
 static struct {
   const char *name;
   int value;
 } parm_constants[] =
 {
-  { "PARAM_BOOLEAN", var_boolean }, /* ARI: var_boolean */
-  { "PARAM_AUTO_BOOLEAN", var_auto_boolean },
-  { "PARAM_UINTEGER", var_uinteger },
-  { "PARAM_INTEGER", var_integer },
-  { "PARAM_STRING", var_string },
-  { "PARAM_STRING_NOESCAPE", var_string_noescape },
-  { "PARAM_OPTIONAL_FILENAME", var_optional_filename },
-  { "PARAM_FILENAME", var_filename },
-  { "PARAM_ZINTEGER", var_zinteger },
-  { "PARAM_ZUINTEGER", var_zuinteger },
-  { "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited },
-  { "PARAM_ENUM", var_enum },
+  { "PARAM_BOOLEAN", param_boolean }, /* ARI: param_boolean */
+  { "PARAM_AUTO_BOOLEAN", param_auto_boolean },
+  { "PARAM_UINTEGER", param_uinteger },
+  { "PARAM_INTEGER", param_integer },
+  { "PARAM_STRING", param_string },
+  { "PARAM_STRING_NOESCAPE", param_string_noescape },
+  { "PARAM_OPTIONAL_FILENAME", param_optional_filename },
+  { "PARAM_FILENAME", param_filename },
+  { "PARAM_ZINTEGER", param_zinteger },
+  { "PARAM_ZUINTEGER", param_zuinteger },
+  { "PARAM_ZUINTEGER_UNLIMITED", param_zuinteger_unlimited },
+  { "PARAM_ENUM", param_enum },
   { NULL, 0 }
 };
 
@@ -80,6 +126,9 @@ struct parmpy_object
   /* The type of the parameter.  */
   enum var_types type;
 
+  /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
+  const literal_def *extra_literals;
+
   /* The value of the parameter.  */
   union parmpy_variable value;
 
@@ -96,18 +145,20 @@ struct parmpy_object
 static setting
 make_setting (parmpy_object *s)
 {
-  if (var_type_uses<bool> (s->type))
-    return setting (s->type, &s->value.boolval);
-  else if (var_type_uses<int> (s->type))
-    return setting (s->type, &s->value.intval);
-  else if (var_type_uses<auto_boolean> (s->type))
-    return setting (s->type, &s->value.autoboolval);
-  else if (var_type_uses<unsigned int> (s->type))
-    return setting (s->type, &s->value.uintval);
-  else if (var_type_uses<std::string> (s->type))
-    return setting (s->type, s->value.stringval);
-  else if (var_type_uses<const char *> (s->type))
-    return setting (s->type, &s->value.cstringval);
+  enum var_types type = s->type;
+
+  if (var_type_uses<bool> (type))
+    return setting (type, &s->value.boolval);
+  else if (var_type_uses<int> (type))
+    return setting (type, &s->value.intval, s->extra_literals);
+  else if (var_type_uses<auto_boolean> (type))
+    return setting (type, &s->value.autoboolval);
+  else if (var_type_uses<unsigned int> (type))
+    return setting (type, &s->value.uintval, s->extra_literals);
+  else if (var_type_uses<std::string> (type))
+    return setting (type, s->value.stringval);
+  else if (var_type_uses<const char *> (type))
+    return setting (type, &s->value.cstringval);
   else
     gdb_assert_not_reached ("unhandled var type");
 }
@@ -234,68 +285,98 @@ set_parameter_value (parmpy_object *self
 	}
       break;
 
-    case var_integer:
-    case var_zinteger:
     case var_uinteger:
-    case var_zuinteger:
-    case var_zuinteger_unlimited:
+    case var_integer:
+    case var_pinteger:
       {
-	long l;
-	int ok;
+	const literal_def *extra_literals = self->extra_literals;
+	enum tribool allowed = TRIBOOL_UNKNOWN;
+	enum var_types var_type = self->type;
+	std::string buffer = "";
+	size_t count = 0;
+	LONGEST val;
 
-	if (value == Py_None
-	    && (self->type == var_uinteger || self->type == var_integer))
-	  l = 0;
-	else if (value == Py_None && self->type == var_zuinteger_unlimited)
-	  l = -1;
-	else if (!PyLong_Check (value))
+	if (extra_literals != nullptr)
 	  {
-	    PyErr_SetString (PyExc_RuntimeError,
-			     _("The value must be integer."));
-	    return -1;
+	    gdb::unique_xmalloc_ptr<char>
+	      str (python_string_to_host_string (value));
+	    const char *s = str != nullptr ? str.get () : nullptr;
+	    PyErr_Clear ();
+
+	    for (const literal_def *l = extra_literals;
+		 l->literal != nullptr;
+		 l++, count++)
+	      {
+		if (count != 0)
+		  buffer += ", ";
+		buffer = buffer + "'" + l->literal + "'";
+		if (allowed == TRIBOOL_UNKNOWN
+		    && ((value == Py_None && !strcmp ("unlimited", l->literal))
+			|| (s != nullptr && !strcmp (s, l->literal))))
+		  {
+		    val = l->use;
+		    allowed = TRIBOOL_TRUE;
+		  }
+	      }
 	  }
-	else if (! gdb_py_int_as_long (value, &l))
-	  return -1;
 
-	switch (self->type)
+	if (allowed == TRIBOOL_UNKNOWN)
 	  {
-	  case var_uinteger:
-	    if (l == 0)
-	      l = UINT_MAX;
-	    /* Fall through.  */
-	  case var_zuinteger:
-	    ok = (l >= 0 && l <= UINT_MAX);
-	    break;
+	    val = PyLong_AsLongLong (value);
 
-	  case var_zuinteger_unlimited:
-	    ok = (l >= -1 && l <= INT_MAX);
-	    break;
+	    if (PyErr_Occurred ())
+	      {
+		if (extra_literals == nullptr)
+		  PyErr_SetString (PyExc_RuntimeError,
+				   _("The value must be integer."));
+		else if (count > 1)
+		  PyErr_SetString (PyExc_RuntimeError,
+				   string_printf (_("integer or one of: %s"),
+						  buffer.c_str ()).c_str ());
+		else
+		  PyErr_SetString (PyExc_RuntimeError,
+				   string_printf (_("integer or %s"),
+						  buffer.c_str ()).c_str ());
+		return -1;
+	      }
 
-	  case var_integer:
-	    ok = (l >= INT_MIN && l <= INT_MAX);
-	    if (l == 0)
-	      l = INT_MAX;
-	    break;
 
-	  case var_zinteger:
-	    ok = (l >= INT_MIN && l <= INT_MAX);
-	    break;
+	    if (extra_literals != nullptr)
+	      for (const literal_def *l = extra_literals;
+		   l->literal != nullptr;
+		   l++)
+		{
+		  if (l->val.has_value () && val == *l->val)
+		    {
+		      allowed = TRIBOOL_TRUE;
+		      val = l->use;
+		      break;
+		    }
+		  else if (val == l->use)
+		    allowed = TRIBOOL_FALSE;
+		}
+	    }
 
-	  default:
-	    gdb_assert_not_reached ("unknown var_ constant");
+	if (allowed == TRIBOOL_UNKNOWN)
+	  {
+	    if (val > UINT_MAX || val < INT_MIN
+		|| (var_type == var_uinteger && val < 0)
+		|| (var_type == var_integer && val > INT_MAX)
+		|| (var_type == var_pinteger && val < 0)
+		|| (var_type == var_pinteger && val > INT_MAX))
+	      allowed = TRIBOOL_FALSE;
 	  }
-
-	if (! ok)
+	if (allowed == TRIBOOL_FALSE)
 	  {
 	    PyErr_SetString (PyExc_RuntimeError,
 			     _("Range exceeded."));
 	    return -1;
 	  }
 
-	if (self->type == var_uinteger || self->type == var_zuinteger)
-	  self->value.uintval = (unsigned) l;
+	if (self->type == var_uinteger)
+	  self->value.uintval = (unsigned) val;
 	else
-	  self->value.intval = (int) l;
+	  self->value.intval = (int) val;
 	break;
       }
 
@@ -534,7 +615,8 @@ get_show_value (struct ui_file *file, in
 /* A helper function that dispatches to the appropriate add_setshow
    function.  */
 static void
-add_setshow_generic (int parmclass, enum command_class cmdclass,
+add_setshow_generic (enum var_types type, const literal_def *extra_literals,
+		     enum command_class cmdclass,
 		     gdb::unique_xmalloc_ptr<char> cmd_name,
 		     parmpy_object *self,
 		     const char *set_doc, const char *show_doc,
@@ -544,7 +626,7 @@ add_setshow_generic (int parmclass, enum
 {
   set_show_commands commands;
 
-  switch (parmclass)
+  switch (type)
     {
     case var_boolean:
       commands = add_setshow_boolean_cmd (cmd_name.get (), cmdclass,
@@ -564,18 +646,28 @@ add_setshow_generic (int parmclass, enum
 
     case var_uinteger:
       commands = add_setshow_uinteger_cmd (cmd_name.get (), cmdclass,
-					   &self->value.uintval, set_doc,
+					   &self->value.uintval,
+					   extra_literals, set_doc,
 					   show_doc, help_doc, get_set_value,
 					   get_show_value, set_list, show_list);
       break;
 
     case var_integer:
       commands = add_setshow_integer_cmd (cmd_name.get (), cmdclass,
-					  &self->value.intval, set_doc,
+					  &self->value.intval,
+					  extra_literals, set_doc,
 					  show_doc, help_doc, get_set_value,
 					  get_show_value, set_list, show_list);
       break;
 
+    case var_pinteger:
+      commands = add_setshow_pinteger_cmd (cmd_name.get (), cmdclass,
+					   &self->value.intval,
+					   extra_literals, set_doc,
+					   show_doc, help_doc, get_set_value,
+					   get_show_value, set_list, show_list);
+      break;
+
     case var_string:
       commands = add_setshow_string_cmd (cmd_name.get (), cmdclass,
 					 self->value.stringval, set_doc,
@@ -607,30 +699,6 @@ add_setshow_generic (int parmclass, enum
 					   get_show_value, set_list, show_list);
       break;
 
-    case var_zinteger:
-      commands = add_setshow_zinteger_cmd (cmd_name.get (), cmdclass,
-					   &self->value.intval, set_doc,
-					   show_doc, help_doc, get_set_value,
-					   get_show_value, set_list, show_list);
-      break;
-
-    case var_zuinteger:
-      commands = add_setshow_zuinteger_cmd (cmd_name.get (), cmdclass,
-					    &self->value.uintval, set_doc,
-					    show_doc, help_doc, get_set_value,
-					    get_show_value, set_list,
-					    show_list);
-      break;
-
-    case var_zuinteger_unlimited:
-      commands = add_setshow_zuinteger_unlimited_cmd (cmd_name.get (), cmdclass,
-						      &self->value.intval,
-						      set_doc, show_doc,
-						      help_doc, get_set_value,
-						      get_show_value, set_list,
-						      show_list);
-      break;
-
     case var_enum:
       /* Initialize the value, just in case.  */
       self->value.cstringval = self->enumeration[0];
@@ -740,6 +808,8 @@ parmpy_init (PyObject *self, PyObject *a
   int parmclass, cmdtype;
   PyObject *enum_values = NULL;
   struct cmd_list_element **set_list, **show_list;
+  const literal_def *extra_literals;
+  enum var_types type;
 
   if (! PyArg_ParseTuple (args, "sii|O", &name, &cmdtype, &parmclass,
 			  &enum_values))
@@ -756,33 +826,36 @@ parmpy_init (PyObject *self, PyObject *a
       return -1;
     }
 
-  if (parmclass != var_boolean /* ARI: var_boolean */
-      && parmclass != var_auto_boolean
-      && parmclass != var_uinteger && parmclass != var_integer
-      && parmclass != var_string && parmclass != var_string_noescape
-      && parmclass != var_optional_filename && parmclass != var_filename
-      && parmclass != var_zinteger && parmclass != var_zuinteger
-      && parmclass != var_zuinteger_unlimited && parmclass != var_enum)
+  if (parmclass != param_boolean /* ARI: param_boolean */
+      && parmclass != param_auto_boolean
+      && parmclass != param_uinteger && parmclass != param_integer
+      && parmclass != param_string && parmclass != param_string_noescape
+      && parmclass != param_optional_filename && parmclass != param_filename
+      && parmclass != param_zinteger && parmclass != param_zuinteger
+      && parmclass != param_zuinteger_unlimited && parmclass != param_enum)
     {
       PyErr_SetString (PyExc_RuntimeError,
 		       _("Invalid parameter class argument."));
       return -1;
     }
 
-  if (enum_values && parmclass != var_enum)
+  if (enum_values && parmclass != param_enum)
     {
       PyErr_SetString (PyExc_RuntimeError,
 		       _("Only PARAM_ENUM accepts a fourth argument."));
       return -1;
     }
-  if (parmclass == var_enum)
+  if (parmclass == param_enum)
     {
       if (! compute_enum_values (obj, enum_values))
 	return -1;
     }
   else
     obj->enumeration = NULL;
-  obj->type = (enum var_types) parmclass;
+  type = param_to_var[parmclass].type;
+  extra_literals = param_to_var[parmclass].extra_literals;
+  obj->type = type;
+  obj->extra_literals = extra_literals;
   memset (&obj->value, 0, sizeof (obj->value));
 
   if (var_type_uses<std::string> (obj->type))
@@ -805,7 +878,8 @@ parmpy_init (PyObject *self, PyObject *a
 
   try
     {
-      add_setshow_generic (parmclass, (enum command_class) cmdtype,
+      add_setshow_generic (type, extra_literals,
+			   (enum command_class) cmdtype,
 			   std::move (cmd_name), obj,
 			   set_doc.get (), show_doc.get (),
 			   doc.get (), set_list, show_list);
Index: src/gdb/python/python.c
===================================================================
--- src.orig/gdb/python/python.c
+++ src/gdb/python/python.c
@@ -504,27 +504,45 @@ gdbpy_parameter_value (const setting &va
 	  Py_RETURN_NONE;
       }
 
-    case var_integer:
-      if (var.get<int> () == INT_MAX)
-	Py_RETURN_NONE;
-      /* Fall through.  */
-    case var_zinteger:
-    case var_zuinteger_unlimited:
-      return gdb_py_object_from_longest (var.get<int> ()).release ();
-
     case var_uinteger:
+    case var_integer:
+    case var_pinteger:
       {
-	unsigned int val = var.get<unsigned int> ();
+	LONGEST value
+	  = (var.type () == var_uinteger
+	     ? static_cast<LONGEST> (var.get<unsigned int> ())
+	     : static_cast<LONGEST> (var.get<int> ()));
 
-	if (val == UINT_MAX)
-	  Py_RETURN_NONE;
-	return gdb_py_object_from_ulongest (val).release ();
-      }
+	if (var.extra_literals () != nullptr)
+	  for (const literal_def *l = var.extra_literals ();
+	       l->literal != nullptr;
+	       l++)
+	    if (value == l->use)
+	      {
+		if (strcmp (l->literal, "unlimited") == 0)
+		  {
+		    /* Compatibility hack for API brokennes.  */
+		    if (var.type () == var_pinteger
+			&& l->val.has_value ()
+			&& *l->val == -1)
+		      value = -1;
+		    else
+		      Py_RETURN_NONE;
+		  }
+		else if (l->val.has_value ())
+		  value = *l->val;
+		else
+		  return host_string_to_python_string (l->literal).release ();
+	      }
 
-    case var_zuinteger:
-      {
-	unsigned int val = var.get<unsigned int> ();
-	return gdb_py_object_from_ulongest (val).release ();
+	if (var.type () == var_uinteger)
+	  return
+	    gdb_py_object_from_ulongest
+	      (static_cast<const unsigned int> (value)).release ();
+	else
+	  return
+	    gdb_py_object_from_longest
+	      (static_cast<const int> (value)).release ();
       }
     }
 
Index: src/gdb/testsuite/gdb.base/max-value-size.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/max-value-size.exp
+++ src/gdb/testsuite/gdb.base/max-value-size.exp
@@ -92,4 +92,4 @@ gdb_test "set max-value-size 1" \
 gdb_test "set max-value-size 0" \
     "max-value-size set too low, increasing to \[0-9\]+ bytes"
 gdb_test "set max-value-size -5" \
-    "only -1 is allowed to set as unlimited"
+    "integer -5 out of range"
Index: src/gdb/testsuite/gdb.base/options.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/options.exp
+++ src/gdb/testsuite/gdb.base/options.exp
@@ -97,19 +97,22 @@ proc make_cmd {variant} {
 # 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 -string '' -- $operand"
+    return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\
+	    -string '' -- $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 -string '' -- $operand"
+    return "-flag 1 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\
+	    -string '' -- $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 -string '' -- $operand"
+    return "-flag 0 -xx1 0 -xx2 0 -bool 1 -enum xxx -uint-unl 0 -pint-unl 0\
+	    -string '' -- $operand"
 }
 
 # Return a string for the expected result of running "maint
@@ -117,10 +120,12 @@ proc expect_bool {operand} {
 # 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 -string '' -- $operand"
-    } elseif {$option == "zuinteger-unlimited"} {
-	return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint 0 -zuint-unl $val -string '' -- $operand"
+    if {$option == "uinteger-unlimited"} {
+	return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl $val\
+		-pint-unl 0 -string '' -- $operand"
+    } elseif {$option == "pinteger-unlimited"} {
+	return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0\
+		-pint-unl $val -string '' -- $operand"
     } else {
 	error "unsupported option: $option"
     }
@@ -137,18 +142,19 @@ proc expect_string {str operand} {
 	     && [string range $str end end] == "'")} {
 	set str [string range $str 1 end-1]
     }
-    return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint 0 -zuint-unl 0 -string '$str' -- $operand"
+    return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\
+	    -string '$str' -- $operand"
 }
 
 set all_options {
     "-bool"
     "-enum"
     "-flag"
+    "-pinteger-unlimited"
     "-string"
-    "-uinteger"
+    "-uinteger-unlimited"
     "-xx1"
     "-xx2"
-    "-zuinteger-unlimited"
 }
 
 # Basic option-machinery + "print" command integration tests.
@@ -603,7 +609,8 @@ proc_with_prefix test-flag {variant} {
 
     # 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 -string '' -- non flags args"
+	"-flag 0 -xx1 1 -xx2 1 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\
+	 -string '' -- non flags args"
 
     # Extract 2 known flags in front of unknown flags.
     gdb_test "$cmd -xx1 -xx2 -a -b -c -xx1 --" \
@@ -821,13 +828,13 @@ proc_with_prefix test-boolean {variant}
 }
 
 # Uinteger option tests.  OPTION is which integer option we're
-# testing.  Can be "uinteger" or "zuinteger-unlimited".
+# testing.  Can be "uinteger-unlimited" or "pinteger-unlimited".
 proc_with_prefix test-uinteger {variant option} {
     global all_options
 
     set cmd "[make_cmd $variant] -$option"
 
-    # Test completing a uinteger option:
+    # Test completing an integer option:
     res_test_gdb_complete_multiple \
 	"1 [expect_none ""]" \
 	"$cmd " "" "" {
@@ -851,7 +858,7 @@ proc_with_prefix test-uinteger {variant
     gdb_test "$cmd 1 -- 999" [expect_integer $option "1" "999"]
     gdb_test "$cmd unlimited -- 999" \
 	[expect_integer $option "unlimited" "999"]
-    if {$option == "zuinteger-unlimited"} {
+    if {$option == "pinteger-unlimited"} {
 	gdb_test "$cmd -1 --" [expect_integer $option "unlimited" ""]
 	gdb_test "$cmd 0 --" [expect_integer $option "0" ""]
     } else {
@@ -864,7 +871,7 @@ proc_with_prefix test-uinteger {variant
 	"Expected integer at: unlimitedx --"
 
     # Don't offer completions until we're past the
-    # -uinteger/-zuinteger-unlimited argument.
+    # -uinteger-unlimited/-pinteger-unlimited argument.
     res_test_gdb_complete_none \
 	"1 [expect_none ""]" \
 	"$cmd 1"
@@ -877,15 +884,15 @@ proc_with_prefix test-uinteger {variant
     }
 
     # Try "-1".
-    if {$option == "uinteger"} {
-	# -1 is invalid uinteger.
+    if {$option == "uinteger-unlimited"} {
+	# -1 is invalid uinteger-unlimited.
 	foreach value {"-1" "-1 "} {
 	    res_test_gdb_complete_none \
 		"1 [expect_none ""]" \
 		"$cmd $value"
 	}
     } else {
-	# -1 is valid for zuinteger-unlimited.
+	# -1 is valid for pinteger-unlimited.
 	res_test_gdb_complete_none \
 	    "1 [expect_none ""]" \
 	    "$cmd -1"
@@ -913,7 +920,7 @@ proc_with_prefix test-uinteger {variant
 	res_test_gdb_complete_none "0 " "$cmd 1 "
     }
 
-    # Test completing non-option arguments after "-uinteger 1 ".
+    # Test completing non-option arguments after "-uinteger-unlimited 1 ".
     foreach operand {"x" "x " "1a" "1a " "1-" "1- "} {
 	if {$variant == "require-delimiter"} {
 	    res_test_gdb_complete_none \
@@ -1031,7 +1038,7 @@ foreach_with_prefix cmd {
     test-misc $cmd
     test-flag $cmd
     test-boolean $cmd
-    foreach subcmd {"uinteger" "zuinteger-unlimited" } {
+    foreach subcmd {"uinteger-unlimited" "pinteger-unlimited" } {
 	test-uinteger $cmd $subcmd
     }
     test-enum $cmd
Index: src/gdb/testsuite/gdb.base/settings.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/settings.exp
+++ src/gdb/testsuite/gdb.base/settings.exp
@@ -143,7 +143,7 @@ proc test-integer {variant} {
     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"
+	gdb_test "$set_cmd -2" "integer -2 out of range"
 	check_type "test-settings $variant" "type = int"
     } elseif {$variant == "uinteger" || $variant == "zuinteger"} {
 	# Negative values are not accepted.
Index: src/gdb/testsuite/gdb.base/with.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/with.exp
+++ src/gdb/testsuite/gdb.base/with.exp
@@ -104,7 +104,7 @@ with_test_prefix "maint" {
     test_with_error "zuinteger" "" \
 	"Argument required \\(integer to set it to\\)\\."
     test_with_error "zuinteger-unlimited" "-2" \
-	"only -1 is allowed to set as unlimited"
+	"integer -2 out of range"
     test_with_error "zuinteger-unlimited" "" \
 	"Argument required \\(integer to set it to, or \"unlimited\"\\)\\."
     test_with_error "filename" "" \
Index: src/gdb/testsuite/gdb.guile/scm-parameter.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.guile/scm-parameter.exp
+++ src/gdb/testsuite/gdb.guile/scm-parameter.exp
@@ -114,24 +114,18 @@ foreach_with_prefix param {
 	    \\(3\\) \\(3\\)\\)>"
     switch -- $param {
 	"listsize" {
-	    set param_get_one $param_type_error
-	    set param_get_zero $param_type_error
-	    set param_get_minus_one $param_type_error
-	    set param_get_unlimited $param_type_error
+	    set param_get_zero "#:unlimited"
+	    set param_get_minus_one -1
 	    set param_set_minus_one ""
 	}
 	"print elements" {
-	    set param_get_one 1
 	    set param_get_zero "#:unlimited"
 	    set param_get_minus_one "#:unlimited"
-	    set param_get_unlimited "#:unlimited"
 	    set param_set_minus_one $param_range_error
 	}
 	"max-completions" {
-	    set param_get_one 1
 	    set param_get_zero 0
 	    set param_get_minus_one "#:unlimited"
-	    set param_get_unlimited "#:unlimited"
 	    set param_set_minus_one ""
 	}
 	default {
@@ -142,7 +136,7 @@ foreach_with_prefix param {
     gdb_test_no_output "set $param 1" "test set to 1"
 
     gdb_test "guile (print (parameter-value \"$param\"))" \
-	$param_get_one "test value of 1"
+	1 "test value of 1"
 
     gdb_test_no_output "set $param 0" "test set to 0"
 
@@ -158,7 +152,7 @@ foreach_with_prefix param {
     gdb_test_no_output "set $param unlimited" "test set to 'unlimited'"
 
     gdb_test "guile (print (parameter-value \"$param\"))" \
-	$param_get_unlimited "test value of 'unlimited'"
+	"#:unlimited" "test value of 'unlimited'"
 }
 
 foreach_with_prefix kind {
@@ -190,8 +184,7 @@ foreach_with_prefix kind {
 	     #:unlimited" \
 	    "Error while executing Scheme code\\."]
     set param_minus_one_error "integer -1 out of range"
-    set param_minus_two_range "integer -2 out of range"
-    set param_minus_two_unlimited "only -1 is allowed to set as unlimited"
+    set param_minus_two_error "integer -2 out of range"
     switch -- $kind {
 	PARAM_UINTEGER {
 	    set param_get_zero "#:unlimited"
@@ -200,7 +193,7 @@ foreach_with_prefix kind {
 	    set param_str_unlimited unlimited
 	    set param_set_unlimited ""
 	    set param_set_minus_one $param_minus_one_error
-	    set param_set_minus_two $param_minus_two_range
+	    set param_set_minus_two $param_minus_two_error
 	}
 	PARAM_ZINTEGER {
 	    set param_get_zero 0
@@ -218,7 +211,7 @@ foreach_with_prefix kind {
 	    set param_str_unlimited 2
 	    set param_set_unlimited $param_integer_error
 	    set param_set_minus_one $param_minus_one_error
-	    set param_set_minus_two $param_minus_two_range
+	    set param_set_minus_two $param_minus_two_error
 	}
 	PARAM_ZUINTEGER_UNLIMITED {
 	    set param_get_zero 0
@@ -227,7 +220,7 @@ foreach_with_prefix kind {
 	    set param_str_unlimited unlimited
 	    set param_set_unlimited ""
 	    set param_set_minus_one ""
-	    set param_set_minus_two $param_minus_two_unlimited
+	    set param_set_minus_two $param_minus_two_error
 	}
 	default {
 	    error "invalid kind: $kind"
Index: src/gdb/testsuite/gdb.python/py-parameter.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.python/py-parameter.exp
+++ src/gdb/testsuite/gdb.python/py-parameter.exp
@@ -435,6 +435,7 @@ proc_with_prefix test_integer_parameter
 		set param_get_minus_one None
 		set param_get_minus_five 1
 		set param_get_none None
+		set param_get_unlimited None
 		set param_set_minus_one $param_range_error
 		set param_set_minus_five $param_range_error
 		set param_set_none ""
@@ -444,6 +445,7 @@ proc_with_prefix test_integer_parameter
 		set param_get_minus_one -1
 		set param_get_minus_five -5
 		set param_get_none None
+		set param_get_unlimited None
 		set param_set_minus_one -1
 		set param_set_minus_five -5
 		set param_set_none ""
@@ -453,6 +455,7 @@ proc_with_prefix test_integer_parameter
 		set param_get_minus_one -1
 		set param_get_minus_five -5
 		set param_get_none 5
+		set param_get_unlimited 0
 		set param_set_minus_one ""
 		set param_set_minus_five ""
 		set param_set_none $param_integer_error
@@ -462,6 +465,7 @@ proc_with_prefix test_integer_parameter
 		set param_get_minus_one 0
 		set param_get_minus_five 1
 		set param_get_none 5
+		set param_get_unlimited 0
 		set param_set_minus_one $param_range_error
 		set param_set_minus_five $param_range_error
 		set param_set_none $param_integer_error
@@ -471,6 +475,7 @@ proc_with_prefix test_integer_parameter
 		set param_get_minus_one -1
 		set param_get_minus_five 1
 		set param_get_none -1
+		set param_get_unlimited -1
 		set param_set_minus_one ""
 		set param_set_minus_five $param_range_error
 		set param_set_none ""
@@ -527,6 +532,16 @@ proc_with_prefix test_integer_parameter
 
 	gdb_test "python print(gdb.parameter('test-$kind'))" \
 	    $param_get_zero "test value of 0 via gdb.parameter"
+
+	py_param_test_maybe_no_output \
+	    "python test_param_$kind.value = 'unlimited'" \
+	    $param_set_none "test set to 'unlimited'"
+
+	gdb_test "python print(test_param_$kind.value)" \
+	    $param_get_unlimited "test value of 'unlimited'"
+
+	gdb_test "python print(gdb.parameter('test-$kind'))" \
+	    $param_get_unlimited "test value of 'unlimited' via gdb.parameter"
     }
 }
 
Index: src/gdb/valprint.c
===================================================================
--- src.orig/gdb/valprint.c
+++ src/gdb/valprint.c
@@ -2861,8 +2861,8 @@ 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>;
+using pinteger_option_def
+  = gdb::option::pinteger_option_def<value_print_options>;
 
 /* Definitions of options for the "print" and "compile print"
    commands.  */
@@ -2907,15 +2907,17 @@ static const gdb::option::option_def val
   uinteger_option_def {
     "elements",
     [] (value_print_options *opt) { return &opt->print_max; },
+    uinteger_unlimited_literals,
     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 {
+  pinteger_option_def {
     "max-depth",
     [] (value_print_options *opt) { return &opt->max_depth; },
+    pinteger_unlimited_literals,
     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."),
@@ -2975,6 +2977,7 @@ pretty-printers for that value.")
   uinteger_option_def {
     "repeats",
     [] (value_print_options *opt) { return &opt->repeat_count_threshold; },
+    uinteger_unlimited_literals,
     show_repeat_count_threshold, /* show_cmd_cb */
     N_("Set threshold for repeated print elements."),
     N_("Show threshold for repeated print elements."),

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

* [PATCH v8 5/6] GDB: Add a character string limiting option
  2022-11-24 11:21 [PATCH v8 0/6] gdb: split array and string limiting options Maciej W. Rozycki
                   ` (3 preceding siblings ...)
  2022-11-24 11:22 ` [PATCH v8 4/6] GDB: Allow arbitrary keywords in integer set commands Maciej W. Rozycki
@ 2022-11-24 11:22 ` Maciej W. Rozycki
  2023-01-16 19:35   ` Simon Marchi
  2022-11-24 11:23 ` [PATCH v8 6/6] GDB/testsuite: Expand for character string limiting options Maciej W. Rozycki
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Maciej W. Rozycki @ 2022-11-24 11:22 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Simon Marchi, Tom Tromey, Simon Sobisch

From: Andrew Burgess <andrew.burgess@embecosm.com>

This commit splits the `set/show print elements' option into two.  We 
retain `set/show print elements' for controlling how many elements of an 
array we print, but a new `set/show print characters' setting is added 
which is used for controlling how many characters of a string are 
printed.

The motivation behind this change is to allow users a finer level of 
control over how data is printed, reflecting that, although strings can 
be thought of as arrays of characters, users often want to treat these 
two things differently.

For compatibility reasons by default the `set/show print characters' 
option is set to `elements', which makes the limit for character strings 
follow the setting of the `set/show print elements' option, as it used 
to.  Using `set print characters' with any other value makes the limit 
independent from the `set/show print elements' setting, however it can 
be restored to the default with the `set print characters elements' 
command at any time.

A corresponding `-characters' option for the `print' command is added, 
with the same semantics, i.e. one can use `elements' to make a given 
`print' invocation follow the limit of elements, be it set with the 
`-elements' option also given with the same invocation or taken from the 
`set/show print elements' setting, for characters as well regardless of 
the current setting of the `set/show print characters' option.

The GDB changes are all pretty straightforward, just changing references 
to the old 'print_max' to use a new `get_print_max_chars' helper which 
figures out which of the two of `print_max' and `print_max_chars' values 
to use.

Likewise, the documentation is just updated to reference the new setting 
where appropriate.

To make people's life easier the message shown by `show print elements' 
now indicates if the setting also applies to character strings:

(gdb) set print characters elements
(gdb) show print elements
Limit on string chars or array elements to print is 200.
(gdb) set print characters unlimited
(gdb) show print elements
Limit on array elements to print is 200.
(gdb) 

which keeps it the same as it used to be, although in other contexts the 
setting is described as applying to array elements only, e.g.:

(gdb) help set print elements
Set limit on array elements to print.
"unlimited" causes there to be no limit.
(gdb) 

In the testsuite there are two minor updates, one to add `-characters' 
to the list of completions now shown for the `print' command, and a bare 
minimum pair of checks for the right handling of `set print characters' 
and `show print characters', copied from the corresponding checks for 
`set print elements' and `show print elements' respectively.

Co-Authored-By: Maciej W. Rozycki <macro@embecosm.com>
---
No change from v7.

No change from v6.

Changes from v5:

- Allow the use of `set print characters 0' to stand for `unlimited', and 
  likewise via the corresponding Guile and Python APIs.

- Define PRINT_MAX_CHARS_ELEMENTS and PRINT_MAX_CHARS_UNLIMITED macros 
  and use them throughout instead of hardcoded 0/UINT_MAX, making it 
  easier to track usage.

Changes from v4:

- Move the NEWS entry past GDB 12 and describe `print -characters LIMIT'
  separately.

- Clarify the effect on multi-byte and wide character strings in the 
  manual (borrowing from the original NEWS entry that added support for 
  them).

- Fix issues with `@ref' and `@xref' usage in the manual.

Changes from v3:

- Rewrite in terms of flexible literal keyword handling added with 6/8.

- Add a `set print characters elements' setting, which is also the 
  default, to make the string character limit follow that for array 
  elements in backwards-compatible manner.

- Update documentation accordingly.

- Remove testsuite updates delegated to 8/8, guaranteeing that the new 
  command does not influence any preexisting environment (except for 
  obvious cases where it is expected, such as command completion or help 
  messages).

- Amend gdb.base/options.exp for the added completion for `print' 
  command's `-characters' option.

- Expand gdb.base/default.exp with minimal test cases for `set/show print 
  characters'.

Changes from v2:

- Place the new and the changed command at the right places each in NEWS.

Changes from v1:

- Rename `print_smax' setting throughout to `print_max_chars', and
  likewise `show_print_smax' function to `show_print_max_chars'.

- Document the Python part in the manual.

- Update comments for `print_max' and `print_max_chars' in
  `value_print_options'.

- Fix some typos.
---
 gdb/NEWS                           |   14 ++++++
 gdb/ada-valprint.c                 |    6 +-
 gdb/c-lang.c                       |    4 -
 gdb/c-valprint.c                   |    5 +-
 gdb/doc/gdb.texinfo                |   44 +++++++++++++++++--
 gdb/doc/python.texi                |    5 ++
 gdb/language.h                     |    2 
 gdb/m2-lang.c                      |    3 -
 gdb/m2-valprint.c                  |    4 +
 gdb/p-lang.c                       |    3 -
 gdb/p-valprint.c                   |    9 ++-
 gdb/printcmd.c                     |    9 ++-
 gdb/python/py-value.c              |    4 +
 gdb/testsuite/gdb.base/default.exp |    7 +++
 gdb/testsuite/gdb.base/options.exp |    1 
 gdb/tracepoint.c                   |    4 -
 gdb/valprint.c                     |   84 ++++++++++++++++++++++++++++---------
 gdb/valprint.h                     |   26 +++++++++--
 18 files changed, 185 insertions(+), 49 deletions(-)

gdb-aburgess-print-elements-characters.diff
Index: src/gdb/NEWS
===================================================================
--- src.orig/gdb/NEWS
+++ src/gdb/NEWS
@@ -149,6 +149,20 @@ set style tui-current-position [on|off]
   Whether to style the source and assembly code highlighted by the
   TUI's current position indicator.  The default is off.
 
+set print characters LIMIT
+show print characters
+  This new setting is like 'set print elements', but controls how many
+  characters of a string are printed.  This functionality used to be
+  covered by 'set print elements', but it can be controlled separately
+  now.  LIMIT can be set to a numerical value to request that particular
+  character count, to 'unlimited' to print all characters of a string,
+  or to 'elements', which is also the default, to follow the setting of
+ 'set print elements' as it used to be.
+
+print -characters LIMIT
+  This new option to the 'print' command has the same effect as a temporary
+  use of 'set print characters'.
+
 * Changed commands
 
 document user-defined
Index: src/gdb/ada-valprint.c
===================================================================
--- src.orig/gdb/ada-valprint.c
+++ src/gdb/ada-valprint.c
@@ -469,7 +469,8 @@ printstr (struct ui_file *stream, struct
       return;
     }
 
-  for (i = 0; i < length && things_printed < options->print_max; i += 1)
+  unsigned int print_max_chars = get_print_max_chars (options);
+  for (i = 0; i < length && things_printed < print_max_chars; i += 1)
     {
       /* Position of the character we are examining
 	 to see whether it is repeated.  */
@@ -705,12 +706,13 @@ ada_val_print_string (struct type *type,
      elements up to it.  */
   if (options->stop_print_at_null)
     {
+      unsigned int print_max_chars = get_print_max_chars (options);
       int temp_len;
 
       /* Look for a NULL char.  */
       for (temp_len = 0;
 	   (temp_len < len
-	    && temp_len < options->print_max
+	    && temp_len < print_max_chars
 	    && char_at (valaddr + offset_aligned,
 			temp_len, eltlen, byte_order) != 0);
 	   temp_len += 1);
Index: src/gdb/c-lang.c
===================================================================
--- src.orig/gdb/c-lang.c
+++ src/gdb/c-lang.c
@@ -185,8 +185,8 @@ language_defn::printchar (int c, struct
 /* Print the character string STRING, printing at most LENGTH
    characters.  LENGTH is -1 if the string is nul terminated.  Each
    character is WIDTH bytes long.  Printing stops early if the number
-   hits print_max; repeat counts are printed as appropriate.  Print
-   ellipses at the end if we had to stop before printing LENGTH
+   hits print_max_chars; repeat counts are printed as appropriate.
+   Print ellipses at the end if we had to stop before printing LENGTH
    characters, or if FORCE_ELLIPSES.  */
 
 void
Index: src/gdb/c-valprint.c
===================================================================
--- src.orig/gdb/c-valprint.c
+++ src/gdb/c-valprint.c
@@ -267,11 +267,12 @@ c_value_print_array (struct value *val,
 	     print elements up to it.  */
 	  if (options->stop_print_at_null)
 	    {
+	      unsigned int print_max_chars = get_print_max_chars (options);
 	      unsigned int temp_len;
 
 	      for (temp_len = 0;
 		   (temp_len < len
-		    && temp_len < options->print_max
+		    && temp_len < print_max_chars
 		    && extract_unsigned_integer (valaddr + temp_len * eltlen,
 						 eltlen, byte_order) != 0);
 		   ++temp_len)
@@ -280,7 +281,7 @@ c_value_print_array (struct value *val,
 	      /* Force printstr to print ellipses if
 		 we've printed the maximum characters and
 		 the next character is not \000.  */
-	      if (temp_len == options->print_max && temp_len < len)
+	      if (temp_len == print_max_chars && temp_len < len)
 		{
 		  ULONGEST ival
 		    = extract_unsigned_integer (valaddr + temp_len * eltlen,
Index: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo
+++ src/gdb/doc/gdb.texinfo
@@ -10304,10 +10304,18 @@ Related setting: @ref{set print array}.
 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
+@item -characters @var{number-of-characters}|@code{elements}|@code{unlimited}
+Set limit on string characters to print.  The value @code{elements}
+causes the limit on array elements to print to be used.  The value
 @code{unlimited} causes there to be no limit.  Related setting:
-@ref{set print elements}.
+@ref{set print characters}.
+
+@item -elements @var{number-of-elements}|@code{unlimited}
+Set limit on array elements and optionally string characters to print.
+See @ref{set print characters}, and the @code{-characters} option above
+for when this option applies to strings.  The value @code{unlimited}
+causes there to be no limit.  @xref{set print elements}, for a related
+CLI command.
 
 @item -max-depth @var{depth}|@code{unlimited}
 Set the threshold after which nested structures are replaced with
@@ -11704,6 +11712,31 @@ Don't printing binary values in groups.
 @item show print nibbles
 Show whether to print binary values in groups of four bits.
 
+@anchor{set print characters}
+@item set print characters @var{number-of-characters}
+@itemx set print characters elements
+@itemx set print characters unlimited
+@cindex number of string characters to print
+@cindex limit on number of printed string characters
+Set a limit on how many characters of a string @value{GDBN} will print.
+If @value{GDBN} is printing a large string, it stops printing after it
+has printed the number of characters set by the @code{set print
+characters} command.  This equally applies to multi-byte and wide
+character strings, that is for strings whose character type is
+@code{wchar_t}, @code{char16_t}, or @code{char32_t} it is the number of
+actual characters rather than underlying bytes the encoding uses that
+this setting controls.
+Setting @var{number-of-characters} to @code{elements} means that the
+limit on the number of characters to print follows one for array
+elements; see @ref{set print elements}.
+Setting @var{number-of-characters} to @code{unlimited} means that the
+number of characters to print is unlimited.
+When @value{GDBN} starts, this limit is set to @code{elements}.
+
+@item show print characters
+Display the number of characters of a large string that @value{GDBN}
+will print.
+
 @anchor{set print elements}
 @item set print elements @var{number-of-elements}
 @itemx set print elements unlimited
@@ -11712,7 +11745,8 @@ Show whether to print binary values in g
 Set a limit on how many elements of an array @value{GDBN} will print.
 If @value{GDBN} is printing a large array, it stops printing after it has
 printed the number of elements set by the @code{set print elements} command.
-This limit also applies to the display of strings.
+By default this limit also applies to the display of strings; see
+@ref{set print characters}.
 When @value{GDBN} starts, this limit is set to 200.
 Setting @var{number-of-elements} to @code{unlimited} or zero means
 that the number of elements to print is unlimited.
@@ -15228,7 +15262,7 @@ The optional @var{mods} changes the usua
 @code{s} requests that pointers to chars be handled as strings, in
 particular collecting the contents of the memory being pointed at, up
 to the first zero.  The upper bound is by default the value of the
-@code{print elements} variable; if @code{s} is followed by a decimal
+@code{print characters} variable; if @code{s} is followed by a decimal
 number, that is the upper bound instead.  So for instance
 @samp{collect/s25 mystr} collects as many as 25 characters at
 @samp{mystr}.
Index: src/gdb/doc/python.texi
===================================================================
--- src.orig/gdb/doc/python.texi
+++ src/gdb/doc/python.texi
@@ -1135,6 +1135,11 @@ the @emph{declared} type should be used.
 representation of a C@t{++} object, @code{False} if they shouldn't (see
 @code{set print static-members} in @ref{Print Settings}).
 
+@item max_characters
+Number of string characters to print, @code{0} to follow
+@code{max_elements}, or @code{UINT_MAX} to print an unlimited number
+of characters (see @code{set print characters} in @ref{Print Settings}).
+
 @item max_elements
 Number of array elements to print, or @code{0} to print an unlimited
 number of elements (see @code{set print elements} in @ref{Print
Index: src/gdb/language.h
===================================================================
--- src.orig/gdb/language.h
+++ src/gdb/language.h
@@ -548,7 +548,7 @@ struct language_defn
 			  struct ui_file * stream) const;
 
 /* Print the character string STRING, printing at most LENGTH characters.
-   Printing stops early if the number hits print_max; repeat counts
+   Printing stops early if the number hits print_max_chars; repeat counts
    are printed as appropriate.  Print ellipses at the end if we
    had to stop before printing LENGTH characters, or if FORCE_ELLIPSES.  */
 
Index: src/gdb/m2-lang.c
===================================================================
--- src.orig/gdb/m2-lang.c
+++ src/gdb/m2-lang.c
@@ -169,7 +169,8 @@ m2_language::printstr (struct ui_file *s
       return;
     }
 
-  for (i = 0; i < length && things_printed < options->print_max; ++i)
+  unsigned int print_max_chars = get_print_max_chars (options);
+  for (i = 0; i < length && things_printed < print_max_chars; ++i)
     {
       /* Position of the character we are examining
 	 to see whether it is repeated.  */
Index: src/gdb/m2-valprint.c
===================================================================
--- src.orig/gdb/m2-valprint.c
+++ src/gdb/m2-valprint.c
@@ -327,12 +327,14 @@ m2_language::value_print_inner (struct v
 		 elements up to it.  */
 	      if (options->stop_print_at_null)
 		{
+		  unsigned int print_max_chars = get_print_max_chars (options);
 		  unsigned int temp_len;
 
 		  /* Look for a NULL char.  */
 		  for (temp_len = 0;
 		       (valaddr[temp_len]
-			&& temp_len < len && temp_len < options->print_max);
+			&& temp_len < len
+			&& temp_len < print_max_chars);
 		       temp_len++);
 		  len = temp_len;
 		}
Index: src/gdb/p-lang.c
===================================================================
--- src.orig/gdb/p-lang.c
+++ src/gdb/p-lang.c
@@ -253,7 +253,8 @@ pascal_language::printstr (struct ui_fil
       return;
     }
 
-  for (i = 0; i < length && things_printed < options->print_max; ++i)
+  unsigned int print_max_chars = get_print_max_chars (options);
+  for (i = 0; i < length && things_printed < print_max_chars; ++i)
     {
       /* Position of the character we are examining
 	 to see whether it is repeated.  */
Index: src/gdb/p-valprint.c
===================================================================
--- src.orig/gdb/p-valprint.c
+++ src/gdb/p-valprint.c
@@ -105,13 +105,16 @@ pascal_language::value_print_inner (stru
 		   elements up to it.  */
 		if (options->stop_print_at_null)
 		  {
+		    unsigned int print_max_chars
+		      = get_print_max_chars (options);
 		    unsigned int temp_len;
 
 		    /* Look for a NULL char.  */
 		    for (temp_len = 0;
-			 extract_unsigned_integer (valaddr + temp_len * eltlen,
-						   eltlen, byte_order)
-			   && temp_len < len && temp_len < options->print_max;
+			 (extract_unsigned_integer
+			    (valaddr + temp_len * eltlen, eltlen, byte_order)
+			  && temp_len < len
+			  && temp_len < print_max_chars);
 			 temp_len++);
 		    len = temp_len;
 		  }
Index: src/gdb/printcmd.c
===================================================================
--- src.orig/gdb/printcmd.c
+++ src/gdb/printcmd.c
@@ -957,17 +957,18 @@ find_string_backward (struct gdbarch *gd
 					 chars_to_read * char_size);
       chars_read /= char_size;
       read_error = (chars_read == chars_to_read) ? 0 : 1;
+      unsigned int print_max_chars = get_print_max_chars (options);
       /* Searching for '\0' from the end of buffer in backward direction.  */
       for (i = 0; i < chars_read && count > 0 ; ++i, ++chars_counted)
 	{
 	  int offset = (chars_to_read - i - 1) * char_size;
 
 	  if (integer_is_zero (&buffer[offset], char_size)
-	      || chars_counted == options->print_max)
+	      || chars_counted == print_max_chars)
 	    {
-	      /* Found '\0' or reached print_max.  As OFFSET is the offset to
-		 '\0', we add CHAR_SIZE to return the start address of
-		 a string.  */
+	      /* Found '\0' or reached `print_max_chars'.  As OFFSET
+		 is the offset to '\0', we add CHAR_SIZE to return
+		 the start address of a string.  */
 	      --count;
 	      string_start_addr = addr + offset + char_size;
 	      chars_counted = 0;
Index: src/gdb/python/py-value.c
===================================================================
--- src.orig/gdb/python/py-value.c
+++ src/gdb/python/py-value.c
@@ -647,6 +647,7 @@ valpy_format_string (PyObject *self, PyO
       "actual_objects",		/* See set print object on|off.  */
       "static_members",		/* See set print static-members on|off.  */
       /* C non-bool options.  */
+      "max_characters", 	/* See set print characters N.  */
       "max_elements", 		/* See set print elements N.  */
       "max_depth",		/* See set print max-depth N.  */
       "repeat_threshold",	/* See set print repeats.  */
@@ -695,7 +696,7 @@ valpy_format_string (PyObject *self, PyO
   char *format = NULL;
   if (!gdb_PyArg_ParseTupleAndKeywords (args,
 					kw,
-					"|O!O!O!O!O!O!O!O!O!O!O!O!O!IIIs",
+					"|O!O!O!O!O!O!O!O!O!O!O!O!O!IIIIs",
 					keywords,
 					&PyBool_Type, &raw_obj,
 					&PyBool_Type, &pretty_arrays_obj,
@@ -710,6 +711,7 @@ valpy_format_string (PyObject *self, PyO
 					&PyBool_Type, &deref_refs_obj,
 					&PyBool_Type, &actual_objects_obj,
 					&PyBool_Type, &static_members_obj,
+					&opts.print_max_chars,
 					&opts.print_max,
 					&opts.max_depth,
 					&opts.repeat_count_threshold,
Index: src/gdb/testsuite/gdb.base/default.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/default.exp
+++ src/gdb/testsuite/gdb.base/default.exp
@@ -520,6 +520,10 @@ gdb_test_no_output "set print address" "
 gdb_test_no_output "set print array" "set print array"
 #test set print asm-demangle
 gdb_test_no_output "set print asm-demangle" "set print asm-demangle"
+#test set print characters
+gdb_test "set print characters" \
+	 "Argument required \\(integer to set it to, or one of:\
+	  \"elements\", \"unlimited\"\\)\\."
 #test set print demangle
 gdb_test_no_output "set print demangle" "set print demangle"
 #test set print elements
@@ -664,6 +668,9 @@ gdb_test "show print address" "Printing
 gdb_test "show print array" "Pretty formatting of arrays is on."
 #test show print asm-demangle
 gdb_test "show print asm-demangle" "Demangling of C\[+\]+/ObjC names in disassembly listings is on."
+#test show print characters
+gdb_test "show print characters" \
+	 "Limit on string characters to print is elements\\."
 #test show print demangle
 gdb_test "show print demangle" "Demangling of encoded C\[+\]+/ObjC names when displaying symbols is on."
 #test show print elements
Index: src/gdb/testsuite/gdb.base/options.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/options.exp
+++ src/gdb/testsuite/gdb.base/options.exp
@@ -171,6 +171,7 @@ proc_with_prefix test-print {{prefix ""}
 	"-address"
 	"-array"
 	"-array-indexes"
+	"-characters"
 	"-elements"
 	"-max-depth"
 	"-memory-tag-violations"
Index: src/gdb/tracepoint.c
===================================================================
--- src.orig/gdb/tracepoint.c
+++ src/gdb/tracepoint.c
@@ -541,9 +541,9 @@ decode_agent_options (const char *exp, i
       if (target_supports_string_tracing ())
 	{
 	  /* Allow an optional decimal number giving an explicit maximum
-	     string length, defaulting it to the "print elements" value;
+	     string length, defaulting it to the "print characters" value;
 	     so "collect/s80 mystr" gets at most 80 bytes of string.  */
-	  *trace_string = opts.print_max;
+	  *trace_string = get_print_max_chars (&opts);
 	  exp++;
 	  if (*exp >= '0' && *exp <= '9')
 	    *trace_string = atoi (exp);
Index: src/gdb/valprint.c
===================================================================
--- src.orig/gdb/valprint.c
+++ src/gdb/valprint.c
@@ -94,8 +94,14 @@ static void val_print_type_code_flags (s
 				       int embedded_offset,
 				       struct ui_file *stream);
 
-#define PRINT_MAX_DEFAULT 200	/* Start print_max off at this value.  */
-#define PRINT_MAX_DEPTH_DEFAULT 20	/* Start print_max_depth off at this value. */
+/* Start print_max at this value.  */
+#define PRINT_MAX_DEFAULT 200
+
+/* Start print_max_chars at this value (meaning follow print_max).  */
+#define PRINT_MAX_CHARS_DEFAULT PRINT_MAX_CHARS_ELEMENTS
+
+/* Start print_max_depth at this value. */
+#define PRINT_MAX_DEPTH_DEFAULT 20
 
 struct value_print_options user_print_options =
 {
@@ -108,6 +114,7 @@ struct value_print_options user_print_op
   false,			/* nibblesprint */
   0,				/* objectprint */
   PRINT_MAX_DEFAULT,		/* print_max */
+  PRINT_MAX_CHARS_DEFAULT,	/* print_max_chars */
   10,				/* repeat_count_threshold */
   0,				/* output_format */
   0,				/* format */
@@ -149,17 +156,31 @@ get_formatted_print_options (struct valu
   opts->format = format;
 }
 
+/* Implement 'show print elements'.  */
+
 static void
 show_print_max (struct ui_file *file, int from_tty,
 		struct cmd_list_element *c, const char *value)
 {
+  gdb_printf
+    (file,
+     (user_print_options.print_max_chars != PRINT_MAX_CHARS_ELEMENTS
+      ? _("Limit on array elements to print is %s.\n")
+      : _("Limit on string chars or array elements to print is %s.\n")),
+     value);
+}
+
+/* Implement 'show print characters'.  */
+
+static void
+show_print_max_chars (struct ui_file *file, int from_tty,
+		      struct cmd_list_element *c, const char *value)
+{
   gdb_printf (file,
-	      _("Limit on string chars or array "
-		"elements to print is %s.\n"),
+	      _("Limit on string characters to print is %s.\n"),
 	      value);
 }
 
-
 /* Default input and output radixes, and output format letter.  */
 
 unsigned input_radix = 10;
@@ -2481,9 +2502,9 @@ print_converted_chars_to_obstack (struct
 /* Print the character string STRING, printing at most LENGTH
    characters.  LENGTH is -1 if the string is nul terminated.  TYPE is
    the type of each character.  OPTIONS holds the printing options;
-   printing stops early if the number hits print_max; repeat counts
-   are printed as appropriate.  Print ellipses at the end if we had to
-   stop before printing LENGTH characters, or if FORCE_ELLIPSES.
+   printing stops early if the number hits print_max_chars; repeat
+   counts are printed as appropriate.  Print ellipses at the end if we
+   had to stop before printing LENGTH characters, or if FORCE_ELLIPSES.
    QUOTE_CHAR is the character to print at each end of the string.  If
    C_STYLE_TERMINATOR is true, and the last character is 0, then it is
    omitted.  */
@@ -2537,7 +2558,8 @@ generic_printstr (struct ui_file *stream
   /* Convert characters until the string is over or the maximum
      number of printed characters has been reached.  */
   i = 0;
-  while (i < options->print_max)
+  unsigned int print_max_chars = get_print_max_chars (options);
+  while (i < print_max_chars)
     {
       int r;
 
@@ -2589,7 +2611,7 @@ generic_printstr (struct ui_file *stream
 /* Print a string from the inferior, starting at ADDR and printing up to LEN
    characters, of WIDTH bytes a piece, to STREAM.  If LEN is -1, printing
    stops at the first null byte, otherwise printing proceeds (including null
-   bytes) until either print_max or LEN characters have been printed,
+   bytes) until either print_max_chars or LEN characters have been printed,
    whichever is smaller.  ENCODING is the name of the string's
    encoding.  It can be NULL, in which case the target encoding is
    assumed.  */
@@ -2611,15 +2633,17 @@ val_print_string (struct type *elttype,
   int width = elttype->length ();
 
   /* First we need to figure out the limit on the number of characters we are
-     going to attempt to fetch and print.  This is actually pretty simple.  If
-     LEN >= zero, then the limit is the minimum of LEN and print_max.  If
-     LEN is -1, then the limit is print_max.  This is true regardless of
-     whether print_max is zero, UINT_MAX (unlimited), or something in between,
-     because finding the null byte (or available memory) is what actually
-     limits the fetch.  */
+     going to attempt to fetch and print.  This is actually pretty simple.
+     If LEN >= zero, then the limit is the minimum of LEN and print_max_chars.
+     If LEN is -1, then the limit is print_max_chars.  This is true regardless
+     of whether print_max_chars is zero, UINT_MAX (unlimited), or something in
+     between, because finding the null byte (or available memory) is what
+     actually limits the fetch.  */
 
-  fetchlimit = (len == -1 ? options->print_max : std::min ((unsigned) len,
-							   options->print_max));
+  unsigned int print_max_chars = get_print_max_chars (options);
+  fetchlimit = (len == -1
+		? print_max_chars
+		: std::min ((unsigned) len, print_max_chars));
 
   err = target_read_string (addr, len, width, fetchlimit,
 			    &buffer, &bytes_read);
@@ -2864,6 +2888,15 @@ using uinteger_option_def
 using pinteger_option_def
   = gdb::option::pinteger_option_def<value_print_options>;
 
+/* Extra literals supported with the `set print characters' and
+   `print -characters' commands.  */
+static const literal_def print_characters_literals[] =
+  {
+    { "elements", PRINT_MAX_CHARS_ELEMENTS },
+    { "unlimited", PRINT_MAX_CHARS_UNLIMITED, 0 },
+    { nullptr }
+  };
+
 /* Definitions of options for the "print" and "compile print"
    commands.  */
 static const gdb::option::option_def value_print_option_defs[] = {
@@ -2905,12 +2938,23 @@ static const gdb::option::option_def val
   },
 
   uinteger_option_def {
+    "characters",
+    [] (value_print_options *opt) { return &opt->print_max_chars; },
+    print_characters_literals,
+    show_print_max_chars, /* show_cmd_cb */
+    N_("Set limit on string chars to print."),
+    N_("Show limit on string chars to print."),
+    N_("\"elements\" causes the array element limit to be used.\n"
+       "\"unlimited\" causes there to be no limit."),
+  },
+
+  uinteger_option_def {
     "elements",
     [] (value_print_options *opt) { return &opt->print_max; },
     uinteger_unlimited_literals,
     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_("Set limit on array elements to print."),
+    N_("Show limit on array elements to print."),
     N_("\"unlimited\" causes there to be no limit."),
   },
 
Index: src/gdb/valprint.h
===================================================================
--- src.orig/gdb/valprint.h
+++ src/gdb/valprint.h
@@ -51,12 +51,15 @@ struct value_print_options
      in its vtables.  */
   bool objectprint;
 
-  /* Maximum number of chars to print for a string pointer value or vector
-     contents, or UINT_MAX for no limit.  Note that "set print elements 0"
-     stores UINT_MAX in print_max, which displays in a show command as
-     "unlimited".  */
+  /* Maximum number of elements to print for vector contents, or UINT_MAX
+     for no limit.  Note that "set print elements 0" stores UINT_MAX in
+     print_max, which displays in a show command as "unlimited".  */
   unsigned int print_max;
 
+  /* Maximum number of string chars to print for a string pointer value,
+     zero if to follow the value of print_max, or UINT_MAX for no limit.  */
+  unsigned int print_max_chars;
+
   /* Print repeat counts if there are more than this many repetitions
      of an element in an array.  */
   unsigned int repeat_count_threshold;
@@ -105,6 +108,21 @@ struct value_print_options
   int max_depth;
 };
 
+/* The value to use for `print_max_chars' to follow `print_max'.  */
+#define PRINT_MAX_CHARS_ELEMENTS 0
+
+/* The value to use for `print_max_chars' for no limit.  */
+#define PRINT_MAX_CHARS_UNLIMITED UINT_MAX
+
+/* Return the character count limit for printing strings.  */
+
+static inline unsigned int
+get_print_max_chars (const struct value_print_options *options)
+{
+  return (options->print_max_chars != PRINT_MAX_CHARS_ELEMENTS
+	  ? options->print_max_chars : options->print_max);
+}
+
 /* 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

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

* [PATCH v8 6/6] GDB/testsuite: Expand for character string limiting options
  2022-11-24 11:21 [PATCH v8 0/6] gdb: split array and string limiting options Maciej W. Rozycki
                   ` (4 preceding siblings ...)
  2022-11-24 11:22 ` [PATCH v8 5/6] GDB: Add a character string limiting option Maciej W. Rozycki
@ 2022-11-24 11:23 ` Maciej W. Rozycki
  2023-01-16 21:07   ` Simon Marchi
  2022-12-08 12:05 ` [PING][PATCH v8 0/6] gdb: split array and " Maciej W. Rozycki
  2023-01-09 12:27 ` [PING^2][PATCH " Maciej W. Rozycki
  7 siblings, 1 reply; 32+ messages in thread
From: Maciej W. Rozycki @ 2022-11-24 11:23 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Simon Marchi, Tom Tromey, Simon Sobisch

From: Andrew Burgess <andrew.burgess@embecosm.com>

Modify test cases that verify the operation of the array element limit 
with character strings such that they are executed twice, once with the 
`set print characters' option set to `elements' and the limit controlled 
with the `set print elements' option, and then again with the limit 
controlled with the `set print characters' option instead.  Similarly 
with the `-elements' and `-characters' options for the `print' command. 
Additionally verify that said `print' command options combined yield the 
expected result.

Verify correct $_gdb_setting and $_gdb_setting_str values for the `print  
characters' setting, in particular the `void' value for the `elements' 
default, which has no corresponding integer value exposed.

Add Guile and Python coverage for the `print characters' GDB setting.

There are new tests for Ada and Pascal, as the string printing code for 
these languages is different than the generic string printing code used 
by other languages.  Modula2 also has different string printing code, 
but (a) this is similar to Pascal, and (b) there are no existing modula2 
tests written in Modula2, so I'm not sure how I'd even test the Modula2 
string printing.

Co-Authored-By: Maciej W. Rozycki <macro@embecosm.com>
---
Changes from v7:

- py-parameter.exp regenerated for the special case of returning -1 for 
  the value of `unlimited' with `var_pinteger' parameters in Python code.

No change from v6.1.

Changes from v6:

- Add tests for $_gdb_setting and $_gdb_setting_str values with `print
  characters' to cover the `void' value in particular.

Changes from v5:

- Update testing for the use of `set print characters 0' now permitted.

- Add Guile and Python `print characters' testing.

No changes from v4.

Changes from v3:

- Split off from what is now 7/8; see the change log there for earlier 
  changes.

- Remove test case modifications to switch from the `set print elements' 
  command to `set print characters'; instead run them twice with each of 
  the two commands verified.

- Likewise with the `print -elements' and `print -characters' commands.

- Also cover `print -elements ... -characters ...', i.e. both options 
  combined.

- Expand the Ada and Pascal test cases to cover `set print characters
  elements' too.
---
 gdb/testsuite/gdb.ada/str_chars.exp           |   70 ++++++++++++++++++++++++++
 gdb/testsuite/gdb.ada/str_chars/foo.adb       |   26 +++++++++
 gdb/testsuite/gdb.base/printcmds.exp          |   65 +++++++++++++++++-------
 gdb/testsuite/gdb.base/setshow.exp            |   22 ++++++++
 gdb/testsuite/gdb.guile/scm-parameter.exp     |   11 +++-
 gdb/testsuite/gdb.pascal/str-chars.exp        |   56 ++++++++++++++++++++
 gdb/testsuite/gdb.pascal/str-chars.pas        |   28 ++++++++++
 gdb/testsuite/gdb.python/py-format-string.exp |   47 +++++++++++------
 gdb/testsuite/gdb.python/py-parameter.exp     |   13 ++++
 9 files changed, 301 insertions(+), 37 deletions(-)

gdb-aburgess-print-elements-characters-test.diff
Index: src/gdb/testsuite/gdb.ada/str_chars.exp
===================================================================
--- /dev/null
+++ src/gdb/testsuite/gdb.ada/str_chars.exp
@@ -0,0 +1,70 @@
+# Copyright 2022 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 GDB's 'set print characters' setting works for Ada strings.
+
+load_lib "ada.exp"
+
+if { [skip_ada_tests] } { return -1 }
+
+standard_ada_testfile foo
+
+if {[gdb_compile_ada "${srcfile}" "${binfile}" executable [list debug ]] != "" } {
+  return -1
+}
+
+clean_restart ${testfile}
+
+set bp_location [gdb_get_line_number "STOP" ${testdir}/foo.adb]
+if ![runto "foo.adb:$bp_location" ] then {
+  return -1
+}
+
+gdb_test "print Arg" \
+    " = \"abcdefghijklmnopqrstuvwxyz\"" \
+    "print with default settings"
+
+gdb_test_no_output "set print characters 26"
+gdb_test "print Arg" \
+    " = \"abcdefghijklmnopqrstuvwxyz\"" \
+    "print with character limit of 26"
+
+gdb_test "print -characters 11 -- Arg" \
+    " = \"abcdefghijk\"\\.\\.\\." \
+    "print with character limit of 11"
+
+gdb_test_no_output "set print characters 10"
+gdb_test "print Arg" \
+    " = \"abcdefghij\"\\.\\.\\." \
+    "print with character limit of 10"
+
+gdb_test_no_output "set print characters unlimited"
+gdb_test "print Arg" \
+    " = \"abcdefghijklmnopqrstuvwxyz\"" \
+    "print with unlimited character limit"
+
+# The 'set print elements' command used to control printing of characters
+# in a string, before we created 'set print characters'.  This test makes
+# sure that 'set print elements' doens't effect string printing any more.
+gdb_test_no_output "set print elements 12"
+gdb_test "print Arg" \
+    " = \"abcdefghijklmnopqrstuvwxyz\"" \
+    "print with unlimited character limit, but lower element limit"
+
+# Except when 'set print characters elements' has been used.
+gdb_test_no_output "set print characters elements"
+gdb_test "print Arg" \
+    " = \"abcdefghijkl\"\\.\\.\\." \
+    "print with character limit of elements"
Index: src/gdb/testsuite/gdb.ada/str_chars/foo.adb
===================================================================
--- /dev/null
+++ src/gdb/testsuite/gdb.ada/str_chars/foo.adb
@@ -0,0 +1,26 @@
+--  Copyright 2022 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/>.
+
+procedure Foo is
+
+   procedure Blah (Arg : String) is
+   begin
+     null; -- STOP
+   end;
+
+begin
+
+   Blah ("abcdefghijklmnopqrstuvwxyz");
+end Foo;
Index: src/gdb/testsuite/gdb.base/printcmds.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/printcmds.exp
+++ src/gdb/testsuite/gdb.base/printcmds.exp
@@ -451,11 +451,11 @@ proc test_print_all_chars {} {
 # Test interaction of the number of print elements to print and the
 # repeat count, set to the default of 10.
 
-proc test_print_repeats_10 {} {
+proc test_print_repeats_10_one { setting } {
     global gdb_prompt decimal
 
     for { set x 1 } { $x <= 16 } { incr x } {
-	gdb_test_no_output "set print elements $x" "elements $x repeats"
+	gdb_test_no_output "set print $setting $x" "$setting $x repeats"
 	for { set e 1 } { $e <= 16 } {incr e } {
 	    set v [expr $e - 1]
 	    set command "p &ctable2\[${v}*16\]"
@@ -495,11 +495,18 @@ proc test_print_repeats_10 {} {
 		set xstr "${xstr}\[.\]\[.\]\[.\]"
 	    }
 	    set string " = \[(\]unsigned char \[*\]\[)\] <ctable2(\\+$decimal)?> ${a}${xstr}"
-	    gdb_test "$command" "$string" "$command with print elements set to $x"
+	    gdb_test "$command" "$string" "$command with print $setting set to $x"
 	}
     }
 }
 
+proc test_print_repeats_10 {} {
+    foreach_with_prefix setting { "elements" "characters" } {
+	test_print_repeats_10_one $setting
+    }
+    gdb_test_no_output "set print characters elements"
+}
+
 # This tests whether GDB uses the correct element content offsets
 # (relative to the complete `some_struct' value) when counting value
 # repetitions.
@@ -512,7 +519,7 @@ proc test_print_repeats_embedded_array {
 	"correct element repeats in array embedded at offset > 0"
 }
 
-proc test_print_strings {} {
+proc test_print_strings_one { setting } {
     global gdb_prompt decimal
 
     # We accept "(unsigned char *) " before the string.  char vs. unsigned char
@@ -520,35 +527,35 @@ proc test_print_strings {} {
 
     # Test that setting print elements unlimited doesn't completely suppress
     # printing; this was a bug in older gdb's.
-    gdb_test_no_output "set print elements 0"
+    gdb_test_no_output "set print $setting 0"
     gdb_test "p teststring" \
-	" = (.unsigned char .. )?\"teststring contents\"" "p teststring with elements set to 0"
-    gdb_test_no_output "set print elements 1"
+	" = (.unsigned char .. )?\"teststring contents\"" "p teststring with $setting set to 0"
+    gdb_test_no_output "set print $setting 1"
     gdb_test "p teststring" \
-	" = (.unsigned char .. )?\"t\"\\.\\.\\." "p teststring with elements set to 1"
-    gdb_test_no_output "set print elements 5"
+	" = (.unsigned char .. )?\"t\"\\.\\.\\." "p teststring with $setting set to 1"
+    gdb_test_no_output "set print $setting 5"
     gdb_test "p teststring" \
-	" = (.unsigned char .. )?\"tests\"\\.\\.\\." "p teststring with elements set to 5"
-    gdb_test_no_output "set print elements 19"
+	" = (.unsigned char .. )?\"tests\"\\.\\.\\." "p teststring with $setting set to 5"
+    gdb_test_no_output "set print $setting 19"
     gdb_test "p teststring" \
-	" = (.unsigned char .. )?\"teststring contents\"" "p teststring with elements set to 19"
-    gdb_test_no_output "set print elements 20"
+	" = (.unsigned char .. )?\"teststring contents\"" "p teststring with $setting set to 19"
+    gdb_test_no_output "set print $setting 20"
     gdb_test "p teststring" \
-	" = (.unsigned char .. )?\"teststring contents\"" "p teststring with elements set to 20"
+	" = (.unsigned char .. )?\"teststring contents\"" "p teststring with $setting set to 20"
 
-    gdb_test "p -elements 1 -- teststring" \
+    gdb_test "p -$setting 1 -- teststring" \
 	" = (.unsigned char .. )?\"t\"\\.\\.\\."
-    gdb_test "p -elements 5 -- teststring" \
+    gdb_test "p -$setting 5 -- teststring" \
 	" = (.unsigned char .. )?\"tests\"\\.\\.\\."
-    gdb_test "p -elements 19 -- teststring" \
+    gdb_test "p -$setting 19 -- teststring" \
 	" = (.unsigned char .. )?\"teststring contents\""
-    gdb_test "p -elements 20 -- teststring" \
+    gdb_test "p -$setting 20 -- teststring" \
 	" = (.unsigned char .. )?\"teststring contents\""
 
     gdb_test "print teststring2" \
 	" = \\(charptr\\) \"more contents\""
 
-    gdb_test_no_output "set print elements 8"
+    gdb_test_no_output "set print $setting 8"
 
     # Set the target-charset to ASCII, because the output varies from
     # different charset.
@@ -620,6 +627,26 @@ proc test_print_strings {} {
 	gdb_test "p &ctable1\[31*8\]" \
 	    " = \\(unsigned char \\*\\) <ctable1\\+$decimal> \"\\\\370\\\\371\\\\372\\\\373\\\\374\\\\375\\\\376\\\\377\"..."
     }
+
+    gdb_test_no_output "set print $setting unlimited"
+}
+
+proc test_print_strings {} {
+
+    foreach_with_prefix setting { "elements" "characters" } {
+	test_print_strings_one $setting
+    }
+
+    gdb_test "p -elements 8 -- teststring" \
+	" = (.unsigned char .. )?\"teststring contents\""
+    gdb_test "p -characters 8 -- teststring" \
+	" = (.unsigned char .. )?\"teststri\"\\.\\.\\."
+    gdb_test "p -elements 8 -characters elements -- teststring" \
+	" = (.unsigned char .. )?\"teststri\"\\.\\.\\."
+
+    with_test_prefix strings {
+	gdb_test_no_output "set print characters elements"
+    }
 }
 
 proc test_print_int_arrays {} {
Index: src/gdb/testsuite/gdb.base/setshow.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/setshow.exp
+++ src/gdb/testsuite/gdb.base/setshow.exp
@@ -374,6 +374,27 @@ proc_with_prefix test_setshow_listsize {
     gdb_test "show listsize" "Number of source lines gdb will list by default is 100..*" "show listsize (100)"
 }
 
+proc_with_prefix test_setshow_print_characters {} {
+    clean_restart
+
+    gdb_test "p \$_gdb_setting(\"print characters\")" " = void" \
+	"_gdb_setting print characters default"
+    gdb_test "p \$_gdb_setting_str(\"print characters\")" " = \"elements\"" \
+	"_gdb_setting_str print characters default"
+
+    gdb_test_no_output "set print characters unlimited"
+    gdb_test "p \$_gdb_setting(\"print characters\")" " = 0" \
+	"_gdb_setting print characters unlimited"
+    gdb_test "p \$_gdb_setting_str(\"print characters\")" " = \"unlimited\"" \
+	"_gdb_setting_str print characters unlimited"
+
+    gdb_test_no_output "set print characters 1"
+    gdb_test "p \$_gdb_setting(\"print characters\")" " = 1" \
+	"_gdb_setting print characters 1"
+    gdb_test "p \$_gdb_setting_str(\"print characters\")" " = \"1\"" \
+	"_gdb_setting_str print characters 1"
+}
+
 proc_with_prefix test_setshow_prompt {} {
     clean_restart
 
@@ -493,6 +514,7 @@ test_setshow_height
 test_setshow_history
 test_setshow_language
 test_setshow_listsize
+test_setshow_print_characters
 test_setshow_prompt
 test_setshow_radix
 test_setshow_width
Index: src/gdb/testsuite/gdb.guile/scm-parameter.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.guile/scm-parameter.exp
+++ src/gdb/testsuite/gdb.guile/scm-parameter.exp
@@ -105,6 +105,7 @@ foreach_with_prefix param {
     "listsize"
     "print elements"
     "max-completions"
+    "print characters"
 } {
     set param_range_error "integer -1 out of range"
     set param_type_error \
@@ -118,7 +119,8 @@ foreach_with_prefix param {
 	    set param_get_minus_one -1
 	    set param_set_minus_one ""
 	}
-	"print elements" {
+	"print elements" -
+	"print characters" {
 	    set param_get_zero "#:unlimited"
 	    set param_get_minus_one "#:unlimited"
 	    set param_set_minus_one $param_range_error
@@ -153,6 +155,13 @@ foreach_with_prefix param {
 
     gdb_test "guile (print (parameter-value \"$param\"))" \
 	"#:unlimited" "test value of 'unlimited'"
+
+    if {$param == "print characters"} {
+	gdb_test_no_output "set $param elements" "test set to 'elements'"
+
+	gdb_test "guile (print (parameter-value \"$param\"))" \
+	    "#:elements" "test value of 'elements'"
+    }
 }
 
 foreach_with_prefix kind {
Index: src/gdb/testsuite/gdb.pascal/str-chars.exp
===================================================================
--- /dev/null
+++ src/gdb/testsuite/gdb.pascal/str-chars.exp
@@ -0,0 +1,56 @@
+# Copyright 2022 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/>.
+
+load_lib "pascal.exp"
+
+standard_testfile .pas
+
+if {[gdb_compile_pascal "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug ]] != "" } {
+  return -1
+}
+
+clean_restart ${testfile}
+gdb_breakpoint ${srcfile}:[gdb_get_line_number "set breakpoint 1 here"]
+
+# Verify that "start" lands inside the right procedure.
+if { [gdb_start_cmd] < 0 } {
+    untested start
+    return -1
+}
+
+gdb_continue_to_breakpoint "continue to breakpoint"
+
+gdb_test "print message" " = 'abcdefghijklmnopqrstuvwxyz'" \
+    "print message with the default settings"
+
+gdb_test_no_output "set print elements 10"
+gdb_test "print message" " = 'abcdefghij'\\.\\.\\." \
+    "print message with 'print elements' set to 10"
+
+gdb_test_no_output "set print characters 20"
+gdb_test "print message" " = 'abcdefghijklmnopqrst'\\.\\.\\." \
+    "print message with 'print characters' set to 20"
+
+gdb_test_no_output "set print elements 15"
+gdb_test "print message" " = 'abcdefghijklmnopqrst'\\.\\.\\." \
+    "print message with 'print elements' set to 15"
+
+gdb_test_no_output "set print characters unlimited"
+gdb_test "print message" " = 'abcdefghijklmnopqrstuvwxyz'" \
+    "print message with 'print characters' set to unlimited"
+
+gdb_test_no_output "set print characters elements"
+gdb_test "print message" " = 'abcdefghijklmno'\\.\\.\\." \
+    "print message with 'print characters' set to elements"
Index: src/gdb/testsuite/gdb.pascal/str-chars.pas
===================================================================
--- /dev/null
+++ src/gdb/testsuite/gdb.pascal/str-chars.pas
@@ -0,0 +1,28 @@
+{
+ Copyright 2022 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/>.
+}
+
+program str_char;
+
+var
+   message : string;
+
+begin
+
+   message := 'abcdefghijklmnopqrstuvwxyz';
+   writeln (message)	{ set breakpoint 1 here }
+
+end.
Index: src/gdb/testsuite/gdb.python/py-format-string.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.python/py-format-string.exp
+++ src/gdb/testsuite/gdb.python/py-format-string.exp
@@ -705,13 +705,13 @@ proc_with_prefix test_static_members {}
 }
 
 # Test the max_elements option for gdb.Value.format_string.
-proc_with_prefix test_max_elements {} {
+proc test_max_string_one { setting unlimited } {
   global current_lang
   global default_pointer_regexp
 
   # 200 is the default maximum number of elements, so setting it should
   # not change the output.
-  set opts "max_elements=200"
+  set opts "max_$setting=200"
   with_test_prefix $opts {
     check_format_string "a_point_t" $opts
     check_format_string "a_point_t_pointer" $opts
@@ -722,8 +722,10 @@ proc_with_prefix test_max_elements {} {
     check_format_string "a_binary_string" $opts
     check_format_string "a_binary_string_array" $opts
     check_format_string "a_big_string" $opts
-    check_format_string "an_array" $opts
-    check_format_string "an_array_with_repetition" $opts
+    if { $setting == "elements"} {
+      check_format_string "an_array" $opts
+      check_format_string "an_array_with_repetition" $opts
+    }
     check_format_string "a_symbol_pointer" $opts
 
     if { $current_lang == "c++" } {
@@ -732,7 +734,7 @@ proc_with_prefix test_max_elements {} {
     }
   }
 
-  set opts "max_elements=3"
+  set opts "max_$setting=3"
   with_test_prefix $opts {
     check_format_string "a_point_t" $opts
     check_format_string "a_point_t_pointer" $opts
@@ -749,9 +751,11 @@ proc_with_prefix test_max_elements {} {
       "\"hell\"..."
     check_format_string "a_big_string" $opts \
       [get_cut_big_string 3]
-    check_format_string "an_array" $opts
-    check_format_string "an_array_with_repetition" $opts \
-      "\\{1, 3 <repeats 12 times>...\\}"
+    if { $setting == "elements"} {
+      check_format_string "an_array" $opts
+      check_format_string "an_array_with_repetition" $opts \
+	"\\{1, 3 <repeats 12 times>...\\}"
+    }
     check_format_string "a_symbol_pointer" $opts
 
     if { $current_lang == "c++" } {
@@ -760,9 +764,9 @@ proc_with_prefix test_max_elements {} {
     }
   }
 
-  # Both 1,000 (we don't have that many elements) and 0 (unlimited) should
+  # Both 1,000 (we don't have that many elements) and unlimited should
   # mean no truncation.
-  foreach opts { "max_elements=1000" "max_elements=0" } {
+  foreach opts [list "max_$setting=1000" "max_$setting=$unlimited"] {
     with_test_prefix $opts {
       check_format_string "a_point_t" $opts
       check_format_string "a_point_t_pointer" $opts
@@ -774,8 +778,10 @@ proc_with_prefix test_max_elements {} {
       check_format_string "a_binary_string_array" $opts
       check_format_string "a_big_string" $opts \
         [get_cut_big_string 1000]
-      check_format_string "an_array" $opts
-      check_format_string "an_array_with_repetition" $opts
+      if { $setting == "elements"} {
+	check_format_string "an_array" $opts
+	check_format_string "an_array_with_repetition" $opts
+      }
       check_format_string "a_symbol_pointer" $opts
 
       if { $current_lang == "c++" } {
@@ -785,15 +791,24 @@ proc_with_prefix test_max_elements {} {
     }
   }
 
-  with_temp_option "set print elements 4" "set print elements 200" {
+  with_temp_option "set print $setting 4" "set print $setting 200" {
     check_format_string "a_string" "" \
       "${default_pointer_regexp} \"hell\"..."
     check_format_string "a_binary_string" "" \
       "${default_pointer_regexp} \"hell\"..."
     check_format_string "a_binary_string_array" "" \
       "\"hell\"..."
-    check_format_string "an_array_with_repetition" "" \
-      "\\{1, 3 <repeats 12 times>...\\}"
+    if { $setting == "elements"} {
+      check_format_string "an_array_with_repetition" "" \
+	"\\{1, 3 <repeats 12 times>...\\}"
+    }
+  }
+}
+
+proc_with_prefix test_max_string {} {
+  foreach_with_prefix setting { "elements" "characters" } {
+    test_max_string_one $setting \
+      [string map {elements 0 characters 4294967295} $setting]
   }
 }
 
@@ -1153,7 +1168,7 @@ proc_with_prefix test_all_common {} {
   test_deref_refs
   test_actual_objects
   test_static_members
-  test_max_elements
+  test_max_string
   test_max_depth
   test_repeat_threshold
   test_format
Index: src/gdb/testsuite/gdb.python/py-parameter.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.python/py-parameter.exp
+++ src/gdb/testsuite/gdb.python/py-parameter.exp
@@ -338,6 +338,7 @@ proc_with_prefix test_gdb_parameter { }
 	"listsize"
 	"print elements"
 	"max-completions"
+	"print characters"
     } {
 	clean_restart
 
@@ -350,7 +351,8 @@ proc_with_prefix test_gdb_parameter { }
 		set param_get_unlimited None
 		set param_set_minus_one ""
 	    }
-	    "print elements" {
+	    "print elements" -
+	    "print characters" {
 		set param_get_zero None
 		set param_get_minus_one None
 		set param_get_none None
@@ -399,6 +401,15 @@ proc_with_prefix test_gdb_parameter { }
 
 	gdb_test "python print(gdb.parameter('$param'))" \
 	    $param_get_unlimited "test value of 'unlimited'"
+
+	if {$param == "print characters"} {
+	    gdb_test_no_output \
+		"python gdb.set_parameter('$param', 'elements')" \
+		"test set to 'elements'"
+
+	    gdb_test "python print(gdb.parameter('$param'))" \
+		elements "test value of 'elements'"
+	}
     }
 
     clean_restart

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

* Re: [PATCH v8 4/6] GDB: Allow arbitrary keywords in integer set commands
  2022-11-24 11:22 ` [PATCH v8 4/6] GDB: Allow arbitrary keywords in integer set commands Maciej W. Rozycki
@ 2022-11-24 11:57   ` Eli Zaretskii
  2023-01-12 20:48   ` Simon Marchi
  1 sibling, 0 replies; 32+ messages in thread
From: Eli Zaretskii @ 2022-11-24 11:57 UTC (permalink / raw)
  To: Maciej W. Rozycki; +Cc: gdb-patches, aburgess, simon.marchi, tom, simonsobisch

> Date: Thu, 24 Nov 2022 11:22:34 +0000 (GMT)
> From: "Maciej W. Rozycki" <macro@embecosm.com>
> cc: Andrew Burgess <aburgess@redhat.com>, 
>  Simon Marchi <simon.marchi@polymtl.ca>, Tom Tromey <tom@tromey.com>, 
>  Simon Sobisch <simonsobisch@web.de>
> 
> New change in v4.
> ---
>  gdb/cli/cli-cmds.c                        |   59 ++---
>  gdb/cli/cli-decode.c                      |  321 +++++++++++++++++++++++-------
>  gdb/cli/cli-option.c                      |  117 +++++++---
>  gdb/cli/cli-option.h                      |   54 +++--
>  gdb/cli/cli-setshow.c                     |  245 ++++++++++------------
>  gdb/cli/cli-setshow.h                     |   20 -
>  gdb/command.h                             |  110 +++++++---
>  gdb/doc/python.texi                       |   28 +-
>  gdb/guile/scm-param.c                     |  319 +++++++++++++++++++----------
>  gdb/maint-test-options.c                  |   44 ++--
>  gdb/python/py-param.c                     |  286 ++++++++++++++++----------
>  gdb/python/python.c                       |   52 +++-
>  gdb/testsuite/gdb.base/max-value-size.exp |    2 
>  gdb/testsuite/gdb.base/options.exp        |   47 ++--
>  gdb/testsuite/gdb.base/settings.exp       |    2 
>  gdb/testsuite/gdb.base/with.exp           |    2 
>  gdb/testsuite/gdb.guile/scm-parameter.exp |   23 --
>  gdb/testsuite/gdb.python/py-parameter.exp |   15 +
>  gdb/valprint.c                            |    9 
>  19 files changed, 1133 insertions(+), 622 deletions(-)

OK for the documentation part, thanks.

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

* [PING][PATCH v8 0/6] gdb: split array and string limiting options
  2022-11-24 11:21 [PATCH v8 0/6] gdb: split array and string limiting options Maciej W. Rozycki
                   ` (5 preceding siblings ...)
  2022-11-24 11:23 ` [PATCH v8 6/6] GDB/testsuite: Expand for character string limiting options Maciej W. Rozycki
@ 2022-12-08 12:05 ` Maciej W. Rozycki
  2023-01-09 12:27 ` [PING^2][PATCH " Maciej W. Rozycki
  7 siblings, 0 replies; 32+ messages in thread
From: Maciej W. Rozycki @ 2022-12-08 12:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Simon Marchi, Tom Tromey, Simon Sobisch

On Thu, 24 Nov 2022, Maciej W. Rozycki wrote:

>  Here is v8 of the series to split array and string limiting options.
> 
>  Since no consensus was reached as to fixing PARAM_ZUINTEGER_UNLIMITED 
> API for the `unlimited' setting I have dropped what used to be change 1/4 
> in v7 and adjusted what used to be change 2/4 accordingly.  I have also 
> moved extra literals out of the `erased_args' structure as requested.
> 
>  While updating inline documentation in the course of the latter action I 
> have noticed that it has gone out of sync WRT actual code for numerous 
> functions in cli-decode.c.  So as not to mix up additions with fixes I 
> have made preparatory changes 1/6 through 3/6 that address the problems 
> with documentation first and then changes 4/6 through 6/6 respectively map 
> to changes 2/4 through 4/4 from v7.

 Ping for: 
<https://sourceware.org/pipermail/gdb-patches/2022-November/194121.html>

  Maciej

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

* [PING^2][PATCH v8 0/6] gdb: split array and string limiting options
  2022-11-24 11:21 [PATCH v8 0/6] gdb: split array and string limiting options Maciej W. Rozycki
                   ` (6 preceding siblings ...)
  2022-12-08 12:05 ` [PING][PATCH v8 0/6] gdb: split array and " Maciej W. Rozycki
@ 2023-01-09 12:27 ` Maciej W. Rozycki
  7 siblings, 0 replies; 32+ messages in thread
From: Maciej W. Rozycki @ 2023-01-09 12:27 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Simon Marchi, Tom Tromey, Simon Sobisch

On Thu, 24 Nov 2022, Maciej W. Rozycki wrote:

>  Here is v8 of the series to split array and string limiting options.
> 
>  Since no consensus was reached as to fixing PARAM_ZUINTEGER_UNLIMITED 
> API for the `unlimited' setting I have dropped what used to be change 1/4 
> in v7 and adjusted what used to be change 2/4 accordingly.  I have also 
> moved extra literals out of the `erased_args' structure as requested.
> 
>  While updating inline documentation in the course of the latter action I 
> have noticed that it has gone out of sync WRT actual code for numerous 
> functions in cli-decode.c.  So as not to mix up additions with fixes I 
> have made preparatory changes 1/6 through 3/6 that address the problems 
> with documentation first and then changes 4/6 through 6/6 respectively map 
> to changes 2/4 through 4/4 from v7.

 Ping for:
<https://sourceware.org/pipermail/gdb-patches/2022-November/194121.html>

  Maciej

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

* Re: [PATCH v8 1/6] GDB: Fix documentation for `theclass' parameters in cli-decode.c
  2022-11-24 11:21 ` [PATCH v8 1/6] GDB: Fix documentation for `theclass' parameters in cli-decode.c Maciej W. Rozycki
@ 2023-01-12 18:36   ` Simon Marchi
  2023-01-18 21:56     ` Maciej W. Rozycki
  0 siblings, 1 reply; 32+ messages in thread
From: Simon Marchi @ 2023-01-12 18:36 UTC (permalink / raw)
  To: Maciej W. Rozycki, gdb-patches; +Cc: Andrew Burgess, Tom Tromey, Simon Sobisch



On 11/24/22 06:21, Maciej W. Rozycki wrote:
> Rename CLASS to THECLASS in the documentation for `theclass' parameters 
> throughout cli-decode.c, complementing commit fe978cb071b4 ("C++ keyword 
> cleanliness, mostly auto-generated").

This is ok, if you find more of these, I think you can push them as
obvious.

Approved-By: Simon Marchi <simon.marchi@efficios.com>

Simon

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

* Re: [PATCH v8 2/6] GDB: Add missing inline documentation for `add_setshow_cmd_full'
  2022-11-24 11:22 ` [PATCH v8 2/6] GDB: Add missing inline documentation for `add_setshow_cmd_full' Maciej W. Rozycki
@ 2023-01-12 18:40   ` Simon Marchi
  2023-01-18 23:24     ` [COMMITTED PATCH v9 2.0/6] " Maciej W. Rozycki
  0 siblings, 1 reply; 32+ messages in thread
From: Simon Marchi @ 2023-01-12 18:40 UTC (permalink / raw)
  To: Maciej W. Rozycki, gdb-patches; +Cc: Andrew Burgess, Tom Tromey, Simon Sobisch



On 11/24/22 06:22, Maciej W. Rozycki wrote:
> Complement commit 1d7fe7f01b93 ("gdb: Introduce setting construct 
> within cmd_list_element") and add missing description for 
> `add_setshow_cmd_full'.
> ---
> New change in v8, split off from 2/4 in v7.
> ---
>  gdb/cli/cli-decode.c |   12 ++++++++++++
>  1 file changed, 12 insertions(+)
> 
> gdb-setshow-cmd-full-doc.diff
> Index: src/gdb/cli/cli-decode.c
> ===================================================================
> --- src.orig/gdb/cli/cli-decode.c
> +++ src/gdb/cli/cli-decode.c
> @@ -580,6 +580,18 @@ add_setshow_cmd_full_erased (const char
>    return {set, show};
>  }
>  
> +/* Add element named NAME to both the command SET_LIST and SHOW_LIST.

"to both the command" doesn't really make sense.  I'd say "to both
command lists SET_LIST and SHOW_LIST", or "to both lists SET_LIST and
SHOW_LIST".

I see that this text comes from add_setshow_cmd_full_erased, it can be
fixed there too.  Also, it would be fine to just document the duplicated
parameters in one function and refer to that in the other function.

Simon

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

* Re: [PATCH v8 3/6] GDB: Add references to erased args in cli-decode.c
  2022-11-24 11:22 ` [PATCH v8 3/6] GDB: Add references to erased args in cli-decode.c Maciej W. Rozycki
@ 2023-01-12 18:46   ` Simon Marchi
  2023-01-18 23:41     ` Maciej W. Rozycki
  0 siblings, 1 reply; 32+ messages in thread
From: Simon Marchi @ 2023-01-12 18:46 UTC (permalink / raw)
  To: Maciej W. Rozycki, gdb-patches; +Cc: Andrew Burgess, Tom Tromey, Simon Sobisch



On 11/24/22 06:22, Maciej W. Rozycki wrote:
> Complement commit 1d7fe7f01b93 ("gdb: Introduce setting construct within 
> cmd_list_element") and commit 702991711a91 ("gdb: Have setter and getter 
> callbacks for settings") and update inline documentation accordingly for 
> `add_set_or_show_cmd' and `add_setshow_cmd_full_erased', documenting the 
> `args' parameter and removing references to `var', `set_setting_func' 
> and `get_setting_func'.

If you want to change them so they cross-reference a single place where
these parameters are documented, it would be fine too.  But in any case:

Approved-By: Simon Marchi <simon.marchi@efficios.com>

Simon

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

* Re: [PATCH v8 4/6] GDB: Allow arbitrary keywords in integer set commands
  2022-11-24 11:22 ` [PATCH v8 4/6] GDB: Allow arbitrary keywords in integer set commands Maciej W. Rozycki
  2022-11-24 11:57   ` Eli Zaretskii
@ 2023-01-12 20:48   ` Simon Marchi
  2023-01-19 21:17     ` Maciej W. Rozycki
  1 sibling, 1 reply; 32+ messages in thread
From: Simon Marchi @ 2023-01-12 20:48 UTC (permalink / raw)
  To: Maciej W. Rozycki, gdb-patches; +Cc: Andrew Burgess, Tom Tromey, Simon Sobisch



On 11/24/22 06:22, Maciej W. Rozycki wrote:
> Rather than just `unlimited' allow the integer set commands (or command 
> options) to define arbitrary keywords for the user to use, removing 
> hardcoded arrangements for the `unlimited' keyword.
> 
> Remove the confusingly named `var_zinteger', `var_zuinteger' and 
> `var_zuinteger_unlimited' `set'/`show' command variable types redefining 
> them in terms of `var_uinteger', `var_integer' and `var_pinteger', which 
> have the range of [0;UINT_MAX], [INT_MIN;INT_MAX], and [0;INT_MAX] each.
> 
> Following existing practice `var_pinteger' allows extra negative values 
> to be used, however unlike `var_zuinteger_unlimited' any number of such 
> values can be defined rather than just `-1'.
> 
> The "p" in `var_pinteger' stands for "positive", for the lack of a more 
> appropriate unambiguous letter, even though 0 obviously is not positive; 
> "n" would be confusing as to whether it stands for "non-negative" or 
> "negative".
> 
> Add a new structure, `literal_def', the entries of which define extra 
> keywords allowed for a command and numerical values they correspond to.  
> Those values are not verified against the basic range supported by the 
> underlying variable type, allowing extra values to be allowed outside 
> that range, which may or may not be individually made visible to the 
> user.  An optional value translation is possible with the structure to 
> follow the existing practice for some commands where user-entered 0 is 
> internally translated to UINT_MAX or INT_MAX.  Such translation can now 
> be arbitrary.  Literals defined by this structure are automatically used 
> for completion as necessary.
> 
> So for example:
> 
> const literal_def integer_unlimited_literals[] =
>   {
>     { "unlimited", INT_MAX, 0 },
>     { nullptr }
>   };
> 
> defines an extra `unlimited' keyword and a user-visible 0 value, both of 
> which get translated to INT_MAX for the setting to be used with.
> 
> Similarly:
> 
> const literal_def zuinteger_unlimited_literals[] =
>   {
>     { "unlimited", -1, -1 },
>     { nullptr }
>   };
> 
> defines the same keyword and a corresponding user-visible -1 value that 
> is used for the requested setting.  If the last member were omitted (or 
> set to `{}') here, then only the keyword would be allowed for the user 
> to enter and while -1 would still be used internally trying to enter it 
> as a part of a command would result in an "integer -1 out of range" 
> error.
> 
> Use said error message in all cases (citing the invalid value requested) 
> replacing "only -1 is allowed to set as unlimited" previously used for 
> `var_zuinteger_unlimited' settings only rather than propagating it to 
> `var_pinteger' type.  It could only be used for the specific case where 
> a single extra `unlimited' keyword was defined standing for -1 and the 
> use of numeric equivalents is discouraged anyway as it is for historical 
> reasons only that they expose GDB internals, confusingly different 
> across variable types.  Similarly update the "must be >= -1" Guile error 
> message.
> 
> Redefine Guile and Python parameter types in terms of the new variable 
> types and interpret extra keywords as Scheme keywords and Python strings 
> used to communicate corresponding parameter values.  Do not add a new
> PARAM_INTEGER Guile parameter type, however do handle the `var_integer' 
> variable type now, permitting existing parameters defined by GDB proper, 
> such as `listsize', to be accessed from Scheme code.
> 
> With these changes in place it should be trivial for a Scheme or Python 
> programmer to expand the syntax of the `make-parameter' command and the 
> `gdb.Parameter' class initializer to have arbitrary extra literals along 
> with their internal representation supplied.
> 
> Update the testsuite accordingly.

This is a nice cleanup I think.  I didn't want to delay this further, so
I did the best review I could given the time I had.  I'm fine with you
merging this (you can add my Approved-By), we can always follow up with
more fixes.  If others would like to review it, it's always appreciated,
but given the time it's been sitting unreviewed, I kind of doubt that
will happen.

I noted a few minor things:

> ---
> Changes from v7:
> 
> - Handle the special case of returning -1 for the value of `unlimited' 
>   with `var_pinteger' parameters in Python code.
> 
> - Update Python documentation accordingly, including `unlimited' case 
>   descriptions from what used to be in 1/4.
> 
> - Pass extra literals outside erased args with `add_set_or_show_cmd' and 
>   up the call chain.
> 
> No change from v6.
> 
> Changes from v5:
> 
> - Add a translation layer from Guile and Python parameter types to new
>   GDB variable types and remove `var_zuinteger', `var_uinteger', and 
>   `var_zuinteger_unlimited' variable types altogether now.
> 
> - Add an optional `extra_literals' initialiser to the `setting' class
>   constructor.
> 
> - Remove the "only -1 is allowed to set as unlimited" error message
>   altogether now rather than propagating it to `var_pinteger' type.
> 
> - Make the `val' member of `struct literal_def' optional and remove the
>   `allow' member; simplify processing accordingly.
> 
> - Rename `zuinteger_unlimited_literals' to `pinteger_unlimited_literals',
>   making the names of all `*_unlimited_literals' arrays consistent with 
>   the corresponding `var_*' variable types.
> 
> - Rename `struct integer_option_def' to `struct pinteger_option_def',
>   observing it's come from `struct zuinteger_unlimited_option_def' and 
>   what used to be the `var_zuinteger_unlimited' now has `var_pinteger' 
>   semantics.
> 
> - Update the names of test flags used by `maint test-options' accordingly.
> 
> - Add constructor variants to `struct uinteger_option_def' and `struct
>   pinteger_option_def' that allow one to skip the `extra_literals' 
>   initialiser altogether rather than having to pass in `nullptr'.
> 
> - Update Python documentation mentioning the use of literal `unlimited'
>   with the respective parameter types.
> 
> No change from v4.
> 
> New change in v4.
> ---
>  gdb/cli/cli-cmds.c                        |   59 ++---
>  gdb/cli/cli-decode.c                      |  321 +++++++++++++++++++++++-------
>  gdb/cli/cli-option.c                      |  117 +++++++---
>  gdb/cli/cli-option.h                      |   54 +++--
>  gdb/cli/cli-setshow.c                     |  245 ++++++++++------------
>  gdb/cli/cli-setshow.h                     |   20 -
>  gdb/command.h                             |  110 +++++++---
>  gdb/doc/python.texi                       |   28 +-
>  gdb/guile/scm-param.c                     |  319 +++++++++++++++++++----------
>  gdb/maint-test-options.c                  |   44 ++--
>  gdb/python/py-param.c                     |  286 ++++++++++++++++----------
>  gdb/python/python.c                       |   52 +++-
>  gdb/testsuite/gdb.base/max-value-size.exp |    2 
>  gdb/testsuite/gdb.base/options.exp        |   47 ++--
>  gdb/testsuite/gdb.base/settings.exp       |    2 
>  gdb/testsuite/gdb.base/with.exp           |    2 
>  gdb/testsuite/gdb.guile/scm-parameter.exp |   23 --
>  gdb/testsuite/gdb.python/py-parameter.exp |   15 +
>  gdb/valprint.c                            |    9 
>  19 files changed, 1133 insertions(+), 622 deletions(-)
> 
> gdb-setshow-cmd-extra-literals.diff
> Index: src/gdb/cli/cli-cmds.c
> ===================================================================
> --- src.orig/gdb/cli/cli-cmds.c
> +++ src/gdb/cli/cli-cmds.c
> @@ -2213,22 +2213,40 @@ value_from_setting (const setting &var,
>  {
>    switch (var.type ())
>      {
> +    case var_uinteger:
>      case var_integer:
> -      if (var.get<int> () == INT_MAX)
> -	return value_from_longest (builtin_type (gdbarch)->builtin_int,
> -				   0);
> -      else
> -	return value_from_longest (builtin_type (gdbarch)->builtin_int,
> -				   var.get<int> ());
> -    case var_zinteger:
> -      return value_from_longest (builtin_type (gdbarch)->builtin_int,
> -				 var.get<int> ());
> +    case var_pinteger:
> +      {
> +	LONGEST value
> +	  = (var.type () == var_uinteger
> +	     ? static_cast<LONGEST> (var.get<unsigned int> ())
> +	     : static_cast<LONGEST> (var.get<int> ()));
> +
> +	if (var.extra_literals () != nullptr)
> +	  for (const literal_def *l = var.extra_literals ();
> +	       l->literal != nullptr;
> +	       l++)
> +	    if (value == l->use)
> +	      {
> +		if (l->val.has_value ())
> +		  value = *l->val;
> +		else
> +		  return allocate_value (builtin_type (gdbarch)->builtin_void);

I was wondering about this line returning void.  As far as I understand,
this isn't used today.  It would only be used if we defined a
literal_def without a user-visible value.  And the consequence would be
that for this setting, using $_gdb_setting("foo") would yield void for
that setting value.

> +		break;
> +	      }
> +
> +	if (var.type () == var_uinteger)
> +	  return
> +	    value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int,
> +				 static_cast<const unsigned int> (value));
> +	else
> +	  return
> +	    value_from_longest (builtin_type (gdbarch)->builtin_int,
> +				static_cast<const int> (value));

Unnecessary consts in casts.

> Index: src/gdb/cli/cli-option.c
> ===================================================================
> --- src.orig/gdb/cli/cli-option.c
> +++ src/gdb/cli/cli-option.c
> @@ -38,7 +38,7 @@ union option_value
>    /* For var_uinteger options.  */
>    unsigned int uinteger;
>  
> -  /* For var_zuinteger_unlimited options.  */
> +  /* For var_integer and var_pinteger options.  */
>    int integer;
>  
>    /* For var_enum options.  */
> @@ -356,42 +356,55 @@ parse_option (gdb::array_view<const opti
>  	return option_def_and_value {*match, match_ctx, val};
>        }
>      case var_uinteger:
> -    case var_zuinteger_unlimited:
> +    case var_integer:
> +    case var_pinteger:
>        {
>  	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))
> +	    if (match->extra_literals != nullptr)

The two ifs could be merged, it would help reduce the indentation a bit.

>  	      {
> -		completion->tracker.add_completion
> -		  (make_unique_xstrdup ("unlimited"));
> -		return {};
> +		/* Convenience to let the user know what the option can
> +		   accept.  Make sure there's no common prefix between
> +		   "NUMBER" and all the strings when adding new ones,
> +		   so that readline doesn't do a partial match.  */
> +		if (**args == '\0')
> +		  {
> +		    completion->tracker.add_completion
> +		      (make_unique_xstrdup ("NUMBER"));
> +		    for (const literal_def *l = match->extra_literals;
> +			 l->literal != nullptr;
> +			 l++)
> +		      completion->tracker.add_completion
> +			(make_unique_xstrdup (l->literal));
> +		    return {};
> +		  }
> +		else
> +		  {
> +		    bool completions = false;
> +		    for (const literal_def *l = match->extra_literals;
> +			 l->literal != nullptr;
> +			 l++)
> +		      if (startswith (l->literal, *args))
> +			{
> +			  completion->tracker.add_completion
> +			    (make_unique_xstrdup (l->literal));
> +			  completions = true;
> +			}
> +		    if (completions)
> +		      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};
> -	  }
> +	LONGEST v = parse_cli_var_integer (match->type,
> +					   match->extra_literals,
> +					   args, false);
> +	option_value val;
> +	if (match->type == var_uinteger)
> +	  val.uinteger = v;
>  	else
> -	  {
> -	    option_value val;
> -	    val.uinteger = parse_cli_var_uinteger (match->type, args, false);
> -	    return option_def_and_value {*match, match_ctx, val};
> -	  }
> +	  val.integer = v;
> +	return option_def_and_value {*match, match_ctx, val};
>        }
>      case var_enum:
>        {
> @@ -593,7 +606,8 @@ save_option_value_in_ctx (gdb::optional<
>        *ov->option.var_address.uinteger (ov->option, ov->ctx)
>  	= ov->value->uinteger;
>        break;
> -    case var_zuinteger_unlimited:
> +    case var_integer:
> +    case var_pinteger:
>        *ov->option.var_address.integer (ov->option, ov->ctx)
>  	= ov->value->integer;
>        break;
> @@ -664,8 +678,20 @@ get_val_type_str (const option_def &opt,
>      case var_boolean:
>        return "[on|off]";
>      case var_uinteger:
> -    case var_zuinteger_unlimited:
> -      return "NUMBER|unlimited";
> +    case var_integer:
> +    case var_pinteger:
> +      {
> +	buffer = "NUMBER";
> +	if (opt.extra_literals != nullptr)
> +	  for (const literal_def *l = opt.extra_literals;
> +	       l->literal != nullptr;
> +	       l++)
> +	    {
> +	      buffer += '|';
> +	      buffer += l->literal;
> +	    }
> +	return buffer.c_str ();
> +      }
>      case var_enum:
>        {
>  	buffer = "";
> @@ -789,20 +815,31 @@ add_setshow_cmds_for_options (command_cl
>  	{
>  	  add_setshow_uinteger_cmd (option.name, cmd_class,
>  				    option.var_address.uinteger (option, data),
> +				    option.extra_literals,
>  				    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)
> +      else if (option.type == var_integer)
>  	{
> -	  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);
> +	  add_setshow_integer_cmd (option.name, cmd_class,
> +				   option.var_address.integer (option, data),
> +				   option.extra_literals,
> +				   option.set_doc, option.show_doc,
> +				   option.help_doc,
> +				   nullptr, option.show_cmd_cb,
> +				   set_list, show_list);
> +	}
> +      else if (option.type == var_pinteger)
> +	{
> +	  add_setshow_pinteger_cmd (option.name, cmd_class,
> +				    option.var_address.integer (option, data),
> +				    option.extra_literals,
> +				    option.set_doc, option.show_doc,
> +				    option.help_doc,
> +				    nullptr, option.show_cmd_cb,
> +				    set_list, show_list);
>  	}
>        else if (option.type == var_enum)
>  	{
> Index: src/gdb/cli/cli-option.h
> ===================================================================
> --- src.orig/gdb/cli/cli-option.h
> +++ src/gdb/cli/cli-option.h
> @@ -49,12 +49,13 @@ struct option_def
>       used to create the option's "set/show" commands.  */
>    constexpr option_def (const char *name_,
>  			var_types var_type_,
> +			const literal_def *extra_literals_,
>  			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_),
> +    : name (name_), type (var_type_), extra_literals (extra_literals_),
>        erased_get_var_address (erased_get_var_address_),
>        var_address {},
>        show_cmd_cb (show_cmd_cb_),
> @@ -68,6 +69,9 @@ struct option_def
>    /* The option's type.  */
>    var_types type;
>  
> +  /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
> +  const literal_def *extra_literals;
> +
>    /* A function that gets the controlling variable's address, type
>       erased.  */
>    erased_get_var_address_ftype *erased_get_var_address;
> @@ -160,7 +164,7 @@ struct boolean_option_def : option_def
>  		      const char *set_doc_,
>  		      const char *show_doc_ = nullptr,
>  		      const char *help_doc_ = nullptr)
> -    : option_def (long_option_, var_boolean,
> +    : option_def (long_option_, var_boolean, nullptr,
>  		  (erased_get_var_address_ftype *) get_var_address_cb_,
>  		  show_cmd_cb_,
>  		  set_doc_, show_doc_, help_doc_)
> @@ -207,37 +211,59 @@ struct uinteger_option_def : option_def
>  {
>    uinteger_option_def (const char *long_option_,
>  		       unsigned int *(*get_var_address_cb_) (Context *),
> +		       const literal_def *extra_literals_,
>  		       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,
> +    : option_def (long_option_, var_uinteger, extra_literals_,
>  		  (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>;
>    }
> +
> +  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)
> +    : uinteger_option_def (long_option_, get_var_address_cb_, nullptr,
> +			   show_cmd_cb_, set_doc_, show_doc_, help_doc_)
> +  { /* Nothing.  */ }
>  };
>  
> -/* A var_zuinteger_unlimited command line option.  */
> +/* A var_pinteger command line option.  */
>  
>  template<typename Context>
> -struct zuinteger_unlimited_option_def : option_def
> +struct pinteger_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,
> +  pinteger_option_def (const char *long_option_,
> +		       int *(*get_var_address_cb_) (Context *),
> +		       const literal_def *extra_literals_,
> +		       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_pinteger, extra_literals_,
>  		  (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>;
>    }
> +
> +  pinteger_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)
> +    : pinteger_option_def (long_option_, get_var_address_cb_, nullptr,
> +			   show_cmd_cb_, set_doc_, show_doc_, help_doc_)
> +  { /* Nothing.  */ }
>  };
>  
>  /* An var_enum command line option.  */
> @@ -252,7 +278,7 @@ struct enum_option_def : option_def
>  		   const char *set_doc_,
>  		   const char *show_doc_ = nullptr,
>  		   const char *help_doc_ = nullptr)
> -    : option_def (long_option_, var_enum,
> +    : option_def (long_option_, var_enum, nullptr,
>  		  (erased_get_var_address_ftype *) get_var_address_cb_,
>  		  show_cmd_cb_,
>  		  set_doc_, show_doc_, help_doc_)
> @@ -273,7 +299,7 @@ struct string_option_def : option_def
>  		     const char *set_doc_,
>  		     const char *show_doc_ = nullptr,
>  		     const char *help_doc_ = nullptr)
> -    : option_def (long_option_, var_string,
> +    : option_def (long_option_, var_string, nullptr,
>  		  (erased_get_var_address_ftype *) get_var_address_cb_,
>  		  show_cmd_cb_,
>  		  set_doc_, show_doc_, help_doc_)
> Index: src/gdb/cli/cli-setshow.c
> ===================================================================
> --- src.orig/gdb/cli/cli-setshow.c
> +++ src/gdb/cli/cli-setshow.c
> @@ -149,10 +149,11 @@ deprecated_show_value_hack (struct ui_fi
>      }
>  }
>  
> -/* Returns true if ARG is "unlimited".  */
> +/* Returns true and the value in VAL if ARG is an accepted literal.  */
>  
>  static bool
> -is_unlimited_literal (const char **arg, bool expression)
> +get_literal_val (LONGEST &val, const literal_def *extra_literals,
> +		 const char **arg, bool expression)
>  {
>    *arg = skip_spaces (*arg);
>  
> @@ -162,85 +163,104 @@ is_unlimited_literal (const char **arg,
>  
>    size_t len = p - *arg;
>  
> -  if (len > 0 && strncmp ("unlimited", *arg, len) == 0)
> -    {
> -      *arg += len;
> -
> -      /* If parsing an expression (i.e., parsing for a "set" command),
> -	 anything after "unlimited" is junk.  For options, anything
> -	 after "unlimited" might be a command argument or another
> -	 option.  */
> -      if (expression)
> +  if (len > 0 && extra_literals != nullptr)
> +    for (const literal_def *l = extra_literals;
> +	 l->literal != nullptr;
> +	 l++)
> +      if (strncmp (l->literal, *arg, len) == 0)
>  	{
> -	  const char *after = skip_spaces (*arg);
> -	  if (*after != '\0')
> -	    error (_("Junk after \"%.*s\": %s"),
> -		   (int) len, unl_start, after);
> -	}
> +	  *arg += len;
>  
> -      return true;
> -    }
> +	  /* If parsing an expression (i.e., parsing for a "set" command),
> +	     anything after the literal is junk.  For options, anything
> +	     after the literal might be a command argument or another
> +	     option.  */
> +	  if (expression)
> +	    {
> +	      const char *after = skip_spaces (*arg);
> +	      if (*after != '\0')
> +		error (_("Junk after \"%.*s\": %s"),
> +		       (int) len, unl_start, after);
> +	    }
> +
> +	  val = l->use;
> +	  return true;
> +	}
>  
>    return false;
>  }
>  
>  /* See cli-setshow.h.  */
>  
> -unsigned int
> -parse_cli_var_uinteger (var_types var_type, const char **arg,
> -			bool expression)
> +LONGEST
> +parse_cli_var_integer (var_types var_type, const literal_def *extra_literals,
> +		       const char **arg, bool expression)

The function here could perhaps  take a `const setting &`, instead of var_type
and extra_literals, which are properties of a setting.

> @@ -623,36 +614,32 @@ get_setshow_command_value_string (const
>  	}
>        break;
>      case var_uinteger:
> -    case var_zuinteger:
> -      {
> -	const unsigned int value = var.get<unsigned int> ();
> -
> -	if (var.type () == var_uinteger
> -	    && value == UINT_MAX)
> -	  stb.puts ("unlimited");
> -	else
> -	  stb.printf ("%u", value);
> -      }
> -      break;
>      case var_integer:
> -    case var_zinteger:
> +    case var_pinteger:
>        {
> -	const int value = var.get<int> ();
> +	bool printed = false;
> +	const LONGEST value
> +	  = (var.type () == var_uinteger
> +	     ? static_cast<LONGEST> (var.get<unsigned int> ())
> +	     : static_cast<LONGEST> (var.get<int> ()));
>  
> -	if (var.type () == var_integer
> -	    && value == INT_MAX)
> -	  stb.puts ("unlimited");
> -	else
> -	  stb.printf ("%d", value);
> -      }
> -      break;
> -    case var_zuinteger_unlimited:
> -      {
> -	const int value = var.get<int> ();
> -	if (value == -1)
> -	  stb.puts ("unlimited");
> -	else
> -	  stb.printf ("%d", value);
> +	if (var.extra_literals () != nullptr)
> +	  for (const literal_def *l = var.extra_literals ();
> +	       l->literal != nullptr;
> +	       l++)
> +	    if (value == l->use)
> +	      {
> +		stb.puts (l->literal);
> +		printed = true;
> +		break;
> +	      }
> +	if (!printed)
> +	  {
> +	    if (var.type () == var_uinteger)
> +	      stb.printf ("%u", static_cast<const unsigned int> (value));
> +	    else
> +	      stb.printf ("%d", static_cast<const int> (value));

The consts are unnecessary here.

> @@ -106,22 +108,25 @@ enum var_types
>      var_optional_filename,
>      /* String which stores a filename.  (*VAR) is a std::string.  */
>      var_filename,
> -    /* ZeroableInteger.  *VAR is an int.  Like var_integer except
> -       that zero really means zero.  */
> -    var_zinteger,
> -    /* ZeroableUnsignedInteger.  *VAR is an unsigned int.  Zero really
> -       means zero.  */
> -    var_zuinteger,
> -    /* ZeroableUnsignedInteger with unlimited value.  *VAR is an int,
> -       but its range is [0, INT_MAX].  -1 stands for unlimited and
> -       other negative numbers are not allowed.  */
> -    var_zuinteger_unlimited,
>      /* Enumerated type.  Can only have one of the specified values.
>         *VAR is a char pointer to the name of the element that we
>         find.  */
>      var_enum
>    };
>  
> +/* A structure describing an extra literal accepted and shown in place
> +   of a number.  */
> +struct literal_def
> +  {
> +    /* The literal to define, e.g. "unlimited".  */
> +    const char *literal;
> +    /* The number to substitute internally for LITERAL or VAL;
> +       the use of this number is not allowed (unless the same as VAL).  */
> +    LONGEST use;
> +    /* An optional number accepted that stands for the literal.  */
> +    gdb::optional<LONGEST> val;

Could you please add an empty line between each field?  I find that
makes it easier to read.

> +  };

The body of the struct should have one less indentation level (curly
braces at column 0).

> @@ -218,8 +222,8 @@ struct setting
>  
>       Type T must match the var type VAR_TYPE (see VAR_TYPE_USES).  */
>    template<typename T>
> -  setting (var_types var_type, T *var)
> -    : m_var_type (var_type), m_var (var)
> +  setting (var_types var_type, T *var, const void *extra_literals = nullptr)
> +    : m_var_type (var_type), m_var (var), m_extra_literals (extra_literals)

Any reason why extra_literals and m_extra_literals are void pointers,
rather than literal_def pointers?

> @@ -110,6 +116,50 @@ struct param_smob
>    SCM containing_scm;
>  };
>  
> +/* Guile parameter types as in PARAMETER_TYPES later on.  */
> +
> +typedef enum param_types
> +  {
> +    param_boolean,
> +    param_auto_boolean,
> +    param_zinteger,
> +    param_uinteger,
> +    param_zuinteger,
> +    param_zuinteger_unlimited,
> +    param_string,
> +    param_string_noescape,
> +    param_optional_filename,
> +    param_filename,
> +    param_enum,
> +  }
> +param_types;

Just "enum param_types, no need for the typedef.  Braces at column 0.

> +
> +/* Translation from Guile parameters to GDB variable types.  Keep in the
> +   same order as PARAM_TYPES due to C++'s lack of designated initializers.  */
> +
> +static const struct
> +  {
> +    /* The type of the parameter.  */
> +    enum var_types type;
> +
> +    /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
> +    const literal_def *extra_literals;
> +  }

Braces at column 0 above.

> @@ -631,20 +672,29 @@ pascm_param_value (const setting &var, i
>  	  return auto_keyword;
>        }
>  
> -    case var_zuinteger_unlimited:
> -      if (var.get<int> () == -1)
> -	return unlimited_keyword;
> -      gdb_assert (var.get<int> () >= 0);
> -      /* Fall through.  */
> -    case var_zinteger:
> -      return scm_from_int (var.get<int> ());
> -
>      case var_uinteger:
> -      if (var.get<unsigned int> ()== UINT_MAX)
> -	return unlimited_keyword;
> -      /* Fall through.  */
> -    case var_zuinteger:
> -      return scm_from_uint (var.get<unsigned int> ());
> +    case var_integer:
> +    case var_pinteger:
> +      {
> +	LONGEST value
> +	  = (var.type () == var_uinteger
> +	     ? static_cast<LONGEST> (var.get<unsigned int> ())
> +	     : static_cast<LONGEST> (var.get<int> ()));
> +
> +	if (var.extra_literals () != nullptr)
> +	  for (const literal_def *l = var.extra_literals ();
> +	       l->literal != nullptr;
> +	       l++)
> +	    if (value == l->use)
> +	      return scm_from_latin1_keyword (l->literal);
> +	if (var.type () == var_pinteger)
> +	  gdb_assert (value >= 0);
> +
> +	if (var.type () == var_uinteger)
> +	  return scm_from_uint (static_cast<const unsigned int> (value));
> +	else
> +	  return scm_from_int (static_cast<const int> (value));

Unnecessary consts in casts.

> +      }
>  
>      default:
>        break;
> @@ -735,53 +785,91 @@ pascm_set_param_value_x (param_smob *p_s
>  	var.set<enum auto_boolean> (AUTO_BOOLEAN_FALSE);
>        break;
>  
> -    case var_zinteger:
> +    case var_integer:
>      case var_uinteger:
> -    case var_zuinteger:
> -    case var_zuinteger_unlimited:
> -      if (var.type () == var_uinteger
> -	  || var.type () == var_zuinteger_unlimited)
> -	{
> -	  SCM_ASSERT_TYPE (scm_is_integer (value)
> -			   || scm_is_eq (value, unlimited_keyword),
> -			   value, arg_pos, func_name,
> -			   _("integer or #:unlimited"));
> -	  if (scm_is_eq (value, unlimited_keyword))
> +    case var_pinteger:
> +      {
> +	const literal_def *extra_literals = p_smob->extra_literals;
> +	enum tribool allowed = TRIBOOL_UNKNOWN;
> +	enum var_types var_type = var.type ();
> +	bool integer = scm_is_integer (value);
> +	bool keyword = scm_is_keyword (value);
> +	std::string buffer = "";
> +	size_t count = 0;
> +	LONGEST val;
> +
> +	if (extra_literals != nullptr)
> +	  for (const literal_def *l = extra_literals;
> +	       l->literal != nullptr;
> +	       l++, count++)
>  	    {
> -	      if (var.type () == var_uinteger)
> -		var.set<unsigned int> (UINT_MAX);
> -	      else
> -		var.set<int> (-1);
> -	      break;
> +	      if (count != 0)
> +		buffer += ", ";
> +	      buffer = buffer + "#:" + l->literal;
> +	      if (keyword
> +		  && allowed == TRIBOOL_UNKNOWN
> +		  && scm_is_eq (value,
> +				scm_from_latin1_keyword (l->literal)))
> +		{
> +		  val = l->use;
> +		  allowed = TRIBOOL_TRUE;
> +		}
>  	    }
> -	}
> -      else
> -	{
> -	  SCM_ASSERT_TYPE (scm_is_integer (value), value, arg_pos, func_name,
> -			   _("integer"));
> -	}
>  
> -      if (var.type () == var_uinteger
> -	  || var.type () == var_zuinteger)
> -	{
> -	  unsigned int u = scm_to_uint (value);
> +	if (allowed == TRIBOOL_UNKNOWN)
> +	  {
> +	    if (extra_literals == nullptr)
> +	      SCM_ASSERT_TYPE (integer, value, arg_pos, func_name,
> +			       _("integer"));
> +	    else if (count > 1)
> +	      SCM_ASSERT_TYPE (integer, value, arg_pos, func_name,
> +			       string_printf (_("integer or one of: %s"),
> +					      buffer.c_str ()).c_str ());
> +	    else
> +	      SCM_ASSERT_TYPE (integer, value, arg_pos, func_name,
> +			       string_printf (_("integer or %s"),
> +					      buffer.c_str ()).c_str ());
>  
> -	  if (var.type () == var_uinteger && u == 0)
> -	    u = UINT_MAX;
> -	  var.set<unsigned int> (u);
> -	}
> -      else
> -	{
> -	  int i = scm_to_int (value);
> +	    val = (var_type == var_uinteger
> +		   ? static_cast<LONGEST> (scm_to_uint (value))
> +		   : static_cast<LONGEST> (scm_to_int (value)));
>  
> -	  if (var.type () == var_zuinteger_unlimited && i < -1)
> -	    {
> -	      gdbscm_out_of_range_error (func_name, arg_pos, value,
> -					 _("must be >= -1"));
> +	    if (extra_literals != nullptr)
> +	      for (const literal_def *l = extra_literals;
> +		   l->literal != nullptr;
> +		   l++)
> +		{
> +		  if (l->val.has_value () && val == *l->val)
> +		    {
> +		      allowed = TRIBOOL_TRUE;
> +		      val = l->use;
> +		      break;
> +		    }
> +		  else if (val == l->use)
> +		    allowed = TRIBOOL_FALSE;
> +		}
>  	    }
> -	  var.set<int> (i);
> -	}
> -      break;
> +
> +	if (allowed == TRIBOOL_UNKNOWN)
> +	  {
> +	    if (val > UINT_MAX || val < INT_MIN
> +		|| (var_type == var_uinteger && val < 0)
> +		|| (var_type == var_integer && val > INT_MAX)
> +		|| (var_type == var_pinteger && val < 0)
> +		|| (var_type == var_pinteger && val > INT_MAX))
> +	      allowed = TRIBOOL_FALSE;
> +	  }
> +	if (allowed == TRIBOOL_FALSE)
> +	  gdbscm_out_of_range_error (func_name, arg_pos, value,
> +				     _("integer out of range"));
> +
> +	if (var_type == var_uinteger)
> +	  var.set<unsigned int> (static_cast<const unsigned int> (val));
> +	else
> +	  var.set<int> (static_cast<const int> (val));

Unnecessary consts in casts.

> Index: src/gdb/python/py-param.c
> ===================================================================
> --- src.orig/gdb/python/py-param.c
> +++ src/gdb/python/py-param.c
> @@ -28,24 +28,70 @@
>  #include "language.h"
>  #include "arch-utils.h"
>  
> +/* Python parameter types as in PARM_CONSTANTS below.  */
> +
> +typedef enum param_types
> +  {
> +    param_boolean,
> +    param_auto_boolean,
> +    param_uinteger,
> +    param_integer,
> +    param_string,
> +    param_string_noescape,
> +    param_optional_filename,
> +    param_filename,
> +    param_zinteger,
> +    param_zuinteger,
> +    param_zuinteger_unlimited,
> +    param_enum,
> +  }
> +param_types;

Just "enum param_types, no need for the typedef.  Braces at column 0.

I think that having two `param_types` enums (here and in the Guile
support) technically violates the one definition rule.  Not sure if that
matters in practice.

> +
> +/* Translation from Python parameters to GDB variable types.  Keep in the
> +   same order as PARAM_TYPES due to C++'s lack of designated initializers.  */
> +
> +static const struct
> +  {
> +    /* The type of the parameter.  */
> +    enum var_types type;
> +
> +    /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
> +    const literal_def *extra_literals;
> +  }

Braces at column 0.

> Index: src/gdb/python/python.c
> ===================================================================
> --- src.orig/gdb/python/python.c
> +++ src/gdb/python/python.c
> @@ -504,27 +504,45 @@ gdbpy_parameter_value (const setting &va
>  	  Py_RETURN_NONE;
>        }
>  
> -    case var_integer:
> -      if (var.get<int> () == INT_MAX)
> -	Py_RETURN_NONE;
> -      /* Fall through.  */
> -    case var_zinteger:
> -    case var_zuinteger_unlimited:
> -      return gdb_py_object_from_longest (var.get<int> ()).release ();
> -
>      case var_uinteger:
> +    case var_integer:
> +    case var_pinteger:
>        {
> -	unsigned int val = var.get<unsigned int> ();
> +	LONGEST value
> +	  = (var.type () == var_uinteger
> +	     ? static_cast<LONGEST> (var.get<unsigned int> ())
> +	     : static_cast<LONGEST> (var.get<int> ()));
>  
> -	if (val == UINT_MAX)
> -	  Py_RETURN_NONE;
> -	return gdb_py_object_from_ulongest (val).release ();
> -      }
> +	if (var.extra_literals () != nullptr)
> +	  for (const literal_def *l = var.extra_literals ();
> +	       l->literal != nullptr;
> +	       l++)
> +	    if (value == l->use)
> +	      {
> +		if (strcmp (l->literal, "unlimited") == 0)
> +		  {
> +		    /* Compatibility hack for API brokennes.  */

brokenness

> +		    if (var.type () == var_pinteger
> +			&& l->val.has_value ()
> +			&& *l->val == -1)
> +		      value = -1;
> +		    else
> +		      Py_RETURN_NONE;
> +		  }
> +		else if (l->val.has_value ())
> +		  value = *l->val;
> +		else
> +		  return host_string_to_python_string (l->literal).release ();
> +	      }
>  
> -    case var_zuinteger:
> -      {
> -	unsigned int val = var.get<unsigned int> ();
> -	return gdb_py_object_from_ulongest (val).release ();
> +	if (var.type () == var_uinteger)
> +	  return
> +	    gdb_py_object_from_ulongest
> +	      (static_cast<const unsigned int> (value)).release ();
> +	else
> +	  return
> +	    gdb_py_object_from_longest
> +	      (static_cast<const int> (value)).release ();

Unnecessary const in casts.

Simon

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

* Re: [PATCH v8 5/6] GDB: Add a character string limiting option
  2022-11-24 11:22 ` [PATCH v8 5/6] GDB: Add a character string limiting option Maciej W. Rozycki
@ 2023-01-16 19:35   ` Simon Marchi
  2023-01-19 21:17     ` Maciej W. Rozycki
  0 siblings, 1 reply; 32+ messages in thread
From: Simon Marchi @ 2023-01-16 19:35 UTC (permalink / raw)
  To: Maciej W. Rozycki, gdb-patches; +Cc: Andrew Burgess, Tom Tromey, Simon Sobisch



On 11/24/22 06:22, Maciej W. Rozycki wrote:
> From: Andrew Burgess <andrew.burgess@embecosm.com>
> 
> This commit splits the `set/show print elements' option into two.  We 
> retain `set/show print elements' for controlling how many elements of an 
> array we print, but a new `set/show print characters' setting is added 
> which is used for controlling how many characters of a string are 
> printed.
> 
> The motivation behind this change is to allow users a finer level of 
> control over how data is printed, reflecting that, although strings can 
> be thought of as arrays of characters, users often want to treat these 
> two things differently.
> 
> For compatibility reasons by default the `set/show print characters' 
> option is set to `elements', which makes the limit for character strings 
> follow the setting of the `set/show print elements' option, as it used 
> to.  Using `set print characters' with any other value makes the limit 
> independent from the `set/show print elements' setting, however it can 
> be restored to the default with the `set print characters elements' 
> command at any time.
> 
> A corresponding `-characters' option for the `print' command is added, 
> with the same semantics, i.e. one can use `elements' to make a given 
> `print' invocation follow the limit of elements, be it set with the 
> `-elements' option also given with the same invocation or taken from the 
> `set/show print elements' setting, for characters as well regardless of 
> the current setting of the `set/show print characters' option.
> 
> The GDB changes are all pretty straightforward, just changing references 
> to the old 'print_max' to use a new `get_print_max_chars' helper which 
> figures out which of the two of `print_max' and `print_max_chars' values 
> to use.
> 
> Likewise, the documentation is just updated to reference the new setting 
> where appropriate.
> 
> To make people's life easier the message shown by `show print elements' 
> now indicates if the setting also applies to character strings:
> 
> (gdb) set print characters elements
> (gdb) show print elements
> Limit on string chars or array elements to print is 200.
> (gdb) set print characters unlimited
> (gdb) show print elements
> Limit on array elements to print is 200.
> (gdb) 
> 
> which keeps it the same as it used to be, although in other contexts the 
> setting is described as applying to array elements only, e.g.:
> 
> (gdb) help set print elements
> Set limit on array elements to print.
> "unlimited" causes there to be no limit.

I think it would be helpful to say here that if this value influences
"set print characters", if "set print characters" is set to "elements".

Otherwise, the patch LGTM:

Approved-By: Simon Marchi <simon.marchi@efficios.com>

Simon

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

* Re: [PATCH v8 6/6] GDB/testsuite: Expand for character string limiting options
  2022-11-24 11:23 ` [PATCH v8 6/6] GDB/testsuite: Expand for character string limiting options Maciej W. Rozycki
@ 2023-01-16 21:07   ` Simon Marchi
  2023-01-19 21:18     ` Maciej W. Rozycki
  0 siblings, 1 reply; 32+ messages in thread
From: Simon Marchi @ 2023-01-16 21:07 UTC (permalink / raw)
  To: Maciej W. Rozycki, gdb-patches; +Cc: Andrew Burgess, Tom Tromey, Simon Sobisch


Hi,

I noted a few minor comments, once they are fixed this LGTM:

Approved-By: Simon Marchi <simon.marchi@efficios.com>

From my point of view, there's not need to send a new version.

> Index: src/gdb/testsuite/gdb.base/printcmds.exp
> ===================================================================
> --- src.orig/gdb/testsuite/gdb.base/printcmds.exp
> +++ src/gdb/testsuite/gdb.base/printcmds.exp
> @@ -451,11 +451,11 @@ proc test_print_all_chars {} {
>  # Test interaction of the number of print elements to print and the
>  # repeat count, set to the default of 10.
>  
> -proc test_print_repeats_10 {} {
> +proc test_print_repeats_10_one { setting } {

Please expand the doc to say what SETTING is.

> Index: src/gdb/testsuite/gdb.pascal/str-chars.exp
> ===================================================================
> --- /dev/null
> +++ src/gdb/testsuite/gdb.pascal/str-chars.exp
> @@ -0,0 +1,56 @@
> +# Copyright 2022 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/>.
> +
> +load_lib "pascal.exp"
> +
> +standard_testfile .pas
> +
> +if {[gdb_compile_pascal "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug ]] != "" } {
> +  return -1
> +}
> +
> +clean_restart ${testfile}
> +gdb_breakpoint ${srcfile}:[gdb_get_line_number "set breakpoint 1 here"]
> +
> +# Verify that "start" lands inside the right procedure.
> +if { [gdb_start_cmd] < 0 } {
> +    untested start
> +    return -1
> +}

Can you update this to use runto_main, mirroring what was done in this
commit?

https://gitlab.com/gnutools/binutils-gdb/-/commit/4a6bdfb9baa27e29151c7e97ae2abbe902f53638

> Index: src/gdb/testsuite/gdb.python/py-format-string.exp
> ===================================================================
> --- src.orig/gdb/testsuite/gdb.python/py-format-string.exp
> +++ src/gdb/testsuite/gdb.python/py-format-string.exp
> @@ -705,13 +705,13 @@ proc_with_prefix test_static_members {}
>  }
>  
>  # Test the max_elements option for gdb.Value.format_string.
> -proc_with_prefix test_max_elements {} {
> +proc test_max_string_one { setting unlimited } {

The comment above should be expanded a bit.

> @@ -722,8 +722,10 @@ proc_with_prefix test_max_elements {} {
>      check_format_string "a_binary_string" $opts
>      check_format_string "a_binary_string_array" $opts
>      check_format_string "a_big_string" $opts
> -    check_format_string "an_array" $opts
> -    check_format_string "an_array_with_repetition" $opts
> +    if { $setting == "elements"} {

Space before closing bracket (just for consistency with the opening
one).

> @@ -785,15 +791,24 @@ proc_with_prefix test_max_elements {} {
>      }
>    }
>  
> -  with_temp_option "set print elements 4" "set print elements 200" {
> +  with_temp_option "set print $setting 4" "set print $setting 200" {
>      check_format_string "a_string" "" \
>        "${default_pointer_regexp} \"hell\"..."
>      check_format_string "a_binary_string" "" \
>        "${default_pointer_regexp} \"hell\"..."
>      check_format_string "a_binary_string_array" "" \
>        "\"hell\"..."
> -    check_format_string "an_array_with_repetition" "" \
> -      "\\{1, 3 <repeats 12 times>...\\}"
> +    if { $setting == "elements"} {
> +      check_format_string "an_array_with_repetition" "" \
> +	"\\{1, 3 <repeats 12 times>...\\}"
> +    }
> +  }
> +}
> +
> +proc_with_prefix test_max_string {} {

Is the name (and the name of test_max_string_one) accurate?  Does it
test only strings, or strings and arrays?  Just wondering if
test_max_strings_and_arrays would be more accurate.

> +  foreach_with_prefix setting { "elements" "characters" } {

The foreach_with_prefix here is not really necessary, because all tests
under test_max_string_one are under a proc_with_prefix that depends on
the setting passed.  You can remove it if you want to make test names a
bit shorter, but I'm fine with leaving it as well for extra clarity.

> +    test_max_string_one $setting \
> +      [string map {elements 0 characters 4294967295} $setting]

Can you put a comment here and/or in test_max_string_one's comment,
explaining what the last parameter does?  It's not clear to me what is
done.

Simon

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

* Re: [PATCH v8 1/6] GDB: Fix documentation for `theclass' parameters in cli-decode.c
  2023-01-12 18:36   ` Simon Marchi
@ 2023-01-18 21:56     ` Maciej W. Rozycki
  0 siblings, 0 replies; 32+ messages in thread
From: Maciej W. Rozycki @ 2023-01-18 21:56 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches, Andrew Burgess, Tom Tromey, Simon Sobisch

On Thu, 12 Jan 2023, Simon Marchi wrote:

> > Rename CLASS to THECLASS in the documentation for `theclass' parameters 
> > throughout cli-decode.c, complementing commit fe978cb071b4 ("C++ keyword 
> > cleanliness, mostly auto-generated").
> 
> This is ok, if you find more of these, I think you can push them as
> obvious.

 Committed now, thanks for your review.

  Maciej

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

* [COMMITTED PATCH v9 2.0/6] GDB: Add missing inline documentation for `add_setshow_cmd_full'
  2023-01-12 18:40   ` Simon Marchi
@ 2023-01-18 23:24     ` Maciej W. Rozycki
  2023-01-18 23:24       ` [COMMITTED PATCH v9 2.1/6] GDB: Correct inline documentation for `add_setshow_cmd_full_erased' Maciej W. Rozycki
  2023-01-18 23:24       ` [COMMITTED PATCH v9 2.2/6] GDB: Add missing inline documentation for `add_setshow_cmd_full' Maciej W. Rozycki
  0 siblings, 2 replies; 32+ messages in thread
From: Maciej W. Rozycki @ 2023-01-18 23:24 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches, Andrew Burgess, Tom Tromey, Simon Sobisch

On Thu, 12 Jan 2023, Simon Marchi wrote:

> > Index: src/gdb/cli/cli-decode.c
> > ===================================================================
> > --- src.orig/gdb/cli/cli-decode.c
> > +++ src/gdb/cli/cli-decode.c
> > @@ -580,6 +580,18 @@ add_setshow_cmd_full_erased (const char
> >    return {set, show};
> >  }
> >  
> > +/* Add element named NAME to both the command SET_LIST and SHOW_LIST.
> 
> "to both the command" doesn't really make sense.  I'd say "to both
> command lists SET_LIST and SHOW_LIST", or "to both lists SET_LIST and
> SHOW_LIST".
> 
> I see that this text comes from add_setshow_cmd_full_erased, it can be
> fixed there too.  Also, it would be fine to just document the duplicated
> parameters in one function and refer to that in the other function.

 Fair enough.  Indeed I have copied the sentence verbatim from the other 
place.  I have fixed this and applied as in this mini sub-series.  Thank 
you for your review.

  Maciej

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

* [COMMITTED PATCH v9 2.1/6] GDB: Correct inline documentation for `add_setshow_cmd_full_erased'
  2023-01-18 23:24     ` [COMMITTED PATCH v9 2.0/6] " Maciej W. Rozycki
@ 2023-01-18 23:24       ` Maciej W. Rozycki
  2023-01-18 23:24       ` [COMMITTED PATCH v9 2.2/6] GDB: Add missing inline documentation for `add_setshow_cmd_full' Maciej W. Rozycki
  1 sibling, 0 replies; 32+ messages in thread
From: Maciej W. Rozycki @ 2023-01-18 23:24 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches, Andrew Burgess, Tom Tromey, Simon Sobisch

Use proper English in the description of SET_LIST and SHOW_LIST.
---
New change in v9.
---
 gdb/cli/cli-decode.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

gdb-setshow-cmd-full-erased-doc.diff
Index: src/gdb/cli/cli-decode.c
===================================================================
--- src.orig/gdb/cli/cli-decode.c
+++ src/gdb/cli/cli-decode.c
@@ -524,7 +524,7 @@ add_set_or_show_cmd (const char *name,
   return c;
 }
 
-/* Add element named NAME to both the command SET_LIST and SHOW_LIST.
+/* Add element named NAME to both command lists SET_LIST and SHOW_LIST.
    THECLASS is as in add_cmd.  VAR_TYPE is the kind of thing we are
    setting.  VAR is address of the variable being controlled by this
    command.  If nullptr is given as VAR, then both SET_SETTING_FUNC and

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

* [COMMITTED PATCH v9 2.2/6] GDB: Add missing inline documentation for `add_setshow_cmd_full'
  2023-01-18 23:24     ` [COMMITTED PATCH v9 2.0/6] " Maciej W. Rozycki
  2023-01-18 23:24       ` [COMMITTED PATCH v9 2.1/6] GDB: Correct inline documentation for `add_setshow_cmd_full_erased' Maciej W. Rozycki
@ 2023-01-18 23:24       ` Maciej W. Rozycki
  1 sibling, 0 replies; 32+ messages in thread
From: Maciej W. Rozycki @ 2023-01-18 23:24 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches, Andrew Burgess, Tom Tromey, Simon Sobisch

Complement commit 1d7fe7f01b93 ("gdb: Introduce setting construct 
within cmd_list_element") and add missing description for 
`add_setshow_cmd_full'.
---
Changes from v8:

- Use proper English in the description of SET_LIST and SHOW_LIST.

New change in v8, split off from 2/4 in v7.
---
 gdb/cli/cli-decode.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)

gdb-setshow-cmd-full-doc.diff
Index: src/gdb/cli/cli-decode.c
===================================================================
--- src.orig/gdb/cli/cli-decode.c
+++ src/gdb/cli/cli-decode.c
@@ -580,6 +580,18 @@ add_setshow_cmd_full_erased (const char
   return {set, show};
 }
 
+/* Add element named NAME to both command lists SET_LIST and SHOW_LIST.
+   THECLASS is as in add_cmd.  VAR_TYPE is the kind of thing we are
+   setting.  VAR is address of the variable being controlled by this
+   command.  If nullptr is given as VAR, then both SET_SETTING_FUNC and
+   GET_SETTING_FUNC must be provided.  SET_SETTING_FUNC and GET_SETTING_FUNC
+   are callbacks used to access and modify the underlying property,
+   whatever its storage is.  SET_FUNC and SHOW_FUNC are the callback
+   functions (if non-NULL).  SET_DOC, SHOW_DOC and HELP_DOC are the
+   documentation strings.
+
+   Return the newly created set and show commands.  */
+
 template<typename T>
 static set_show_commands
 add_setshow_cmd_full (const char *name,

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

* Re: [PATCH v8 3/6] GDB: Add references to erased args in cli-decode.c
  2023-01-12 18:46   ` Simon Marchi
@ 2023-01-18 23:41     ` Maciej W. Rozycki
  0 siblings, 0 replies; 32+ messages in thread
From: Maciej W. Rozycki @ 2023-01-18 23:41 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches, Andrew Burgess, Tom Tromey, Simon Sobisch

On Thu, 12 Jan 2023, Simon Marchi wrote:

> > Complement commit 1d7fe7f01b93 ("gdb: Introduce setting construct within 
> > cmd_list_element") and commit 702991711a91 ("gdb: Have setter and getter 
> > callbacks for settings") and update inline documentation accordingly for 
> > `add_set_or_show_cmd' and `add_setshow_cmd_full_erased', documenting the 
> > `args' parameter and removing references to `var', `set_setting_func' 
> > and `get_setting_func'.
> 
> If you want to change them so they cross-reference a single place where
> these parameters are documented, it would be fine too.  But in any case:

 This is good enough however, so I have applied it unchanged.  Thank you 
for your review.

  Maciej

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

* Re: [PATCH v8 4/6] GDB: Allow arbitrary keywords in integer set commands
  2023-01-12 20:48   ` Simon Marchi
@ 2023-01-19 21:17     ` Maciej W. Rozycki
  2023-01-19 21:18       ` [COMMITTED PATCH v9 " Maciej W. Rozycki
  2023-01-20 17:16       ` [PATCH v8 " Simon Marchi
  0 siblings, 2 replies; 32+ messages in thread
From: Maciej W. Rozycki @ 2023-01-19 21:17 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches, Andrew Burgess, Tom Tromey, Simon Sobisch

On Thu, 12 Jan 2023, Simon Marchi wrote:

> This is a nice cleanup I think.  I didn't want to delay this further, so
> I did the best review I could given the time I had.  I'm fine with you
> merging this (you can add my Approved-By), we can always follow up with
> more fixes.  If others would like to review it, it's always appreciated,
> but given the time it's been sitting unreviewed, I kind of doubt that
> will happen.

 Given your rationale I chose to commit this change, despite a concern 
with not using `setting', as this code is correct regardless and as you 
say we can always update it further.  See below for details.  Thank you 
for your review.

> > +	    if (value == l->use)
> > +	      {
> > +		if (l->val.has_value ())
> > +		  value = *l->val;
> > +		else
> > +		  return allocate_value (builtin_type (gdbarch)->builtin_void);
> 
> I was wondering about this line returning void.  As far as I understand,
> this isn't used today.  It would only be used if we defined a
> literal_def without a user-visible value.  And the consequence would be
> that for this setting, using $_gdb_setting("foo") would yield void for
> that setting value.

 It's actually used with the next patch and even covered in the testsuite 
(with 6/6):

    gdb_test "p \$_gdb_setting(\"print characters\")" " = void" \
	"_gdb_setting print characters default"

I didn't think it would make sense to move the implementation of this code 
path to the next patch even though this one does not make use of it.

 Overall I think it will be best if we do not expose internals and add new 
magic numbers now that we have the infrastructure that makes it possible 
to avoid.  I only made an exception following Andrew's request here: 
<https://sourceware.org/pipermail/gdb-patches/2022-June/190389.html> to 
make `set print characters' accept "0" for "unlimited" for consistency 
with `set print elements'.

> > +	if (var.type () == var_uinteger)
> > +	  return
> > +	    value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int,
> > +				 static_cast<const unsigned int> (value));
> > +	else
> > +	  return
> > +	    value_from_longest (builtin_type (gdbarch)->builtin_int,
> > +				static_cast<const int> (value));
> 
> Unnecessary consts in casts.

 Fixed, thanks.

> >  	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))
> > +	    if (match->extra_literals != nullptr)
> 
> The two ifs could be merged, it would help reduce the indentation a bit.

 Good catch, thanks!  Fixed.

> > -unsigned int
> > -parse_cli_var_uinteger (var_types var_type, const char **arg,
> > -			bool expression)
> > +LONGEST
> > +parse_cli_var_integer (var_types var_type, const literal_def *extra_literals,
> > +		       const char **arg, bool expression)
> 
> The function here could perhaps  take a `const setting &`, instead of var_type
> and extra_literals, which are properties of a setting.

 It is used by `parse_option' however, where no setting is involved, so 
it's not clear to me how to do that.  I could make an artificial setting I 
suppose, but I feel like it's kind of creating a problem to fit the 
solution.  Let me know if I've missed something.

> > +	if (!printed)
> > +	  {
> > +	    if (var.type () == var_uinteger)
> > +	      stb.printf ("%u", static_cast<const unsigned int> (value));
> > +	    else
> > +	      stb.printf ("%d", static_cast<const int> (value));
> 
> The consts are unnecessary here.

 Fixed, thanks.

> > @@ -106,22 +108,25 @@ enum var_types
> >      var_optional_filename,
> >      /* String which stores a filename.  (*VAR) is a std::string.  */
> >      var_filename,
> > -    /* ZeroableInteger.  *VAR is an int.  Like var_integer except
> > -       that zero really means zero.  */
> > -    var_zinteger,
> > -    /* ZeroableUnsignedInteger.  *VAR is an unsigned int.  Zero really
> > -       means zero.  */
> > -    var_zuinteger,
> > -    /* ZeroableUnsignedInteger with unlimited value.  *VAR is an int,
> > -       but its range is [0, INT_MAX].  -1 stands for unlimited and
> > -       other negative numbers are not allowed.  */
> > -    var_zuinteger_unlimited,
> >      /* Enumerated type.  Can only have one of the specified values.
> >         *VAR is a char pointer to the name of the element that we
> >         find.  */
> >      var_enum
> >    };
> >  
> > +/* A structure describing an extra literal accepted and shown in place
> > +   of a number.  */
> > +struct literal_def
> > +  {
> > +    /* The literal to define, e.g. "unlimited".  */
> > +    const char *literal;
> > +    /* The number to substitute internally for LITERAL or VAL;
> > +       the use of this number is not allowed (unless the same as VAL).  */
> > +    LONGEST use;
> > +    /* An optional number accepted that stands for the literal.  */
> > +    gdb::optional<LONGEST> val;
> 
> Could you please add an empty line between each field?  I find that
> makes it easier to read.

 Done (`enum var_types' above seems inconsistent about it).

> > +  };
> 
> The body of the struct should have one less indentation level (curly
> braces at column 0).

 OK (again, just copied `enum var_types' style here).

> >    template<typename T>
> > -  setting (var_types var_type, T *var)
> > -    : m_var_type (var_type), m_var (var)
> > +  setting (var_types var_type, T *var, const void *extra_literals = nullptr)
> > +    : m_var_type (var_type), m_var (var), m_extra_literals (extra_literals)
> 
> Any reason why extra_literals and m_extra_literals are void pointers,
> rather than literal_def pointers?

 I guess it's been an artefact from the previous approach which used 
`erased_args'.  Updated accordingly now.

> > +/* Guile parameter types as in PARAMETER_TYPES later on.  */
> > +
> > +typedef enum param_types
> > +  {
> > +    param_boolean,
> > +    param_auto_boolean,
> > +    param_zinteger,
> > +    param_uinteger,
> > +    param_zuinteger,
> > +    param_zuinteger_unlimited,
> > +    param_string,
> > +    param_string_noescape,
> > +    param_optional_filename,
> > +    param_filename,
> > +    param_enum,
> > +  }
> > +param_types;
> 
> Just "enum param_types, no need for the typedef.  Braces at column 0.

 Fixed.

> > +static const struct
> > +  {
> > +    /* The type of the parameter.  */
> > +    enum var_types type;
> > +
> > +    /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
> > +    const literal_def *extra_literals;
> > +  }
> 
> Braces at column 0 above.

 Ditto.

> > +	if (var.type () == var_uinteger)
> > +	  return scm_from_uint (static_cast<const unsigned int> (value));
> > +	else
> > +	  return scm_from_int (static_cast<const int> (value));
> 
> Unnecessary consts in casts.

 Fixed.

> > +	if (var_type == var_uinteger)
> > +	  var.set<unsigned int> (static_cast<const unsigned int> (val));
> > +	else
> > +	  var.set<int> (static_cast<const int> (val));
> 
> Unnecessary consts in casts.

 Likewise.

> > +/* Python parameter types as in PARM_CONSTANTS below.  */
> > +
> > +typedef enum param_types
> > +  {
> > +    param_boolean,
> > +    param_auto_boolean,
> > +    param_uinteger,
> > +    param_integer,
> > +    param_string,
> > +    param_string_noescape,
> > +    param_optional_filename,
> > +    param_filename,
> > +    param_zinteger,
> > +    param_zuinteger,
> > +    param_zuinteger_unlimited,
> > +    param_enum,
> > +  }
> > +param_types;
> 
> Just "enum param_types, no need for the typedef.  Braces at column 0.

 Fixed.

> I think that having two `param_types` enums (here and in the Guile
> support) technically violates the one definition rule.  Not sure if that
> matters in practice.

 There's no violation as they are defined in separate translation units 
each and both have local scope.  And so do the respective enumeration 
constants.

> > +/* Translation from Python parameters to GDB variable types.  Keep in the
> > +   same order as PARAM_TYPES due to C++'s lack of designated initializers.  */
> > +
> > +static const struct
> > +  {
> > +    /* The type of the parameter.  */
> > +    enum var_types type;
> > +
> > +    /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
> > +    const literal_def *extra_literals;
> > +  }
> 
> Braces at column 0.

 Fixed.

> > +		if (strcmp (l->literal, "unlimited") == 0)
> > +		  {
> > +		    /* Compatibility hack for API brokennes.  */
> 
> brokenness

 Good catch, thanks!

> > +	if (var.type () == var_uinteger)
> > +	  return
> > +	    gdb_py_object_from_ulongest
> > +	      (static_cast<const unsigned int> (value)).release ();
> > +	else
> > +	  return
> > +	    gdb_py_object_from_longest
> > +	      (static_cast<const int> (value)).release ();
> 
> Unnecessary const in casts.

 Fixed too.

 I'm posting the final version of the patch committed, in a reply to this 
message.

  Maciej

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

* Re: [PATCH v8 5/6] GDB: Add a character string limiting option
  2023-01-16 19:35   ` Simon Marchi
@ 2023-01-19 21:17     ` Maciej W. Rozycki
  2023-01-19 21:19       ` [COMMITTED PATCH v9 " Maciej W. Rozycki
  0 siblings, 1 reply; 32+ messages in thread
From: Maciej W. Rozycki @ 2023-01-19 21:17 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches, Andrew Burgess, Tom Tromey, Simon Sobisch

On Mon, 16 Jan 2023, Simon Marchi wrote:

> > which keeps it the same as it used to be, although in other contexts the 
> > setting is described as applying to array elements only, e.g.:
> > 
> > (gdb) help set print elements
> > Set limit on array elements to print.
> > "unlimited" causes there to be no limit.
> 
> I think it would be helpful to say here that if this value influences
> "set print characters", if "set print characters" is set to "elements".

 I have added this clarification:

This setting also applies to string chars when "print characters"
is set to "elements".

and updated the commit description accordingly.  I'm sending the final 
version of the patch applied, in a reply to this message.  Thank you for 
your review.

  Maciej

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

* Re: [PATCH v8 6/6] GDB/testsuite: Expand for character string limiting options
  2023-01-16 21:07   ` Simon Marchi
@ 2023-01-19 21:18     ` Maciej W. Rozycki
  2023-01-19 21:20       ` [COMMITTED PATCH v9 " Maciej W. Rozycki
  0 siblings, 1 reply; 32+ messages in thread
From: Maciej W. Rozycki @ 2023-01-19 21:18 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches, Andrew Burgess, Tom Tromey, Simon Sobisch

On Mon, 16 Jan 2023, Simon Marchi wrote:

> >  # Test interaction of the number of print elements to print and the
> >  # repeat count, set to the default of 10.
> >  
> > -proc test_print_repeats_10 {} {
> > +proc test_print_repeats_10_one { setting } {
> 
> Please expand the doc to say what SETTING is.

 Done.

> > +clean_restart ${testfile}
> > +gdb_breakpoint ${srcfile}:[gdb_get_line_number "set breakpoint 1 here"]
> > +
> > +# Verify that "start" lands inside the right procedure.
> > +if { [gdb_start_cmd] < 0 } {
> > +    untested start
> > +    return -1
> > +}
> 
> Can you update this to use runto_main, mirroring what was done in this
> commit?
> 
> https://gitlab.com/gnutools/binutils-gdb/-/commit/4a6bdfb9baa27e29151c7e97ae2abbe902f53638

 Done.

> >  # Test the max_elements option for gdb.Value.format_string.
> > -proc_with_prefix test_max_elements {} {
> > +proc test_max_string_one { setting unlimited } {
> 
> The comment above should be expanded a bit.

 Done.

> > -    check_format_string "an_array" $opts
> > -    check_format_string "an_array_with_repetition" $opts
> > +    if { $setting == "elements"} {
> 
> Space before closing bracket (just for consistency with the opening
> one).

 Fixed, thanks.

> > -    check_format_string "an_array_with_repetition" "" \
> > -      "\\{1, 3 <repeats 12 times>...\\}"
> > +    if { $setting == "elements"} {
> > +      check_format_string "an_array_with_repetition" "" \
> > +	"\\{1, 3 <repeats 12 times>...\\}"
> > +    }
> > +  }
> > +}
> > +
> > +proc_with_prefix test_max_string {} {
> 
> Is the name (and the name of test_max_string_one) accurate?  Does it
> test only strings, or strings and arrays?  Just wondering if
> test_max_strings_and_arrays would be more accurate.

 Ultimately it verifies `gdb.Value.format_string', so I think the name is 
accurate enough, even though with a couple of test cases we do use it with 
non-string arrays.

> > +  foreach_with_prefix setting { "elements" "characters" } {
> 
> The foreach_with_prefix here is not really necessary, because all tests
> under test_max_string_one are under a proc_with_prefix that depends on
> the setting passed.  You can remove it if you want to make test names a
> bit shorter, but I'm fine with leaving it as well for extra clarity.

 I've left it for consistency with `test_max_string_one', which also uses 
redundant `with_test_prefix' invocations throughout, so we have e.g.:

PASS: gdb.python/py-format-string.exp: format_string: lang_c: test_all_common: test_max_string: setting=elements: max_elements=200: a_point_t with option max_elements=200

where both "setting=elements: " and "max_elements=200: " could be removed, 
because we have "with option max_elements=200" already.  I guess this 
could be done with a separate change if there's a value in it.

> > +    test_max_string_one $setting \
> > +      [string map {elements 0 characters 4294967295} $setting]
> 
> Can you put a comment here and/or in test_max_string_one's comment,
> explaining what the last parameter does?  It's not clear to me what is
> done.

 Fixed with the `test_max_string_one' description.

 I have also fixed the Ada test case to use `require allow_ada_tests', 
recently introduced, and updated the copyright years for new files 
throughout.

 I'm posting the final version of the patch committed, in a reply to
this message.  Thank you for your review.

  Maciej

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

* [COMMITTED PATCH v9 4/6] GDB: Allow arbitrary keywords in integer set commands
  2023-01-19 21:17     ` Maciej W. Rozycki
@ 2023-01-19 21:18       ` Maciej W. Rozycki
  2023-01-20 17:16       ` [PATCH v8 " Simon Marchi
  1 sibling, 0 replies; 32+ messages in thread
From: Maciej W. Rozycki @ 2023-01-19 21:18 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches, Andrew Burgess, Tom Tromey, Simon Sobisch

Rather than just `unlimited' allow the integer set commands (or command 
options) to define arbitrary keywords for the user to use, removing 
hardcoded arrangements for the `unlimited' keyword.

Remove the confusingly named `var_zinteger', `var_zuinteger' and 
`var_zuinteger_unlimited' `set'/`show' command variable types redefining 
them in terms of `var_uinteger', `var_integer' and `var_pinteger', which 
have the range of [0;UINT_MAX], [INT_MIN;INT_MAX], and [0;INT_MAX] each.

Following existing practice `var_pinteger' allows extra negative values 
to be used, however unlike `var_zuinteger_unlimited' any number of such 
values can be defined rather than just `-1'.

The "p" in `var_pinteger' stands for "positive", for the lack of a more 
appropriate unambiguous letter, even though 0 obviously is not positive; 
"n" would be confusing as to whether it stands for "non-negative" or 
"negative".

Add a new structure, `literal_def', the entries of which define extra 
keywords allowed for a command and numerical values they correspond to.  
Those values are not verified against the basic range supported by the 
underlying variable type, allowing extra values to be allowed outside 
that range, which may or may not be individually made visible to the 
user.  An optional value translation is possible with the structure to 
follow the existing practice for some commands where user-entered 0 is 
internally translated to UINT_MAX or INT_MAX.  Such translation can now 
be arbitrary.  Literals defined by this structure are automatically used 
for completion as necessary.

So for example:

const literal_def integer_unlimited_literals[] =
  {
    { "unlimited", INT_MAX, 0 },
    { nullptr }
  };

defines an extra `unlimited' keyword and a user-visible 0 value, both of 
which get translated to INT_MAX for the setting to be used with.

Similarly:

const literal_def zuinteger_unlimited_literals[] =
  {
    { "unlimited", -1, -1 },
    { nullptr }
  };

defines the same keyword and a corresponding user-visible -1 value that 
is used for the requested setting.  If the last member were omitted (or 
set to `{}') here, then only the keyword would be allowed for the user 
to enter and while -1 would still be used internally trying to enter it 
as a part of a command would result in an "integer -1 out of range" 
error.

Use said error message in all cases (citing the invalid value requested) 
replacing "only -1 is allowed to set as unlimited" previously used for 
`var_zuinteger_unlimited' settings only rather than propagating it to 
`var_pinteger' type.  It could only be used for the specific case where 
a single extra `unlimited' keyword was defined standing for -1 and the 
use of numeric equivalents is discouraged anyway as it is for historical 
reasons only that they expose GDB internals, confusingly different 
across variable types.  Similarly update the "must be >= -1" Guile error 
message.

Redefine Guile and Python parameter types in terms of the new variable 
types and interpret extra keywords as Scheme keywords and Python strings 
used to communicate corresponding parameter values.  Do not add a new
PARAM_INTEGER Guile parameter type, however do handle the `var_integer' 
variable type now, permitting existing parameters defined by GDB proper, 
such as `listsize', to be accessed from Scheme code.

With these changes in place it should be trivial for a Scheme or Python 
programmer to expand the syntax of the `make-parameter' command and the 
`gdb.Parameter' class initializer to have arbitrary extra literals along 
with their internal representation supplied.

Update the testsuite accordingly.

Approved-By: Simon Marchi <simon.marchi@efficios.com>
---
Changes from v8:

- Remove needless `const' specifiers from casts throughout.

- Fold a nested `if' statement into its outer one.

- Add newlines separating `struct literal_def' members.

- Correct `enum' and `struct' indentation throughout.

- Use `literal_def *' rather than `void *' as the type for the
  `m_extra_literals' member and `extra_literals' constructor parameter of 
  `struct setting'.

- Discard `typedef' from `enum param_types' definition.

- Fix a typo in "brokenness".

Changes from v7:

- Handle the special case of returning -1 for the value of `unlimited' 
  with `var_pinteger' parameters in Python code.

- Update Python documentation accordingly, including `unlimited' case 
  descriptions from what used to be in 1/4.

- Pass extra literals outside erased args with `add_set_or_show_cmd' and 
  up the call chain.

No change from v6.

Changes from v5:

- Add a translation layer from Guile and Python parameter types to new
  GDB variable types and remove `var_zuinteger', `var_uinteger', and 
  `var_zuinteger_unlimited' variable types altogether now.

- Add an optional `extra_literals' initialiser to the `setting' class
  constructor.

- Remove the "only -1 is allowed to set as unlimited" error message
  altogether now rather than propagating it to `var_pinteger' type.

- Make the `val' member of `struct literal_def' optional and remove the
  `allow' member; simplify processing accordingly.

- Rename `zuinteger_unlimited_literals' to `pinteger_unlimited_literals',
  making the names of all `*_unlimited_literals' arrays consistent with 
  the corresponding `var_*' variable types.

- Rename `struct integer_option_def' to `struct pinteger_option_def',
  observing it's come from `struct zuinteger_unlimited_option_def' and 
  what used to be the `var_zuinteger_unlimited' now has `var_pinteger' 
  semantics.

- Update the names of test flags used by `maint test-options' accordingly.

- Add constructor variants to `struct uinteger_option_def' and `struct
  pinteger_option_def' that allow one to skip the `extra_literals' 
  initialiser altogether rather than having to pass in `nullptr'.

- Update Python documentation mentioning the use of literal `unlimited'
  with the respective parameter types.

No change from v4.

New change in v4.
---
 gdb/cli/cli-cmds.c                        |   59 ++---
 gdb/cli/cli-decode.c                      |  321 +++++++++++++++++++++++-------
 gdb/cli/cli-option.c                      |  104 ++++++---
 gdb/cli/cli-option.h                      |   54 +++--
 gdb/cli/cli-setshow.c                     |  245 ++++++++++------------
 gdb/cli/cli-setshow.h                     |   20 -
 gdb/command.h                             |  113 ++++++++--
 gdb/doc/python.texi                       |   28 +-
 gdb/guile/scm-param.c                     |  318 +++++++++++++++++++----------
 gdb/maint-test-options.c                  |   44 ++--
 gdb/python/py-param.c                     |  286 ++++++++++++++++----------
 gdb/python/python.c                       |   52 +++-
 gdb/testsuite/gdb.base/max-value-size.exp |    2 
 gdb/testsuite/gdb.base/options.exp        |   47 ++--
 gdb/testsuite/gdb.base/settings.exp       |    2 
 gdb/testsuite/gdb.base/with.exp           |    2 
 gdb/testsuite/gdb.guile/scm-parameter.exp |   23 --
 gdb/testsuite/gdb.python/py-parameter.exp |   15 +
 gdb/valprint.c                            |    9 
 19 files changed, 1127 insertions(+), 617 deletions(-)

gdb-setshow-cmd-extra-literals.diff
Index: src/gdb/cli/cli-cmds.c
===================================================================
--- src.orig/gdb/cli/cli-cmds.c
+++ src/gdb/cli/cli-cmds.c
@@ -2213,22 +2213,40 @@ value_from_setting (const setting &var,
 {
   switch (var.type ())
     {
+    case var_uinteger:
     case var_integer:
-      if (var.get<int> () == INT_MAX)
-	return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				   0);
-      else
-	return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				   var.get<int> ());
-    case var_zinteger:
-      return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				 var.get<int> ());
+    case var_pinteger:
+      {
+	LONGEST value
+	  = (var.type () == var_uinteger
+	     ? static_cast<LONGEST> (var.get<unsigned int> ())
+	     : static_cast<LONGEST> (var.get<int> ()));
+
+	if (var.extra_literals () != nullptr)
+	  for (const literal_def *l = var.extra_literals ();
+	       l->literal != nullptr;
+	       l++)
+	    if (value == l->use)
+	      {
+		if (l->val.has_value ())
+		  value = *l->val;
+		else
+		  return allocate_value (builtin_type (gdbarch)->builtin_void);
+		break;
+	      }
+
+	if (var.type () == var_uinteger)
+	  return
+	    value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int,
+				 static_cast<unsigned int> (value));
+	else
+	  return
+	    value_from_longest (builtin_type (gdbarch)->builtin_int,
+				static_cast<int> (value));
+      }
     case var_boolean:
       return value_from_longest (builtin_type (gdbarch)->builtin_int,
 				 var.get<bool> () ? 1 : 0);
-    case var_zuinteger_unlimited:
-      return value_from_longest (builtin_type (gdbarch)->builtin_int,
-				 var.get<int> ());
     case var_auto_boolean:
       {
 	int val;
@@ -2250,17 +2268,6 @@ value_from_setting (const setting &var,
 	return value_from_longest (builtin_type (gdbarch)->builtin_int,
 				   val);
       }
-    case var_uinteger:
-      if (var.get<unsigned int> () == UINT_MAX)
-	return value_from_ulongest
-	  (builtin_type (gdbarch)->builtin_unsigned_int, 0);
-      else
-	return value_from_ulongest
-	  (builtin_type (gdbarch)->builtin_unsigned_int,
-	   var.get<unsigned int> ());
-    case var_zuinteger:
-      return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int,
-				  var.get<unsigned int> ());
     case var_string:
     case var_string_noescape:
     case var_optional_filename:
@@ -2330,13 +2337,11 @@ str_value_from_setting (const setting &v
 {
   switch (var.type ())
     {
+    case var_uinteger:
     case var_integer:
-    case var_zinteger:
+    case var_pinteger:
     case var_boolean:
-    case var_zuinteger_unlimited:
     case var_auto_boolean:
-    case var_uinteger:
-    case var_zuinteger:
       {
 	std::string cmd_val = get_setshow_command_value_string (var);
 
Index: src/gdb/cli/cli-decode.c
===================================================================
--- src.orig/gdb/cli/cli-decode.c
+++ src/gdb/cli/cli-decode.c
@@ -496,6 +496,8 @@ empty_func (const char *args, int from_t
    TYPE is set_cmd or show_cmd.
    THECLASS is as in add_cmd.
    VAR_TYPE is the kind of thing we are setting.
+   EXTRA_LITERALS if non-NULL define extra literals to be accepted in lieu of
+   a number for integer variables.
    ARGS is a pre-validated type-erased reference to the variable being
    controlled by this command.
    DOC is the documentation string.  */
@@ -505,6 +507,7 @@ add_set_or_show_cmd (const char *name,
 		     enum cmd_types type,
 		     enum command_class theclass,
 		     var_types var_type,
+		     const literal_def *extra_literals,
 		     const setting::erased_args &arg,
 		     const char *doc,
 		     struct cmd_list_element **list)
@@ -513,7 +516,7 @@ add_set_or_show_cmd (const char *name,
 
   gdb_assert (type == set_cmd || type == show_cmd);
   c->type = type;
-  c->var.emplace (var_type, arg);
+  c->var.emplace (var_type, extra_literals, arg);
 
   /* This needs to be something besides NULL so that this isn't
      treated as a help class.  */
@@ -523,10 +526,12 @@ add_set_or_show_cmd (const char *name,
 
 /* Add element named NAME to both command lists SET_LIST and SHOW_LIST.
    THECLASS is as in add_cmd.  VAR_TYPE is the kind of thing we are
-   setting.  ARGS is a pre-validated type-erased reference to the
-   variable being controlled by this command.  SET_FUNC and SHOW_FUNC
-   are the callback functions (if non-NULL).  SET_DOC, SHOW_DOC and
-   HELP_DOC are the documentation strings.
+   setting.  EXTRA_LITERALS if non-NULL define extra literals to be
+   accepted in lieu of a number for integer variables.  ARGS is a
+   pre-validated type-erased reference to the variable being controlled
+   by this command.  SET_FUNC and SHOW_FUNC are the callback functions
+   (if non-NULL).  SET_DOC, SHOW_DOC and HELP_DOC are the documentation
+   strings.
 
    Return the newly created set and show commands.  */
 
@@ -534,6 +539,7 @@ static set_show_commands
 add_setshow_cmd_full_erased (const char *name,
 			     enum command_class theclass,
 			     var_types var_type,
+			     const literal_def *extra_literals,
 			     const setting::erased_args &args,
 			     const char *set_doc, const char *show_doc,
 			     const char *help_doc,
@@ -557,14 +563,16 @@ add_setshow_cmd_full_erased (const char
       full_set_doc = make_unique_xstrdup (set_doc);
       full_show_doc = make_unique_xstrdup (show_doc);
     }
-  set = add_set_or_show_cmd (name, set_cmd, theclass, var_type, args,
+  set = add_set_or_show_cmd (name, set_cmd, theclass, var_type,
+			     extra_literals, args,
 			     full_set_doc.release (), set_list);
   set->doc_allocated = 1;
 
   if (set_func != NULL)
     set->func = set_func;
 
-  show = add_set_or_show_cmd (name, show_cmd, theclass, var_type, args,
+  show = add_set_or_show_cmd (name, show_cmd, theclass, var_type,
+			      extra_literals, args,
 			      full_show_doc.release (), show_list);
   show->doc_allocated = 1;
   show->show_value_func = show_func;
@@ -575,14 +583,41 @@ add_setshow_cmd_full_erased (const char
   return {set, show};
 }
 
+/* Completes on integer commands that support extra literals.  */
+
+static void
+integer_literals_completer (struct cmd_list_element *c,
+			    completion_tracker &tracker,
+			    const char *text, const char *word)
+{
+  const literal_def *extra_literals = c->var->extra_literals ();
+
+  if (*text == '\0')
+    {
+      tracker.add_completion (make_unique_xstrdup ("NUMBER"));
+      for (const literal_def *l = extra_literals;
+	   l->literal != nullptr;
+	   l++)
+	tracker.add_completion (make_unique_xstrdup (l->literal));
+    }
+  else
+    for (const literal_def *l = extra_literals;
+	 l->literal != nullptr;
+	 l++)
+      if (startswith (l->literal, text))
+	tracker.add_completion (make_unique_xstrdup (l->literal));
+}
+
 /* Add element named NAME to both command lists SET_LIST and SHOW_LIST.
    THECLASS is as in add_cmd.  VAR_TYPE is the kind of thing we are
    setting.  VAR is address of the variable being controlled by this
-   command.  If nullptr is given as VAR, then both SET_SETTING_FUNC and
-   GET_SETTING_FUNC must be provided.  SET_SETTING_FUNC and GET_SETTING_FUNC
-   are callbacks used to access and modify the underlying property,
-   whatever its storage is.  SET_FUNC and SHOW_FUNC are the callback
-   functions (if non-NULL).  SET_DOC, SHOW_DOC and HELP_DOC are the
+   command.  EXTRA_LITERALS if non-NULL define extra literals to be
+   accepted in lieu of a number for integer variables.  If nullptr is
+   given as VAR, then both SET_SETTING_FUNC and GET_SETTING_FUNC must
+   be provided.  SET_SETTING_FUNC and GET_SETTING_FUNC are callbacks
+   used to access and modify the underlying property, whatever its
+   storage is.  SET_FUNC and SHOW_FUNC are the callback functions
+   (if non-NULL).  SET_DOC, SHOW_DOC and HELP_DOC are the
    documentation strings.
 
    Return the newly created set and show commands.  */
@@ -592,6 +627,7 @@ static set_show_commands
 add_setshow_cmd_full (const char *name,
 		      enum command_class theclass,
 		      var_types var_type, T *var,
+		      const literal_def *extra_literals,
 		      const char *set_doc, const char *show_doc,
 		      const char *help_doc,
 		      typename setting_func_types<T>::set set_setting_func,
@@ -604,16 +640,43 @@ add_setshow_cmd_full (const char *name,
   auto erased_args
     = setting::erase_args (var_type, var,
 			   set_setting_func, get_setting_func);
+  auto cmds = add_setshow_cmd_full_erased (name,
+					   theclass,
+					   var_type, extra_literals,
+					   erased_args,
+					   set_doc, show_doc,
+					   help_doc,
+					   set_func,
+					   show_func,
+					   set_list,
+					   show_list);
 
-  return add_setshow_cmd_full_erased (name,
-				      theclass,
-				      var_type, erased_args,
-				      set_doc, show_doc,
-				      help_doc,
-				      set_func,
-				      show_func,
-				      set_list,
-				      show_list);
+  if (extra_literals != nullptr)
+    set_cmd_completer (cmds.set, integer_literals_completer);
+
+  return cmds;
+}
+
+/* Same as above but omitting EXTRA_LITERALS.  */
+
+template<typename T>
+static set_show_commands
+add_setshow_cmd_full (const char *name,
+		      enum command_class theclass,
+		      var_types var_type, T *var,
+		      const char *set_doc, const char *show_doc,
+		      const char *help_doc,
+		      typename setting_func_types<T>::set set_setting_func,
+		      typename setting_func_types<T>::get get_setting_func,
+		      cmd_func_ftype *set_func,
+		      show_value_ftype *show_func,
+		      struct cmd_list_element **set_list,
+		      struct cmd_list_element **show_list)
+{
+  return add_setshow_cmd_full (name, theclass, var_type, var, nullptr,
+			       set_doc, show_doc, help_doc,
+			       set_setting_func, get_setting_func,
+			       set_func, show_func, set_list, show_list);
 }
 
 /* Add element named NAME to command list LIST (the list for set or
@@ -982,25 +1045,6 @@ add_setshow_optional_filename_cmd (const
   return cmds;
 }
 
-/* Completes on literal "unlimited".  Used by integer commands that
-   support a special "unlimited" value.  */
-
-static void
-integer_unlimited_completer (struct cmd_list_element *ignore,
-			     completion_tracker &tracker,
-			     const char *text, const char *word)
-{
-  static const char * const keywords[] =
-    {
-      "unlimited",
-      NULL,
-    };
-
-  if (*text == '\0')
-    tracker.add_completion (make_unique_xstrdup ("NUMBER"));
-  complete_on_enum (tracker, keywords, text, word);
-}
-
 /* Add element named NAME to both the set and show command LISTs (the
    list for set/show or some sublist thereof).  THECLASS is as in
    add_cmd.  VAR is address of the variable which will contain the
@@ -1009,6 +1053,55 @@ integer_unlimited_completer (struct cmd_
 
 set_show_commands
 add_setshow_integer_cmd (const char *name, enum command_class theclass,
+			 int *var, const literal_def *extra_literals,
+			 const char *set_doc, const char *show_doc,
+			 const char *help_doc,
+			 cmd_func_ftype *set_func,
+			 show_value_ftype *show_func,
+			 struct cmd_list_element **set_list,
+			 struct cmd_list_element **show_list)
+{
+  set_show_commands commands
+    = add_setshow_cmd_full<int> (name, theclass, var_integer, var,
+				 extra_literals, set_doc, show_doc,
+				 help_doc, nullptr, nullptr, set_func,
+				 show_func, set_list, show_list);
+  return commands;
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_integer_cmd (const char *name, command_class theclass,
+			 const literal_def *extra_literals,
+			 const char *set_doc, const char *show_doc,
+			 const char *help_doc,
+			 setting_func_types<int>::set set_func,
+			 setting_func_types<int>::get get_func,
+			 show_value_ftype *show_func,
+			 cmd_list_element **set_list,
+			 cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<int> (name, theclass, var_integer, nullptr,
+					 extra_literals, set_doc, show_doc,
+					 help_doc, set_func, get_func, nullptr,
+					 show_func, set_list, show_list);
+  return cmds;
+}
+
+/* Accept `unlimited' or 0, translated internally to INT_MAX.  */
+const literal_def integer_unlimited_literals[] =
+  {
+    { "unlimited", INT_MAX, 0 },
+    { nullptr }
+  };
+
+/* Same as above but using `integer_unlimited_literals', with a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_integer_cmd (const char *name, enum command_class theclass,
 			 int *var,
 			 const char *set_doc, const char *show_doc,
 			 const char *help_doc,
@@ -1019,12 +1112,10 @@ add_setshow_integer_cmd (const char *nam
 {
   set_show_commands commands
     = add_setshow_cmd_full<int> (name, theclass, var_integer, var,
+				 integer_unlimited_literals,
 				 set_doc, show_doc, help_doc,
 				 nullptr, nullptr, set_func,
 				 show_func, set_list, show_list);
-
-  set_cmd_completer (commands.set, integer_unlimited_completer);
-
   return commands;
 }
 
@@ -1042,12 +1133,54 @@ add_setshow_integer_cmd (const char *nam
 			 cmd_list_element **show_list)
 {
   auto cmds = add_setshow_cmd_full<int> (name, theclass, var_integer, nullptr,
+					 integer_unlimited_literals,
 					 set_doc, show_doc, help_doc, set_func,
 					 get_func, nullptr, show_func, set_list,
 					 show_list);
+  return cmds;
+}
 
-  set_cmd_completer (cmds.set, integer_unlimited_completer);
+/* 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.  */
+
+set_show_commands
+add_setshow_pinteger_cmd (const char *name, enum command_class theclass,
+			  int *var, const literal_def *extra_literals,
+			  const char *set_doc, const char *show_doc,
+			  const char *help_doc,
+			  cmd_func_ftype *set_func,
+			  show_value_ftype *show_func,
+			  struct cmd_list_element **set_list,
+			  struct cmd_list_element **show_list)
+{
+  set_show_commands commands
+    = add_setshow_cmd_full<int> (name, theclass, var_pinteger, var,
+				 extra_literals, set_doc, show_doc,
+				 help_doc, nullptr, nullptr, set_func,
+				 show_func, set_list, show_list);
+  return commands;
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
 
+set_show_commands
+add_setshow_pinteger_cmd (const char *name, command_class theclass,
+			  const literal_def *extra_literals,
+			  const char *set_doc, const char *show_doc,
+			  const char *help_doc,
+			  setting_func_types<int>::set set_func,
+			  setting_func_types<int>::get get_func,
+			  show_value_ftype *show_func,
+			  cmd_list_element **set_list,
+			  cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<int> (name, theclass, var_pinteger, nullptr,
+					 extra_literals, set_doc, show_doc,
+					 help_doc, set_func, get_func, nullptr,
+					 show_func, set_list, show_list);
   return cmds;
 }
 
@@ -1058,7 +1191,7 @@ add_setshow_integer_cmd (const char *nam
 
 set_show_commands
 add_setshow_uinteger_cmd (const char *name, enum command_class theclass,
-			  unsigned int *var,
+			  unsigned int *var, const literal_def *extra_literals,
 			  const char *set_doc, const char *show_doc,
 			  const char *help_doc,
 			  cmd_func_ftype *set_func,
@@ -1068,12 +1201,9 @@ add_setshow_uinteger_cmd (const char *na
 {
   set_show_commands commands
     = add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger, var,
-					  set_doc, show_doc, help_doc,
-					  nullptr, nullptr, set_func,
+					  extra_literals, set_doc, show_doc,
+					  help_doc, nullptr, nullptr, set_func,
 					  show_func, set_list, show_list);
-
-  set_cmd_completer (commands.set, integer_unlimited_completer);
-
   return commands;
 }
 
@@ -1082,6 +1212,7 @@ add_setshow_uinteger_cmd (const char *na
 
 set_show_commands
 add_setshow_uinteger_cmd (const char *name, command_class theclass,
+			  const literal_def *extra_literals,
 			  const char *set_doc, const char *show_doc,
 			  const char *help_doc,
 			  setting_func_types<unsigned int>::set set_func,
@@ -1091,13 +1222,63 @@ add_setshow_uinteger_cmd (const char *na
 			  cmd_list_element **show_list)
 {
   auto cmds = add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger,
-						  nullptr, set_doc, show_doc,
-						  help_doc, set_func, get_func,
-						  nullptr, show_func, set_list,
+						  nullptr, extra_literals,
+						  set_doc, show_doc, help_doc,
+						  set_func, get_func, nullptr,
+						  show_func, set_list,
 						  show_list);
+  return cmds;
+}
 
-  set_cmd_completer (cmds.set, integer_unlimited_completer);
+/* Accept `unlimited' or 0, translated internally to UINT_MAX.  */
+const literal_def uinteger_unlimited_literals[] =
+  {
+    { "unlimited", UINT_MAX, 0 },
+    { nullptr }
+  };
+
+/* Same as above but using `uinteger_unlimited_literals', with a pointer
+   to a global storage buffer.  */
+
+set_show_commands
+add_setshow_uinteger_cmd (const char *name, enum command_class theclass,
+			  unsigned int *var,
+			  const char *set_doc, const char *show_doc,
+			  const char *help_doc,
+			  cmd_func_ftype *set_func,
+			  show_value_ftype *show_func,
+			  struct cmd_list_element **set_list,
+			  struct cmd_list_element **show_list)
+{
+  set_show_commands commands
+    = add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger, var,
+					  uinteger_unlimited_literals,
+					  set_doc, show_doc, help_doc, nullptr,
+					  nullptr, set_func, show_func,
+					  set_list, show_list);
+  return commands;
+}
+
+/* Same as above but using a getter and a setter function instead of a pointer
+   to a global storage buffer.  */
 
+set_show_commands
+add_setshow_uinteger_cmd (const char *name, command_class theclass,
+			  const char *set_doc, const char *show_doc,
+			  const char *help_doc,
+			  setting_func_types<unsigned int>::set set_func,
+			  setting_func_types<unsigned int>::get get_func,
+			  show_value_ftype *show_func,
+			  cmd_list_element **set_list,
+			  cmd_list_element **show_list)
+{
+  auto cmds = add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger,
+						  nullptr,
+						  uinteger_unlimited_literals,
+						  set_doc, show_doc, help_doc,
+						  set_func, get_func, nullptr,
+						  show_func, set_list,
+						  show_list);
   return cmds;
 }
 
@@ -1116,7 +1297,7 @@ add_setshow_zinteger_cmd (const char *na
 			  struct cmd_list_element **set_list,
 			  struct cmd_list_element **show_list)
 {
-  return add_setshow_cmd_full<int> (name, theclass, var_zinteger, var,
+  return add_setshow_cmd_full<int> (name, theclass, var_integer, var,
 				    set_doc, show_doc, help_doc,
 				    nullptr, nullptr, set_func,
 				    show_func, set_list, show_list);
@@ -1135,12 +1316,22 @@ add_setshow_zinteger_cmd (const char *na
 			  cmd_list_element **set_list,
 			  cmd_list_element **show_list)
 {
-  return add_setshow_cmd_full<int> (name, theclass, var_zinteger, nullptr,
+  return add_setshow_cmd_full<int> (name, theclass, var_integer, nullptr,
 				    set_doc, show_doc, help_doc, set_func,
 				    get_func, nullptr, show_func, set_list,
 				    show_list);
 }
 
+/* Accept `unlimited' or -1, using -1 internally.  */
+const literal_def pinteger_unlimited_literals[] =
+  {
+    { "unlimited", -1, -1 },
+    { nullptr }
+  };
+
+/* Same as above but using `pinteger_unlimited_literals', with a pointer
+   to a global storage buffer.  */
+
 set_show_commands
 add_setshow_zuinteger_unlimited_cmd (const char *name,
 				     enum command_class theclass,
@@ -1154,13 +1345,11 @@ add_setshow_zuinteger_unlimited_cmd (con
 				     struct cmd_list_element **show_list)
 {
   set_show_commands commands
-    = add_setshow_cmd_full<int> (name, theclass, var_zuinteger_unlimited, var,
+    = add_setshow_cmd_full<int> (name, theclass, var_pinteger, var,
+				 pinteger_unlimited_literals,
 				 set_doc, show_doc, help_doc, nullptr,
 				 nullptr, set_func, show_func, set_list,
 				 show_list);
-
-  set_cmd_completer (commands.set, integer_unlimited_completer);
-
   return commands;
 }
 
@@ -1178,13 +1367,11 @@ add_setshow_zuinteger_unlimited_cmd (con
 				     cmd_list_element **show_list)
 {
   auto cmds
-    = add_setshow_cmd_full<int> (name, theclass, var_zuinteger_unlimited,
-				 nullptr, set_doc, show_doc, help_doc, set_func,
+    = add_setshow_cmd_full<int> (name, theclass, var_pinteger, nullptr,
+				 pinteger_unlimited_literals,
+				 set_doc, show_doc, help_doc, set_func,
 				 get_func, nullptr, show_func, set_list,
 				 show_list);
-
-  set_cmd_completer (cmds.set, integer_unlimited_completer);
-
   return cmds;
 }
 
@@ -1203,7 +1390,7 @@ add_setshow_zuinteger_cmd (const char *n
 			   struct cmd_list_element **set_list,
 			   struct cmd_list_element **show_list)
 {
-  return add_setshow_cmd_full<unsigned int> (name, theclass, var_zuinteger,
+  return add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger,
 					     var, set_doc, show_doc, help_doc,
 					     nullptr, nullptr, set_func,
 					     show_func, set_list, show_list);
@@ -1222,7 +1409,7 @@ add_setshow_zuinteger_cmd (const char *n
 			   cmd_list_element **set_list,
 			   cmd_list_element **show_list)
 {
-  return add_setshow_cmd_full<unsigned int> (name, theclass, var_zuinteger,
+  return add_setshow_cmd_full<unsigned int> (name, theclass, var_uinteger,
 					     nullptr, set_doc, show_doc,
 					     help_doc, set_func, get_func,
 					     nullptr, show_func, set_list,
Index: src/gdb/cli/cli-option.c
===================================================================
--- src.orig/gdb/cli/cli-option.c
+++ src/gdb/cli/cli-option.c
@@ -38,7 +38,7 @@ union option_value
   /* For var_uinteger options.  */
   unsigned int uinteger;
 
-  /* For var_zuinteger_unlimited options.  */
+  /* For var_integer and var_pinteger options.  */
   int integer;
 
   /* For var_enum options.  */
@@ -356,42 +356,52 @@ parse_option (gdb::array_view<const opti
 	return option_def_and_value {*match, match_ctx, val};
       }
     case var_uinteger:
-    case var_zuinteger_unlimited:
+    case var_integer:
+    case var_pinteger:
       {
-	if (completion != nullptr)
+	if (completion != nullptr && match->extra_literals != nullptr)
 	  {
+	    /* Convenience to let the user know what the option can
+	       accept.  Make sure there's no common prefix between
+	       "NUMBER" and all the strings when adding new ones,
+	       so that readline doesn't do a partial match.  */
 	    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"));
+		for (const literal_def *l = match->extra_literals;
+		     l->literal != nullptr;
+		     l++)
+		  completion->tracker.add_completion
+		    (make_unique_xstrdup (l->literal));
 		return {};
 	      }
-	    else if (startswith ("unlimited", *args))
+	    else
 	      {
-		completion->tracker.add_completion
-		  (make_unique_xstrdup ("unlimited"));
-		return {};
+		bool completions = false;
+		for (const literal_def *l = match->extra_literals;
+		     l->literal != nullptr;
+		     l++)
+		  if (startswith (l->literal, *args))
+		    {
+		      completion->tracker.add_completion
+			(make_unique_xstrdup (l->literal));
+		      completions = true;
+		    }
+		if (completions)
+		  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};
-	  }
+	LONGEST v = parse_cli_var_integer (match->type,
+					   match->extra_literals,
+					   args, false);
+	option_value val;
+	if (match->type == var_uinteger)
+	  val.uinteger = v;
 	else
-	  {
-	    option_value val;
-	    val.uinteger = parse_cli_var_uinteger (match->type, args, false);
-	    return option_def_and_value {*match, match_ctx, val};
-	  }
+	  val.integer = v;
+	return option_def_and_value {*match, match_ctx, val};
       }
     case var_enum:
       {
@@ -593,7 +603,8 @@ save_option_value_in_ctx (gdb::optional<
       *ov->option.var_address.uinteger (ov->option, ov->ctx)
 	= ov->value->uinteger;
       break;
-    case var_zuinteger_unlimited:
+    case var_integer:
+    case var_pinteger:
       *ov->option.var_address.integer (ov->option, ov->ctx)
 	= ov->value->integer;
       break;
@@ -664,8 +675,20 @@ get_val_type_str (const option_def &opt,
     case var_boolean:
       return "[on|off]";
     case var_uinteger:
-    case var_zuinteger_unlimited:
-      return "NUMBER|unlimited";
+    case var_integer:
+    case var_pinteger:
+      {
+	buffer = "NUMBER";
+	if (opt.extra_literals != nullptr)
+	  for (const literal_def *l = opt.extra_literals;
+	       l->literal != nullptr;
+	       l++)
+	    {
+	      buffer += '|';
+	      buffer += l->literal;
+	    }
+	return buffer.c_str ();
+      }
     case var_enum:
       {
 	buffer = "";
@@ -789,20 +812,31 @@ add_setshow_cmds_for_options (command_cl
 	{
 	  add_setshow_uinteger_cmd (option.name, cmd_class,
 				    option.var_address.uinteger (option, data),
+				    option.extra_literals,
 				    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)
+      else if (option.type == var_integer)
 	{
-	  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);
+	  add_setshow_integer_cmd (option.name, cmd_class,
+				   option.var_address.integer (option, data),
+				   option.extra_literals,
+				   option.set_doc, option.show_doc,
+				   option.help_doc,
+				   nullptr, option.show_cmd_cb,
+				   set_list, show_list);
+	}
+      else if (option.type == var_pinteger)
+	{
+	  add_setshow_pinteger_cmd (option.name, cmd_class,
+				    option.var_address.integer (option, data),
+				    option.extra_literals,
+				    option.set_doc, option.show_doc,
+				    option.help_doc,
+				    nullptr, option.show_cmd_cb,
+				    set_list, show_list);
 	}
       else if (option.type == var_enum)
 	{
Index: src/gdb/cli/cli-option.h
===================================================================
--- src.orig/gdb/cli/cli-option.h
+++ src/gdb/cli/cli-option.h
@@ -49,12 +49,13 @@ struct option_def
      used to create the option's "set/show" commands.  */
   constexpr option_def (const char *name_,
 			var_types var_type_,
+			const literal_def *extra_literals_,
 			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_),
+    : name (name_), type (var_type_), extra_literals (extra_literals_),
       erased_get_var_address (erased_get_var_address_),
       var_address {},
       show_cmd_cb (show_cmd_cb_),
@@ -68,6 +69,9 @@ struct option_def
   /* The option's type.  */
   var_types type;
 
+  /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
+  const literal_def *extra_literals;
+
   /* A function that gets the controlling variable's address, type
      erased.  */
   erased_get_var_address_ftype *erased_get_var_address;
@@ -160,7 +164,7 @@ struct boolean_option_def : option_def
 		      const char *set_doc_,
 		      const char *show_doc_ = nullptr,
 		      const char *help_doc_ = nullptr)
-    : option_def (long_option_, var_boolean,
+    : option_def (long_option_, var_boolean, nullptr,
 		  (erased_get_var_address_ftype *) get_var_address_cb_,
 		  show_cmd_cb_,
 		  set_doc_, show_doc_, help_doc_)
@@ -207,37 +211,59 @@ struct uinteger_option_def : option_def
 {
   uinteger_option_def (const char *long_option_,
 		       unsigned int *(*get_var_address_cb_) (Context *),
+		       const literal_def *extra_literals_,
 		       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,
+    : option_def (long_option_, var_uinteger, extra_literals_,
 		  (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>;
   }
+
+  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)
+    : uinteger_option_def (long_option_, get_var_address_cb_, nullptr,
+			   show_cmd_cb_, set_doc_, show_doc_, help_doc_)
+  { /* Nothing.  */ }
 };
 
-/* A var_zuinteger_unlimited command line option.  */
+/* A var_pinteger command line option.  */
 
 template<typename Context>
-struct zuinteger_unlimited_option_def : option_def
+struct pinteger_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,
+  pinteger_option_def (const char *long_option_,
+		       int *(*get_var_address_cb_) (Context *),
+		       const literal_def *extra_literals_,
+		       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_pinteger, extra_literals_,
 		  (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>;
   }
+
+  pinteger_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)
+    : pinteger_option_def (long_option_, get_var_address_cb_, nullptr,
+			   show_cmd_cb_, set_doc_, show_doc_, help_doc_)
+  { /* Nothing.  */ }
 };
 
 /* An var_enum command line option.  */
@@ -252,7 +278,7 @@ struct enum_option_def : option_def
 		   const char *set_doc_,
 		   const char *show_doc_ = nullptr,
 		   const char *help_doc_ = nullptr)
-    : option_def (long_option_, var_enum,
+    : option_def (long_option_, var_enum, nullptr,
 		  (erased_get_var_address_ftype *) get_var_address_cb_,
 		  show_cmd_cb_,
 		  set_doc_, show_doc_, help_doc_)
@@ -273,7 +299,7 @@ struct string_option_def : option_def
 		     const char *set_doc_,
 		     const char *show_doc_ = nullptr,
 		     const char *help_doc_ = nullptr)
-    : option_def (long_option_, var_string,
+    : option_def (long_option_, var_string, nullptr,
 		  (erased_get_var_address_ftype *) get_var_address_cb_,
 		  show_cmd_cb_,
 		  set_doc_, show_doc_, help_doc_)
Index: src/gdb/cli/cli-setshow.c
===================================================================
--- src.orig/gdb/cli/cli-setshow.c
+++ src/gdb/cli/cli-setshow.c
@@ -149,10 +149,11 @@ deprecated_show_value_hack (struct ui_fi
     }
 }
 
-/* Returns true if ARG is "unlimited".  */
+/* Returns true and the value in VAL if ARG is an accepted literal.  */
 
 static bool
-is_unlimited_literal (const char **arg, bool expression)
+get_literal_val (LONGEST &val, const literal_def *extra_literals,
+		 const char **arg, bool expression)
 {
   *arg = skip_spaces (*arg);
 
@@ -162,85 +163,104 @@ is_unlimited_literal (const char **arg,
 
   size_t len = p - *arg;
 
-  if (len > 0 && strncmp ("unlimited", *arg, len) == 0)
-    {
-      *arg += len;
-
-      /* If parsing an expression (i.e., parsing for a "set" command),
-	 anything after "unlimited" is junk.  For options, anything
-	 after "unlimited" might be a command argument or another
-	 option.  */
-      if (expression)
+  if (len > 0 && extra_literals != nullptr)
+    for (const literal_def *l = extra_literals;
+	 l->literal != nullptr;
+	 l++)
+      if (strncmp (l->literal, *arg, len) == 0)
 	{
-	  const char *after = skip_spaces (*arg);
-	  if (*after != '\0')
-	    error (_("Junk after \"%.*s\": %s"),
-		   (int) len, unl_start, after);
-	}
+	  *arg += len;
 
-      return true;
-    }
+	  /* If parsing an expression (i.e., parsing for a "set" command),
+	     anything after the literal is junk.  For options, anything
+	     after the literal might be a command argument or another
+	     option.  */
+	  if (expression)
+	    {
+	      const char *after = skip_spaces (*arg);
+	      if (*after != '\0')
+		error (_("Junk after \"%.*s\": %s"),
+		       (int) len, unl_start, after);
+	    }
+
+	  val = l->use;
+	  return true;
+	}
 
   return false;
 }
 
 /* See cli-setshow.h.  */
 
-unsigned int
-parse_cli_var_uinteger (var_types var_type, const char **arg,
-			bool expression)
+LONGEST
+parse_cli_var_integer (var_types var_type, const literal_def *extra_literals,
+		       const char **arg, bool expression)
 {
   LONGEST val;
 
   if (*arg == nullptr || **arg == '\0')
     {
-      if (var_type == var_uinteger)
-	error_no_arg (_("integer to set it to, or \"unlimited\""));
-      else
+      if (extra_literals == nullptr)
 	error_no_arg (_("integer to set it to"));
-    }
-
-  if (var_type == var_uinteger && is_unlimited_literal (arg, expression))
-    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.  */
+      else
+	{
+	  std::string buffer = "";
+	  size_t count = 0;
 
-int
-parse_cli_var_zuinteger_unlimited (const char **arg, bool expression)
-{
-  LONGEST val;
+	  for (const literal_def *l = extra_literals;
+	       l->literal != nullptr;
+	       l++, count++)
+	    {
+	      if (count != 0)
+		buffer += ", ";
+	      buffer = buffer + '"' + l->literal + '"';
+	    }
+	  if (count > 1)
+	    error_no_arg
+	      (string_printf (_("integer to set it to, or one of: %s"),
+			      buffer.c_str ()).c_str ());
+	  else
+	    error_no_arg
+	      (string_printf (_("integer to set it to, or %s"),
+			      buffer.c_str ()).c_str ());
+	}
+    }
 
-  if (*arg == nullptr || **arg == '\0')
-    error_no_arg (_("integer to set it to, or \"unlimited\""));
+  if (!get_literal_val (val, extra_literals, arg, expression))
+    {
+      if (expression)
+	val = parse_and_eval_long (*arg);
+      else
+	val = get_ulongest (arg);
 
-  if (is_unlimited_literal (arg, expression))
-    val = -1;
-  else if (expression)
-    val = parse_and_eval_long (*arg);
-  else
-    val = get_ulongest (arg);
+      enum tribool allowed = TRIBOOL_UNKNOWN;
+      if (extra_literals != nullptr)
+	{
+	  for (const literal_def *l = extra_literals;
+	       l->literal != nullptr;
+	       l++)
+	    if (l->val.has_value () && val == *l->val)
+	      {
+		allowed = TRIBOOL_TRUE;
+		val = l->use;
+		break;
+	      }
+	    else if (val == l->use)
+	      allowed = TRIBOOL_FALSE;
+	}
 
-  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"));
+      if (allowed == TRIBOOL_UNKNOWN)
+	{
+	  if (val > UINT_MAX || val < INT_MIN
+	      || (var_type == var_uinteger && val < 0)
+	      || (var_type == var_integer && val > INT_MAX)
+	      || (var_type == var_pinteger && val < 0)
+	      || (var_type == var_pinteger && val > INT_MAX))
+	    allowed = TRIBOOL_FALSE;
+	}
+      if (allowed == TRIBOOL_FALSE)
+	error (_("integer %s out of range"), plongest (val));
+    }
 
   return val;
 }
@@ -405,41 +425,18 @@ do_set_command (const char *arg, int fro
       option_changed = c->var->set<enum auto_boolean> (parse_auto_binary_operation (arg));
       break;
     case var_uinteger:
-    case var_zuinteger:
       option_changed
-	= c->var->set<unsigned int> (parse_cli_var_uinteger (c->var->type (),
-							     &arg, true));
+	= c->var->set<unsigned int> (parse_cli_var_integer (c->var->type (),
+							    c->var->
+							    extra_literals (),
+							    &arg, true));
       break;
     case var_integer:
-    case var_zinteger:
-      {
-	LONGEST val;
-
-	if (*arg == '\0')
-	  {
-	    if (c->var->type () == var_integer)
-	      error_no_arg (_("integer to set it to, or \"unlimited\""));
-	    else
-	      error_no_arg (_("integer to set it to"));
-	  }
-
-	if (c->var->type () == var_integer && is_unlimited_literal (&arg, true))
-	  val = 0;
-	else
-	  val = parse_and_eval_long (arg);
-
-	if (val == 0 && c->var->type () == var_integer)
-	  val = INT_MAX;
-	else if (val < INT_MIN
-		 /* For var_integer, don't let the user set the value
-		    to INT_MAX directly, as that exposes an
-		    implementation detail to the user interface.  */
-		 || (c->var->type () == var_integer && val >= INT_MAX)
-		 || (c->var->type () == var_zinteger && val > INT_MAX))
-	  error (_("integer %s out of range"), plongest (val));
-
-	option_changed = c->var->set<int> (val);
-      }
+    case var_pinteger:
+      option_changed
+	= c->var->set<int> (parse_cli_var_integer (c->var->type (),
+						   c->var->extra_literals (),
+						   &arg, true));
       break;
     case var_enum:
       {
@@ -454,10 +451,6 @@ do_set_command (const char *arg, int fro
 	option_changed = c->var->set<const char *> (match);
       }
       break;
-    case var_zuinteger_unlimited:
-      option_changed = c->var->set<int>
-	(parse_cli_var_zuinteger_unlimited (&arg, true));
-      break;
     default:
       error (_("gdb internal error: bad var_type in do_setshow_command"));
     }
@@ -551,7 +544,6 @@ do_set_command (const char *arg, int fro
 	  }
 	  break;
 	case var_uinteger:
-	case var_zuinteger:
 	  {
 	    char s[64];
 
@@ -560,8 +552,7 @@ do_set_command (const char *arg, int fro
 	  }
 	  break;
 	case var_integer:
-	case var_zinteger:
-	case var_zuinteger_unlimited:
+	case var_pinteger:
 	  {
 	    char s[64];
 
@@ -623,36 +614,32 @@ get_setshow_command_value_string (const
 	}
       break;
     case var_uinteger:
-    case var_zuinteger:
-      {
-	const unsigned int value = var.get<unsigned int> ();
-
-	if (var.type () == var_uinteger
-	    && value == UINT_MAX)
-	  stb.puts ("unlimited");
-	else
-	  stb.printf ("%u", value);
-      }
-      break;
     case var_integer:
-    case var_zinteger:
+    case var_pinteger:
       {
-	const int value = var.get<int> ();
+	bool printed = false;
+	const LONGEST value
+	  = (var.type () == var_uinteger
+	     ? static_cast<LONGEST> (var.get<unsigned int> ())
+	     : static_cast<LONGEST> (var.get<int> ()));
 
-	if (var.type () == var_integer
-	    && value == INT_MAX)
-	  stb.puts ("unlimited");
-	else
-	  stb.printf ("%d", value);
-      }
-      break;
-    case var_zuinteger_unlimited:
-      {
-	const int value = var.get<int> ();
-	if (value == -1)
-	  stb.puts ("unlimited");
-	else
-	  stb.printf ("%d", value);
+	if (var.extra_literals () != nullptr)
+	  for (const literal_def *l = var.extra_literals ();
+	       l->literal != nullptr;
+	       l++)
+	    if (value == l->use)
+	      {
+		stb.puts (l->literal);
+		printed = true;
+		break;
+	      }
+	if (!printed)
+	  {
+	    if (var.type () == var_uinteger)
+	      stb.printf ("%u", static_cast<unsigned int> (value));
+	    else
+	      stb.printf ("%d", static_cast<int> (value));
+	  }
       }
       break;
     default:
Index: src/gdb/cli/cli-setshow.h
===================================================================
--- src.orig/gdb/cli/cli-setshow.h
+++ src/gdb/cli/cli-setshow.h
@@ -29,21 +29,19 @@ extern int parse_cli_boolean_value (cons
    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
+/* Parse ARG, an option to a var_uinteger, var_integer or var_pinteger
+   variable.  Return the parsed value on success or throw an error.  If
+   EXTRA_LITERALS is non-null, then interpret those literals accordingly.
+   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);
+extern LONGEST parse_cli_var_integer (var_types var_type,
+				      const literal_def *extra_literals,
+				      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
Index: src/gdb/command.h
===================================================================
--- src.orig/gdb/command.h
+++ src/gdb/command.h
@@ -84,16 +84,18 @@ enum var_types
        value.  */
     var_auto_boolean,
 
-    /* Unsigned Integer.  *VAR is an unsigned int.  The user can type
-       0 to mean "unlimited", which is stored in *VAR as UINT_MAX.  */
+    /* Unsigned Integer.  *VAR is an unsigned int.  In the Guile and Python
+       APIs 0 means unlimited, which is stored in *VAR as UINT_MAX.  */
     var_uinteger,
 
-    /* Like var_uinteger but signed.  *VAR is an int.  The user can
-       type 0 to mean "unlimited", which is stored in *VAR as
-       INT_MAX.  The only remaining use of it is the Python API.
-       Don't use it elsewhere.  */
+    /* Like var_uinteger but signed.  *VAR is an int.  In the Guile and
+       Python APIs 0 means unlimited, which is stored in *VAR as INT_MAX.  */
     var_integer,
 
+    /* Like var_integer but negative numbers are not allowed,
+       except for special values.  *VAR is an int.  */
+    var_pinteger,
+
     /* String which the user enters with escapes (e.g. the user types
        \n and it is a real newline in the stored string).
        *VAR is a std::string, "" if the string is empty.  */
@@ -106,22 +108,27 @@ enum var_types
     var_optional_filename,
     /* String which stores a filename.  (*VAR) is a std::string.  */
     var_filename,
-    /* ZeroableInteger.  *VAR is an int.  Like var_integer except
-       that zero really means zero.  */
-    var_zinteger,
-    /* ZeroableUnsignedInteger.  *VAR is an unsigned int.  Zero really
-       means zero.  */
-    var_zuinteger,
-    /* ZeroableUnsignedInteger with unlimited value.  *VAR is an int,
-       but its range is [0, INT_MAX].  -1 stands for unlimited and
-       other negative numbers are not allowed.  */
-    var_zuinteger_unlimited,
     /* Enumerated type.  Can only have one of the specified values.
        *VAR is a char pointer to the name of the element that we
        find.  */
     var_enum
   };
 
+/* A structure describing an extra literal accepted and shown in place
+   of a number.  */
+struct literal_def
+{
+  /* The literal to define, e.g. "unlimited".  */
+  const char *literal;
+
+  /* The number to substitute internally for LITERAL or VAL;
+     the use of this number is not allowed (unless the same as VAL).  */
+  LONGEST use;
+
+  /* An optional number accepted that stands for the literal.  */
+  gdb::optional<LONGEST> val;
+};
+
 /* Return true if a setting of type VAR_TYPE is backed with type T.
 
    This function is left without definition intentionally.  This template is
@@ -152,15 +159,14 @@ inline bool var_type_uses<enum auto_bool
 template<>
 inline bool var_type_uses<unsigned int> (var_types t)
 {
-  return (t == var_uinteger || t == var_zinteger || t == var_zuinteger);
+  return t == var_uinteger;
 }
 
 /* Return true if a setting of type T is backed by an int variable.  */
 template<>
 inline bool var_type_uses<int> (var_types t)
 {
-  return (t == var_integer || t == var_zinteger
-	  || t == var_zuinteger_unlimited);
+  return t == var_integer || t == var_pinteger;
 }
 
 /* Return true if a setting of type T is backed by a std::string variable.  */
@@ -218,8 +224,9 @@ struct setting
 
      Type T must match the var type VAR_TYPE (see VAR_TYPE_USES).  */
   template<typename T>
-  setting (var_types var_type, T *var)
-    : m_var_type (var_type), m_var (var)
+  setting (var_types var_type, T *var,
+	   const literal_def *extra_literals = nullptr)
+    : m_var_type (var_type), m_var (var), m_extra_literals (extra_literals)
   {
     gdb_assert (var != nullptr);
     gdb_assert (var_type_uses<T> (var_type));
@@ -258,12 +265,14 @@ struct setting
     };
   }
 
-  /* Create a setting backed by pre-validated type-erased args.
-     ERASED_VAR's fields' real types must match the var type VAR_TYPE
-     (see VAR_TYPE_USES).  */
-  setting (var_types var_type, const erased_args &args)
+  /* Create a setting backed by pre-validated type-erased args and using
+     EXTRA_LITERALS.  ERASED_VAR's fields' real types must match the var
+     type VAR_TYPE (see VAR_TYPE_USES).  */
+  setting (var_types var_type, const literal_def *extra_literals,
+	   const erased_args &args)
     : m_var_type (var_type),
       m_var (args.var),
+      m_extra_literals (extra_literals),
       m_getter (args.getter),
       m_setter (args.setter)
   {
@@ -294,6 +303,10 @@ struct setting
   var_types type () const
   { return m_var_type; }
 
+  /* Access any extra literals accepted.  */
+  const literal_def *extra_literals () const
+  { return m_extra_literals; }
+
   /* Return the current value.
 
      The template parameter T is the type of the variable used to store the
@@ -356,6 +369,9 @@ struct setting
      non-nullptr.  */
   void *m_var = nullptr;
 
+  /* Any extra literals accepted.  */
+  const literal_def *m_extra_literals = nullptr;
+
   /* Pointer to a user provided getter.  */
   erased_func m_getter = nullptr;
 
@@ -651,6 +667,11 @@ typedef void (show_value_ftype) (struct
    instead print the value out directly.  */
 extern show_value_ftype deprecated_show_value_hack;
 
+/* Various sets of extra literals accepted.  */
+extern const literal_def integer_unlimited_literals[];
+extern const literal_def uinteger_unlimited_literals[];
+extern const literal_def pinteger_unlimited_literals[];
+
 extern set_show_commands add_setshow_enum_cmd
   (const char *name, command_class theclass, const char *const *enumlist,
    const char **var, const char *set_doc, const char *show_doc,
@@ -747,6 +768,20 @@ extern set_show_commands add_setshow_opt
    cmd_list_element **show_list);
 
 extern set_show_commands add_setshow_integer_cmd
+  (const char *name, command_class theclass, int *var,
+   const literal_def *extra_literals, const char *set_doc,
+   const char *show_doc, const char *help_doc, cmd_func_ftype *set_func,
+   show_value_ftype *show_func, cmd_list_element **set_list,
+   cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_integer_cmd
+  (const char *name, command_class theclass, const literal_def *extra_literals,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   setting_func_types<int>::set set_func,
+   setting_func_types<int>::get get_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_integer_cmd
   (const char *name, command_class theclass, int *var, const char *set_doc,
    const char *show_doc, const char *help_doc, cmd_func_ftype *set_func,
    show_value_ftype *show_func, cmd_list_element **set_list,
@@ -759,6 +794,34 @@ extern set_show_commands add_setshow_int
    setting_func_types<int>::get get_func, show_value_ftype *show_func,
    cmd_list_element **set_list, cmd_list_element **show_list);
 
+extern set_show_commands add_setshow_pinteger_cmd
+  (const char *name, command_class theclass, int *var,
+   const literal_def *extra_literals, const char *set_doc,
+   const char *show_doc, const char *help_doc, cmd_func_ftype *set_func,
+   show_value_ftype *show_func, cmd_list_element **set_list,
+   cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_pinteger_cmd
+  (const char *name, command_class theclass, const literal_def *extra_literals,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   setting_func_types<int>::set set_func,
+   setting_func_types<int>::get get_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_uinteger_cmd
+  (const char *name, command_class theclass, unsigned int *var,
+   const literal_def *extra_literals,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   cmd_func_ftype *set_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
+extern set_show_commands add_setshow_uinteger_cmd
+  (const char *name, command_class theclass, const literal_def *extra_literals,
+   const char *set_doc, const char *show_doc, const char *help_doc,
+   setting_func_types<unsigned int>::set set_func,
+   setting_func_types<unsigned int>::get get_func, show_value_ftype *show_func,
+   cmd_list_element **set_list, cmd_list_element **show_list);
+
 extern set_show_commands add_setshow_uinteger_cmd
   (const char *name, command_class theclass, unsigned int *var,
    const char *set_doc, const char *show_doc, const char *help_doc,
Index: src/gdb/doc/python.texi
===================================================================
--- src.orig/gdb/doc/python.texi
+++ src/gdb/doc/python.texi
@@ -4611,14 +4611,18 @@ Python, true and false are represented u
 @findex PARAM_UINTEGER
 @findex gdb.PARAM_UINTEGER
 @item gdb.PARAM_UINTEGER
-The value is an unsigned integer.  The value of 0 should be
-interpreted to mean ``unlimited''.
+The value is an unsigned integer.  The value of @code{None} should be
+interpreted to mean ``unlimited'' (literal @code{'unlimited'} can also
+be used to set that value), and the value of 0 is reserved and should
+not be used.
 
 @findex PARAM_INTEGER
 @findex gdb.PARAM_INTEGER
 @item gdb.PARAM_INTEGER
-The value is a signed integer.  The value of 0 should be interpreted
-to mean ``unlimited''.
+The value is a signed integer.  The value of @code{None} should be
+interpreted to mean ``unlimited'' (literal @code{'unlimited'} can also
+be used to set that value), and the value of 0 is reserved and should
+not be used.
 
 @findex PARAM_STRING
 @findex gdb.PARAM_STRING
@@ -4648,21 +4652,23 @@ The value is a filename.  This is just l
 @findex PARAM_ZINTEGER
 @findex gdb.PARAM_ZINTEGER
 @item gdb.PARAM_ZINTEGER
-The value is an integer.  This is like @code{PARAM_INTEGER}, except 0
-is interpreted as itself.
+The value is a signed integer.  This is like @code{PARAM_INTEGER},
+except that 0 is allowed and the value of @code{None} is not supported.
 
 @findex PARAM_ZUINTEGER
 @findex gdb.PARAM_ZUINTEGER
 @item gdb.PARAM_ZUINTEGER
-The value is an unsigned integer.  This is like @code{PARAM_INTEGER},
-except 0 is interpreted as itself, and the value cannot be negative.
+The value is an unsigned integer.  This is like @code{PARAM_UINTEGER},
+except that 0 is allowed and the value of @code{None} is not supported.
 
 @findex PARAM_ZUINTEGER_UNLIMITED
 @findex gdb.PARAM_ZUINTEGER_UNLIMITED
 @item gdb.PARAM_ZUINTEGER_UNLIMITED
-The value is a signed integer.  This is like @code{PARAM_ZUINTEGER},
-except the special value -1 should be interpreted to mean
-``unlimited''.  Other negative values are not allowed.
+The value is a signed integer.  This is like @code{PARAM_INTEGER}
+including that the value of @code{None} should be interpreted to mean
+``unlimited'' (literal @code{'unlimited'} can also be used to set that
+value), except that 0 is allowed, and the value cannot be negative,
+except the special value -1 is returned for the setting of ``unlimited''.
 
 @findex PARAM_ENUM
 @findex gdb.PARAM_ENUM
Index: src/gdb/guile/scm-param.c
===================================================================
--- src.orig/gdb/guile/scm-param.c
+++ src/gdb/guile/scm-param.c
@@ -75,9 +75,15 @@ struct param_smob
   /* One of the COMMAND_* constants.  */
   enum command_class cmd_class;
 
+  /* Guile parameter type name.  */
+  const char *pname;
+
   /* The type of the parameter.  */
   enum var_types type;
 
+  /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
+  const literal_def *extra_literals;
+
   /* The docs for the parameter.  */
   char *set_doc;
   char *show_doc;
@@ -110,6 +116,49 @@ struct param_smob
   SCM containing_scm;
 };
 
+/* Guile parameter types as in PARAMETER_TYPES later on.  */
+
+enum param_types
+{
+  param_boolean,
+  param_auto_boolean,
+  param_zinteger,
+  param_uinteger,
+  param_zuinteger,
+  param_zuinteger_unlimited,
+  param_string,
+  param_string_noescape,
+  param_optional_filename,
+  param_filename,
+  param_enum,
+};
+
+/* Translation from Guile parameters to GDB variable types.  Keep in the
+   same order as PARAM_TYPES due to C++'s lack of designated initializers.  */
+
+static const struct
+{
+  /* The type of the parameter.  */
+  enum var_types type;
+
+  /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
+  const literal_def *extra_literals;
+}
+param_to_var[] =
+{
+  { var_boolean },
+  { var_auto_boolean },
+  { var_integer },
+  { var_uinteger, uinteger_unlimited_literals },
+  { var_uinteger },
+  { var_pinteger, pinteger_unlimited_literals },
+  { var_string },
+  { var_string_noescape },
+  { var_optional_filename },
+  { var_filename },
+  { var_enum }
+};
+
 /* Wraps a setting around an existing param_smob.  This abstraction
    is used to manipulate the value in S->VALUE in a type safe manner using
    the setting interface.  */
@@ -117,18 +166,20 @@ struct param_smob
 static setting
 make_setting (param_smob *s)
 {
-  if (var_type_uses<bool> (s->type))
-    return setting (s->type, &s->value.boolval);
-  else if (var_type_uses<int> (s->type))
-    return setting (s->type, &s->value.intval);
-  else if (var_type_uses<auto_boolean> (s->type))
-    return setting (s->type, &s->value.autoboolval);
-  else if (var_type_uses<unsigned int> (s->type))
-    return setting (s->type, &s->value.uintval);
-  else if (var_type_uses<std::string> (s->type))
-    return setting (s->type, s->value.stringval);
-  else if (var_type_uses<const char *> (s->type))
-    return setting (s->type, &s->value.cstringval);
+  enum var_types type = s->type;
+
+  if (var_type_uses<bool> (type))
+    return setting (type, &s->value.boolval);
+  else if (var_type_uses<int> (type))
+    return setting (type, &s->value.intval, s->extra_literals);
+  else if (var_type_uses<auto_boolean> (type))
+    return setting (type, &s->value.autoboolval);
+  else if (var_type_uses<unsigned int> (type))
+    return setting (type, &s->value.uintval, s->extra_literals);
+  else if (var_type_uses<std::string> (type))
+    return setting (type, s->value.stringval);
+  else if (var_type_uses<const char *> (type))
+    return setting (type, &s->value.cstringval);
   else
     gdb_assert_not_reached ("unhandled var type");
 }
@@ -149,10 +200,9 @@ static SCM set_doc_keyword;
 static SCM show_doc_keyword;
 static SCM initial_value_keyword;
 static SCM auto_keyword;
-static SCM unlimited_keyword;
 
 static int pascm_is_valid (param_smob *);
-static const char *pascm_param_type_name (enum var_types type);
+static const char *pascm_param_type_name (enum param_types type);
 static SCM pascm_param_value (const setting &var, int arg_pos,
 			      const char *func_name);
 \f
@@ -171,7 +221,7 @@ pascm_print_param_smob (SCM self, SCM po
   if (! pascm_is_valid (p_smob))
     scm_puts (" {invalid}", port);
 
-  gdbscm_printf (port, " %s ", pascm_param_type_name (p_smob->type));
+  gdbscm_printf (port, " %s ", p_smob->pname);
 
   value = pascm_param_value (make_setting (p_smob), GDBSCM_ARG_NONE, NULL);
   scm_display (value, port);
@@ -369,7 +419,9 @@ pascm_show_func (struct ui_file *file, i
    function.  */
 
 static set_show_commands
-add_setshow_generic (enum var_types param_type, enum command_class cmd_class,
+add_setshow_generic (enum var_types param_type,
+		     const literal_def *extra_literals,
+		     enum command_class cmd_class,
 		     char *cmd_name, param_smob *self,
 		     char *set_doc, char *show_doc, char *help_doc,
 		     cmd_func_ftype *set_func,
@@ -398,32 +450,26 @@ add_setshow_generic (enum var_types para
 
     case var_uinteger:
       commands = add_setshow_uinteger_cmd (cmd_name, cmd_class,
-					   &self->value.uintval, set_doc,
-					   show_doc, help_doc, set_func,
-					   show_func, set_list, show_list);
-      break;
-
-    case var_zinteger:
-      commands = add_setshow_zinteger_cmd (cmd_name, cmd_class,
-					   &self->value.intval, set_doc,
+					   &self->value.uintval,
+					   extra_literals, set_doc,
 					   show_doc, help_doc, set_func,
 					   show_func, set_list, show_list);
       break;
 
-    case var_zuinteger:
-      commands = add_setshow_zuinteger_cmd (cmd_name, cmd_class,
-					    &self->value.uintval, set_doc,
-					    show_doc, help_doc, set_func,
-					    show_func, set_list, show_list);
+    case var_integer:
+      commands = add_setshow_integer_cmd (cmd_name, cmd_class,
+					  &self->value.intval,
+					  extra_literals, set_doc,
+					  show_doc, help_doc, set_func,
+					  show_func, set_list, show_list);
       break;
 
-    case var_zuinteger_unlimited:
-      commands = add_setshow_zuinteger_unlimited_cmd (cmd_name, cmd_class,
-						      &self->value.intval,
-						      set_doc, show_doc,
-						      help_doc, set_func,
-						      show_func, set_list,
-						      show_list);
+    case var_pinteger:
+      commands = add_setshow_pinteger_cmd (cmd_name, cmd_class,
+					   &self->value.intval,
+					   extra_literals, set_doc,
+					   show_doc, help_doc, set_func,
+					   show_func, set_list, show_list);
       break;
 
     case var_string:
@@ -532,19 +578,17 @@ compute_enum_list (SCM enum_values_scm,
 
 static const scheme_integer_constant parameter_types[] =
 {
-  /* Note: var_integer is deprecated, and intentionally does not
-     appear here.  */
-  { "PARAM_BOOLEAN", var_boolean }, /* ARI: var_boolean */
-  { "PARAM_AUTO_BOOLEAN", var_auto_boolean },
-  { "PARAM_ZINTEGER", var_zinteger },
-  { "PARAM_UINTEGER", var_uinteger },
-  { "PARAM_ZUINTEGER", var_zuinteger },
-  { "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited },
-  { "PARAM_STRING", var_string },
-  { "PARAM_STRING_NOESCAPE", var_string_noescape },
-  { "PARAM_OPTIONAL_FILENAME", var_optional_filename },
-  { "PARAM_FILENAME", var_filename },
-  { "PARAM_ENUM", var_enum },
+  { "PARAM_BOOLEAN", param_boolean }, /* ARI: param_boolean */
+  { "PARAM_AUTO_BOOLEAN", param_auto_boolean },
+  { "PARAM_ZINTEGER", param_zinteger },
+  { "PARAM_UINTEGER", param_uinteger },
+  { "PARAM_ZUINTEGER", param_zuinteger },
+  { "PARAM_ZUINTEGER_UNLIMITED", param_zuinteger_unlimited },
+  { "PARAM_STRING", param_string },
+  { "PARAM_STRING_NOESCAPE", param_string_noescape },
+  { "PARAM_OPTIONAL_FILENAME", param_optional_filename },
+  { "PARAM_FILENAME", param_filename },
+  { "PARAM_ENUM", param_enum },
 
   END_INTEGER_CONSTANTS
 };
@@ -568,7 +612,7 @@ pascm_valid_parameter_type_p (int param_
 /* Return PARAM_TYPE as a string.  */
 
 static const char *
-pascm_param_type_name (enum var_types param_type)
+pascm_param_type_name (enum param_types param_type)
 {
   int i;
 
@@ -588,10 +632,6 @@ pascm_param_type_name (enum var_types pa
 static SCM
 pascm_param_value (const setting &var, int arg_pos, const char *func_name)
 {
-  /* Note: We *could* support var_integer here in case someone is trying to get
-     the value of a Python-created parameter (which is the only place that
-     still supports var_integer).  To further discourage its use we do not.  */
-
   switch (var.type ())
     {
     case var_string:
@@ -631,20 +671,29 @@ pascm_param_value (const setting &var, i
 	  return auto_keyword;
       }
 
-    case var_zuinteger_unlimited:
-      if (var.get<int> () == -1)
-	return unlimited_keyword;
-      gdb_assert (var.get<int> () >= 0);
-      /* Fall through.  */
-    case var_zinteger:
-      return scm_from_int (var.get<int> ());
-
     case var_uinteger:
-      if (var.get<unsigned int> ()== UINT_MAX)
-	return unlimited_keyword;
-      /* Fall through.  */
-    case var_zuinteger:
-      return scm_from_uint (var.get<unsigned int> ());
+    case var_integer:
+    case var_pinteger:
+      {
+	LONGEST value
+	  = (var.type () == var_uinteger
+	     ? static_cast<LONGEST> (var.get<unsigned int> ())
+	     : static_cast<LONGEST> (var.get<int> ()));
+
+	if (var.extra_literals () != nullptr)
+	  for (const literal_def *l = var.extra_literals ();
+	       l->literal != nullptr;
+	       l++)
+	    if (value == l->use)
+	      return scm_from_latin1_keyword (l->literal);
+	if (var.type () == var_pinteger)
+	  gdb_assert (value >= 0);
+
+	if (var.type () == var_uinteger)
+	  return scm_from_uint (static_cast<unsigned int> (value));
+	else
+	  return scm_from_int (static_cast<int> (value));
+      }
 
     default:
       break;
@@ -735,53 +784,91 @@ pascm_set_param_value_x (param_smob *p_s
 	var.set<enum auto_boolean> (AUTO_BOOLEAN_FALSE);
       break;
 
-    case var_zinteger:
+    case var_integer:
     case var_uinteger:
-    case var_zuinteger:
-    case var_zuinteger_unlimited:
-      if (var.type () == var_uinteger
-	  || var.type () == var_zuinteger_unlimited)
-	{
-	  SCM_ASSERT_TYPE (scm_is_integer (value)
-			   || scm_is_eq (value, unlimited_keyword),
-			   value, arg_pos, func_name,
-			   _("integer or #:unlimited"));
-	  if (scm_is_eq (value, unlimited_keyword))
+    case var_pinteger:
+      {
+	const literal_def *extra_literals = p_smob->extra_literals;
+	enum tribool allowed = TRIBOOL_UNKNOWN;
+	enum var_types var_type = var.type ();
+	bool integer = scm_is_integer (value);
+	bool keyword = scm_is_keyword (value);
+	std::string buffer = "";
+	size_t count = 0;
+	LONGEST val;
+
+	if (extra_literals != nullptr)
+	  for (const literal_def *l = extra_literals;
+	       l->literal != nullptr;
+	       l++, count++)
 	    {
-	      if (var.type () == var_uinteger)
-		var.set<unsigned int> (UINT_MAX);
-	      else
-		var.set<int> (-1);
-	      break;
+	      if (count != 0)
+		buffer += ", ";
+	      buffer = buffer + "#:" + l->literal;
+	      if (keyword
+		  && allowed == TRIBOOL_UNKNOWN
+		  && scm_is_eq (value,
+				scm_from_latin1_keyword (l->literal)))
+		{
+		  val = l->use;
+		  allowed = TRIBOOL_TRUE;
+		}
 	    }
-	}
-      else
-	{
-	  SCM_ASSERT_TYPE (scm_is_integer (value), value, arg_pos, func_name,
-			   _("integer"));
-	}
 
-      if (var.type () == var_uinteger
-	  || var.type () == var_zuinteger)
-	{
-	  unsigned int u = scm_to_uint (value);
+	if (allowed == TRIBOOL_UNKNOWN)
+	  {
+	    if (extra_literals == nullptr)
+	      SCM_ASSERT_TYPE (integer, value, arg_pos, func_name,
+			       _("integer"));
+	    else if (count > 1)
+	      SCM_ASSERT_TYPE (integer, value, arg_pos, func_name,
+			       string_printf (_("integer or one of: %s"),
+					      buffer.c_str ()).c_str ());
+	    else
+	      SCM_ASSERT_TYPE (integer, value, arg_pos, func_name,
+			       string_printf (_("integer or %s"),
+					      buffer.c_str ()).c_str ());
 
-	  if (var.type () == var_uinteger && u == 0)
-	    u = UINT_MAX;
-	  var.set<unsigned int> (u);
-	}
-      else
-	{
-	  int i = scm_to_int (value);
+	    val = (var_type == var_uinteger
+		   ? static_cast<LONGEST> (scm_to_uint (value))
+		   : static_cast<LONGEST> (scm_to_int (value)));
 
-	  if (var.type () == var_zuinteger_unlimited && i < -1)
-	    {
-	      gdbscm_out_of_range_error (func_name, arg_pos, value,
-					 _("must be >= -1"));
+	    if (extra_literals != nullptr)
+	      for (const literal_def *l = extra_literals;
+		   l->literal != nullptr;
+		   l++)
+		{
+		  if (l->val.has_value () && val == *l->val)
+		    {
+		      allowed = TRIBOOL_TRUE;
+		      val = l->use;
+		      break;
+		    }
+		  else if (val == l->use)
+		    allowed = TRIBOOL_FALSE;
+		}
 	    }
-	  var.set<int> (i);
-	}
-      break;
+
+	if (allowed == TRIBOOL_UNKNOWN)
+	  {
+	    if (val > UINT_MAX || val < INT_MIN
+		|| (var_type == var_uinteger && val < 0)
+		|| (var_type == var_integer && val > INT_MAX)
+		|| (var_type == var_pinteger && val < 0)
+		|| (var_type == var_pinteger && val > INT_MAX))
+	      allowed = TRIBOOL_FALSE;
+	  }
+	if (allowed == TRIBOOL_FALSE)
+	  gdbscm_out_of_range_error (func_name, arg_pos, value,
+				     _("integer out of range"));
+
+	if (var_type == var_uinteger)
+	  var.set<unsigned int> (static_cast<unsigned int> (val));
+	else
+	  var.set<int> (static_cast<int> (val));
+
+	break;
+      }
 
     default:
       gdb_assert_not_reached ("bad parameter type");
@@ -858,7 +945,7 @@ gdbscm_make_parameter (SCM name_scm, SCM
   char *s;
   char *name;
   int cmd_class = no_class;
-  int param_type = var_boolean; /* ARI: var_boolean */
+  int param_type = param_boolean; /* ARI: param_boolean */
   SCM enum_list_scm = SCM_BOOL_F;
   SCM set_func = SCM_BOOL_F, show_func = SCM_BOOL_F;
   char *doc = NULL, *set_doc = NULL, *show_doc = NULL;
@@ -913,12 +1000,12 @@ gdbscm_make_parameter (SCM name_scm, SCM
 				 scm_from_int (param_type),
 				 _("invalid parameter type argument"));
     }
-  if (enum_list_arg_pos > 0 && param_type != var_enum)
+  if (enum_list_arg_pos > 0 && param_type != param_enum)
     {
       gdbscm_misc_error (FUNC_NAME, enum_list_arg_pos, enum_list_scm,
 		_("#:enum-values can only be provided with PARAM_ENUM"));
     }
-  if (enum_list_arg_pos < 0 && param_type == var_enum)
+  if (enum_list_arg_pos < 0 && param_type == param_enum)
     {
       gdbscm_misc_error (FUNC_NAME, GDBSCM_ARG_NONE, SCM_BOOL_F,
 			 _("PARAM_ENUM requires an enum-values argument"));
@@ -933,7 +1020,7 @@ gdbscm_make_parameter (SCM name_scm, SCM
       SCM_ASSERT_TYPE (gdbscm_is_procedure (show_func), show_func,
 		       show_func_arg_pos, FUNC_NAME, _("procedure"));
     }
-  if (param_type == var_enum)
+  if (param_type == param_enum)
     {
       /* Note: enum_list lives in GC space, so we don't have to worry about
 	 freeing it if we later throw an exception.  */
@@ -950,7 +1037,10 @@ gdbscm_make_parameter (SCM name_scm, SCM
      freeing them if we throw an exception.  */
   p_smob->name = name;
   p_smob->cmd_class = (enum command_class) cmd_class;
-  p_smob->type = (enum var_types) param_type;
+  p_smob->pname
+    = pascm_param_type_name (static_cast<enum param_types> (param_type));
+  p_smob->type = param_to_var[param_type].type;
+  p_smob->extra_literals = param_to_var[param_type].extra_literals;
   p_smob->doc = doc;
   p_smob->set_doc = set_doc;
   p_smob->show_doc = show_doc;
@@ -1031,7 +1121,8 @@ gdbscm_register_parameter_x (SCM self)
   try
     {
       p_smob->commands = add_setshow_generic
-	(p_smob->type, p_smob->cmd_class, p_smob->cmd_name, p_smob,
+	(p_smob->type, p_smob->extra_literals,
+	 p_smob->cmd_class, p_smob->cmd_name, p_smob,
 	 p_smob->set_doc, p_smob->show_doc, p_smob->doc,
 	 (gdbscm_is_procedure (p_smob->set_func) ? pascm_set_func : NULL),
 	 (gdbscm_is_procedure (p_smob->show_func) ? pascm_show_func : NULL),
@@ -1203,5 +1294,4 @@ gdbscm_initialize_parameters (void)
   show_doc_keyword = scm_from_latin1_keyword ("show-doc");
   initial_value_keyword = scm_from_latin1_keyword ("initial-value");
   auto_keyword = scm_from_latin1_keyword ("auto");
-  unlimited_keyword = scm_from_latin1_keyword ("unlimited");
 }
Index: src/gdb/maint-test-options.c
===================================================================
--- src.orig/gdb/maint-test-options.c
+++ src/gdb/maint-test-options.c
@@ -61,13 +61,15 @@
    available kinds of commands (boolean, enum, flag, string, uinteger):
 
     (gdb) maint test-options require-delimiter -[TAB]
-    -bool      -enum      -flag      -string     -uinteger   -xx1       -xx2
+    -bool                -pinteger-unlimited  -xx1
+    -enum                -string              -xx2
+    -flag                -uinteger-unlimited
 
     (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]
+    (gdb) maint test-options require-delimiter -uinteger-unlimited [TAB]
     NUMBER     unlimited
 
    '-xx1' and '-xx2' are flag options too.  They exist in order to
@@ -76,14 +78,14 @@
   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
+   -flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint-unl 0 -pint-unl 0 -string '' -- 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
+   -flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0 -string '' -- -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
+   -flag 1 -xx1 0 -xx2 0 -bool 0 -enum yyy -uint-unl 0 -pint-unl 0 -string '' -- cmdarg
 
   The "maint show test-options-completion-result" command exists in
   order to do something similar for completion:
@@ -131,8 +133,8 @@ struct test_options_opts
   bool xx2_opt = false;
   bool boolean_opt = false;
   const char *enum_opt = test_options_enum_values_xxx;
-  unsigned int uint_opt = 0;
-  int zuint_unl_opt = 0;
+  unsigned int uint_unl_opt = 0;
+  int pint_unl_opt = 0;
   std::string string_opt;
 
   test_options_opts () = default;
@@ -145,18 +147,18 @@ struct test_options_opts
   {
     gdb_printf (file,
 		_("-flag %d -xx1 %d -xx2 %d -bool %d "
-		  "-enum %s -uint %s -zuint-unl %s -string '%s' -- %s\n"),
+		  "-enum %s -uint-unl %s -pint-unl %s -string '%s' -- %s\n"),
 		flag_opt,
 		xx1_opt,
 		xx2_opt,
 		boolean_opt,
 		enum_opt,
-		(uint_opt == UINT_MAX
+		(uint_unl_opt == UINT_MAX
 		 ? "unlimited"
-		 : pulongest (uint_opt)),
-		(zuint_unl_opt == -1
+		 : pulongest (uint_unl_opt)),
+		(pint_unl_opt == -1
 		 ? "unlimited"
-		 : plongest (zuint_unl_opt)),
+		 : plongest (pint_unl_opt)),
 		string_opt.c_str (),
 		args);
   }
@@ -203,22 +205,24 @@ static const gdb::option::option_def tes
     N_("An enum option."),
   },
 
-  /* A uinteger option.  */
+  /* A uinteger + "unlimited" option.  */
   gdb::option::uinteger_option_def<test_options_opts> {
-    "uinteger",
-    [] (test_options_opts *opts) { return &opts->uint_opt; },
+    "uinteger-unlimited",
+    [] (test_options_opts *opts) { return &opts->uint_unl_opt; },
+    uinteger_unlimited_literals,
     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; },
+  /* A pinteger + "unlimited" option.  */
+  gdb::option::pinteger_option_def<test_options_opts> {
+    "pinteger-unlimited",
+    [] (test_options_opts *opts) { return &opts->pint_unl_opt; },
+    pinteger_unlimited_literals,
     nullptr, /* show_cmd_cb */
-    N_("A zuinteger-unlimited option."),
+    N_("A pinteger-unlimited option."),
     nullptr, /* show_doc */
     nullptr, /* help_doc */
   },
Index: src/gdb/python/py-param.c
===================================================================
--- src.orig/gdb/python/py-param.c
+++ src/gdb/python/py-param.c
@@ -28,24 +28,70 @@
 #include "language.h"
 #include "arch-utils.h"
 
+/* Python parameter types as in PARM_CONSTANTS below.  */
+
+enum param_types
+{
+  param_boolean,
+  param_auto_boolean,
+  param_uinteger,
+  param_integer,
+  param_string,
+  param_string_noescape,
+  param_optional_filename,
+  param_filename,
+  param_zinteger,
+  param_zuinteger,
+  param_zuinteger_unlimited,
+  param_enum,
+}
+param_types;
+
+/* Translation from Python parameters to GDB variable types.  Keep in the
+   same order as PARAM_TYPES due to C++'s lack of designated initializers.  */
+
+static const struct
+{
+  /* The type of the parameter.  */
+  enum var_types type;
+
+  /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
+  const literal_def *extra_literals;
+}
+param_to_var[] =
+{
+  { var_boolean },
+  { var_auto_boolean },
+  { var_uinteger, uinteger_unlimited_literals },
+  { var_integer, integer_unlimited_literals },
+  { var_string },
+  { var_string_noescape },
+  { var_optional_filename },
+  { var_filename },
+  { var_integer },
+  { var_uinteger },
+  { var_pinteger, pinteger_unlimited_literals },
+  { var_enum }
+};
+
 /* Parameter constants and their values.  */
 static struct {
   const char *name;
   int value;
 } parm_constants[] =
 {
-  { "PARAM_BOOLEAN", var_boolean }, /* ARI: var_boolean */
-  { "PARAM_AUTO_BOOLEAN", var_auto_boolean },
-  { "PARAM_UINTEGER", var_uinteger },
-  { "PARAM_INTEGER", var_integer },
-  { "PARAM_STRING", var_string },
-  { "PARAM_STRING_NOESCAPE", var_string_noescape },
-  { "PARAM_OPTIONAL_FILENAME", var_optional_filename },
-  { "PARAM_FILENAME", var_filename },
-  { "PARAM_ZINTEGER", var_zinteger },
-  { "PARAM_ZUINTEGER", var_zuinteger },
-  { "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited },
-  { "PARAM_ENUM", var_enum },
+  { "PARAM_BOOLEAN", param_boolean }, /* ARI: param_boolean */
+  { "PARAM_AUTO_BOOLEAN", param_auto_boolean },
+  { "PARAM_UINTEGER", param_uinteger },
+  { "PARAM_INTEGER", param_integer },
+  { "PARAM_STRING", param_string },
+  { "PARAM_STRING_NOESCAPE", param_string_noescape },
+  { "PARAM_OPTIONAL_FILENAME", param_optional_filename },
+  { "PARAM_FILENAME", param_filename },
+  { "PARAM_ZINTEGER", param_zinteger },
+  { "PARAM_ZUINTEGER", param_zuinteger },
+  { "PARAM_ZUINTEGER_UNLIMITED", param_zuinteger_unlimited },
+  { "PARAM_ENUM", param_enum },
   { NULL, 0 }
 };
 
@@ -80,6 +126,9 @@ struct parmpy_object
   /* The type of the parameter.  */
   enum var_types type;
 
+  /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
+  const literal_def *extra_literals;
+
   /* The value of the parameter.  */
   union parmpy_variable value;
 
@@ -96,18 +145,20 @@ struct parmpy_object
 static setting
 make_setting (parmpy_object *s)
 {
-  if (var_type_uses<bool> (s->type))
-    return setting (s->type, &s->value.boolval);
-  else if (var_type_uses<int> (s->type))
-    return setting (s->type, &s->value.intval);
-  else if (var_type_uses<auto_boolean> (s->type))
-    return setting (s->type, &s->value.autoboolval);
-  else if (var_type_uses<unsigned int> (s->type))
-    return setting (s->type, &s->value.uintval);
-  else if (var_type_uses<std::string> (s->type))
-    return setting (s->type, s->value.stringval);
-  else if (var_type_uses<const char *> (s->type))
-    return setting (s->type, &s->value.cstringval);
+  enum var_types type = s->type;
+
+  if (var_type_uses<bool> (type))
+    return setting (type, &s->value.boolval);
+  else if (var_type_uses<int> (type))
+    return setting (type, &s->value.intval, s->extra_literals);
+  else if (var_type_uses<auto_boolean> (type))
+    return setting (type, &s->value.autoboolval);
+  else if (var_type_uses<unsigned int> (type))
+    return setting (type, &s->value.uintval, s->extra_literals);
+  else if (var_type_uses<std::string> (type))
+    return setting (type, s->value.stringval);
+  else if (var_type_uses<const char *> (type))
+    return setting (type, &s->value.cstringval);
   else
     gdb_assert_not_reached ("unhandled var type");
 }
@@ -234,68 +285,98 @@ set_parameter_value (parmpy_object *self
 	}
       break;
 
-    case var_integer:
-    case var_zinteger:
     case var_uinteger:
-    case var_zuinteger:
-    case var_zuinteger_unlimited:
+    case var_integer:
+    case var_pinteger:
       {
-	long l;
-	int ok;
+	const literal_def *extra_literals = self->extra_literals;
+	enum tribool allowed = TRIBOOL_UNKNOWN;
+	enum var_types var_type = self->type;
+	std::string buffer = "";
+	size_t count = 0;
+	LONGEST val;
 
-	if (value == Py_None
-	    && (self->type == var_uinteger || self->type == var_integer))
-	  l = 0;
-	else if (value == Py_None && self->type == var_zuinteger_unlimited)
-	  l = -1;
-	else if (!PyLong_Check (value))
+	if (extra_literals != nullptr)
 	  {
-	    PyErr_SetString (PyExc_RuntimeError,
-			     _("The value must be integer."));
-	    return -1;
+	    gdb::unique_xmalloc_ptr<char>
+	      str (python_string_to_host_string (value));
+	    const char *s = str != nullptr ? str.get () : nullptr;
+	    PyErr_Clear ();
+
+	    for (const literal_def *l = extra_literals;
+		 l->literal != nullptr;
+		 l++, count++)
+	      {
+		if (count != 0)
+		  buffer += ", ";
+		buffer = buffer + "'" + l->literal + "'";
+		if (allowed == TRIBOOL_UNKNOWN
+		    && ((value == Py_None && !strcmp ("unlimited", l->literal))
+			|| (s != nullptr && !strcmp (s, l->literal))))
+		  {
+		    val = l->use;
+		    allowed = TRIBOOL_TRUE;
+		  }
+	      }
 	  }
-	else if (! gdb_py_int_as_long (value, &l))
-	  return -1;
 
-	switch (self->type)
+	if (allowed == TRIBOOL_UNKNOWN)
 	  {
-	  case var_uinteger:
-	    if (l == 0)
-	      l = UINT_MAX;
-	    /* Fall through.  */
-	  case var_zuinteger:
-	    ok = (l >= 0 && l <= UINT_MAX);
-	    break;
+	    val = PyLong_AsLongLong (value);
 
-	  case var_zuinteger_unlimited:
-	    ok = (l >= -1 && l <= INT_MAX);
-	    break;
+	    if (PyErr_Occurred ())
+	      {
+		if (extra_literals == nullptr)
+		  PyErr_SetString (PyExc_RuntimeError,
+				   _("The value must be integer."));
+		else if (count > 1)
+		  PyErr_SetString (PyExc_RuntimeError,
+				   string_printf (_("integer or one of: %s"),
+						  buffer.c_str ()).c_str ());
+		else
+		  PyErr_SetString (PyExc_RuntimeError,
+				   string_printf (_("integer or %s"),
+						  buffer.c_str ()).c_str ());
+		return -1;
+	      }
 
-	  case var_integer:
-	    ok = (l >= INT_MIN && l <= INT_MAX);
-	    if (l == 0)
-	      l = INT_MAX;
-	    break;
 
-	  case var_zinteger:
-	    ok = (l >= INT_MIN && l <= INT_MAX);
-	    break;
+	    if (extra_literals != nullptr)
+	      for (const literal_def *l = extra_literals;
+		   l->literal != nullptr;
+		   l++)
+		{
+		  if (l->val.has_value () && val == *l->val)
+		    {
+		      allowed = TRIBOOL_TRUE;
+		      val = l->use;
+		      break;
+		    }
+		  else if (val == l->use)
+		    allowed = TRIBOOL_FALSE;
+		}
+	    }
 
-	  default:
-	    gdb_assert_not_reached ("unknown var_ constant");
+	if (allowed == TRIBOOL_UNKNOWN)
+	  {
+	    if (val > UINT_MAX || val < INT_MIN
+		|| (var_type == var_uinteger && val < 0)
+		|| (var_type == var_integer && val > INT_MAX)
+		|| (var_type == var_pinteger && val < 0)
+		|| (var_type == var_pinteger && val > INT_MAX))
+	      allowed = TRIBOOL_FALSE;
 	  }
-
-	if (! ok)
+	if (allowed == TRIBOOL_FALSE)
 	  {
 	    PyErr_SetString (PyExc_RuntimeError,
 			     _("Range exceeded."));
 	    return -1;
 	  }
 
-	if (self->type == var_uinteger || self->type == var_zuinteger)
-	  self->value.uintval = (unsigned) l;
+	if (self->type == var_uinteger)
+	  self->value.uintval = (unsigned) val;
 	else
-	  self->value.intval = (int) l;
+	  self->value.intval = (int) val;
 	break;
       }
 
@@ -534,7 +615,8 @@ get_show_value (struct ui_file *file, in
 /* A helper function that dispatches to the appropriate add_setshow
    function.  */
 static void
-add_setshow_generic (int parmclass, enum command_class cmdclass,
+add_setshow_generic (enum var_types type, const literal_def *extra_literals,
+		     enum command_class cmdclass,
 		     gdb::unique_xmalloc_ptr<char> cmd_name,
 		     parmpy_object *self,
 		     const char *set_doc, const char *show_doc,
@@ -544,7 +626,7 @@ add_setshow_generic (int parmclass, enum
 {
   set_show_commands commands;
 
-  switch (parmclass)
+  switch (type)
     {
     case var_boolean:
       commands = add_setshow_boolean_cmd (cmd_name.get (), cmdclass,
@@ -564,18 +646,28 @@ add_setshow_generic (int parmclass, enum
 
     case var_uinteger:
       commands = add_setshow_uinteger_cmd (cmd_name.get (), cmdclass,
-					   &self->value.uintval, set_doc,
+					   &self->value.uintval,
+					   extra_literals, set_doc,
 					   show_doc, help_doc, get_set_value,
 					   get_show_value, set_list, show_list);
       break;
 
     case var_integer:
       commands = add_setshow_integer_cmd (cmd_name.get (), cmdclass,
-					  &self->value.intval, set_doc,
+					  &self->value.intval,
+					  extra_literals, set_doc,
 					  show_doc, help_doc, get_set_value,
 					  get_show_value, set_list, show_list);
       break;
 
+    case var_pinteger:
+      commands = add_setshow_pinteger_cmd (cmd_name.get (), cmdclass,
+					   &self->value.intval,
+					   extra_literals, set_doc,
+					   show_doc, help_doc, get_set_value,
+					   get_show_value, set_list, show_list);
+      break;
+
     case var_string:
       commands = add_setshow_string_cmd (cmd_name.get (), cmdclass,
 					 self->value.stringval, set_doc,
@@ -607,30 +699,6 @@ add_setshow_generic (int parmclass, enum
 					   get_show_value, set_list, show_list);
       break;
 
-    case var_zinteger:
-      commands = add_setshow_zinteger_cmd (cmd_name.get (), cmdclass,
-					   &self->value.intval, set_doc,
-					   show_doc, help_doc, get_set_value,
-					   get_show_value, set_list, show_list);
-      break;
-
-    case var_zuinteger:
-      commands = add_setshow_zuinteger_cmd (cmd_name.get (), cmdclass,
-					    &self->value.uintval, set_doc,
-					    show_doc, help_doc, get_set_value,
-					    get_show_value, set_list,
-					    show_list);
-      break;
-
-    case var_zuinteger_unlimited:
-      commands = add_setshow_zuinteger_unlimited_cmd (cmd_name.get (), cmdclass,
-						      &self->value.intval,
-						      set_doc, show_doc,
-						      help_doc, get_set_value,
-						      get_show_value, set_list,
-						      show_list);
-      break;
-
     case var_enum:
       /* Initialize the value, just in case.  */
       self->value.cstringval = self->enumeration[0];
@@ -740,6 +808,8 @@ parmpy_init (PyObject *self, PyObject *a
   int parmclass, cmdtype;
   PyObject *enum_values = NULL;
   struct cmd_list_element **set_list, **show_list;
+  const literal_def *extra_literals;
+  enum var_types type;
 
   if (! PyArg_ParseTuple (args, "sii|O", &name, &cmdtype, &parmclass,
 			  &enum_values))
@@ -756,33 +826,36 @@ parmpy_init (PyObject *self, PyObject *a
       return -1;
     }
 
-  if (parmclass != var_boolean /* ARI: var_boolean */
-      && parmclass != var_auto_boolean
-      && parmclass != var_uinteger && parmclass != var_integer
-      && parmclass != var_string && parmclass != var_string_noescape
-      && parmclass != var_optional_filename && parmclass != var_filename
-      && parmclass != var_zinteger && parmclass != var_zuinteger
-      && parmclass != var_zuinteger_unlimited && parmclass != var_enum)
+  if (parmclass != param_boolean /* ARI: param_boolean */
+      && parmclass != param_auto_boolean
+      && parmclass != param_uinteger && parmclass != param_integer
+      && parmclass != param_string && parmclass != param_string_noescape
+      && parmclass != param_optional_filename && parmclass != param_filename
+      && parmclass != param_zinteger && parmclass != param_zuinteger
+      && parmclass != param_zuinteger_unlimited && parmclass != param_enum)
     {
       PyErr_SetString (PyExc_RuntimeError,
 		       _("Invalid parameter class argument."));
       return -1;
     }
 
-  if (enum_values && parmclass != var_enum)
+  if (enum_values && parmclass != param_enum)
     {
       PyErr_SetString (PyExc_RuntimeError,
 		       _("Only PARAM_ENUM accepts a fourth argument."));
       return -1;
     }
-  if (parmclass == var_enum)
+  if (parmclass == param_enum)
     {
       if (! compute_enum_values (obj, enum_values))
 	return -1;
     }
   else
     obj->enumeration = NULL;
-  obj->type = (enum var_types) parmclass;
+  type = param_to_var[parmclass].type;
+  extra_literals = param_to_var[parmclass].extra_literals;
+  obj->type = type;
+  obj->extra_literals = extra_literals;
   memset (&obj->value, 0, sizeof (obj->value));
 
   if (var_type_uses<std::string> (obj->type))
@@ -805,7 +878,8 @@ parmpy_init (PyObject *self, PyObject *a
 
   try
     {
-      add_setshow_generic (parmclass, (enum command_class) cmdtype,
+      add_setshow_generic (type, extra_literals,
+			   (enum command_class) cmdtype,
 			   std::move (cmd_name), obj,
 			   set_doc.get (), show_doc.get (),
 			   doc.get (), set_list, show_list);
Index: src/gdb/python/python.c
===================================================================
--- src.orig/gdb/python/python.c
+++ src/gdb/python/python.c
@@ -504,27 +504,45 @@ gdbpy_parameter_value (const setting &va
 	  Py_RETURN_NONE;
       }
 
-    case var_integer:
-      if (var.get<int> () == INT_MAX)
-	Py_RETURN_NONE;
-      /* Fall through.  */
-    case var_zinteger:
-    case var_zuinteger_unlimited:
-      return gdb_py_object_from_longest (var.get<int> ()).release ();
-
     case var_uinteger:
+    case var_integer:
+    case var_pinteger:
       {
-	unsigned int val = var.get<unsigned int> ();
+	LONGEST value
+	  = (var.type () == var_uinteger
+	     ? static_cast<LONGEST> (var.get<unsigned int> ())
+	     : static_cast<LONGEST> (var.get<int> ()));
 
-	if (val == UINT_MAX)
-	  Py_RETURN_NONE;
-	return gdb_py_object_from_ulongest (val).release ();
-      }
+	if (var.extra_literals () != nullptr)
+	  for (const literal_def *l = var.extra_literals ();
+	       l->literal != nullptr;
+	       l++)
+	    if (value == l->use)
+	      {
+		if (strcmp (l->literal, "unlimited") == 0)
+		  {
+		    /* Compatibility hack for API brokenness.  */
+		    if (var.type () == var_pinteger
+			&& l->val.has_value ()
+			&& *l->val == -1)
+		      value = -1;
+		    else
+		      Py_RETURN_NONE;
+		  }
+		else if (l->val.has_value ())
+		  value = *l->val;
+		else
+		  return host_string_to_python_string (l->literal).release ();
+	      }
 
-    case var_zuinteger:
-      {
-	unsigned int val = var.get<unsigned int> ();
-	return gdb_py_object_from_ulongest (val).release ();
+	if (var.type () == var_uinteger)
+	  return
+	    gdb_py_object_from_ulongest
+	      (static_cast<unsigned int> (value)).release ();
+	else
+	  return
+	    gdb_py_object_from_longest
+	      (static_cast<int> (value)).release ();
       }
     }
 
Index: src/gdb/testsuite/gdb.base/max-value-size.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/max-value-size.exp
+++ src/gdb/testsuite/gdb.base/max-value-size.exp
@@ -92,4 +92,4 @@ gdb_test "set max-value-size 1" \
 gdb_test "set max-value-size 0" \
     "max-value-size set too low, increasing to \[0-9\]+ bytes"
 gdb_test "set max-value-size -5" \
-    "only -1 is allowed to set as unlimited"
+    "integer -5 out of range"
Index: src/gdb/testsuite/gdb.base/options.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/options.exp
+++ src/gdb/testsuite/gdb.base/options.exp
@@ -98,19 +98,22 @@ proc make_cmd {variant} {
 # 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 -string '' -- $operand"
+    return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\
+	    -string '' -- $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 -string '' -- $operand"
+    return "-flag 1 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\
+	    -string '' -- $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 -string '' -- $operand"
+    return "-flag 0 -xx1 0 -xx2 0 -bool 1 -enum xxx -uint-unl 0 -pint-unl 0\
+	    -string '' -- $operand"
 }
 
 # Return a string for the expected result of running "maint
@@ -118,10 +121,12 @@ proc expect_bool {operand} {
 # 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 -string '' -- $operand"
-    } elseif {$option == "zuinteger-unlimited"} {
-	return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint 0 -zuint-unl $val -string '' -- $operand"
+    if {$option == "uinteger-unlimited"} {
+	return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl $val\
+		-pint-unl 0 -string '' -- $operand"
+    } elseif {$option == "pinteger-unlimited"} {
+	return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0\
+		-pint-unl $val -string '' -- $operand"
     } else {
 	error "unsupported option: $option"
     }
@@ -138,18 +143,19 @@ proc expect_string {str operand} {
 	     && [string range $str end end] == "'")} {
 	set str [string range $str 1 end-1]
     }
-    return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint 0 -zuint-unl 0 -string '$str' -- $operand"
+    return "-flag 0 -xx1 0 -xx2 0 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\
+	    -string '$str' -- $operand"
 }
 
 set all_options {
     "-bool"
     "-enum"
     "-flag"
+    "-pinteger-unlimited"
     "-string"
-    "-uinteger"
+    "-uinteger-unlimited"
     "-xx1"
     "-xx2"
-    "-zuinteger-unlimited"
 }
 
 # Basic option-machinery + "print" command integration tests.
@@ -604,7 +610,8 @@ proc_with_prefix test-flag {variant} {
 
     # 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 -string '' -- non flags args"
+	"-flag 0 -xx1 1 -xx2 1 -bool 0 -enum xxx -uint-unl 0 -pint-unl 0\
+	 -string '' -- non flags args"
 
     # Extract 2 known flags in front of unknown flags.
     gdb_test "$cmd -xx1 -xx2 -a -b -c -xx1 --" \
@@ -822,13 +829,13 @@ proc_with_prefix test-boolean {variant}
 }
 
 # Uinteger option tests.  OPTION is which integer option we're
-# testing.  Can be "uinteger" or "zuinteger-unlimited".
+# testing.  Can be "uinteger-unlimited" or "pinteger-unlimited".
 proc_with_prefix test-uinteger {variant option} {
     global all_options
 
     set cmd "[make_cmd $variant] -$option"
 
-    # Test completing a uinteger option:
+    # Test completing an integer option:
     res_test_gdb_complete_multiple \
 	"1 [expect_none ""]" \
 	"$cmd " "" "" {
@@ -852,7 +859,7 @@ proc_with_prefix test-uinteger {variant
     gdb_test "$cmd 1 -- 999" [expect_integer $option "1" "999"]
     gdb_test "$cmd unlimited -- 999" \
 	[expect_integer $option "unlimited" "999"]
-    if {$option == "zuinteger-unlimited"} {
+    if {$option == "pinteger-unlimited"} {
 	gdb_test "$cmd -1 --" [expect_integer $option "unlimited" ""]
 	gdb_test "$cmd 0 --" [expect_integer $option "0" ""]
     } else {
@@ -865,7 +872,7 @@ proc_with_prefix test-uinteger {variant
 	"Expected integer at: unlimitedx --"
 
     # Don't offer completions until we're past the
-    # -uinteger/-zuinteger-unlimited argument.
+    # -uinteger-unlimited/-pinteger-unlimited argument.
     res_test_gdb_complete_none \
 	"1 [expect_none ""]" \
 	"$cmd 1"
@@ -878,15 +885,15 @@ proc_with_prefix test-uinteger {variant
     }
 
     # Try "-1".
-    if {$option == "uinteger"} {
-	# -1 is invalid uinteger.
+    if {$option == "uinteger-unlimited"} {
+	# -1 is invalid uinteger-unlimited.
 	foreach value {"-1" "-1 "} {
 	    res_test_gdb_complete_none \
 		"1 [expect_none ""]" \
 		"$cmd $value"
 	}
     } else {
-	# -1 is valid for zuinteger-unlimited.
+	# -1 is valid for pinteger-unlimited.
 	res_test_gdb_complete_none \
 	    "1 [expect_none ""]" \
 	    "$cmd -1"
@@ -914,7 +921,7 @@ proc_with_prefix test-uinteger {variant
 	res_test_gdb_complete_none "0 " "$cmd 1 "
     }
 
-    # Test completing non-option arguments after "-uinteger 1 ".
+    # Test completing non-option arguments after "-uinteger-unlimited 1 ".
     foreach operand {"x" "x " "1a" "1a " "1-" "1- "} {
 	if {$variant == "require-delimiter"} {
 	    res_test_gdb_complete_none \
@@ -1032,7 +1039,7 @@ foreach_with_prefix cmd {
     test-misc $cmd
     test-flag $cmd
     test-boolean $cmd
-    foreach subcmd {"uinteger" "zuinteger-unlimited" } {
+    foreach subcmd {"uinteger-unlimited" "pinteger-unlimited" } {
 	test-uinteger $cmd $subcmd
     }
     test-enum $cmd
Index: src/gdb/testsuite/gdb.base/settings.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/settings.exp
+++ src/gdb/testsuite/gdb.base/settings.exp
@@ -143,7 +143,7 @@ proc test-integer {variant} {
     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"
+	gdb_test "$set_cmd -2" "integer -2 out of range"
 	check_type "test-settings $variant" "type = int"
     } elseif {$variant == "uinteger" || $variant == "zuinteger"} {
 	# Negative values are not accepted.
Index: src/gdb/testsuite/gdb.base/with.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/with.exp
+++ src/gdb/testsuite/gdb.base/with.exp
@@ -104,7 +104,7 @@ with_test_prefix "maint" {
     test_with_error "zuinteger" "" \
 	"Argument required \\(integer to set it to\\)\\."
     test_with_error "zuinteger-unlimited" "-2" \
-	"only -1 is allowed to set as unlimited"
+	"integer -2 out of range"
     test_with_error "zuinteger-unlimited" "" \
 	"Argument required \\(integer to set it to, or \"unlimited\"\\)\\."
     test_with_error "filename" "" \
Index: src/gdb/testsuite/gdb.guile/scm-parameter.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.guile/scm-parameter.exp
+++ src/gdb/testsuite/gdb.guile/scm-parameter.exp
@@ -113,24 +113,18 @@ foreach_with_prefix param {
 	    \\(3\\) \\(3\\)\\)>"
     switch -- $param {
 	"listsize" {
-	    set param_get_one $param_type_error
-	    set param_get_zero $param_type_error
-	    set param_get_minus_one $param_type_error
-	    set param_get_unlimited $param_type_error
+	    set param_get_zero "#:unlimited"
+	    set param_get_minus_one -1
 	    set param_set_minus_one ""
 	}
 	"print elements" {
-	    set param_get_one 1
 	    set param_get_zero "#:unlimited"
 	    set param_get_minus_one "#:unlimited"
-	    set param_get_unlimited "#:unlimited"
 	    set param_set_minus_one $param_range_error
 	}
 	"max-completions" {
-	    set param_get_one 1
 	    set param_get_zero 0
 	    set param_get_minus_one "#:unlimited"
-	    set param_get_unlimited "#:unlimited"
 	    set param_set_minus_one ""
 	}
 	default {
@@ -141,7 +135,7 @@ foreach_with_prefix param {
     gdb_test_no_output "set $param 1" "test set to 1"
 
     gdb_test "guile (print (parameter-value \"$param\"))" \
-	$param_get_one "test value of 1"
+	1 "test value of 1"
 
     gdb_test_no_output "set $param 0" "test set to 0"
 
@@ -157,7 +151,7 @@ foreach_with_prefix param {
     gdb_test_no_output "set $param unlimited" "test set to 'unlimited'"
 
     gdb_test "guile (print (parameter-value \"$param\"))" \
-	$param_get_unlimited "test value of 'unlimited'"
+	"#:unlimited" "test value of 'unlimited'"
 }
 
 foreach_with_prefix kind {
@@ -189,8 +183,7 @@ foreach_with_prefix kind {
 	     #:unlimited" \
 	    "Error while executing Scheme code\\."]
     set param_minus_one_error "integer -1 out of range"
-    set param_minus_two_range "integer -2 out of range"
-    set param_minus_two_unlimited "only -1 is allowed to set as unlimited"
+    set param_minus_two_error "integer -2 out of range"
     switch -- $kind {
 	PARAM_UINTEGER {
 	    set param_get_zero "#:unlimited"
@@ -199,7 +192,7 @@ foreach_with_prefix kind {
 	    set param_str_unlimited unlimited
 	    set param_set_unlimited ""
 	    set param_set_minus_one $param_minus_one_error
-	    set param_set_minus_two $param_minus_two_range
+	    set param_set_minus_two $param_minus_two_error
 	}
 	PARAM_ZINTEGER {
 	    set param_get_zero 0
@@ -217,7 +210,7 @@ foreach_with_prefix kind {
 	    set param_str_unlimited 2
 	    set param_set_unlimited $param_integer_error
 	    set param_set_minus_one $param_minus_one_error
-	    set param_set_minus_two $param_minus_two_range
+	    set param_set_minus_two $param_minus_two_error
 	}
 	PARAM_ZUINTEGER_UNLIMITED {
 	    set param_get_zero 0
@@ -226,7 +219,7 @@ foreach_with_prefix kind {
 	    set param_str_unlimited unlimited
 	    set param_set_unlimited ""
 	    set param_set_minus_one ""
-	    set param_set_minus_two $param_minus_two_unlimited
+	    set param_set_minus_two $param_minus_two_error
 	}
 	default {
 	    error "invalid kind: $kind"
Index: src/gdb/testsuite/gdb.python/py-parameter.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.python/py-parameter.exp
+++ src/gdb/testsuite/gdb.python/py-parameter.exp
@@ -434,6 +434,7 @@ proc_with_prefix test_integer_parameter
 		set param_get_minus_one None
 		set param_get_minus_five 1
 		set param_get_none None
+		set param_get_unlimited None
 		set param_set_minus_one $param_range_error
 		set param_set_minus_five $param_range_error
 		set param_set_none ""
@@ -443,6 +444,7 @@ proc_with_prefix test_integer_parameter
 		set param_get_minus_one -1
 		set param_get_minus_five -5
 		set param_get_none None
+		set param_get_unlimited None
 		set param_set_minus_one -1
 		set param_set_minus_five -5
 		set param_set_none ""
@@ -452,6 +454,7 @@ proc_with_prefix test_integer_parameter
 		set param_get_minus_one -1
 		set param_get_minus_five -5
 		set param_get_none 5
+		set param_get_unlimited 0
 		set param_set_minus_one ""
 		set param_set_minus_five ""
 		set param_set_none $param_integer_error
@@ -461,6 +464,7 @@ proc_with_prefix test_integer_parameter
 		set param_get_minus_one 0
 		set param_get_minus_five 1
 		set param_get_none 5
+		set param_get_unlimited 0
 		set param_set_minus_one $param_range_error
 		set param_set_minus_five $param_range_error
 		set param_set_none $param_integer_error
@@ -470,6 +474,7 @@ proc_with_prefix test_integer_parameter
 		set param_get_minus_one -1
 		set param_get_minus_five 1
 		set param_get_none -1
+		set param_get_unlimited -1
 		set param_set_minus_one ""
 		set param_set_minus_five $param_range_error
 		set param_set_none ""
@@ -526,6 +531,16 @@ proc_with_prefix test_integer_parameter
 
 	gdb_test "python print(gdb.parameter('test-$kind'))" \
 	    $param_get_zero "test value of 0 via gdb.parameter"
+
+	py_param_test_maybe_no_output \
+	    "python test_param_$kind.value = 'unlimited'" \
+	    $param_set_none "test set to 'unlimited'"
+
+	gdb_test "python print(test_param_$kind.value)" \
+	    $param_get_unlimited "test value of 'unlimited'"
+
+	gdb_test "python print(gdb.parameter('test-$kind'))" \
+	    $param_get_unlimited "test value of 'unlimited' via gdb.parameter"
     }
 }
 
Index: src/gdb/valprint.c
===================================================================
--- src.orig/gdb/valprint.c
+++ src/gdb/valprint.c
@@ -2861,8 +2861,8 @@ 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>;
+using pinteger_option_def
+  = gdb::option::pinteger_option_def<value_print_options>;
 
 /* Definitions of options for the "print" and "compile print"
    commands.  */
@@ -2907,15 +2907,17 @@ static const gdb::option::option_def val
   uinteger_option_def {
     "elements",
     [] (value_print_options *opt) { return &opt->print_max; },
+    uinteger_unlimited_literals,
     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 {
+  pinteger_option_def {
     "max-depth",
     [] (value_print_options *opt) { return &opt->max_depth; },
+    pinteger_unlimited_literals,
     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."),
@@ -2975,6 +2977,7 @@ pretty-printers for that value.")
   uinteger_option_def {
     "repeats",
     [] (value_print_options *opt) { return &opt->repeat_count_threshold; },
+    uinteger_unlimited_literals,
     show_repeat_count_threshold, /* show_cmd_cb */
     N_("Set threshold for repeated print elements."),
     N_("Show threshold for repeated print elements."),

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

* [COMMITTED PATCH v9 5/6] GDB: Add a character string limiting option
  2023-01-19 21:17     ` Maciej W. Rozycki
@ 2023-01-19 21:19       ` Maciej W. Rozycki
  2023-01-21  9:57         ` Simon Sobisch
  0 siblings, 1 reply; 32+ messages in thread
From: Maciej W. Rozycki @ 2023-01-19 21:19 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches, Andrew Burgess, Tom Tromey, Simon Sobisch

From: Andrew Burgess <andrew.burgess@embecosm.com>

This commit splits the `set/show print elements' option into two.  We 
retain `set/show print elements' for controlling how many elements of an 
array we print, but a new `set/show print characters' setting is added 
which is used for controlling how many characters of a string are 
printed.

The motivation behind this change is to allow users a finer level of 
control over how data is printed, reflecting that, although strings can 
be thought of as arrays of characters, users often want to treat these 
two things differently.

For compatibility reasons by default the `set/show print characters' 
option is set to `elements', which makes the limit for character strings 
follow the setting of the `set/show print elements' option, as it used 
to.  Using `set print characters' with any other value makes the limit 
independent from the `set/show print elements' setting, however it can 
be restored to the default with the `set print characters elements' 
command at any time.

A corresponding `-characters' option for the `print' command is added, 
with the same semantics, i.e. one can use `elements' to make a given 
`print' invocation follow the limit of elements, be it set with the 
`-elements' option also given with the same invocation or taken from the 
`set/show print elements' setting, for characters as well regardless of 
the current setting of the `set/show print characters' option.

The GDB changes are all pretty straightforward, just changing references 
to the old 'print_max' to use a new `get_print_max_chars' helper which 
figures out which of the two of `print_max' and `print_max_chars' values 
to use.

Likewise, the documentation is just updated to reference the new setting 
where appropriate.

To make people's life easier the message shown by `show print elements' 
now indicates if the setting also applies to character strings:

(gdb) set print characters elements
(gdb) show print elements
Limit on string chars or array elements to print is 200.
(gdb) set print characters unlimited
(gdb) show print elements
Limit on array elements to print is 200.
(gdb) 

and the help text shows the dependency as well:

(gdb) help set print elements
Set limit on array elements to print.
"unlimited" causes there to be no limit.
This setting also applies to string chars when "print characters"
is set to "elements".
(gdb) 

In the testsuite there are two minor updates, one to add `-characters' 
to the list of completions now shown for the `print' command, and a bare 
minimum pair of checks for the right handling of `set print characters' 
and `show print characters', copied from the corresponding checks for 
`set print elements' and `show print elements' respectively.

Co-Authored-By: Maciej W. Rozycki <macro@embecosm.com>
Approved-By: Simon Marchi <simon.marchi@efficios.com>
---
Changes from v8:

- Update the help text for the `print characters' setting.

No change from v7.

No change from v6.

Changes from v5:

- Allow the use of `set print characters 0' to stand for `unlimited', and 
  likewise via the corresponding Guile and Python APIs.

- Define PRINT_MAX_CHARS_ELEMENTS and PRINT_MAX_CHARS_UNLIMITED macros 
  and use them throughout instead of hardcoded 0/UINT_MAX, making it 
  easier to track usage.

Changes from v4:

- Move the NEWS entry past GDB 12 and describe `print -characters LIMIT'
  separately.

- Clarify the effect on multi-byte and wide character strings in the 
  manual (borrowing from the original NEWS entry that added support for 
  them).

- Fix issues with `@ref' and `@xref' usage in the manual.

Changes from v3:

- Rewrite in terms of flexible literal keyword handling added with 6/8.

- Add a `set print characters elements' setting, which is also the 
  default, to make the string character limit follow that for array 
  elements in backwards-compatible manner.

- Update documentation accordingly.

- Remove testsuite updates delegated to 8/8, guaranteeing that the new 
  command does not influence any preexisting environment (except for 
  obvious cases where it is expected, such as command completion or help 
  messages).

- Amend gdb.base/options.exp for the added completion for `print' 
  command's `-characters' option.

- Expand gdb.base/default.exp with minimal test cases for `set/show print 
  characters'.

Changes from v2:

- Place the new and the changed command at the right places each in NEWS.

Changes from v1:

- Rename `print_smax' setting throughout to `print_max_chars', and
  likewise `show_print_smax' function to `show_print_max_chars'.

- Document the Python part in the manual.

- Update comments for `print_max' and `print_max_chars' in
  `value_print_options'.

- Fix some typos.
---
 gdb/NEWS                           |   14 +++++
 gdb/ada-valprint.c                 |    6 +-
 gdb/c-lang.c                       |    4 -
 gdb/c-valprint.c                   |    5 +-
 gdb/doc/gdb.texinfo                |   44 ++++++++++++++++--
 gdb/doc/python.texi                |    5 ++
 gdb/language.h                     |    2 
 gdb/m2-lang.c                      |    3 -
 gdb/m2-valprint.c                  |    4 +
 gdb/p-lang.c                       |    3 -
 gdb/p-valprint.c                   |    9 ++-
 gdb/printcmd.c                     |    9 ++-
 gdb/python/py-value.c              |    4 +
 gdb/testsuite/gdb.base/default.exp |    7 ++
 gdb/testsuite/gdb.base/options.exp |    1 
 gdb/tracepoint.c                   |    4 -
 gdb/valprint.c                     |   88 ++++++++++++++++++++++++++++---------
 gdb/valprint.h                     |   26 +++++++++-
 18 files changed, 188 insertions(+), 50 deletions(-)

gdb-aburgess-print-elements-characters.diff
Index: src/gdb/NEWS
===================================================================
--- src.orig/gdb/NEWS
+++ src/gdb/NEWS
@@ -174,6 +174,20 @@ set style tui-current-position [on|off]
   Whether to style the source and assembly code highlighted by the
   TUI's current position indicator.  The default is off.
 
+set print characters LIMIT
+show print characters
+  This new setting is like 'set print elements', but controls how many
+  characters of a string are printed.  This functionality used to be
+  covered by 'set print elements', but it can be controlled separately
+  now.  LIMIT can be set to a numerical value to request that particular
+  character count, to 'unlimited' to print all characters of a string,
+  or to 'elements', which is also the default, to follow the setting of
+ 'set print elements' as it used to be.
+
+print -characters LIMIT
+  This new option to the 'print' command has the same effect as a temporary
+  use of 'set print characters'.
+
 * Changed commands
 
 document user-defined
Index: src/gdb/ada-valprint.c
===================================================================
--- src.orig/gdb/ada-valprint.c
+++ src/gdb/ada-valprint.c
@@ -469,7 +469,8 @@ printstr (struct ui_file *stream, struct
       return;
     }
 
-  for (i = 0; i < length && things_printed < options->print_max; i += 1)
+  unsigned int print_max_chars = get_print_max_chars (options);
+  for (i = 0; i < length && things_printed < print_max_chars; i += 1)
     {
       /* Position of the character we are examining
 	 to see whether it is repeated.  */
@@ -705,12 +706,13 @@ ada_val_print_string (struct type *type,
      elements up to it.  */
   if (options->stop_print_at_null)
     {
+      unsigned int print_max_chars = get_print_max_chars (options);
       int temp_len;
 
       /* Look for a NULL char.  */
       for (temp_len = 0;
 	   (temp_len < len
-	    && temp_len < options->print_max
+	    && temp_len < print_max_chars
 	    && char_at (valaddr + offset_aligned,
 			temp_len, eltlen, byte_order) != 0);
 	   temp_len += 1);
Index: src/gdb/c-lang.c
===================================================================
--- src.orig/gdb/c-lang.c
+++ src/gdb/c-lang.c
@@ -185,8 +185,8 @@ language_defn::printchar (int c, struct
 /* Print the character string STRING, printing at most LENGTH
    characters.  LENGTH is -1 if the string is nul terminated.  Each
    character is WIDTH bytes long.  Printing stops early if the number
-   hits print_max; repeat counts are printed as appropriate.  Print
-   ellipses at the end if we had to stop before printing LENGTH
+   hits print_max_chars; repeat counts are printed as appropriate.
+   Print ellipses at the end if we had to stop before printing LENGTH
    characters, or if FORCE_ELLIPSES.  */
 
 void
Index: src/gdb/c-valprint.c
===================================================================
--- src.orig/gdb/c-valprint.c
+++ src/gdb/c-valprint.c
@@ -267,11 +267,12 @@ c_value_print_array (struct value *val,
 	     print elements up to it.  */
 	  if (options->stop_print_at_null)
 	    {
+	      unsigned int print_max_chars = get_print_max_chars (options);
 	      unsigned int temp_len;
 
 	      for (temp_len = 0;
 		   (temp_len < len
-		    && temp_len < options->print_max
+		    && temp_len < print_max_chars
 		    && extract_unsigned_integer (valaddr + temp_len * eltlen,
 						 eltlen, byte_order) != 0);
 		   ++temp_len)
@@ -280,7 +281,7 @@ c_value_print_array (struct value *val,
 	      /* Force printstr to print ellipses if
 		 we've printed the maximum characters and
 		 the next character is not \000.  */
-	      if (temp_len == options->print_max && temp_len < len)
+	      if (temp_len == print_max_chars && temp_len < len)
 		{
 		  ULONGEST ival
 		    = extract_unsigned_integer (valaddr + temp_len * eltlen,
Index: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo
+++ src/gdb/doc/gdb.texinfo
@@ -10309,10 +10309,18 @@ Related setting: @ref{set print array}.
 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
+@item -characters @var{number-of-characters}|@code{elements}|@code{unlimited}
+Set limit on string characters to print.  The value @code{elements}
+causes the limit on array elements to print to be used.  The value
 @code{unlimited} causes there to be no limit.  Related setting:
-@ref{set print elements}.
+@ref{set print characters}.
+
+@item -elements @var{number-of-elements}|@code{unlimited}
+Set limit on array elements and optionally string characters to print.
+See @ref{set print characters}, and the @code{-characters} option above
+for when this option applies to strings.  The value @code{unlimited}
+causes there to be no limit.  @xref{set print elements}, for a related
+CLI command.
 
 @item -max-depth @var{depth}|@code{unlimited}
 Set the threshold after which nested structures are replaced with
@@ -11709,6 +11717,31 @@ Don't printing binary values in groups.
 @item show print nibbles
 Show whether to print binary values in groups of four bits.
 
+@anchor{set print characters}
+@item set print characters @var{number-of-characters}
+@itemx set print characters elements
+@itemx set print characters unlimited
+@cindex number of string characters to print
+@cindex limit on number of printed string characters
+Set a limit on how many characters of a string @value{GDBN} will print.
+If @value{GDBN} is printing a large string, it stops printing after it
+has printed the number of characters set by the @code{set print
+characters} command.  This equally applies to multi-byte and wide
+character strings, that is for strings whose character type is
+@code{wchar_t}, @code{char16_t}, or @code{char32_t} it is the number of
+actual characters rather than underlying bytes the encoding uses that
+this setting controls.
+Setting @var{number-of-characters} to @code{elements} means that the
+limit on the number of characters to print follows one for array
+elements; see @ref{set print elements}.
+Setting @var{number-of-characters} to @code{unlimited} means that the
+number of characters to print is unlimited.
+When @value{GDBN} starts, this limit is set to @code{elements}.
+
+@item show print characters
+Display the number of characters of a large string that @value{GDBN}
+will print.
+
 @anchor{set print elements}
 @item set print elements @var{number-of-elements}
 @itemx set print elements unlimited
@@ -11717,7 +11750,8 @@ Show whether to print binary values in g
 Set a limit on how many elements of an array @value{GDBN} will print.
 If @value{GDBN} is printing a large array, it stops printing after it has
 printed the number of elements set by the @code{set print elements} command.
-This limit also applies to the display of strings.
+By default this limit also applies to the display of strings; see
+@ref{set print characters}.
 When @value{GDBN} starts, this limit is set to 200.
 Setting @var{number-of-elements} to @code{unlimited} or zero means
 that the number of elements to print is unlimited.
@@ -15233,7 +15267,7 @@ The optional @var{mods} changes the usua
 @code{s} requests that pointers to chars be handled as strings, in
 particular collecting the contents of the memory being pointed at, up
 to the first zero.  The upper bound is by default the value of the
-@code{print elements} variable; if @code{s} is followed by a decimal
+@code{print characters} variable; if @code{s} is followed by a decimal
 number, that is the upper bound instead.  So for instance
 @samp{collect/s25 mystr} collects as many as 25 characters at
 @samp{mystr}.
Index: src/gdb/doc/python.texi
===================================================================
--- src.orig/gdb/doc/python.texi
+++ src/gdb/doc/python.texi
@@ -1135,6 +1135,11 @@ the @emph{declared} type should be used.
 representation of a C@t{++} object, @code{False} if they shouldn't (see
 @code{set print static-members} in @ref{Print Settings}).
 
+@item max_characters
+Number of string characters to print, @code{0} to follow
+@code{max_elements}, or @code{UINT_MAX} to print an unlimited number
+of characters (see @code{set print characters} in @ref{Print Settings}).
+
 @item max_elements
 Number of array elements to print, or @code{0} to print an unlimited
 number of elements (see @code{set print elements} in @ref{Print
Index: src/gdb/language.h
===================================================================
--- src.orig/gdb/language.h
+++ src/gdb/language.h
@@ -548,7 +548,7 @@ struct language_defn
 			  struct ui_file * stream) const;
 
 /* Print the character string STRING, printing at most LENGTH characters.
-   Printing stops early if the number hits print_max; repeat counts
+   Printing stops early if the number hits print_max_chars; repeat counts
    are printed as appropriate.  Print ellipses at the end if we
    had to stop before printing LENGTH characters, or if FORCE_ELLIPSES.  */
 
Index: src/gdb/m2-lang.c
===================================================================
--- src.orig/gdb/m2-lang.c
+++ src/gdb/m2-lang.c
@@ -169,7 +169,8 @@ m2_language::printstr (struct ui_file *s
       return;
     }
 
-  for (i = 0; i < length && things_printed < options->print_max; ++i)
+  unsigned int print_max_chars = get_print_max_chars (options);
+  for (i = 0; i < length && things_printed < print_max_chars; ++i)
     {
       /* Position of the character we are examining
 	 to see whether it is repeated.  */
Index: src/gdb/m2-valprint.c
===================================================================
--- src.orig/gdb/m2-valprint.c
+++ src/gdb/m2-valprint.c
@@ -327,12 +327,14 @@ m2_language::value_print_inner (struct v
 		 elements up to it.  */
 	      if (options->stop_print_at_null)
 		{
+		  unsigned int print_max_chars = get_print_max_chars (options);
 		  unsigned int temp_len;
 
 		  /* Look for a NULL char.  */
 		  for (temp_len = 0;
 		       (valaddr[temp_len]
-			&& temp_len < len && temp_len < options->print_max);
+			&& temp_len < len
+			&& temp_len < print_max_chars);
 		       temp_len++);
 		  len = temp_len;
 		}
Index: src/gdb/p-lang.c
===================================================================
--- src.orig/gdb/p-lang.c
+++ src/gdb/p-lang.c
@@ -253,7 +253,8 @@ pascal_language::printstr (struct ui_fil
       return;
     }
 
-  for (i = 0; i < length && things_printed < options->print_max; ++i)
+  unsigned int print_max_chars = get_print_max_chars (options);
+  for (i = 0; i < length && things_printed < print_max_chars; ++i)
     {
       /* Position of the character we are examining
 	 to see whether it is repeated.  */
Index: src/gdb/p-valprint.c
===================================================================
--- src.orig/gdb/p-valprint.c
+++ src/gdb/p-valprint.c
@@ -105,13 +105,16 @@ pascal_language::value_print_inner (stru
 		   elements up to it.  */
 		if (options->stop_print_at_null)
 		  {
+		    unsigned int print_max_chars
+		      = get_print_max_chars (options);
 		    unsigned int temp_len;
 
 		    /* Look for a NULL char.  */
 		    for (temp_len = 0;
-			 extract_unsigned_integer (valaddr + temp_len * eltlen,
-						   eltlen, byte_order)
-			   && temp_len < len && temp_len < options->print_max;
+			 (extract_unsigned_integer
+			    (valaddr + temp_len * eltlen, eltlen, byte_order)
+			  && temp_len < len
+			  && temp_len < print_max_chars);
 			 temp_len++);
 		    len = temp_len;
 		  }
Index: src/gdb/printcmd.c
===================================================================
--- src.orig/gdb/printcmd.c
+++ src/gdb/printcmd.c
@@ -957,17 +957,18 @@ find_string_backward (struct gdbarch *gd
 					 chars_to_read * char_size);
       chars_read /= char_size;
       read_error = (chars_read == chars_to_read) ? 0 : 1;
+      unsigned int print_max_chars = get_print_max_chars (options);
       /* Searching for '\0' from the end of buffer in backward direction.  */
       for (i = 0; i < chars_read && count > 0 ; ++i, ++chars_counted)
 	{
 	  int offset = (chars_to_read - i - 1) * char_size;
 
 	  if (integer_is_zero (&buffer[offset], char_size)
-	      || chars_counted == options->print_max)
+	      || chars_counted == print_max_chars)
 	    {
-	      /* Found '\0' or reached print_max.  As OFFSET is the offset to
-		 '\0', we add CHAR_SIZE to return the start address of
-		 a string.  */
+	      /* Found '\0' or reached `print_max_chars'.  As OFFSET
+		 is the offset to '\0', we add CHAR_SIZE to return
+		 the start address of a string.  */
 	      --count;
 	      string_start_addr = addr + offset + char_size;
 	      chars_counted = 0;
Index: src/gdb/python/py-value.c
===================================================================
--- src.orig/gdb/python/py-value.c
+++ src/gdb/python/py-value.c
@@ -647,6 +647,7 @@ valpy_format_string (PyObject *self, PyO
       "actual_objects",		/* See set print object on|off.  */
       "static_members",		/* See set print static-members on|off.  */
       /* C non-bool options.  */
+      "max_characters", 	/* See set print characters N.  */
       "max_elements", 		/* See set print elements N.  */
       "max_depth",		/* See set print max-depth N.  */
       "repeat_threshold",	/* See set print repeats.  */
@@ -695,7 +696,7 @@ valpy_format_string (PyObject *self, PyO
   char *format = NULL;
   if (!gdb_PyArg_ParseTupleAndKeywords (args,
 					kw,
-					"|O!O!O!O!O!O!O!O!O!O!O!O!O!IIIs",
+					"|O!O!O!O!O!O!O!O!O!O!O!O!O!IIIIs",
 					keywords,
 					&PyBool_Type, &raw_obj,
 					&PyBool_Type, &pretty_arrays_obj,
@@ -710,6 +711,7 @@ valpy_format_string (PyObject *self, PyO
 					&PyBool_Type, &deref_refs_obj,
 					&PyBool_Type, &actual_objects_obj,
 					&PyBool_Type, &static_members_obj,
+					&opts.print_max_chars,
 					&opts.print_max,
 					&opts.max_depth,
 					&opts.repeat_count_threshold,
Index: src/gdb/testsuite/gdb.base/default.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/default.exp
+++ src/gdb/testsuite/gdb.base/default.exp
@@ -520,6 +520,10 @@ gdb_test_no_output "set print address" "
 gdb_test_no_output "set print array" "set print array"
 #test set print asm-demangle
 gdb_test_no_output "set print asm-demangle" "set print asm-demangle"
+#test set print characters
+gdb_test "set print characters" \
+	 "Argument required \\(integer to set it to, or one of:\
+	  \"elements\", \"unlimited\"\\)\\."
 #test set print demangle
 gdb_test_no_output "set print demangle" "set print demangle"
 #test set print elements
@@ -664,6 +668,9 @@ gdb_test "show print address" "Printing
 gdb_test "show print array" "Pretty formatting of arrays is on."
 #test show print asm-demangle
 gdb_test "show print asm-demangle" "Demangling of C\[+\]+/ObjC names in disassembly listings is on."
+#test show print characters
+gdb_test "show print characters" \
+	 "Limit on string characters to print is elements\\."
 #test show print demangle
 gdb_test "show print demangle" "Demangling of encoded C\[+\]+/ObjC names when displaying symbols is on."
 #test show print elements
Index: src/gdb/testsuite/gdb.base/options.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/options.exp
+++ src/gdb/testsuite/gdb.base/options.exp
@@ -172,6 +172,7 @@ proc_with_prefix test-print {{prefix ""}
 	"-address"
 	"-array"
 	"-array-indexes"
+	"-characters"
 	"-elements"
 	"-max-depth"
 	"-memory-tag-violations"
Index: src/gdb/tracepoint.c
===================================================================
--- src.orig/gdb/tracepoint.c
+++ src/gdb/tracepoint.c
@@ -541,9 +541,9 @@ decode_agent_options (const char *exp, i
       if (target_supports_string_tracing ())
 	{
 	  /* Allow an optional decimal number giving an explicit maximum
-	     string length, defaulting it to the "print elements" value;
+	     string length, defaulting it to the "print characters" value;
 	     so "collect/s80 mystr" gets at most 80 bytes of string.  */
-	  *trace_string = opts.print_max;
+	  *trace_string = get_print_max_chars (&opts);
 	  exp++;
 	  if (*exp >= '0' && *exp <= '9')
 	    *trace_string = atoi (exp);
Index: src/gdb/valprint.c
===================================================================
--- src.orig/gdb/valprint.c
+++ src/gdb/valprint.c
@@ -94,8 +94,14 @@ static void val_print_type_code_flags (s
 				       int embedded_offset,
 				       struct ui_file *stream);
 
-#define PRINT_MAX_DEFAULT 200	/* Start print_max off at this value.  */
-#define PRINT_MAX_DEPTH_DEFAULT 20	/* Start print_max_depth off at this value. */
+/* Start print_max at this value.  */
+#define PRINT_MAX_DEFAULT 200
+
+/* Start print_max_chars at this value (meaning follow print_max).  */
+#define PRINT_MAX_CHARS_DEFAULT PRINT_MAX_CHARS_ELEMENTS
+
+/* Start print_max_depth at this value. */
+#define PRINT_MAX_DEPTH_DEFAULT 20
 
 struct value_print_options user_print_options =
 {
@@ -108,6 +114,7 @@ struct value_print_options user_print_op
   false,			/* nibblesprint */
   false,			/* objectprint */
   PRINT_MAX_DEFAULT,		/* print_max */
+  PRINT_MAX_CHARS_DEFAULT,	/* print_max_chars */
   10,				/* repeat_count_threshold */
   0,				/* output_format */
   0,				/* format */
@@ -149,17 +156,31 @@ get_formatted_print_options (struct valu
   opts->format = format;
 }
 
+/* Implement 'show print elements'.  */
+
 static void
 show_print_max (struct ui_file *file, int from_tty,
 		struct cmd_list_element *c, const char *value)
 {
+  gdb_printf
+    (file,
+     (user_print_options.print_max_chars != PRINT_MAX_CHARS_ELEMENTS
+      ? _("Limit on array elements to print is %s.\n")
+      : _("Limit on string chars or array elements to print is %s.\n")),
+     value);
+}
+
+/* Implement 'show print characters'.  */
+
+static void
+show_print_max_chars (struct ui_file *file, int from_tty,
+		      struct cmd_list_element *c, const char *value)
+{
   gdb_printf (file,
-	      _("Limit on string chars or array "
-		"elements to print is %s.\n"),
+	      _("Limit on string characters to print is %s.\n"),
 	      value);
 }
 
-
 /* Default input and output radixes, and output format letter.  */
 
 unsigned input_radix = 10;
@@ -2481,9 +2502,9 @@ print_converted_chars_to_obstack (struct
 /* Print the character string STRING, printing at most LENGTH
    characters.  LENGTH is -1 if the string is nul terminated.  TYPE is
    the type of each character.  OPTIONS holds the printing options;
-   printing stops early if the number hits print_max; repeat counts
-   are printed as appropriate.  Print ellipses at the end if we had to
-   stop before printing LENGTH characters, or if FORCE_ELLIPSES.
+   printing stops early if the number hits print_max_chars; repeat
+   counts are printed as appropriate.  Print ellipses at the end if we
+   had to stop before printing LENGTH characters, or if FORCE_ELLIPSES.
    QUOTE_CHAR is the character to print at each end of the string.  If
    C_STYLE_TERMINATOR is true, and the last character is 0, then it is
    omitted.  */
@@ -2537,7 +2558,8 @@ generic_printstr (struct ui_file *stream
   /* Convert characters until the string is over or the maximum
      number of printed characters has been reached.  */
   i = 0;
-  while (i < options->print_max)
+  unsigned int print_max_chars = get_print_max_chars (options);
+  while (i < print_max_chars)
     {
       int r;
 
@@ -2589,7 +2611,7 @@ generic_printstr (struct ui_file *stream
 /* Print a string from the inferior, starting at ADDR and printing up to LEN
    characters, of WIDTH bytes a piece, to STREAM.  If LEN is -1, printing
    stops at the first null byte, otherwise printing proceeds (including null
-   bytes) until either print_max or LEN characters have been printed,
+   bytes) until either print_max_chars or LEN characters have been printed,
    whichever is smaller.  ENCODING is the name of the string's
    encoding.  It can be NULL, in which case the target encoding is
    assumed.  */
@@ -2611,15 +2633,17 @@ val_print_string (struct type *elttype,
   int width = elttype->length ();
 
   /* First we need to figure out the limit on the number of characters we are
-     going to attempt to fetch and print.  This is actually pretty simple.  If
-     LEN >= zero, then the limit is the minimum of LEN and print_max.  If
-     LEN is -1, then the limit is print_max.  This is true regardless of
-     whether print_max is zero, UINT_MAX (unlimited), or something in between,
-     because finding the null byte (or available memory) is what actually
-     limits the fetch.  */
+     going to attempt to fetch and print.  This is actually pretty simple.
+     If LEN >= zero, then the limit is the minimum of LEN and print_max_chars.
+     If LEN is -1, then the limit is print_max_chars.  This is true regardless
+     of whether print_max_chars is zero, UINT_MAX (unlimited), or something in
+     between, because finding the null byte (or available memory) is what
+     actually limits the fetch.  */
 
-  fetchlimit = (len == -1 ? options->print_max : std::min ((unsigned) len,
-							   options->print_max));
+  unsigned int print_max_chars = get_print_max_chars (options);
+  fetchlimit = (len == -1
+		? print_max_chars
+		: std::min ((unsigned) len, print_max_chars));
 
   err = target_read_string (addr, len, width, fetchlimit,
 			    &buffer, &bytes_read);
@@ -2864,6 +2888,15 @@ using uinteger_option_def
 using pinteger_option_def
   = gdb::option::pinteger_option_def<value_print_options>;
 
+/* Extra literals supported with the `set print characters' and
+   `print -characters' commands.  */
+static const literal_def print_characters_literals[] =
+  {
+    { "elements", PRINT_MAX_CHARS_ELEMENTS },
+    { "unlimited", PRINT_MAX_CHARS_UNLIMITED, 0 },
+    { nullptr }
+  };
+
 /* Definitions of options for the "print" and "compile print"
    commands.  */
 static const gdb::option::option_def value_print_option_defs[] = {
@@ -2905,13 +2938,26 @@ static const gdb::option::option_def val
   },
 
   uinteger_option_def {
+    "characters",
+    [] (value_print_options *opt) { return &opt->print_max_chars; },
+    print_characters_literals,
+    show_print_max_chars, /* show_cmd_cb */
+    N_("Set limit on string chars to print."),
+    N_("Show limit on string chars to print."),
+    N_("\"elements\" causes the array element limit to be used.\n"
+       "\"unlimited\" causes there to be no limit."),
+  },
+
+  uinteger_option_def {
     "elements",
     [] (value_print_options *opt) { return &opt->print_max; },
     uinteger_unlimited_literals,
     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."),
+    N_("Set limit on array elements to print."),
+    N_("Show limit on array elements to print."),
+    N_("\"unlimited\" causes there to be no limit.\n"
+       "This setting also applies to string chars when \"print characters\"\n"
+       "is set to \"elements\"."),
   },
 
   pinteger_option_def {
Index: src/gdb/valprint.h
===================================================================
--- src.orig/gdb/valprint.h
+++ src/gdb/valprint.h
@@ -51,12 +51,15 @@ struct value_print_options
      in its vtables.  */
   bool objectprint;
 
-  /* Maximum number of chars to print for a string pointer value or vector
-     contents, or UINT_MAX for no limit.  Note that "set print elements 0"
-     stores UINT_MAX in print_max, which displays in a show command as
-     "unlimited".  */
+  /* Maximum number of elements to print for vector contents, or UINT_MAX
+     for no limit.  Note that "set print elements 0" stores UINT_MAX in
+     print_max, which displays in a show command as "unlimited".  */
   unsigned int print_max;
 
+  /* Maximum number of string chars to print for a string pointer value,
+     zero if to follow the value of print_max, or UINT_MAX for no limit.  */
+  unsigned int print_max_chars;
+
   /* Print repeat counts if there are more than this many repetitions
      of an element in an array.  */
   unsigned int repeat_count_threshold;
@@ -105,6 +108,21 @@ struct value_print_options
   int max_depth;
 };
 
+/* The value to use for `print_max_chars' to follow `print_max'.  */
+#define PRINT_MAX_CHARS_ELEMENTS 0
+
+/* The value to use for `print_max_chars' for no limit.  */
+#define PRINT_MAX_CHARS_UNLIMITED UINT_MAX
+
+/* Return the character count limit for printing strings.  */
+
+static inline unsigned int
+get_print_max_chars (const struct value_print_options *options)
+{
+  return (options->print_max_chars != PRINT_MAX_CHARS_ELEMENTS
+	  ? options->print_max_chars : options->print_max);
+}
+
 /* 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

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

* [COMMITTED PATCH v9 6/6] GDB/testsuite: Expand for character string limiting options
  2023-01-19 21:18     ` Maciej W. Rozycki
@ 2023-01-19 21:20       ` Maciej W. Rozycki
  0 siblings, 0 replies; 32+ messages in thread
From: Maciej W. Rozycki @ 2023-01-19 21:20 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches, Andrew Burgess, Tom Tromey, Simon Sobisch

From: Andrew Burgess <andrew.burgess@embecosm.com>

Modify test cases that verify the operation of the array element limit 
with character strings such that they are executed twice, once with the 
`set print characters' option set to `elements' and the limit controlled 
with the `set print elements' option, and then again with the limit 
controlled with the `set print characters' option instead.  Similarly 
with the `-elements' and `-characters' options for the `print' command. 
Additionally verify that said `print' command options combined yield the 
expected result.

Verify correct $_gdb_setting and $_gdb_setting_str values for the `print  
characters' setting, in particular the `void' value for the `elements' 
default, which has no corresponding integer value exposed.

Add Guile and Python coverage for the `print characters' GDB setting.

There are new tests for Ada and Pascal, as the string printing code for 
these languages is different than the generic string printing code used 
by other languages.  Modula2 also has different string printing code, 
but (a) this is similar to Pascal, and (b) there are no existing modula2 
tests written in Modula2, so I'm not sure how I'd even test the Modula2 
string printing.

Co-Authored-By: Maciej W. Rozycki <macro@embecosm.com>
Approved-By: Simon Marchi <simon.marchi@efficios.com>
---
Changes from v8:

- Update internal Python test case documentation.

- Change the Pascal test case to use `runto_main'.

- Fix the Ada test case to use `require allow_ada_tests'.

- Update copyright years for new files throughout.

- Fix a minor formatting issue with a closing brace.

Changes from v7:

- py-parameter.exp regenerated for the special case of returning -1 for 
  the value of `unlimited' with `var_pinteger' parameters in Python code.

No change from v6.1.

Changes from v6:

- Add tests for $_gdb_setting and $_gdb_setting_str values with `print
  characters' to cover the `void' value in particular.

Changes from v5:

- Update testing for the use of `set print characters 0' now permitted.

- Add Guile and Python `print characters' testing.

No changes from v4.

Changes from v3:

- Split off from what is now 7/8; see the change log there for earlier 
  changes.

- Remove test case modifications to switch from the `set print elements' 
  command to `set print characters'; instead run them twice with each of 
  the two commands verified.

- Likewise with the `print -elements' and `print -characters' commands.

- Also cover `print -elements ... -characters ...', i.e. both options 
  combined.

- Expand the Ada and Pascal test cases to cover `set print characters
  elements' too.
---
 gdb/testsuite/gdb.ada/str_chars.exp           |   70 ++++++++++++++++++++++++++
 gdb/testsuite/gdb.ada/str_chars/foo.adb       |   26 +++++++++
 gdb/testsuite/gdb.base/printcmds.exp          |   68 +++++++++++++++++--------
 gdb/testsuite/gdb.base/setshow.exp            |   22 ++++++++
 gdb/testsuite/gdb.guile/scm-parameter.exp     |   11 +++-
 gdb/testsuite/gdb.pascal/str-chars.exp        |   55 ++++++++++++++++++++
 gdb/testsuite/gdb.pascal/str-chars.pas        |   28 ++++++++++
 gdb/testsuite/gdb.python/py-format-string.exp |   51 +++++++++++++-----
 gdb/testsuite/gdb.python/py-parameter.exp     |   13 ++++
 9 files changed, 306 insertions(+), 38 deletions(-)

gdb-aburgess-print-elements-characters-test.diff
Index: src/gdb/testsuite/gdb.ada/str_chars.exp
===================================================================
--- /dev/null
+++ src/gdb/testsuite/gdb.ada/str_chars.exp
@@ -0,0 +1,70 @@
+# Copyright 2023 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 GDB's 'set print characters' setting works for Ada strings.
+
+load_lib "ada.exp"
+
+require allow_ada_tests
+
+standard_ada_testfile foo
+
+if {[gdb_compile_ada "${srcfile}" "${binfile}" executable [list debug ]] != "" } {
+  return -1
+}
+
+clean_restart ${testfile}
+
+set bp_location [gdb_get_line_number "STOP" ${testdir}/foo.adb]
+if ![runto "foo.adb:$bp_location" ] then {
+  return -1
+}
+
+gdb_test "print Arg" \
+    " = \"abcdefghijklmnopqrstuvwxyz\"" \
+    "print with default settings"
+
+gdb_test_no_output "set print characters 26"
+gdb_test "print Arg" \
+    " = \"abcdefghijklmnopqrstuvwxyz\"" \
+    "print with character limit of 26"
+
+gdb_test "print -characters 11 -- Arg" \
+    " = \"abcdefghijk\"\\.\\.\\." \
+    "print with character limit of 11"
+
+gdb_test_no_output "set print characters 10"
+gdb_test "print Arg" \
+    " = \"abcdefghij\"\\.\\.\\." \
+    "print with character limit of 10"
+
+gdb_test_no_output "set print characters unlimited"
+gdb_test "print Arg" \
+    " = \"abcdefghijklmnopqrstuvwxyz\"" \
+    "print with unlimited character limit"
+
+# The 'set print elements' command used to control printing of characters
+# in a string, before we created 'set print characters'.  This test makes
+# sure that 'set print elements' doens't effect string printing any more.
+gdb_test_no_output "set print elements 12"
+gdb_test "print Arg" \
+    " = \"abcdefghijklmnopqrstuvwxyz\"" \
+    "print with unlimited character limit, but lower element limit"
+
+# Except when 'set print characters elements' has been used.
+gdb_test_no_output "set print characters elements"
+gdb_test "print Arg" \
+    " = \"abcdefghijkl\"\\.\\.\\." \
+    "print with character limit of elements"
Index: src/gdb/testsuite/gdb.ada/str_chars/foo.adb
===================================================================
--- /dev/null
+++ src/gdb/testsuite/gdb.ada/str_chars/foo.adb
@@ -0,0 +1,26 @@
+--  Copyright 2023 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/>.
+
+procedure Foo is
+
+   procedure Blah (Arg : String) is
+   begin
+     null; -- STOP
+   end;
+
+begin
+
+   Blah ("abcdefghijklmnopqrstuvwxyz");
+end Foo;
Index: src/gdb/testsuite/gdb.base/printcmds.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/printcmds.exp
+++ src/gdb/testsuite/gdb.base/printcmds.exp
@@ -449,13 +449,14 @@ proc test_print_all_chars {} {
 }
 
 # Test interaction of the number of print elements to print and the
-# repeat count, set to the default of 10.
+# repeat count, set to the default of 10.  SETTING is the print
+# setting to verify, either "elements" or "characters".
 
-proc test_print_repeats_10 {} {
+proc test_print_repeats_10_one { setting } {
     global gdb_prompt decimal
 
     for { set x 1 } { $x <= 16 } { incr x } {
-	gdb_test_no_output "set print elements $x" "elements $x repeats"
+	gdb_test_no_output "set print $setting $x" "$setting $x repeats"
 	for { set e 1 } { $e <= 16 } {incr e } {
 	    set v [expr $e - 1]
 	    set command "p &ctable2\[${v}*16\]"
@@ -495,11 +496,18 @@ proc test_print_repeats_10 {} {
 		set xstr "${xstr}\[.\]\[.\]\[.\]"
 	    }
 	    set string " = \[(\]unsigned char \[*\]\[)\] <ctable2(\\+$decimal)?> ${a}${xstr}"
-	    gdb_test "$command" "$string" "$command with print elements set to $x"
+	    gdb_test "$command" "$string" "$command with print $setting set to $x"
 	}
     }
 }
 
+proc test_print_repeats_10 {} {
+    foreach_with_prefix setting { "elements" "characters" } {
+	test_print_repeats_10_one $setting
+    }
+    gdb_test_no_output "set print characters elements"
+}
+
 # This tests whether GDB uses the correct element content offsets
 # (relative to the complete `some_struct' value) when counting value
 # repetitions.
@@ -512,7 +520,7 @@ proc test_print_repeats_embedded_array {
 	"correct element repeats in array embedded at offset > 0"
 }
 
-proc test_print_strings {} {
+proc test_print_strings_one { setting } {
     global gdb_prompt decimal
 
     # We accept "(unsigned char *) " before the string.  char vs. unsigned char
@@ -520,35 +528,35 @@ proc test_print_strings {} {
 
     # Test that setting print elements unlimited doesn't completely suppress
     # printing; this was a bug in older gdb's.
-    gdb_test_no_output "set print elements 0"
+    gdb_test_no_output "set print $setting 0"
     gdb_test "p teststring" \
-	" = (.unsigned char .. )?\"teststring contents\"" "p teststring with elements set to 0"
-    gdb_test_no_output "set print elements 1"
+	" = (.unsigned char .. )?\"teststring contents\"" "p teststring with $setting set to 0"
+    gdb_test_no_output "set print $setting 1"
     gdb_test "p teststring" \
-	" = (.unsigned char .. )?\"t\"\\.\\.\\." "p teststring with elements set to 1"
-    gdb_test_no_output "set print elements 5"
+	" = (.unsigned char .. )?\"t\"\\.\\.\\." "p teststring with $setting set to 1"
+    gdb_test_no_output "set print $setting 5"
     gdb_test "p teststring" \
-	" = (.unsigned char .. )?\"tests\"\\.\\.\\." "p teststring with elements set to 5"
-    gdb_test_no_output "set print elements 19"
+	" = (.unsigned char .. )?\"tests\"\\.\\.\\." "p teststring with $setting set to 5"
+    gdb_test_no_output "set print $setting 19"
     gdb_test "p teststring" \
-	" = (.unsigned char .. )?\"teststring contents\"" "p teststring with elements set to 19"
-    gdb_test_no_output "set print elements 20"
+	" = (.unsigned char .. )?\"teststring contents\"" "p teststring with $setting set to 19"
+    gdb_test_no_output "set print $setting 20"
     gdb_test "p teststring" \
-	" = (.unsigned char .. )?\"teststring contents\"" "p teststring with elements set to 20"
+	" = (.unsigned char .. )?\"teststring contents\"" "p teststring with $setting set to 20"
 
-    gdb_test "p -elements 1 -- teststring" \
+    gdb_test "p -$setting 1 -- teststring" \
 	" = (.unsigned char .. )?\"t\"\\.\\.\\."
-    gdb_test "p -elements 5 -- teststring" \
+    gdb_test "p -$setting 5 -- teststring" \
 	" = (.unsigned char .. )?\"tests\"\\.\\.\\."
-    gdb_test "p -elements 19 -- teststring" \
+    gdb_test "p -$setting 19 -- teststring" \
 	" = (.unsigned char .. )?\"teststring contents\""
-    gdb_test "p -elements 20 -- teststring" \
+    gdb_test "p -$setting 20 -- teststring" \
 	" = (.unsigned char .. )?\"teststring contents\""
 
     gdb_test "print teststring2" \
 	" = \\(charptr\\) \"more contents\""
 
-    gdb_test_no_output "set print elements 8"
+    gdb_test_no_output "set print $setting 8"
 
     # Set the target-charset to ASCII, because the output varies from
     # different charset.
@@ -620,6 +628,26 @@ proc test_print_strings {} {
 	gdb_test "p &ctable1\[31*8\]" \
 	    " = \\(unsigned char \\*\\) <ctable1\\+$decimal> \"\\\\370\\\\371\\\\372\\\\373\\\\374\\\\375\\\\376\\\\377\"..."
     }
+
+    gdb_test_no_output "set print $setting unlimited"
+}
+
+proc test_print_strings {} {
+
+    foreach_with_prefix setting { "elements" "characters" } {
+	test_print_strings_one $setting
+    }
+
+    gdb_test "p -elements 8 -- teststring" \
+	" = (.unsigned char .. )?\"teststring contents\""
+    gdb_test "p -characters 8 -- teststring" \
+	" = (.unsigned char .. )?\"teststri\"\\.\\.\\."
+    gdb_test "p -elements 8 -characters elements -- teststring" \
+	" = (.unsigned char .. )?\"teststri\"\\.\\.\\."
+
+    with_test_prefix strings {
+	gdb_test_no_output "set print characters elements"
+    }
 }
 
 proc test_print_int_arrays {} {
Index: src/gdb/testsuite/gdb.base/setshow.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/setshow.exp
+++ src/gdb/testsuite/gdb.base/setshow.exp
@@ -374,6 +374,27 @@ proc_with_prefix test_setshow_listsize {
     gdb_test "show listsize" "Number of source lines gdb will list by default is 100..*" "show listsize (100)"
 }
 
+proc_with_prefix test_setshow_print_characters {} {
+    clean_restart
+
+    gdb_test "p \$_gdb_setting(\"print characters\")" " = void" \
+	"_gdb_setting print characters default"
+    gdb_test "p \$_gdb_setting_str(\"print characters\")" " = \"elements\"" \
+	"_gdb_setting_str print characters default"
+
+    gdb_test_no_output "set print characters unlimited"
+    gdb_test "p \$_gdb_setting(\"print characters\")" " = 0" \
+	"_gdb_setting print characters unlimited"
+    gdb_test "p \$_gdb_setting_str(\"print characters\")" " = \"unlimited\"" \
+	"_gdb_setting_str print characters unlimited"
+
+    gdb_test_no_output "set print characters 1"
+    gdb_test "p \$_gdb_setting(\"print characters\")" " = 1" \
+	"_gdb_setting print characters 1"
+    gdb_test "p \$_gdb_setting_str(\"print characters\")" " = \"1\"" \
+	"_gdb_setting_str print characters 1"
+}
+
 proc_with_prefix test_setshow_prompt {} {
     clean_restart
 
@@ -493,6 +514,7 @@ test_setshow_height
 test_setshow_history
 test_setshow_language
 test_setshow_listsize
+test_setshow_print_characters
 test_setshow_prompt
 test_setshow_radix
 test_setshow_width
Index: src/gdb/testsuite/gdb.guile/scm-parameter.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.guile/scm-parameter.exp
+++ src/gdb/testsuite/gdb.guile/scm-parameter.exp
@@ -104,6 +104,7 @@ foreach_with_prefix param {
     "listsize"
     "print elements"
     "max-completions"
+    "print characters"
 } {
     set param_range_error "integer -1 out of range"
     set param_type_error \
@@ -117,7 +118,8 @@ foreach_with_prefix param {
 	    set param_get_minus_one -1
 	    set param_set_minus_one ""
 	}
-	"print elements" {
+	"print elements" -
+	"print characters" {
 	    set param_get_zero "#:unlimited"
 	    set param_get_minus_one "#:unlimited"
 	    set param_set_minus_one $param_range_error
@@ -152,6 +154,13 @@ foreach_with_prefix param {
 
     gdb_test "guile (print (parameter-value \"$param\"))" \
 	"#:unlimited" "test value of 'unlimited'"
+
+    if {$param == "print characters"} {
+	gdb_test_no_output "set $param elements" "test set to 'elements'"
+
+	gdb_test "guile (print (parameter-value \"$param\"))" \
+	    "#:elements" "test value of 'elements'"
+    }
 }
 
 foreach_with_prefix kind {
Index: src/gdb/testsuite/gdb.pascal/str-chars.exp
===================================================================
--- /dev/null
+++ src/gdb/testsuite/gdb.pascal/str-chars.exp
@@ -0,0 +1,55 @@
+# Copyright 2023 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/>.
+
+load_lib "pascal.exp"
+
+standard_testfile .pas
+
+if {[gdb_compile_pascal "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug ]] != "" } {
+  return -1
+}
+
+clean_restart ${testfile}
+
+if { ![runto_main] } {
+    return
+}
+
+gdb_breakpoint ${srcfile}:[gdb_get_line_number "set breakpoint 1 here"]
+
+gdb_continue_to_breakpoint "continue to breakpoint"
+
+gdb_test "print message" " = 'abcdefghijklmnopqrstuvwxyz'" \
+    "print message with the default settings"
+
+gdb_test_no_output "set print elements 10"
+gdb_test "print message" " = 'abcdefghij'\\.\\.\\." \
+    "print message with 'print elements' set to 10"
+
+gdb_test_no_output "set print characters 20"
+gdb_test "print message" " = 'abcdefghijklmnopqrst'\\.\\.\\." \
+    "print message with 'print characters' set to 20"
+
+gdb_test_no_output "set print elements 15"
+gdb_test "print message" " = 'abcdefghijklmnopqrst'\\.\\.\\." \
+    "print message with 'print elements' set to 15"
+
+gdb_test_no_output "set print characters unlimited"
+gdb_test "print message" " = 'abcdefghijklmnopqrstuvwxyz'" \
+    "print message with 'print characters' set to unlimited"
+
+gdb_test_no_output "set print characters elements"
+gdb_test "print message" " = 'abcdefghijklmno'\\.\\.\\." \
+    "print message with 'print characters' set to elements"
Index: src/gdb/testsuite/gdb.pascal/str-chars.pas
===================================================================
--- /dev/null
+++ src/gdb/testsuite/gdb.pascal/str-chars.pas
@@ -0,0 +1,28 @@
+{
+ Copyright 2023 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/>.
+}
+
+program str_char;
+
+var
+   message : string;
+
+begin
+
+   message := 'abcdefghijklmnopqrstuvwxyz';
+   writeln (message)	{ set breakpoint 1 here }
+
+end.
Index: src/gdb/testsuite/gdb.python/py-format-string.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.python/py-format-string.exp
+++ src/gdb/testsuite/gdb.python/py-format-string.exp
@@ -704,13 +704,17 @@ proc_with_prefix test_static_members {}
 }
 
 # Test the max_elements option for gdb.Value.format_string.
-proc_with_prefix test_max_elements {} {
+# SETTING is the print setting to verify, either "elements"
+# or "characters".  UNLIMITED is the numeric value to use
+# for the "unlimited" setting.
+
+proc test_max_string_one { setting unlimited } {
   global current_lang
   global default_pointer_regexp
 
   # 200 is the default maximum number of elements, so setting it should
   # not change the output.
-  set opts "max_elements=200"
+  set opts "max_$setting=200"
   with_test_prefix $opts {
     check_format_string "a_point_t" $opts
     check_format_string "a_point_t_pointer" $opts
@@ -721,8 +725,10 @@ proc_with_prefix test_max_elements {} {
     check_format_string "a_binary_string" $opts
     check_format_string "a_binary_string_array" $opts
     check_format_string "a_big_string" $opts
-    check_format_string "an_array" $opts
-    check_format_string "an_array_with_repetition" $opts
+    if { $setting == "elements" } {
+      check_format_string "an_array" $opts
+      check_format_string "an_array_with_repetition" $opts
+    }
     check_format_string "a_symbol_pointer" $opts
 
     if { $current_lang == "c++" } {
@@ -731,7 +737,7 @@ proc_with_prefix test_max_elements {} {
     }
   }
 
-  set opts "max_elements=3"
+  set opts "max_$setting=3"
   with_test_prefix $opts {
     check_format_string "a_point_t" $opts
     check_format_string "a_point_t_pointer" $opts
@@ -748,9 +754,11 @@ proc_with_prefix test_max_elements {} {
       "\"hell\"..."
     check_format_string "a_big_string" $opts \
       [get_cut_big_string 3]
-    check_format_string "an_array" $opts
-    check_format_string "an_array_with_repetition" $opts \
-      "\\{1, 3 <repeats 12 times>...\\}"
+    if { $setting == "elements"} {
+      check_format_string "an_array" $opts
+      check_format_string "an_array_with_repetition" $opts \
+	"\\{1, 3 <repeats 12 times>...\\}"
+    }
     check_format_string "a_symbol_pointer" $opts
 
     if { $current_lang == "c++" } {
@@ -759,9 +767,9 @@ proc_with_prefix test_max_elements {} {
     }
   }
 
-  # Both 1,000 (we don't have that many elements) and 0 (unlimited) should
+  # Both 1,000 (we don't have that many elements) and unlimited should
   # mean no truncation.
-  foreach opts { "max_elements=1000" "max_elements=0" } {
+  foreach opts [list "max_$setting=1000" "max_$setting=$unlimited"] {
     with_test_prefix $opts {
       check_format_string "a_point_t" $opts
       check_format_string "a_point_t_pointer" $opts
@@ -773,8 +781,10 @@ proc_with_prefix test_max_elements {} {
       check_format_string "a_binary_string_array" $opts
       check_format_string "a_big_string" $opts \
         [get_cut_big_string 1000]
-      check_format_string "an_array" $opts
-      check_format_string "an_array_with_repetition" $opts
+      if { $setting == "elements"} {
+	check_format_string "an_array" $opts
+	check_format_string "an_array_with_repetition" $opts
+      }
       check_format_string "a_symbol_pointer" $opts
 
       if { $current_lang == "c++" } {
@@ -784,15 +794,24 @@ proc_with_prefix test_max_elements {} {
     }
   }
 
-  with_temp_option "set print elements 4" "set print elements 200" {
+  with_temp_option "set print $setting 4" "set print $setting 200" {
     check_format_string "a_string" "" \
       "${default_pointer_regexp} \"hell\"..."
     check_format_string "a_binary_string" "" \
       "${default_pointer_regexp} \"hell\"..."
     check_format_string "a_binary_string_array" "" \
       "\"hell\"..."
-    check_format_string "an_array_with_repetition" "" \
-      "\\{1, 3 <repeats 12 times>...\\}"
+    if { $setting == "elements"} {
+      check_format_string "an_array_with_repetition" "" \
+	"\\{1, 3 <repeats 12 times>...\\}"
+    }
+  }
+}
+
+proc_with_prefix test_max_string {} {
+  foreach_with_prefix setting { "elements" "characters" } {
+    test_max_string_one $setting \
+      [string map {elements 0 characters 4294967295} $setting]
   }
 }
 
@@ -1152,7 +1171,7 @@ proc_with_prefix test_all_common {} {
   test_deref_refs
   test_actual_objects
   test_static_members
-  test_max_elements
+  test_max_string
   test_max_depth
   test_repeat_threshold
   test_format
Index: src/gdb/testsuite/gdb.python/py-parameter.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.python/py-parameter.exp
+++ src/gdb/testsuite/gdb.python/py-parameter.exp
@@ -337,6 +337,7 @@ proc_with_prefix test_gdb_parameter { }
 	"listsize"
 	"print elements"
 	"max-completions"
+	"print characters"
     } {
 	clean_restart
 
@@ -349,7 +350,8 @@ proc_with_prefix test_gdb_parameter { }
 		set param_get_unlimited None
 		set param_set_minus_one ""
 	    }
-	    "print elements" {
+	    "print elements" -
+	    "print characters" {
 		set param_get_zero None
 		set param_get_minus_one None
 		set param_get_none None
@@ -398,6 +400,15 @@ proc_with_prefix test_gdb_parameter { }
 
 	gdb_test "python print(gdb.parameter('$param'))" \
 	    $param_get_unlimited "test value of 'unlimited'"
+
+	if {$param == "print characters"} {
+	    gdb_test_no_output \
+		"python gdb.set_parameter('$param', 'elements')" \
+		"test set to 'elements'"
+
+	    gdb_test "python print(gdb.parameter('$param'))" \
+		elements "test value of 'elements'"
+	}
     }
 
     clean_restart

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

* Re: [PATCH v8 4/6] GDB: Allow arbitrary keywords in integer set commands
  2023-01-19 21:17     ` Maciej W. Rozycki
  2023-01-19 21:18       ` [COMMITTED PATCH v9 " Maciej W. Rozycki
@ 2023-01-20 17:16       ` Simon Marchi
  1 sibling, 0 replies; 32+ messages in thread
From: Simon Marchi @ 2023-01-20 17:16 UTC (permalink / raw)
  To: Maciej W. Rozycki; +Cc: gdb-patches, Andrew Burgess, Tom Tromey, Simon Sobisch


>  It is used by `parse_option' however, where no setting is involved, so 
> it's not clear to me how to do that.  I could make an artificial setting I 
> suppose, but I feel like it's kind of creating a problem to fit the 
> solution.  Let me know if I've missed something.

Ah, I probably thought that parse_option had a setting object, nevermind
then.

>>> -    var_zuinteger_unlimited,
>>>      /* Enumerated type.  Can only have one of the specified values.
>>>         *VAR is a char pointer to the name of the element that we
>>>         find.  */
>>>      var_enum
>>>    };
>>>  
>>> +/* A structure describing an extra literal accepted and shown in place
>>> +   of a number.  */
>>> +struct literal_def
>>> +  {
>>> +    /* The literal to define, e.g. "unlimited".  */
>>> +    const char *literal;
>>> +    /* The number to substitute internally for LITERAL or VAL;
>>> +       the use of this number is not allowed (unless the same as VAL).  */
>>> +    LONGEST use;
>>> +    /* An optional number accepted that stands for the literal.  */
>>> +    gdb::optional<LONGEST> val;
>>
>> Could you please add an empty line between each field?  I find that
>> makes it easier to read.
> 
>  Done (`enum var_types' above seems inconsistent about it).

Yeah there are some instances of that, seems like an old style.

Simon

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

* Re: [COMMITTED PATCH v9 5/6] GDB: Add a character string limiting option
  2023-01-19 21:19       ` [COMMITTED PATCH v9 " Maciej W. Rozycki
@ 2023-01-21  9:57         ` Simon Sobisch
  2023-01-21 18:45           ` Simon Marchi
  0 siblings, 1 reply; 32+ messages in thread
From: Simon Sobisch @ 2023-01-21  9:57 UTC (permalink / raw)
  To: Maciej W. Rozycki, Simon Marchi; +Cc: gdb-patches, Andrew Burgess, Tom Tromey

Could this patchset be merged into the GDB 13 branch, please?

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

* Re: [COMMITTED PATCH v9 5/6] GDB: Add a character string limiting option
  2023-01-21  9:57         ` Simon Sobisch
@ 2023-01-21 18:45           ` Simon Marchi
  2023-01-21 19:29             ` Simon Sobisch
  0 siblings, 1 reply; 32+ messages in thread
From: Simon Marchi @ 2023-01-21 18:45 UTC (permalink / raw)
  To: Simon Sobisch, Maciej W. Rozycki; +Cc: gdb-patches, Andrew Burgess, Tom Tromey



On 1/21/23 04:57, Simon Sobisch wrote:
> Could this patchset be merged into the GDB 13 branch, please?

Probably not, it's a pretty invasive patch series to merge in the
pre-release branch.  Normally it's just fixes.

Simon

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

* Re: [COMMITTED PATCH v9 5/6] GDB: Add a character string limiting option
  2023-01-21 18:45           ` Simon Marchi
@ 2023-01-21 19:29             ` Simon Sobisch
  2023-02-07  5:17               ` Joel Brobecker
  0 siblings, 1 reply; 32+ messages in thread
From: Simon Sobisch @ 2023-01-21 19:29 UTC (permalink / raw)
  To: Simon Marchi, Maciej W. Rozycki; +Cc: gdb-patches, Andrew Burgess, Tom Tromey


Am 21.01.2023 um 19:45 schrieb Simon Marchi:
>
>
> On 1/21/23 04:57, Simon Sobisch wrote:
>> Could this patchset be merged into the GDB 13 branch, please?
>
> Probably not, it's a pretty invasive patch series to merge in the
> pre-release branch.  Normally it's just fixes.
>
> Simon

Thanks for the note, just to recheck that I'm understanding this
correct: as soon as a version branch is created it is considered
"stable"? I've thought this only happens after the first release from
that branch.

Simon

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

* Re: [COMMITTED PATCH v9 5/6] GDB: Add a character string limiting option
  2023-01-21 19:29             ` Simon Sobisch
@ 2023-02-07  5:17               ` Joel Brobecker
  0 siblings, 0 replies; 32+ messages in thread
From: Joel Brobecker @ 2023-02-07  5:17 UTC (permalink / raw)
  To: Simon Sobisch via Gdb-patches
  Cc: Simon Marchi, Maciej W. Rozycki, Andrew Burgess, Tom Tromey,
	Joel Brobecker

Hello Simon,

On Sat, Jan 21, 2023 at 08:29:07PM +0100, Simon Sobisch via Gdb-patches wrote:
> 
> Am 21.01.2023 um 19:45 schrieb Simon Marchi:
> > 
> > 
> > On 1/21/23 04:57, Simon Sobisch wrote:
> > > Could this patchset be merged into the GDB 13 branch, please?
> > 
> > Probably not, it's a pretty invasive patch series to merge in the
> > pre-release branch.  Normally it's just fixes.
> > 
> > Simon
> 
> Thanks for the note, just to recheck that I'm understanding this
> correct: as soon as a version branch is created it is considered
> "stable"? I've thought this only happens after the first release from
> that branch.

There is always a judgement call for each change we look at,
but essentially, you can indeed consider that once a branch
has been cut, we want that branch to be stable. That's basically
the objective, so that allow us to produce a first release of
good quality.

-- 
Joel

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

end of thread, other threads:[~2023-02-07  5:17 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-24 11:21 [PATCH v8 0/6] gdb: split array and string limiting options Maciej W. Rozycki
2022-11-24 11:21 ` [PATCH v8 1/6] GDB: Fix documentation for `theclass' parameters in cli-decode.c Maciej W. Rozycki
2023-01-12 18:36   ` Simon Marchi
2023-01-18 21:56     ` Maciej W. Rozycki
2022-11-24 11:22 ` [PATCH v8 2/6] GDB: Add missing inline documentation for `add_setshow_cmd_full' Maciej W. Rozycki
2023-01-12 18:40   ` Simon Marchi
2023-01-18 23:24     ` [COMMITTED PATCH v9 2.0/6] " Maciej W. Rozycki
2023-01-18 23:24       ` [COMMITTED PATCH v9 2.1/6] GDB: Correct inline documentation for `add_setshow_cmd_full_erased' Maciej W. Rozycki
2023-01-18 23:24       ` [COMMITTED PATCH v9 2.2/6] GDB: Add missing inline documentation for `add_setshow_cmd_full' Maciej W. Rozycki
2022-11-24 11:22 ` [PATCH v8 3/6] GDB: Add references to erased args in cli-decode.c Maciej W. Rozycki
2023-01-12 18:46   ` Simon Marchi
2023-01-18 23:41     ` Maciej W. Rozycki
2022-11-24 11:22 ` [PATCH v8 4/6] GDB: Allow arbitrary keywords in integer set commands Maciej W. Rozycki
2022-11-24 11:57   ` Eli Zaretskii
2023-01-12 20:48   ` Simon Marchi
2023-01-19 21:17     ` Maciej W. Rozycki
2023-01-19 21:18       ` [COMMITTED PATCH v9 " Maciej W. Rozycki
2023-01-20 17:16       ` [PATCH v8 " Simon Marchi
2022-11-24 11:22 ` [PATCH v8 5/6] GDB: Add a character string limiting option Maciej W. Rozycki
2023-01-16 19:35   ` Simon Marchi
2023-01-19 21:17     ` Maciej W. Rozycki
2023-01-19 21:19       ` [COMMITTED PATCH v9 " Maciej W. Rozycki
2023-01-21  9:57         ` Simon Sobisch
2023-01-21 18:45           ` Simon Marchi
2023-01-21 19:29             ` Simon Sobisch
2023-02-07  5:17               ` Joel Brobecker
2022-11-24 11:23 ` [PATCH v8 6/6] GDB/testsuite: Expand for character string limiting options Maciej W. Rozycki
2023-01-16 21:07   ` Simon Marchi
2023-01-19 21:18     ` Maciej W. Rozycki
2023-01-19 21:20       ` [COMMITTED PATCH v9 " Maciej W. Rozycki
2022-12-08 12:05 ` [PING][PATCH v8 0/6] gdb: split array and " Maciej W. Rozycki
2023-01-09 12:27 ` [PING^2][PATCH " Maciej W. Rozycki

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