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

[RFC] Teach getopts to handle -o and +o separately



I really like that zsh's getopts handles both -o and +o option variants, but a
major limitation is that the two variants are linked by the one letter
specification — there's no way to tell zsh that you don't want one or the other,
and more importantly there's no way to specify that one variant should take an
argument and one should not.

I'd like to propose that a - or + following an option letter (or preceding it,
if that seems nicer) restrict that letter specification to the - or + variant
respectively. With this change, the following sort of thing becomes possible:

  % which testopts
  testopts () {
  local OPTARG OPTIND opt
    while getopts a-a:+bc opt
    do
      print -r - $opt${OPTARG:+:$OPTARG}
    done
  }

  % testopts -a +a
  a
  testopts:2: argument expected after +a option
  :

  % testopts -bab +bab
  b
  a
  b
  +b
  +a:b

Of course, this eliminates - and + as valid 'letters' in the optstring. But it
seems unlikely that anyone actually needs +-, -+, or ++ (-- is already
effectively unusable for obvious reasons). Also, POSIX says:

  >The use of other option characters that are not alphanumeric produces
  >unspecified results.

So we're fine there.

Below is a kind of silly-looking patch that implements the change. If the idea
is sound i can try to make it nicer (along with adding docs and tests obv), but
in any case i think it can be done without too many LOC and without touching the
scarier parts of that function.

Does this seem viable at all?

dana


diff --git a/Src/builtin.c b/Src/builtin.c
index 8dcdcc024..f099e3263 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -5512,7 +5512,8 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun
     optbuf[lenoptbuf - 1] = opch;
 
     /* check for legality */
-    if(opch == ':' || !(p = memchr(optstr, opch, lenoptstr))) {
+    if(opch == ':' || opch == '?' || opch == '-' || opch == '+'
+	|| !(p = memchr(optstr, opch, lenoptstr))) {
 	p = "?";
     err:
 	zsfree(zoptarg);
@@ -5528,6 +5529,19 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun
 	return 0;
     }
 
+    scan:
+	if (!p) {
+	    p = "?";
+	    goto err;
+	}
+	if ((lenoptbuf == 1 && (p[1] == '+' || (p[1] == ':' && p[2] == '+')))
+	    || (lenoptbuf == 2 && (p[1] == '-' || (p[1] == ':' && p[2] == '-')))
+	    ) {
+	    p++;
+	    p = memchr(p, opch, strlen(p));
+	    goto scan;
+	}
+
     /* check for required argument */
     if(p[1] == ':') {
 	if(optcind == lenstr) {



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