$ cat > opts.c <<'EOT' && cc -Wall -W opts.c -o opts
#include <stdio.h>
#include <unistd.h>
int main(int ac, char **av){
        int c, i;
        while((c = getopt(ac, av, "q")) != -1)
                if(c != '?' && c != ':') printf("OPT %c\n", c);
        for(i = optind; i < ac; i++) printf("%s\n", av[i]);
}
EOT

$ ./opts -q- arg
OPT q
-q-
arg

On any other platform (except those where OpenBSD's getopt_long.c
made its way) this will complain about an unknown option "-", and
leave "optind" pointing to the next argument, not to "-q-":

linux$ ./opts -q- arg
OPT q
./opts: illegal option -- -
arg

[or "unrecognized option: -", "invalid option -- '-'", etc]

------------------------------------------------------------------------

The code in getopt_long.c says:
                /*
                 * If the user specified "-" and  '-' isn't listed in
                 * options, return -1 (non-option) as per POSIX.
                 * Otherwise, it is an unknown option character (or ':').
                 */
                if (optchar == (int)'-' && *place == '\0')
                        return (-1);

If that is supposed to catch a "--" option end marker, it doesn't seem to,
because it's already caught by the code above it:

        if (optreset || !*place) {              /* update scanning pointer */
                ...
                /*
                 * If we have "-" do nothing, if "--" we are done.
                 */
                if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
                        optind++;
                        place = EMSG;
                        ...
                        return (-1);
                }

If not, could you point me where the POSIX standard requires the
OpenBSD's behavior?

------------------------------------------------------------------------

In case this is not a completely bogus report, maybe the patch below
could do -- not tested very much.

The extra "oli == options" condition is to handle the GNU getopt's
"optstring starting with '-' => \1=optarg" extension, also implemented
by OpenBSD's getopt_long.c.

If this is correct, these are sub-bugs in the current code, that this
patch is also fixing:
        getopt("-p", ["prog", "-p--", "--", "arg"])
                => fails to skip the "--"
        getopt("p-:", ["prog", "-p--", "--", "arg"])
                => complains about unknown option "-", but still sets
                        the "-" option to "--" (not to "-"!)

--- lib/libc/stdlib/getopt_long.c~
+++ lib/libc/stdlib/getopt_long.c
@@ -418,15 +418,8 @@
        }
 
        if ((optchar = (int)*place++) == (int)':' ||
-           (optchar == (int)'-' && *place != '\0') ||
-           (oli = strchr(options, optchar)) == NULL) {
-               /*
-                * If the user specified "-" and  '-' isn't listed in
-                * options, return -1 (non-option) as per POSIX.
-                * Otherwise, it is an unknown option character (or ':').
-                */
-               if (optchar == (int)'-' && *place == '\0')
-                       return (-1);
+           ((oli = strchr(options, optchar)) == NULL ||
+            (optchar == (int)'-' && oli == options))) {
                if (!*place)
                        ++optind;
                if (PRINT_ERROR)

Reply via email to