diff mbox series

[03/25] qemu-option: warn for short-form boolean options

Message ID 20210118163113.780171-4-pbonzini@redhat.com (mailing list archive)
State New, archived
Headers show
Series qemu-option, keyval, vl: switch -object/-M/-accel to keyval parsing | expand

Commit Message

Paolo Bonzini Jan. 18, 2021, 4:30 p.m. UTC
Options such as "server" or "nowait", that are commonly found in -chardev,
are sugar for "server=on" and "wait=off".  This is quite surprising and
also does not have any notion of typing attached.  It is even possible to
do "-device e1000,noid" and get a device with "id=off".

Deprecate it and print a warning when it is encountered.  In general,
this short form for boolean options only seems to be in wide use for
-chardev and -spice.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 docs/system/deprecated.rst |  6 ++++++
 tests/test-qemu-opts.c     |  2 +-
 util/qemu-option.c         | 29 ++++++++++++++++++-----------
 3 files changed, 25 insertions(+), 12 deletions(-)

Comments

Markus Armbruster Jan. 19, 2021, 3:56 p.m. UTC | #1
Paolo Bonzini <pbonzini@redhat.com> writes:

> Options such as "server" or "nowait", that are commonly found in -chardev,
> are sugar for "server=on" and "wait=off".  This is quite surprising and
> also does not have any notion of typing attached.  It is even possible to
> do "-device e1000,noid" and get a device with "id=off".
>
> Deprecate it and print a warning when it is encountered.  In general,
> this short form for boolean options only seems to be in wide use for
> -chardev and -spice.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  docs/system/deprecated.rst |  6 ++++++
>  tests/test-qemu-opts.c     |  2 +-
>  util/qemu-option.c         | 29 ++++++++++++++++++-----------
>  3 files changed, 25 insertions(+), 12 deletions(-)
>
> diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst
> index e20bfcb17a..e71faefbe5 100644
> --- a/docs/system/deprecated.rst
> +++ b/docs/system/deprecated.rst
> @@ -127,6 +127,12 @@ Drives with interface types other than ``if=none`` are for onboard
>  devices.  It is possible to use drives the board doesn't pick up with
>  -device.  This usage is now deprecated.  Use ``if=none`` instead.
>  
> +Short-form boolean options (since 5.2)

6.0

> +''''''''''''''''''''''''''''''''''''''
> +
> +Boolean options such as ``share=on``/``share=off`` can be written
> +in short form as ``share`` and ``noshare``.  This is deprecated
> +and will cause a warning.
>  
>  QEMU Machine Protocol (QMP) commands
>  ------------------------------------
> diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c
> index 2aab831d10..8bbb17b1c7 100644
> --- a/tests/test-qemu-opts.c
> +++ b/tests/test-qemu-opts.c
> @@ -515,7 +515,7 @@ static void test_opts_parse(void)
>      error_free_or_abort(&err);
>      g_assert(!opts);
>  
> -    /* Implied value */
> +    /* Implied value (qemu_opts_parse warns but accepts it) */
>      opts = qemu_opts_parse(&opts_list_03, "an,noaus,noaus=",
>                             false, &error_abort);
>      g_assert_cmpuint(opts_count(opts), ==, 3);
> diff --git a/util/qemu-option.c b/util/qemu-option.c
> index 5f27d4369d..40564a12eb 100644
> --- a/util/qemu-option.c
> +++ b/util/qemu-option.c
> @@ -756,10 +756,12 @@ void qemu_opts_print(QemuOpts *opts, const char *separator)
>  
>  static const char *get_opt_name_value(const char *params,
>                                        const char *firstname,
> +                                      bool warn_on_flag,
>                                        bool *help_wanted,
>                                        char **name, char **value)
>  {
>      const char *p;
> +    const char *prefix = "";
>      size_t len;
>      bool is_help = false;
>  
> @@ -776,10 +778,15 @@ static const char *get_opt_name_value(const char *params,
>              if (strncmp(*name, "no", 2) == 0) {
>                  memmove(*name, *name + 2, strlen(*name + 2) + 1);
>                  *value = g_strdup("off");
> +                prefix = "no";
>              } else {
>                  *value = g_strdup("on");
>                  is_help = is_help_option(*name);
>              }
> +            if (!is_help && warn_on_flag) {
> +                warn_report("short-form boolean option '%s%s' deprecated", prefix, *name);
> +                error_printf("Please use %s=%s instead\n", *name, *value);
> +            }

If @warn_on_flag, we warn except for "help" and "?".  The exception
applies regardless of @help_wanted.  Shouldn't we except *only*
recognized help requests?

>          }
>      } else {
>          /* found "foo=bar,more" */
> @@ -801,14 +808,14 @@ static const char *get_opt_name_value(const char *params,
>  
>  static bool opts_do_parse(QemuOpts *opts, const char *params,
>                            const char *firstname, bool prepend,
> -                          bool *help_wanted, Error **errp)
> +                          bool warn_on_flag, bool *help_wanted, Error **errp)
>  {
>      char *option, *value;
>      const char *p;
>      QemuOpt *opt;
>  
>      for (p = params; *p;) {
> -        p = get_opt_name_value(p, firstname, help_wanted, &option, &value);
> +        p = get_opt_name_value(p, firstname, warn_on_flag, help_wanted, &option, &value);

Long line.  Break it before the three output arguments.

>          if (help_wanted && *help_wanted) {
>              g_free(option);
>              g_free(value);
> @@ -839,7 +846,7 @@ static char *opts_parse_id(const char *params)
>      char *name, *value;
>  
>      for (p = params; *p;) {
> -        p = get_opt_name_value(p, NULL, NULL, &name, &value);
> +        p = get_opt_name_value(p, NULL, false, NULL, &name, &value);
>          if (!strcmp(name, "id")) {
>              g_free(name);
>              return value;
> @@ -858,7 +865,7 @@ bool has_help_option(const char *params)
>      bool ret = false;
>  
>      for (p = params; *p;) {
> -        p = get_opt_name_value(p, NULL, &ret, &name, &value);
> +        p = get_opt_name_value(p, NULL, false, &ret, &name, &value);
>          g_free(name);
>          g_free(value);
>          if (ret) {
> @@ -878,12 +885,12 @@ bool has_help_option(const char *params)
>  bool qemu_opts_do_parse(QemuOpts *opts, const char *params,
>                         const char *firstname, Error **errp)
>  {
> -    return opts_do_parse(opts, params, firstname, false, NULL, errp);
> +    return opts_do_parse(opts, params, firstname, false, false, NULL, errp);
>  }
>  
>  static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
>                              bool permit_abbrev, bool defaults,
> -                            bool *help_wanted, Error **errp)
> +                            bool warn_on_flag, bool *help_wanted, Error **errp)
>  {
>      const char *firstname;
>      char *id = opts_parse_id(params);
> @@ -906,8 +913,8 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
>          return NULL;
>      }
>  
> -    if (!opts_do_parse(opts, params, firstname, defaults, help_wanted,
> -                       errp)) {
> +    if (!opts_do_parse(opts, params, firstname, defaults,
> +                       warn_on_flag, help_wanted, errp)) {
>          qemu_opts_del(opts);
>          return NULL;
>      }
> @@ -925,7 +932,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
>  QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params,
>                            bool permit_abbrev, Error **errp)
>  {
> -    return opts_parse(list, params, permit_abbrev, false, NULL, errp);
> +    return opts_parse(list, params, permit_abbrev, false, false, NULL, errp);
>  }
>  
>  /**
> @@ -943,7 +950,7 @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
>      QemuOpts *opts;
>      bool help_wanted = false;
>  
> -    opts = opts_parse(list, params, permit_abbrev, false,
> +    opts = opts_parse(list, params, permit_abbrev, false, true,
>                        opts_accepts_any(list) ? NULL : &help_wanted,
>                        &err);
>      if (!opts) {

This function now warns, except for "help" and "?".  The exception
applies even when we treat "help" and "?" as sugar for "help=on" and
"?=on" because opts_accepts_any().

It is the only spot that enables the warning.

Does all user input flow through qemu_opts_parse_noisily()?

> @@ -962,7 +969,7 @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
>  {
>      QemuOpts *opts;
>  
> -    opts = opts_parse(list, params, permit_abbrev, true, NULL, NULL);
> +    opts = opts_parse(list, params, permit_abbrev, true, false, NULL, NULL);
>      assert(opts);
>  }
Paolo Bonzini Jan. 19, 2021, 5:04 p.m. UTC | #2
On 19/01/21 16:56, Markus Armbruster wrote:
>> +            if (!is_help && warn_on_flag) {
>> +                warn_report("short-form boolean option '%s%s' deprecated", prefix, *name);
>> +                error_printf("Please use %s=%s instead\n", *name, *value);
>> +            }
> 
> If @warn_on_flag, we warn except for "help" and "?".  The exception
> applies regardless of @help_wanted.  Shouldn't we except*only*
> recognized help requests?

Suggesting "help=yes" would be worse.

>>
>> -    opts = opts_parse(list, params, permit_abbrev, false,
>> +    opts = opts_parse(list, params, permit_abbrev, false, true,
>>                        opts_accepts_any(list) ? NULL : &help_wanted,
>>                        &err);
>>      if (!opts) {
> 
> This function now warns, except for "help" and "?".  The exception
> applies even when we treat "help" and "?" as sugar for "help=on" and
> "?=on" because opts_accepts_any().
> 
> It is the only spot that enables the warning.
> 
> Does all user input flow through qemu_opts_parse_noisily()?
> 

I was going to say yes, but -vnc (and worse, the QMP version of "change 
vnc") is parsed by qemu_opts_parse() via ui/vnc.c (besides being used by 
lots of tests).  -vnc has several boolean options, and though Libvirt 
only uses "sasl" it does so in the short form.

My solution would be to deprecate the QMP "change vnc" command, and 
postpone switching -vnc to qemu_opts_parse_noisily to 6.2.

The main reason to warn for short-form boolean options, is to block them 
for command line options that are switched to keyval[1].  Adding a 
warning does not necessarily imply removing in two releases.

Paolo

[1] This series already does that for -M, -accel and -object.  This 
means that applying this series would change the command line 
incompatibly without a two-release deprecation.  It's up for discussion 
whether to do so, or delay the application of those patches to 6.2.  It 
would be a pity to hold the dependent changes for effectively a year, 
but it's not a big deal.
Markus Armbruster Jan. 20, 2021, 8:42 a.m. UTC | #3
Paolo Bonzini <pbonzini@redhat.com> writes:

> On 19/01/21 16:56, Markus Armbruster wrote:
>>> +            if (!is_help && warn_on_flag) {
>>> +                warn_report("short-form boolean option '%s%s' deprecated", prefix, *name);
>>> +                error_printf("Please use %s=%s instead\n", *name, *value);
>>> +            }
>> 
>> If @warn_on_flag, we warn except for "help" and "?".  The exception
>> applies regardless of @help_wanted.  Shouldn't we except*only*
>> recognized help requests?
>
> Suggesting "help=yes" would be worse.

Would it?

get_opt_name_value() parses one parameter from params into *name and
*value.  if help_wanted && is_help, it additionally sets *help_wanted to
true.  is_help is true when the parameter is "help" or "?".

How could a parameter "help" be handled?

get_opt_name_value() will set

    *name = g_strdup("help");
    *value = g_strdup("on");

If help_wanted, additionally:

    *help_wanted = true;

Callers that pass non-null help_wanted can do whatever they want with
that.  The actual callers do honor the help request.  The deprecation
warning obviously needs to be suppressed for them.

Callers that pass null help_wanted will treat this just like any other
parameter.  Use of the boolean sugar is just as deprecated for this
parameter as it is for all the others.  Suppressing the deprecation
warning feels wrong.

The alternative is to *outlaw* parameters "help" and "?" in QemuOpts.
I'd be cool with that.

>>> -    opts = opts_parse(list, params, permit_abbrev, false,
>>> +    opts = opts_parse(list, params, permit_abbrev, false, true,
>>>                        opts_accepts_any(list) ? NULL : &help_wanted,
>>>                        &err);
>>>      if (!opts) {
>> 
>> This function now warns, except for "help" and "?".  The exception
>> applies even when we treat "help" and "?" as sugar for "help=on" and
>> "?=on" because opts_accepts_any().
>> 
>> It is the only spot that enables the warning.
>> 
>> Does all user input flow through qemu_opts_parse_noisily()?
>> 
>
> I was going to say yes, but -vnc (and worse, the QMP version of "change 
> vnc") is parsed by qemu_opts_parse() via ui/vnc.c (besides being used by 
> lots of tests).  -vnc has several boolean options, and though Libvirt 
> only uses "sasl" it does so in the short form.
>
> My solution would be to deprecate the QMP "change vnc" command, and 
> postpone switching -vnc to qemu_opts_parse_noisily to 6.2.

QMP command 'change' was deprecated long ago, in 2.5.0 (commit
24fb41330, in 2015).  This predated appendix "Deprecated features"
(which has since become docs/system/deprecated.rst), and remained
missing there until I corrected it in commit 6d570ca10 (v4.2.0).

> The main reason to warn for short-form boolean options, is to block them 
> for command line options that are switched to keyval[1].  Adding a 
> warning does not necessarily imply removing in two releases.

Understand.

> Paolo
>
> [1] This series already does that for -M, -accel and -object.  This 
> means that applying this series would change the command line 
> incompatibly without a two-release deprecation.  It's up for discussion 
> whether to do so, or delay the application of those patches to 6.2.  It 
> would be a pity to hold the dependent changes for effectively a year, 
> but it's not a big deal.

Concur.
Paolo Bonzini Jan. 20, 2021, 12:40 p.m. UTC | #4
On 20/01/21 09:42, Markus Armbruster wrote:
> The alternative is to *outlaw* parameters "help" and "?" in QemuOpts.
> I'd be cool with that. >
>> My solution would be to deprecate the QMP "change vnc" command, and
>> postpone switching -vnc to qemu_opts_parse_noisily to 6.2.
> 
> QMP command 'change' was deprecated long ago, in 2.5.0 (commit
> 24fb41330, in 2015).  This predated appendix "Deprecated features"
> (which has since become docs/system/deprecated.rst), and remained
> missing there until I corrected it in commit 6d570ca10 (v4.2.0).

Removal patch coming then, together with switching vnc_parse to 
qemu_opts_parse_noisily.

That would restrict qemu_opts_parse to tests, and implicitly outlaw 
parameters "help" and "?".  The other problem would be solved, albeit a 
bit indirectly.

>> The main reason to warn for short-form boolean options, is to block them
>> for command line options that are switched to keyval[1].  Adding a
>> warning does not necessarily imply removing in two releases.
> 
> Understand.
> 
>> [1] This series already does that for -M, -accel and -object.  This
>> means that applying this series would change the command line
>> incompatibly without a two-release deprecation.  It's up for discussion
>> whether to do so, or delay the application of those patches to 6.2.  It
>> would be a pity to hold the dependent changes for effectively a year,
>> but it's not a big deal.
> 
> Concur.

Verbose please. :)  Do you think we should delay the conversion of 
-M/-accel/-object to keyval until 6.2?

Paolo
Markus Armbruster Jan. 20, 2021, 12:59 p.m. UTC | #5
Paolo Bonzini <pbonzini@redhat.com> writes:

> On 20/01/21 09:42, Markus Armbruster wrote:
>> The alternative is to *outlaw* parameters "help" and "?" in QemuOpts.
>> I'd be cool with that. >
>>> My solution would be to deprecate the QMP "change vnc" command, and
>>> postpone switching -vnc to qemu_opts_parse_noisily to 6.2.
>> 
>> QMP command 'change' was deprecated long ago, in 2.5.0 (commit
>> 24fb41330, in 2015).  This predated appendix "Deprecated features"
>> (which has since become docs/system/deprecated.rst), and remained
>> missing there until I corrected it in commit 6d570ca10 (v4.2.0).
>
> Removal patch coming then, together with switching vnc_parse to 
> qemu_opts_parse_noisily.
>
> That would restrict qemu_opts_parse to tests, and implicitly outlaw 
> parameters "help" and "?".  The other problem would be solved, albeit a 
> bit indirectly.

Please remember to mention the outlaws in a commit message.

>>> The main reason to warn for short-form boolean options, is to block them
>>> for command line options that are switched to keyval[1].  Adding a
>>> warning does not necessarily imply removing in two releases.
>> 
>> Understand.
>> 
>>> [1] This series already does that for -M, -accel and -object.  This
>>> means that applying this series would change the command line
>>> incompatibly without a two-release deprecation.  It's up for discussion
>>> whether to do so, or delay the application of those patches to 6.2.  It
>>> would be a pity to hold the dependent changes for effectively a year,
>>> but it's not a big deal.
>> 
>> Concur.
>
> Verbose please. :)  Do you think we should delay the conversion of 
> -M/-accel/-object to keyval until 6.2?

I concurred with "it's up for discussion".  I'm happy do discuss, of
course.

Delaying reduces (but does not eliminate) the risk of breaking stuff
that uses the sugar.  Is that worth the loss of momentum?  Hard to say.

I think we can ignore non-boolean parameters, because if you're using
the sugar with those, you're kind of begging for some punishment :)

What are the boolean parameters, and is the *any* evidence of use with
the sugar in the wild?
Paolo Bonzini Jan. 20, 2021, 2:05 p.m. UTC | #6
On 20/01/21 13:59, Markus Armbruster wrote:
>> Verbose please.   Do you think we should delay the conversion of 
>> -M/-accel/-object to keyval until 6.2?
> 
> I concurred with "it's up for discussion".  I'm happy do discuss, of
> course.

> What are the boolean parameters, and is there *any* evidence of use with
> the sugar in the wild?

There is no evidence for -machine, -accel or -object.

Notably (and for Libvirt only):

- '-object' takes a detour through JSON, and uses sometimes =on/=off or 
  sometimes =yes/=no.

- '-accel' is not used at all

- '-machine' does not use short-form boolean options by sheer luck.

The only known usage in Libvirt, for instance, is for "-chardev" and "-vnc".

Paolo
diff mbox series

Patch

diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst
index e20bfcb17a..e71faefbe5 100644
--- a/docs/system/deprecated.rst
+++ b/docs/system/deprecated.rst
@@ -127,6 +127,12 @@  Drives with interface types other than ``if=none`` are for onboard
 devices.  It is possible to use drives the board doesn't pick up with
 -device.  This usage is now deprecated.  Use ``if=none`` instead.
 
+Short-form boolean options (since 5.2)
+''''''''''''''''''''''''''''''''''''''
+
+Boolean options such as ``share=on``/``share=off`` can be written
+in short form as ``share`` and ``noshare``.  This is deprecated
+and will cause a warning.
 
 QEMU Machine Protocol (QMP) commands
 ------------------------------------
diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c
index 2aab831d10..8bbb17b1c7 100644
--- a/tests/test-qemu-opts.c
+++ b/tests/test-qemu-opts.c
@@ -515,7 +515,7 @@  static void test_opts_parse(void)
     error_free_or_abort(&err);
     g_assert(!opts);
 
-    /* Implied value */
+    /* Implied value (qemu_opts_parse warns but accepts it) */
     opts = qemu_opts_parse(&opts_list_03, "an,noaus,noaus=",
                            false, &error_abort);
     g_assert_cmpuint(opts_count(opts), ==, 3);
diff --git a/util/qemu-option.c b/util/qemu-option.c
index 5f27d4369d..40564a12eb 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -756,10 +756,12 @@  void qemu_opts_print(QemuOpts *opts, const char *separator)
 
 static const char *get_opt_name_value(const char *params,
                                       const char *firstname,
+                                      bool warn_on_flag,
                                       bool *help_wanted,
                                       char **name, char **value)
 {
     const char *p;
+    const char *prefix = "";
     size_t len;
     bool is_help = false;
 
@@ -776,10 +778,15 @@  static const char *get_opt_name_value(const char *params,
             if (strncmp(*name, "no", 2) == 0) {
                 memmove(*name, *name + 2, strlen(*name + 2) + 1);
                 *value = g_strdup("off");
+                prefix = "no";
             } else {
                 *value = g_strdup("on");
                 is_help = is_help_option(*name);
             }
+            if (!is_help && warn_on_flag) {
+                warn_report("short-form boolean option '%s%s' deprecated", prefix, *name);
+                error_printf("Please use %s=%s instead\n", *name, *value);
+            }
         }
     } else {
         /* found "foo=bar,more" */
@@ -801,14 +808,14 @@  static const char *get_opt_name_value(const char *params,
 
 static bool opts_do_parse(QemuOpts *opts, const char *params,
                           const char *firstname, bool prepend,
-                          bool *help_wanted, Error **errp)
+                          bool warn_on_flag, bool *help_wanted, Error **errp)
 {
     char *option, *value;
     const char *p;
     QemuOpt *opt;
 
     for (p = params; *p;) {
-        p = get_opt_name_value(p, firstname, help_wanted, &option, &value);
+        p = get_opt_name_value(p, firstname, warn_on_flag, help_wanted, &option, &value);
         if (help_wanted && *help_wanted) {
             g_free(option);
             g_free(value);
@@ -839,7 +846,7 @@  static char *opts_parse_id(const char *params)
     char *name, *value;
 
     for (p = params; *p;) {
-        p = get_opt_name_value(p, NULL, NULL, &name, &value);
+        p = get_opt_name_value(p, NULL, false, NULL, &name, &value);
         if (!strcmp(name, "id")) {
             g_free(name);
             return value;
@@ -858,7 +865,7 @@  bool has_help_option(const char *params)
     bool ret = false;
 
     for (p = params; *p;) {
-        p = get_opt_name_value(p, NULL, &ret, &name, &value);
+        p = get_opt_name_value(p, NULL, false, &ret, &name, &value);
         g_free(name);
         g_free(value);
         if (ret) {
@@ -878,12 +885,12 @@  bool has_help_option(const char *params)
 bool qemu_opts_do_parse(QemuOpts *opts, const char *params,
                        const char *firstname, Error **errp)
 {
-    return opts_do_parse(opts, params, firstname, false, NULL, errp);
+    return opts_do_parse(opts, params, firstname, false, false, NULL, errp);
 }
 
 static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
                             bool permit_abbrev, bool defaults,
-                            bool *help_wanted, Error **errp)
+                            bool warn_on_flag, bool *help_wanted, Error **errp)
 {
     const char *firstname;
     char *id = opts_parse_id(params);
@@ -906,8 +913,8 @@  static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
         return NULL;
     }
 
-    if (!opts_do_parse(opts, params, firstname, defaults, help_wanted,
-                       errp)) {
+    if (!opts_do_parse(opts, params, firstname, defaults,
+                       warn_on_flag, help_wanted, errp)) {
         qemu_opts_del(opts);
         return NULL;
     }
@@ -925,7 +932,7 @@  static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
 QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params,
                           bool permit_abbrev, Error **errp)
 {
-    return opts_parse(list, params, permit_abbrev, false, NULL, errp);
+    return opts_parse(list, params, permit_abbrev, false, false, NULL, errp);
 }
 
 /**
@@ -943,7 +950,7 @@  QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
     QemuOpts *opts;
     bool help_wanted = false;
 
-    opts = opts_parse(list, params, permit_abbrev, false,
+    opts = opts_parse(list, params, permit_abbrev, false, true,
                       opts_accepts_any(list) ? NULL : &help_wanted,
                       &err);
     if (!opts) {
@@ -962,7 +969,7 @@  void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
 {
     QemuOpts *opts;
 
-    opts = opts_parse(list, params, permit_abbrev, true, NULL, NULL);
+    opts = opts_parse(list, params, permit_abbrev, true, false, NULL, NULL);
     assert(opts);
 }