I happen to like the getopt_long interface designed by the GNU
project. It's easy to learn, setup and use. Once it's in place
it's set and forget.
I agree, and what is more, I say, it is a grammar already, if a simple one. You declare what you accept and what's to be done, making it a DSL expressed as an array of structs.
The only thing it lacks is that old getopt is a bag on the side rather than being integrated: struct option should have an additional member "char short_option", where '\0' means "no short option". Given that feature and three per-program values "progname" (argv[0] by default), "version", and "usage_string", the --version and --help options can be processed inside getopt itself. I especially like that you pass per-option pointers saying where to put the value, so no case statement required, just create some global or local variables and pass in their addresses. Automatic support for "--nofoo" given "--foo" would be good as well.