You're quite right that the man page glosses right over these details, but enough hints can be gleaned from the source code, e.g., glibc's implementation in glibc-x.y.z/posix/getopt.c's _getopt_internal_r
. (Perhaps that's the only interesting implementation of this GNU extension function?)
That code sets optopt
to 0 when it encounters an erroneous long option, which I guess is useful to distinguish this case from an erroneous short option, when optopt
will surely be non-NUL.
The error messages produced when opterr != 0
mostly print out the erroneous long option as argv[optind]
, and later code (always or -- conservatively -- at least mostly) later increments optind
before returning.
Hence consider this program:
#include <getopt.h>
#include <stdio.h>
int main(int argc, char **argv) {
struct option longopts[] = {
{ "foo", no_argument, NULL, 'F' },
{ NULL, 0, NULL, 0 }
};
int c;
do {
int curind = optind;
c = getopt_long(argc, argv, "f", longopts, NULL);
switch (c) {
case 'f': printf("-f\n"); break;
case 'F': printf("--foo\n"); break;
case '?':
if (optopt) printf("bad short opt '%c'\n", optopt);
else printf("bad long opt \"%s\"\n", argv[curind]);
break;
case -1:
break;
default:
printf("returned %d\n", c);
break;
}
} while (c != -1);
return 0;
}
$ ./longopt -f -x --bar --foo
-f
./longopt: invalid option -- 'x'
bad short opt 'x'
./longopt: unrecognized option '--bar'
bad long opt "--bar"
--foo
Thus in these cases, by caching the pre-getopt_long
value of optind
, we're easily able to print out the same bad options as the opterr
messages.
This may not be quite right in all cases, as the glibc implementation's use of its own __nextchar
rather than argv[optind]
(in the "unrecognized option" case) deserves study, but it should be enough to get you started.
If you think carefully about the relationship between optind
and the repeated invocations of getopt_long
, I think printing out argv[cached_optind]
is going to be pretty safe. optopt
exists because for short options you need to know just which character within the word is the problem, but for long options the problem is the whole current word (modulo stripping off option arguments of the form =param
). And the current word is the one that getopt_long
is looking at with the (incoming) optind
value.
In the absence of a guarantee written in the documentation, I would be somewhat less sanguine about taking advantage of the optopt = 0
behaviour though.