getopt_long(): Add support for required argument format

The Linux man page requires that the getopt_long() and getopt_long_only() functions accept arguments to options in a form like:

    --option=argument

This PR adds that missing functionality.

This change effects only getopt_long() and getopt_long_only()

Tested on a simulator NSH configuration with a modified version of the getopt() test in apps/testing/ostest.
This commit is contained in:
Gregory Nutt 2021-04-04 08:55:26 -06:00 committed by Xiang Xiao
parent e7bce54fa9
commit 7de43596da

View File

@ -42,6 +42,60 @@
* Prive Functions * Prive Functions
****************************************************************************/ ****************************************************************************/
/****************************************************************************
* Name: compare_long_option
*
* Description:
* Compare a command argument with a long option, handling the cases:
*
* --option: Any argument is in the next argv entry
* --option=argument: The argument is in the same argv entry
*
****************************************************************************/
static int compare_long_option(FAR const char *cmdarg,
FAR const char *optname,
FAR const char **argument)
{
int result;
*argument = NULL;
for (; ; )
{
int rawchar = *cmdarg++;
int optchar = *optname++;
int cmdchar;
/* The command line option may terminate with either '\0' or '='. */
cmdchar = rawchar;
if (cmdchar == '=')
{
cmdchar = '\0';
}
/* Perform the comparison */
result = cmdchar - optchar;
if (result != 0 || cmdchar == '\0')
{
/* If the '=' is the real terminator, then return the argument
* that follows the '='
*/
if (rawchar == '=')
{
*argument = cmdarg;
}
break;
}
}
return result;
}
/**************************************************************************** /****************************************************************************
* Name: getopt_long_option * Name: getopt_long_option
* *
@ -71,64 +125,104 @@ static int getopt_long_option(FAR struct getopt_s *go,
for (ndx = 0; longopts[ndx].name != NULL; ndx++) for (ndx = 0; longopts[ndx].name != NULL; ndx++)
{ {
if (strcmp(go->go_optptr, longopts[ndx].name) == 0) FAR char *terminator = NULL;
if (compare_long_option(go->go_optptr, longopts[ndx].name,
(FAR const char **)&terminator) == 0)
{ {
/* Found the option with the matching name. Does it have an /* Found the option with the matching name. Does it have an
* required argument? And optional argument? * argument provided in the same argv entry like
* --option=argument?
*/ */
switch (longopts[ndx].has_arg) if (terminator != NULL)
{ {
FAR char *next; /* Skip over the option + argument */
case no_argument: go->go_optptr = NULL;
/* No, no arguments. Just return the option that we go->go_optind++;
* found.
*/
go->go_optptr = NULL; switch (longopts[ndx].has_arg)
go->go_optind++; {
break; case no_argument:
case optional_argument: /* But no argument is expected! */
/* Check if there is a following argument and if that
* following argument is another option.
*/
next = argv[go->go_optind + 1];
if (next == NULL || next[0] == '-')
{
go->go_optptr = NULL;
go->go_optarg = NULL; go->go_optarg = NULL;
return '?';
case optional_argument:
case required_argument:
/* Return the required argument */
go->go_optarg = terminator;
break;
default:
goto errout;
break;
}
}
else
{
/* Does the option have a required argument in the next argv
* entry? An optional argument?
*/
switch (longopts[ndx].has_arg)
{
FAR char *next;
case no_argument:
/* No, no arguments. Just return the argument that we
* found.
*/
go->go_optptr = NULL;
go->go_optind++; go->go_optind++;
break; break;
}
/* Fall through and treat as a required option */ case optional_argument:
/* Check if there is a following argument and if that
* following argument is another option.
*/
case required_argument: next = argv[go->go_optind + 1];
if (next == NULL || next[0] == '-')
{
go->go_optptr = NULL;
go->go_optarg = NULL;
go->go_optind++;
break;
}
/* Verify that the required option is present */ /* Fall through and treat as a required option */
next = argv[go->go_optind + 1]; case required_argument:
if (next == NULL || next[0] == '-')
{
go->go_optptr = NULL;
go->go_optarg = NULL;
go->go_optind++;
return '?';
}
/* Return the required option */ /* Verify that the required argument is present */
go->go_optptr = NULL; next = argv[go->go_optind + 1];
go->go_optarg = next; if (next == NULL || next[0] == '-')
go->go_optind += 2; {
break; go->go_optptr = NULL;
go->go_optarg = NULL;
go->go_optind++;
return '?';
}
default: /* Return the required argument */
goto errout;
break; go->go_optptr = NULL;
go->go_optarg = next;
go->go_optind += 2;
break;
default:
goto errout;
break;
}
} }
/* Setup return value. /* Setup return value.