Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

[PATCH] zparseopts: support empty optspec



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




Messages sorted by: Reverse Date, Date, Thread, Author