{
   switch (var.type ())
     {
+    case var_uinteger:
     case var_integer:
-      if (var.get<int> () == INT_MAX)
-       return value_from_longest (builtin_type (gdbarch)->builtin_int,
-                                  0);
-      else
-       return value_from_longest (builtin_type (gdbarch)->builtin_int,
-                                  var.get<int> ());
-    case var_zinteger:
-      return value_from_longest (builtin_type (gdbarch)->builtin_int,
-                                var.get<int> ());
+    case var_pinteger:
+      {
+       LONGEST value
+         = (var.type () == var_uinteger
+            ? static_cast<LONGEST> (var.get<unsigned int> ())
+            : static_cast<LONGEST> (var.get<int> ()));
+
+       if (var.extra_literals () != nullptr)
+         for (const literal_def *l = var.extra_literals ();
+              l->literal != nullptr;
+              l++)
+           if (value == l->use)
+             {
+               if (l->val.has_value ())
+                 value = *l->val;
+               else
+                 return allocate_value (builtin_type (gdbarch)->builtin_void);
+               break;
+             }
+
+       if (var.type () == var_uinteger)
+         return
+           value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int,
+                                static_cast<unsigned int> (value));
+       else
+         return
+           value_from_longest (builtin_type (gdbarch)->builtin_int,
+                               static_cast<int> (value));
+      }
     case var_boolean:
       return value_from_longest (builtin_type (gdbarch)->builtin_int,
                                 var.get<bool> () ? 1 : 0);
-    case var_zuinteger_unlimited:
-      return value_from_longest (builtin_type (gdbarch)->builtin_int,
-                                var.get<int> ());
     case var_auto_boolean:
       {
        int val;
        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:
 {
   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);
 
 
    TYPE is set_cmd or show_cmd.
    THECLASS is as in add_cmd.
    VAR_TYPE is the kind of thing we are setting.
+   EXTRA_LITERALS if non-NULL define extra literals to be accepted in lieu of
+   a number for integer variables.
    ARGS is a pre-validated type-erased reference to the variable being
    controlled by this command.
    DOC is the documentation string.  */
                     enum cmd_types type,
                     enum command_class theclass,
                     var_types var_type,
+                    const literal_def *extra_literals,
                     const setting::erased_args &arg,
                     const char *doc,
                     struct cmd_list_element **list)
 
   gdb_assert (type == set_cmd || type == show_cmd);
   c->type = type;
-  c->var.emplace (var_type, arg);
+  c->var.emplace (var_type, extra_literals, arg);
 
   /* This needs to be something besides NULL so that this isn't
      treated as a help class.  */
 
 /* Add element named NAME to both command lists SET_LIST and SHOW_LIST.
    THECLASS is as in add_cmd.  VAR_TYPE is the kind of thing we are
-   setting.  ARGS is a pre-validated type-erased reference to the
-   variable being controlled by this command.  SET_FUNC and SHOW_FUNC
-   are the callback functions (if non-NULL).  SET_DOC, SHOW_DOC and
-   HELP_DOC are the documentation strings.
+   setting.  EXTRA_LITERALS if non-NULL define extra literals to be
+   accepted in lieu of a number for integer variables.  ARGS is a
+   pre-validated type-erased reference to the variable being controlled
+   by this command.  SET_FUNC and SHOW_FUNC are the callback functions
+   (if non-NULL).  SET_DOC, SHOW_DOC and HELP_DOC are the documentation
+   strings.
 
    Return the newly created set and show commands.  */
 
 add_setshow_cmd_full_erased (const char *name,
                             enum command_class theclass,
                             var_types var_type,
+                            const literal_def *extra_literals,
                             const setting::erased_args &args,
                             const char *set_doc, const char *show_doc,
                             const char *help_doc,
       full_set_doc = make_unique_xstrdup (set_doc);
       full_show_doc = make_unique_xstrdup (show_doc);
     }
-  set = add_set_or_show_cmd (name, set_cmd, theclass, var_type, args,
+  set = add_set_or_show_cmd (name, set_cmd, theclass, var_type,
+                            extra_literals, args,
                             full_set_doc.release (), set_list);
   set->doc_allocated = 1;
 
   if (set_func != NULL)
     set->func = set_func;
 
-  show = add_set_or_show_cmd (name, show_cmd, theclass, var_type, args,
+  show = add_set_or_show_cmd (name, show_cmd, theclass, var_type,
+                             extra_literals, args,
                              full_show_doc.release (), show_list);
   show->doc_allocated = 1;
   show->show_value_func = show_func;
   return {set, show};
 }
 
+/* Completes on integer commands that support extra literals.  */
+
+static void
+integer_literals_completer (struct cmd_list_element *c,
+                           completion_tracker &tracker,
+                           const char *text, const char *word)
+{
+  const literal_def *extra_literals = c->var->extra_literals ();
+
+  if (*text == '\0')
+    {
+      tracker.add_completion (make_unique_xstrdup ("NUMBER"));
+      for (const literal_def *l = extra_literals;
+          l->literal != nullptr;
+          l++)
+       tracker.add_completion (make_unique_xstrdup (l->literal));
+    }
+  else
+    for (const literal_def *l = extra_literals;
+        l->literal != nullptr;
+        l++)
+      if (startswith (l->literal, text))
+       tracker.add_completion (make_unique_xstrdup (l->literal));
+}
+
 /* Add element named NAME to both command lists SET_LIST and SHOW_LIST.
    THECLASS is as in add_cmd.  VAR_TYPE is the kind of thing we are
    setting.  VAR is address of the variable being controlled by this
-   command.  If nullptr is given as VAR, then both SET_SETTING_FUNC and
-   GET_SETTING_FUNC must be provided.  SET_SETTING_FUNC and GET_SETTING_FUNC
-   are callbacks used to access and modify the underlying property,
-   whatever its storage is.  SET_FUNC and SHOW_FUNC are the callback
-   functions (if non-NULL).  SET_DOC, SHOW_DOC and HELP_DOC are the
+   command.  EXTRA_LITERALS if non-NULL define extra literals to be
+   accepted in lieu of a number for integer variables.  If nullptr is
+   given as VAR, then both SET_SETTING_FUNC and GET_SETTING_FUNC must
+   be provided.  SET_SETTING_FUNC and GET_SETTING_FUNC are callbacks
+   used to access and modify the underlying property, whatever its
+   storage is.  SET_FUNC and SHOW_FUNC are the callback functions
+   (if non-NULL).  SET_DOC, SHOW_DOC and HELP_DOC are the
    documentation strings.
 
    Return the newly created set and show commands.  */
 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,
   auto erased_args
     = setting::erase_args (var_type, var,
                           set_setting_func, get_setting_func);
+  auto cmds = add_setshow_cmd_full_erased (name,
+                                          theclass,
+                                          var_type, extra_literals,
+                                          erased_args,
+                                          set_doc, show_doc,
+                                          help_doc,
+                                          set_func,
+                                          show_func,
+                                          set_list,
+                                          show_list);
+
+  if (extra_literals != nullptr)
+    set_cmd_completer (cmds.set, integer_literals_completer);
 
-  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);
+  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
   return cmds;
 }
 
-/* Completes on literal "unlimited".  Used by integer commands that
-   support a special "unlimited" value.  */
-
-static void
-integer_unlimited_completer (struct cmd_list_element *ignore,
-                            completion_tracker &tracker,
-                            const char *text, const char *word)
-{
-  static const char * const keywords[] =
-    {
-      "unlimited",
-      NULL,
-    };
-
-  if (*text == '\0')
-    tracker.add_completion (make_unique_xstrdup ("NUMBER"));
-  complete_on_enum (tracker, keywords, text, word);
-}
-
 /* Add element named NAME to both the set and show command LISTs (the
    list for set/show or some sublist thereof).  THECLASS is as in
    add_cmd.  VAR is address of the variable which will contain the
    value.  SET_DOC and SHOW_DOC are the documentation strings.  This
    function is only used in Python API.  Please don't use it elsewhere.  */
 
+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,
 {
   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;
 }
 
                         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;
 }
 
 
 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,
 {
   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;
 }
 
 
 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,
                          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;
+}
+
+/* Accept `unlimited' or 0, translated internally to UINT_MAX.  */
+const literal_def uinteger_unlimited_literals[] =
+  {
+    { "unlimited", UINT_MAX, 0 },
+    { nullptr }
+  };
 
-  set_cmd_completer (cmds.set, integer_unlimited_completer);
+/* 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;
 }
 
                          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);
                          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,
                                     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;
 }
 
                                     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;
 }
 
                           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);
                           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,
 
   /* 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.  */
        return option_def_and_value {*match, match_ctx, val};
       }
     case var_uinteger:
-    case var_zuinteger_unlimited:
+    case var_integer:
+    case var_pinteger:
       {
-       if (completion != nullptr)
+       if (completion != nullptr && match->extra_literals != nullptr)
          {
+           /* Convenience to let the user know what the option can
+              accept.  Make sure there's no common prefix between
+              "NUMBER" and all the strings when adding new ones,
+              so that readline doesn't do a partial match.  */
            if (**args == '\0')
              {
-               /* Convenience to let the user know what the option
-                  can accept.  Note there's no common prefix between
-                  the strings on purpose, so that readline doesn't do
-                  a partial match.  */
                completion->tracker.add_completion
                  (make_unique_xstrdup ("NUMBER"));
-               completion->tracker.add_completion
-                 (make_unique_xstrdup ("unlimited"));
+               for (const literal_def *l = match->extra_literals;
+                    l->literal != nullptr;
+                    l++)
+                 completion->tracker.add_completion
+                   (make_unique_xstrdup (l->literal));
                return {};
              }
-           else if (startswith ("unlimited", *args))
+           else
              {
-               completion->tracker.add_completion
-                 (make_unique_xstrdup ("unlimited"));
-               return {};
+               bool completions = false;
+               for (const literal_def *l = match->extra_literals;
+                    l->literal != nullptr;
+                    l++)
+                 if (startswith (l->literal, *args))
+                   {
+                     completion->tracker.add_completion
+                       (make_unique_xstrdup (l->literal));
+                     completions = true;
+                   }
+               if (completions)
+                 return {};
              }
          }
 
-       if (match->type == var_zuinteger_unlimited)
-         {
-           option_value val;
-           val.integer = parse_cli_var_zuinteger_unlimited (args, false);
-           return option_def_and_value {*match, match_ctx, val};
-         }
+       LONGEST v = parse_cli_var_integer (match->type,
+                                          match->extra_literals,
+                                          args, false);
+       option_value val;
+       if (match->type == var_uinteger)
+         val.uinteger = v;
        else
-         {
-           option_value val;
-           val.uinteger = parse_cli_var_uinteger (match->type, args, false);
-           return option_def_and_value {*match, match_ctx, val};
-         }
+         val.integer = v;
+       return option_def_and_value {*match, match_ctx, val};
       }
     case var_enum:
       {
       *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;
     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 = "";
        {
          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_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_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_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)
        {
 
      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_),
   /* 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;
                      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_)
 {
   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.  */
                   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_)
                     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_)
 
     }
 }
 
-/* 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);
 
 
   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;
 }
       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:
       {
        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"));
     }
          }
          break;
        case var_uinteger:
-       case var_zuinteger:
          {
            char s[64];
 
          }
          break;
        case var_integer:
-       case var_zinteger:
-       case var_zuinteger_unlimited:
+       case var_pinteger:
          {
            char s[64];
 
        }
       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:
-      {
-       const int value = var.get<int> ();
-
-       if (var.type () == var_integer
-           && value == INT_MAX)
-         stb.puts ("unlimited");
-       else
-         stb.printf ("%d", value);
-      }
-      break;
-    case var_zuinteger_unlimited:
+    case var_pinteger:
       {
-       const int value = var.get<int> ();
-       if (value == -1)
-         stb.puts ("unlimited");
-       else
-         stb.printf ("%d", value);
+       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.extra_literals () != nullptr)
+         for (const literal_def *l = var.extra_literals ();
+              l->literal != nullptr;
+              l++)
+           if (value == l->use)
+             {
+               stb.puts (l->literal);
+               printed = true;
+               break;
+             }
+       if (!printed)
+         {
+           if (var.type () == var_uinteger)
+             stb.printf ("%u", static_cast<unsigned int> (value));
+           else
+             stb.printf ("%d", static_cast<int> (value));
+         }
       }
       break;
     default:
 
    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
 
        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.  */
     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
 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.  */
 
      Type T must match the var type VAR_TYPE (see VAR_TYPE_USES).  */
   template<typename T>
-  setting (var_types var_type, T *var)
-    : m_var_type (var_type), m_var (var)
+  setting (var_types var_type, T *var,
+          const literal_def *extra_literals = nullptr)
+    : m_var_type (var_type), m_var (var), m_extra_literals (extra_literals)
   {
     gdb_assert (var != nullptr);
     gdb_assert (var_type_uses<T> (var_type));
     };
   }
 
-  /* Create a setting backed by pre-validated type-erased args.
-     ERASED_VAR's fields' real types must match the var type VAR_TYPE
-     (see VAR_TYPE_USES).  */
-  setting (var_types var_type, const erased_args &args)
+  /* Create a setting backed by pre-validated type-erased args and using
+     EXTRA_LITERALS.  ERASED_VAR's fields' real types must match the var
+     type VAR_TYPE (see VAR_TYPE_USES).  */
+  setting (var_types var_type, const literal_def *extra_literals,
+          const erased_args &args)
     : m_var_type (var_type),
       m_var (args.var),
+      m_extra_literals (extra_literals),
       m_getter (args.getter),
       m_setter (args.setter)
   {
   var_types type () const
   { return m_var_type; }
 
+  /* Access any extra literals accepted.  */
+  const literal_def *extra_literals () const
+  { return m_extra_literals; }
+
   /* Return the current value.
 
      The template parameter T is the type of the variable used to store the
      non-nullptr.  */
   void *m_var = nullptr;
 
+  /* Any extra literals accepted.  */
+  const literal_def *m_extra_literals = nullptr;
+
   /* Pointer to a user provided getter.  */
   erased_func m_getter = nullptr;
 
    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,
    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 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,
    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,
 
 @findex PARAM_UINTEGER
 @findex gdb.PARAM_UINTEGER
 @item gdb.PARAM_UINTEGER
-The value is an unsigned integer.  The value of 0 should be
-interpreted to mean ``unlimited''.
+The value is an unsigned integer.  The value of @code{None} should be
+interpreted to mean ``unlimited'' (literal @code{'unlimited'} can also
+be used to set that value), and the value of 0 is reserved and should
+not be used.
 
 @findex PARAM_INTEGER
 @findex gdb.PARAM_INTEGER
 @item gdb.PARAM_INTEGER
-The value is a signed integer.  The value of 0 should be interpreted
-to mean ``unlimited''.
+The value is a signed integer.  The value of @code{None} should be
+interpreted to mean ``unlimited'' (literal @code{'unlimited'} can also
+be used to set that value), and the value of 0 is reserved and should
+not be used.
 
 @findex PARAM_STRING
 @findex gdb.PARAM_STRING
 @findex PARAM_ZINTEGER
 @findex gdb.PARAM_ZINTEGER
 @item gdb.PARAM_ZINTEGER
-The value is an integer.  This is like @code{PARAM_INTEGER}, except 0
-is interpreted as itself.
+The value is a signed integer.  This is like @code{PARAM_INTEGER},
+except that 0 is allowed and the value of @code{None} is not supported.
 
 @findex PARAM_ZUINTEGER
 @findex gdb.PARAM_ZUINTEGER
 @item gdb.PARAM_ZUINTEGER
-The value is an unsigned integer.  This is like @code{PARAM_INTEGER},
-except 0 is interpreted as itself, and the value cannot be negative.
+The value is an unsigned integer.  This is like @code{PARAM_UINTEGER},
+except that 0 is allowed and the value of @code{None} is not supported.
 
 @findex PARAM_ZUINTEGER_UNLIMITED
 @findex gdb.PARAM_ZUINTEGER_UNLIMITED
 @item gdb.PARAM_ZUINTEGER_UNLIMITED
-The value is a signed integer.  This is like @code{PARAM_ZUINTEGER},
-except the special value -1 should be interpreted to mean
-``unlimited''.  Other negative values are not allowed.
+The value is a signed integer.  This is like @code{PARAM_INTEGER}
+including that the value of @code{None} should be interpreted to mean
+``unlimited'' (literal @code{'unlimited'} can also be used to set that
+value), except that 0 is allowed, and the value cannot be negative,
+except the special value -1 is returned for the setting of ``unlimited''.
 
 @findex PARAM_ENUM
 @findex gdb.PARAM_ENUM
 
   /* 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;
   SCM containing_scm;
 };
 
+/* Guile parameter types as in PARAMETER_TYPES later on.  */
+
+enum param_types
+{
+  param_boolean,
+  param_auto_boolean,
+  param_zinteger,
+  param_uinteger,
+  param_zuinteger,
+  param_zuinteger_unlimited,
+  param_string,
+  param_string_noescape,
+  param_optional_filename,
+  param_filename,
+  param_enum,
+};
+
+/* Translation from Guile parameters to GDB variable types.  Keep in the
+   same order as PARAM_TYPES due to C++'s lack of designated initializers.  */
+
+static const struct
+{
+  /* The type of the parameter.  */
+  enum var_types type;
+
+  /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
+  const literal_def *extra_literals;
+}
+param_to_var[] =
+{
+  { var_boolean },
+  { var_auto_boolean },
+  { var_integer },
+  { var_uinteger, uinteger_unlimited_literals },
+  { var_uinteger },
+  { var_pinteger, pinteger_unlimited_literals },
+  { var_string },
+  { var_string_noescape },
+  { var_optional_filename },
+  { var_filename },
+  { var_enum }
+};
+
 /* Wraps a setting around an existing param_smob.  This abstraction
    is used to manipulate the value in S->VALUE in a type safe manner using
    the setting interface.  */
 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");
 }
 static SCM show_doc_keyword;
 static SCM initial_value_keyword;
 static SCM auto_keyword;
-static SCM unlimited_keyword;
 
 static int pascm_is_valid (param_smob *);
-static const char *pascm_param_type_name (enum var_types type);
+static const char *pascm_param_type_name (enum param_types type);
 static SCM pascm_param_value (const setting &var, int arg_pos,
                              const char *func_name);
 \f
   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);
    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,
 
     case var_uinteger:
       commands = add_setshow_uinteger_cmd (cmd_name, cmd_class,
-                                          &self->value.uintval, set_doc,
+                                          &self->value.uintval,
+                                          extra_literals, 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,
-                                          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:
 
 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
 };
 /* 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;
 
 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:
          return auto_keyword;
       }
 
-    case var_zuinteger_unlimited:
-      if (var.get<int> () == -1)
-       return unlimited_keyword;
-      gdb_assert (var.get<int> () >= 0);
-      /* Fall through.  */
-    case var_zinteger:
-      return scm_from_int (var.get<int> ());
-
     case var_uinteger:
-      if (var.get<unsigned int> ()== UINT_MAX)
-       return unlimited_keyword;
-      /* Fall through.  */
-    case var_zuinteger:
-      return scm_from_uint (var.get<unsigned int> ());
+    case var_integer:
+    case var_pinteger:
+      {
+       LONGEST value
+         = (var.type () == var_uinteger
+            ? static_cast<LONGEST> (var.get<unsigned int> ())
+            : static_cast<LONGEST> (var.get<int> ()));
+
+       if (var.extra_literals () != nullptr)
+         for (const literal_def *l = var.extra_literals ();
+              l->literal != nullptr;
+              l++)
+           if (value == l->use)
+             return scm_from_latin1_keyword (l->literal);
+       if (var.type () == var_pinteger)
+         gdb_assert (value >= 0);
+
+       if (var.type () == var_uinteger)
+         return scm_from_uint (static_cast<unsigned int> (value));
+       else
+         return scm_from_int (static_cast<int> (value));
+      }
 
     default:
       break;
        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 ());
+
+           val = (var_type == var_uinteger
+                  ? static_cast<LONGEST> (scm_to_uint (value))
+                  : static_cast<LONGEST> (scm_to_int (value)));
+
+           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 (var.type () == var_uinteger && u == 0)
-           u = UINT_MAX;
-         var.set<unsigned int> (u);
-       }
-      else
-       {
-         int i = scm_to_int (value);
+       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_zuinteger_unlimited && i < -1)
-           {
-             gdbscm_out_of_range_error (func_name, arg_pos, value,
-                                        _("must be >= -1"));
-           }
-         var.set<int> (i);
-       }
-      break;
+       if (var_type == var_uinteger)
+         var.set<unsigned int> (static_cast<unsigned int> (val));
+       else
+         var.set<int> (static_cast<int> (val));
+
+       break;
+      }
 
     default:
       gdb_assert_not_reached ("bad parameter type");
   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;
                                 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"));
       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.  */
      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;
   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),
   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");
 }
 
    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
   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:
   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;
   {
     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);
   }
     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 */
   },
 
 #include "language.h"
 #include "arch-utils.h"
 
+/* Python parameter types as in PARM_CONSTANTS below.  */
+
+enum param_types
+{
+  param_boolean,
+  param_auto_boolean,
+  param_uinteger,
+  param_integer,
+  param_string,
+  param_string_noescape,
+  param_optional_filename,
+  param_filename,
+  param_zinteger,
+  param_zuinteger,
+  param_zuinteger_unlimited,
+  param_enum,
+}
+param_types;
+
+/* Translation from Python parameters to GDB variable types.  Keep in the
+   same order as PARAM_TYPES due to C++'s lack of designated initializers.  */
+
+static const struct
+{
+  /* The type of the parameter.  */
+  enum var_types type;
+
+  /* Extra literals, such as `unlimited', accepted in lieu of a number.  */
+  const literal_def *extra_literals;
+}
+param_to_var[] =
+{
+  { var_boolean },
+  { var_auto_boolean },
+  { var_uinteger, uinteger_unlimited_literals },
+  { var_integer, integer_unlimited_literals },
+  { var_string },
+  { var_string_noescape },
+  { var_optional_filename },
+  { var_filename },
+  { var_integer },
+  { var_uinteger },
+  { var_pinteger, pinteger_unlimited_literals },
+  { var_enum }
+};
+
 /* Parameter constants and their values.  */
 static struct {
   const char *name;
   int value;
 } parm_constants[] =
 {
-  { "PARAM_BOOLEAN", var_boolean }, /* ARI: var_boolean */
-  { "PARAM_AUTO_BOOLEAN", var_auto_boolean },
-  { "PARAM_UINTEGER", var_uinteger },
-  { "PARAM_INTEGER", var_integer },
-  { "PARAM_STRING", var_string },
-  { "PARAM_STRING_NOESCAPE", var_string_noescape },
-  { "PARAM_OPTIONAL_FILENAME", var_optional_filename },
-  { "PARAM_FILENAME", var_filename },
-  { "PARAM_ZINTEGER", var_zinteger },
-  { "PARAM_ZUINTEGER", var_zuinteger },
-  { "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited },
-  { "PARAM_ENUM", var_enum },
+  { "PARAM_BOOLEAN", param_boolean }, /* ARI: param_boolean */
+  { "PARAM_AUTO_BOOLEAN", param_auto_boolean },
+  { "PARAM_UINTEGER", param_uinteger },
+  { "PARAM_INTEGER", param_integer },
+  { "PARAM_STRING", param_string },
+  { "PARAM_STRING_NOESCAPE", param_string_noescape },
+  { "PARAM_OPTIONAL_FILENAME", param_optional_filename },
+  { "PARAM_FILENAME", param_filename },
+  { "PARAM_ZINTEGER", param_zinteger },
+  { "PARAM_ZUINTEGER", param_zuinteger },
+  { "PARAM_ZUINTEGER_UNLIMITED", param_zuinteger_unlimited },
+  { "PARAM_ENUM", param_enum },
   { NULL, 0 }
 };
 
   /* 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;
 
 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");
 }
        }
       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;
-
-       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))
+       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 (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;
-
-         case var_zuinteger_unlimited:
-           ok = (l >= -1 && l <= INT_MAX);
-           break;
-
-         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;
-
-         default:
-           gdb_assert_not_reached ("unknown var_ constant");
+           val = PyLong_AsLongLong (value);
+
+           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;
+             }
+
+
+           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 (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;
       }
 
 /* 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,
 {
   set_show_commands commands;
 
-  switch (parmclass)
+  switch (type)
     {
     case var_boolean:
       commands = add_setshow_boolean_cmd (cmd_name.get (), cmdclass,
 
     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,
                                           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];
   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))
       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))
 
   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);
 
          Py_RETURN_NONE;
       }
 
-    case var_integer:
-      if (var.get<int> () == INT_MAX)
-       Py_RETURN_NONE;
-      /* Fall through.  */
-    case var_zinteger:
-    case var_zuinteger_unlimited:
-      return gdb_py_object_from_longest (var.get<int> ()).release ();
-
     case var_uinteger:
+    case var_integer:
+    case var_pinteger:
       {
-       unsigned int val = var.get<unsigned int> ();
-
-       if (val == UINT_MAX)
-         Py_RETURN_NONE;
-       return gdb_py_object_from_ulongest (val).release ();
-      }
-
-    case var_zuinteger:
-      {
-       unsigned int val = var.get<unsigned int> ();
-       return gdb_py_object_from_ulongest (val).release ();
+       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 (strcmp (l->literal, "unlimited") == 0)
+                 {
+                   /* Compatibility hack for API brokenness.  */
+                   if (var.type () == var_pinteger
+                       && l->val.has_value ()
+                       && *l->val == -1)
+                     value = -1;
+                   else
+                     Py_RETURN_NONE;
+                 }
+               else if (l->val.has_value ())
+                 value = *l->val;
+               else
+                 return host_string_to_python_string (l->literal).release ();
+             }
+
+       if (var.type () == var_uinteger)
+         return
+           gdb_py_object_from_ulongest
+             (static_cast<unsigned int> (value)).release ();
+       else
+         return
+           gdb_py_object_from_longest
+             (static_cast<int> (value)).release ();
       }
     }
 
 
 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"
 
 # 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
 # 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"
     }
             && [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.
 
     # 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 --" \
 }
 
 # 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 " "" "" {
     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 {
        "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"
     }
 
     # 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"
        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 \
     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
 
     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.
 
     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" "" \
 
            \\(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 {
     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"
 
     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 {
             #: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"
            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
            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
            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"
 
                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 ""
                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 ""
                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
                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
                set param_get_minus_one -1
                set param_get_minus_five 1
                set param_get_none -1
+               set param_get_unlimited -1
                set param_set_minus_one ""
                set param_set_minus_five $param_range_error
                set param_set_none ""
 
        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"
     }
 }
 
 
   = 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.  */
   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."),
   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."),