[v7,2/4] GDB: Allow arbitrary keywords in integer set commands

Message ID alpine.DEB.2.20.2210290024310.19931@tpp.orcam.me.uk
State Superseded
Headers
Series gdb: split array and string limiting options |

Commit Message

Maciej W. Rozycki Oct. 29, 2022, 1:53 p.m. UTC
  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.
---
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                      |  302 +++++++++++++++++++++++-----
 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                             |  104 +++++++--
 gdb/doc/python.texi                       |   13 -
 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, 1106 insertions(+), 609 deletions(-)

gdb-setshow-cmd-extra-literals.diff
  

Comments

Eli Zaretskii Oct. 29, 2022, 3:07 p.m. UTC | #1
> Date: Sat, 29 Oct 2022 14:53:29 +0100 (BST)
> From: "Maciej W. Rozycki" <macro@embecosm.com>
> Cc: Simon Sobisch <simonsobisch@web.de>, Tom Tromey <tom@tromey.com>
> 
> New change in v4.
> ---
>  gdb/cli/cli-cmds.c                        |   59 +++--
>  gdb/cli/cli-decode.c                      |  302 +++++++++++++++++++++++-----
>  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                             |  104 +++++++--
>  gdb/doc/python.texi                       |   13 -
>  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, 1106 insertions(+), 609 deletions(-)

OK for the documentation part, thanks.
  
Simon Marchi Oct. 31, 2022, 6:48 p.m. UTC | #2
On 10/29/22 09:53, 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".

We don't have to restrict ourselves to a single letter.  By the end of
reading the commit message, I had already forgotten what the `p` stood
for.  Ideas:

 - var_non_negative_integer
 - var_zero_or_positive_integer
 - some better suggestion

On the other hand, is there any reason why "pintegers" couldn't be
stored as var_uinteger, with the proper literal_def?

Simon
  
Simon Marchi Oct. 31, 2022, 7:22 p.m. UTC | #3
On 10/31/22 14:48, Simon Marchi via Gdb-patches wrote:
> 
> 
> On 10/29/22 09:53, 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".
> 
> We don't have to restrict ourselves to a single letter.  By the end of
> reading the commit message, I had already forgotten what the `p` stood
> for.  Ideas:
> 
>  - var_non_negative_integer
>  - var_zero_or_positive_integer
>  - some better suggestion
> 
> On the other hand, is there any reason why "pintegers" couldn't be
> stored as var_uinteger, with the proper literal_def?
> 
> Simon

I forgot: I didn't have significant comments on the code itself.  Maybe
the only strange thing is the fact that you pass the list of extra
literals in the erased_args structure.  I don't think it should be
there, it's not something that need to be type-erased (cast to void).

Simon
  
Maciej W. Rozycki Nov. 24, 2022, 11:38 a.m. UTC | #4
On Mon, 31 Oct 2022, Simon Marchi wrote:

> >> 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".
> > 
> > We don't have to restrict ourselves to a single letter.  By the end of
> > reading the commit message, I had already forgotten what the `p` stood
> > for.  Ideas:
> > 
> >  - var_non_negative_integer
> >  - var_zero_or_positive_integer
> >  - some better suggestion

 After the switch to C++ our code has already become over-indented and 
overly line-wrapped and therefore hard to read.  Making identifiers longer 
than absolutely necessary only makes things worse in my opinion.

> > On the other hand, is there any reason why "pintegers" couldn't be
> > stored as var_uinteger, with the proper literal_def?

 With `var_uinteger' there's no way to prevent values in the range of 
(INT_MAX; UINT_MAX] from being used.  I think implementing an explicit way 
to exclude this range via a special `literal_def' entry would be an 
overkill.

> I forgot: I didn't have significant comments on the code itself.  Maybe
> the only strange thing is the fact that you pass the list of extra
> literals in the erased_args structure.  I don't think it should be
> there, it's not something that need to be type-erased (cast to void).

 I have adjusted code accordingly then.  Thank you for your review.

  Maciej
  

Patch

Index: src/gdb/cli/cli-cmds.c
===================================================================
--- src.orig/gdb/cli/cli-cmds.c
+++ src/gdb/cli/cli-cmds.c
@@ -2200,22 +2200,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;
@@ -2237,17 +2255,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:
@@ -2317,13 +2324,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
@@ -580,11 +580,51 @@  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.  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.  */
+
 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 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,
@@ -595,18 +635,44 @@  add_setshow_cmd_full (const char *name,
 		      struct cmd_list_element **show_list)
 {
   auto erased_args
-    = setting::erase_args (var_type, var,
+    = setting::erase_args (var_type, var, extra_literals,
 			   set_setting_func, get_setting_func);
+  auto cmds = 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);
 
-  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
@@ -975,25 +1041,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).  CLASS is as in
    add_cmd.  VAR is address of the variable which will contain the
@@ -1002,6 +1049,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,
@@ -1012,12 +1108,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;
 }
 
@@ -1035,12 +1129,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;
 }
 
@@ -1051,7 +1187,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,
@@ -1061,12 +1197,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;
 }
 
@@ -1075,6 +1208,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,
@@ -1084,13 +1218,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;
 }
 
@@ -1109,7 +1293,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);
@@ -1128,12 +1312,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,
@@ -1147,13 +1341,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;
 }
 
@@ -1171,13 +1363,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;
 }
 
@@ -1196,7 +1386,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);
@@ -1215,7 +1405,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));
@@ -232,6 +236,7 @@  struct setting
   struct erased_args
   {
     void *var;
+    const void *extra_literals;
     erased_func setter;
     erased_func getter;
   };
@@ -239,6 +244,7 @@  struct setting
   template<typename T>
   static erased_args erase_args (var_types var_type,
 				 T *var,
+				 const literal_def *extra_literals,
 				 typename setting_func_types<T>::set set_setting_func,
 				 typename setting_func_types<T>::get get_setting_func)
   {
@@ -253,6 +259,7 @@  struct setting
 
     return {
 	var,
+	extra_literals,
 	reinterpret_cast<erased_func> (set_setting_func),
 	reinterpret_cast<erased_func> (get_setting_func)
     };
@@ -264,6 +271,7 @@  struct setting
   setting (var_types var_type, const erased_args &args)
     : m_var_type (var_type),
       m_var (args.var),
+      m_extra_literals (args.extra_literals),
       m_getter (args.getter),
       m_setter (args.setter)
   {
@@ -294,6 +302,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 +368,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 +666,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 +767,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 +793,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
@@ -4599,15 +4599,17 @@  Python, true and false are represented u
 @findex gdb.PARAM_UINTEGER
 @item gdb.PARAM_UINTEGER
 The value is an unsigned integer.  The value of @code{None} should be
-interpreted to mean ``unlimited'', and the value of 0 is reserved and
-should not be used.
+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 @code{None} should be
-interpreted to mean ``unlimited'', and the value of 0 is reserved and
-should not be used.
+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
@@ -4651,7 +4653,8 @@  except that 0 is allowed and the value o
 @item gdb.PARAM_ZUINTEGER_UNLIMITED
 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'', except that 0 is allowed, and the value cannot be negative.
+``unlimited'' (literal @code{'unlimited'} can also be used to set that
+value), except that 0 is allowed, and the value cannot be negative.
 
 In GDB version 12 and earlier, a parameter of this type when read would
 return -1 rather than @code{None} for the setting of ``unlimited''.
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);
 
@@ -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,35 +504,37 @@  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:
-      return gdb_py_object_from_longest (var.get<int> ()).release ();
-
-    case var_zuinteger_unlimited:
-      {
-	int val = var.get<int> ();
-
-	if (val == -1)
-	  Py_RETURN_NONE;
-	return gdb_py_object_from_longest (val).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)
+		  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
@@ -429,6 +429,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 ""
@@ -438,6 +439,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 ""
@@ -447,6 +449,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
@@ -456,6 +459,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
@@ -465,6 +469,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 ""
 		set param_set_minus_five $param_range_error
 		set param_set_none ""
@@ -521,6 +526,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."),