[PATCH] zparseopts: support empty optspec

dana <[email protected]>
Newsgroups gmane.comp.shells.zsh.devel
Message-ID <[email protected]>
both getopt(1) and getopts(1) support an empty optspec, which is useful
if you explicitly want *no* options to be recognised, perhaps to reserve
them for the future. it also handles -- for you

there are currently two equivalent methods with zparseopts, and i think
both of them are accidental:

  zparseopts -D -F -

this one works because we seem to just assume the presence of the -/--
terminator means a spec will follow, bypassing the normal check for one.
i've been using this for testing but i don't think we should support it
officially

  zparseopts -a junk -D -F - -

this one works because, although it does handle the - as a spec, it's
not possible to actually parse an option from it (since -- is always a
terminator). there's really no other way this could work so i think it's
reasonable to handle it specially so that the junk array isn't needed

in addition to that i would like to support a single empty optspec
(which is currently erroneous):

  zparseopts -D -F ''

this matches getopt/s and i think it's easier to understand what it
means

the first option with one -/-- will actually be illegal with a change
i'd like to make later (probably too late for 5.10). the warning i added
to the documentation references this potential change, and it's good
practice anyway even if it doesn't happen

dana


diff --git a/Doc/Zsh/mod_zutil.yo b/Doc/Zsh/mod_zutil.yo
index adbdc92c6..d74334d12 100644
--- a/Doc/Zsh/mod_zutil.yo
+++ b/Doc/Zsh/mod_zutil.yo
@@ -239,7 +239,11 @@ into the var(array) specified with the tt(-a) option; if the optional
 `tt(=)var(array)' is given, it is instead copied into that array, which
 should be declared as a normal array and never as an associative array.
 
-Note that it is an error to give any var(spec) without an
+At least one var(spec) must be given.  If a single spec is given which
+is either an empty string or a single hyphen (as in
+tt(zparseopts -D -F '')), it signifies that no options are recognised.
+(This is useful for future-proofing functions that might gain options
+later.)  Otherwise, it is an error to give any var(spec) without an
 `tt(=)var(array)' unless one of the tt(-a) or tt(-A) options is used.
 
 Unless the tt(-E) option is given, parsing stops at the first string
@@ -300,8 +304,14 @@ var(spec) wins. (This is not an issue with GNU-style parsing.)
 
 The options of tt(zparseopts) itself cannot be stacked because, for
 example, the stack `tt(-DEK)' is indistinguishable from a var(spec) for
-the GNU-style long option `tt(-)tt(-DEK)'.  The options of tt(zparseopts)
-itself are:
+the GNU-style long option `tt(-)tt(-DEK)'.
+
+Nevertheless, it is strongly recommended to distinguish GNU-style long
+option var(spec)s from tt(zparseopts)'s own options by preceding them by
+a `tt(-)', `tt(--)', or short option tt(spec), as in
+tt(zparseopts -a opts - -foo).  In the future this may become mandatory.
+
+The options of tt(zparseopts) itself are:
 
 startitem()
 item(tt(-a) var(array))(
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index de070a53d..f13ac95ac 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -1882,10 +1882,15 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	    break;
 	}
     }
-    if (!o) {
+
+    /* allow a single '' or - spec to signify no options recognised */
+    if (o && *args && !args[1] && (!**args || !strcmp(*args, "-"))) {
+	args++;
+    } else if (!o) {
 	zwarnnam(nam, "missing option descriptions");
 	return 1;
     }
+
     while ((o = dupstring(*args++))) {
 	int f = 0;
 	if (!*o) {
diff --git a/Test/V12zparseopts.ztst b/Test/V12zparseopts.ztst
index 907134230..6a880b49c 100644
--- a/Test/V12zparseopts.ztst
+++ b/Test/V12zparseopts.ztst
@@ -17,12 +17,29 @@
 
 %test
 
+  () { zparseopts -D -F ''; print -r - ${(q+)@} } - foo
+  () { zparseopts -D -F ''; print -r - ${(q+)@} } -x
+  () { zparseopts -D -F - ''; print -r - ${(q+)@} } - foo
+  () { zparseopts -D -F - ''; print -r - ${(q+)@} } -x
+  () { zparseopts -D -F - -; print -r - ${(q+)@} } - foo
+  () { zparseopts -D -F - -; print -r - ${(q+)@} } -x
+0:zparseopts with empty option spec (no options recognised)
+>foo
+?(anon): bad option: -x
+>-x
+>foo
+?(anon): bad option: -x
+>-x
+>foo
+?(anon): bad option: -x
+>-x
+
   () { zparseopts -F } -x
   () { zparseopts -F - } -x
   () { zparseopts -F -- } -x
   () { zparseopts -F - a } -x
   () { zparseopts -F a } -x
-1:zparseopts without option specs, without array
+1:zparseopts without option specs, without array (may change in future)
 ?(anon):zparseopts: missing option descriptions
 ?(anon): bad option: -x
 ?(anon): bad option: -x
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.