On Sun, Mar 15, 2020 at 09:30:22AM +0100, Martijn van Duren wrote:
> > --- 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)
> > 
> From my research you're close, but not quite.
> This particular case is for handling the actual "-" string.

Nope. That's already handled above:

start:
        if (optreset || !*place) {              /* update scanning pointer */
                ...
                if (*(place = nargv[optind]) != '-' ||
                    (place[1] == '\0' && strchr(options, '-') == NULL)) {

Notice that OpenBSD's getopt(3) explicitly allows to use "-" as an
option character, and there may be a /separate/ bug related to that,
but my patch doesn't change or affect it in any way.

But in the case I'm WRONG, could you prove your point with a test?

OpenBSD's getopt_long.c is self-contained. Copy it into some
other directory, apply my patch, and try with the simple getopt-test.c
test-case program at the end of this message:

cp /PATH/TO/OPENBSD-SRC/lib/libc/stdlib/getopt_long.c getopt_long.c
patch -s < MY_PREVIOUS_MESSAGE getopt_long.c
cc -O2 -W -Wall getopt-test.c getopt_long.c -o getopt-test
cc -O2 -W -Wall getopt-test.c -o getopt-test-unpatched

# as expected:

OPTS=pq: ./getopt-test -p - x y z
{./getopt-test} <p> | {-} {x} {y} {z}

OPTS=pq: ./getopt-test -pq - x y z
{./getopt-test} <p> <q>={-} | {x} {y} {z}

OPTS=pq: ./getopt-test -pq - - x y z
{./getopt-test} <p> <q>={-} | {-} {x} {y} {z}

# bugs in the unpatched version, demonstrated:

OPTS=q ./getopt-test-unpatched -q- arg
{./getopt-test-unpatched} <q> | {-q-} {arg}

OPTS=-p ./getopt-test-unpatched -p-- -- arg
getopt-test-unpatched: unknown option -- -
{./getopt-test-unpatched} <p> #U- | {-p--} {--} {arg}

OPTS=p-: ./getopt-test-unpatched -p-- -- arg
getopt-test-unpatched: unknown option -- -
{./getopt-test-unpatched} <p> #U- <->={--} | {arg}

===================================================================
getopt-test.c
===================================================================
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

static int showargs(int ac, char **av, char *os){
        char *s; int c, i;
        if(ac < 1) return 1;
        printf("{%s}", av[0]);
        while((c = getopt(ac, av, os)) != -1)
                switch(c){
                case '?':
                        printf(" #U%c", optopt); break;
                case ':':
                        printf(" #A%c", optopt); break;
                case '\1':
                        printf(" <\\1>={%s}", optarg); break;
                default:
                        if((s = strrchr(os, c)) && s[1] == ':')
                                printf(" <%c>={%s}", c, optarg);
                        else
                                printf(" <%c>", c);
                }
        printf(" |");
        for(i = optind; i < ac; i++) printf(" {%s}", av[i]);
        printf("\n");
        return 0;
}
int main(int ac, char **av){
        char *os;
        if(!(os = getenv("OPTS"))) os = "";
        return showargs(ac, av, os);
}

Reply via email to