Correct some getopt() logic

1. Null pointer dereference:

-  for (ndx = 0; longopts[ndx].name[0] != '\0'; ndx++)
+  for (ndx = 0; longopts[ndx].name != NULL; ndx++)

2. Handle single character long options.  An option like -x could be either a short option or a long option (under getopt_long_only()).  This case was not being handled correctly.

3. Add missing support for optional arguments to short options (indicated with two "::"

This effects all members of the getopt() family of APIs.

Tested on the simulator using extensions to apps/testing/ostest.
This commit is contained in:
Gregory Nutt 2021-04-02 08:34:25 -06:00 committed by Abdelatif Guettouche
parent b7c451c5a4
commit af3c76bb53

View File

@ -69,7 +69,7 @@ static int getopt_long_option(FAR struct getopt_s *go,
* The last element of the option arry must be filled with zeroes. * The last element of the option arry must be filled with zeroes.
*/ */
for (ndx = 0; longopts[ndx].name[0] != '\0'; ndx++) for (ndx = 0; longopts[ndx].name != NULL; ndx++)
{ {
if (strcmp(go->go_optptr, longopts[ndx].name) == 0) if (strcmp(go->go_optptr, longopts[ndx].name) == 0)
{ {
@ -99,6 +99,7 @@ static int getopt_long_option(FAR struct getopt_s *go,
if (next == NULL || next[0] == '-') if (next == NULL || next[0] == '-')
{ {
go->go_optptr = NULL; go->go_optptr = NULL;
go->go_optarg = NULL;
go->go_optind++; go->go_optind++;
break; break;
} }
@ -168,7 +169,6 @@ static int getopt_long_option(FAR struct getopt_s *go,
/* This option is not in the list of valid options */ /* This option is not in the list of valid options */
go->go_optopt = *go->go_optptr; go->go_optopt = *go->go_optptr;
go->go_optptr++;
return '?'; return '?';
errout: errout:
@ -251,6 +251,8 @@ int getopt_common(int argc, FAR char * const argv[],
FAR int *longindex, FAR int *longindex,
enum getopt_mode_e mode) enum getopt_mode_e mode)
{ {
int ret;
/* Get thread-specific getopt() variables */ /* Get thread-specific getopt() variables */
FAR struct getopt_s *go = getoptvars(); FAR struct getopt_s *go = getoptvars();
@ -360,19 +362,22 @@ int getopt_common(int argc, FAR char * const argv[],
/* go->go_optptr now points at the next option and it is not something /* go->go_optptr now points at the next option and it is not something
* crazy. Possibilities: * crazy. Possibilities:
* *
* -o * FORM APPLICABILITY
* -o reqarg * -o getopt(), getopt_long_only()
* -option * -o reqarg getopt(), getopt_long_only()
* -option reqarg * -o optarg getopt_long_only()
* -option optarg * -option getopt_long_only()
* --option reqarg * -option reqarg getopt_long_only()
* --option optarg * -option optarg getopt_long_only()
* --option getopt_long(), getopt_long_only()
* --option reqarg getopt_long(), getopt_long_only()
* --option optarg getopt_long(), getopt_long_only()
* *
* Where: * Where:
* o - Some short option * o - Some short option
* option - Some long option * option - Some long option
* reqarg - A required argument * reqarg - A required argument
* optarg - An optional argument * optarg - An optional argument
*/ */
/* Check for --option forms or -option forms */ /* Check for --option forms or -option forms */
@ -396,14 +401,25 @@ int getopt_common(int argc, FAR char * const argv[],
* must be distinguished from the -o case forms. * must be distinguished from the -o case forms.
*/ */
if (GETOPT_HAVE_LONGONLY(mode) && *(go->go_optptr + 1) != '\0') else if (GETOPT_HAVE_LONGONLY(mode))
{ {
return getopt_long_option(go, argv, longopts, longindex); /* A special case is that the option is of a form like
* -o but is represented as a single character long option.
* In that case, getopt_long_option() will fail with '?' and,
* if it is a single character option, we can just fall
* through to the short option logic.
*/
ret = getopt_long_option(go, argv, longopts, longindex);
if (ret != '?' || *(go->go_optptr + 1) != '\0')
{
return ret;
}
} }
} }
/* Check if the option is in the list of valid short options. /* Check if the option is in the list of valid short options.
* In long option modes, opstring may be NULL. However, but that is * In long option modes, opstring may be NULL. However, that is
* an error in any case here because we have not found any * an error in any case here because we have not found any
* long options. * long options.
*/ */
@ -435,8 +451,8 @@ int getopt_common(int argc, FAR char * const argv[],
return *optchar; return *optchar;
} }
/* Yes, it has a required argument. Is the required argument /* Yes, it may have an argument. Is the argument immediately after
* immediately after the command in this same argument? * the command in this same argument?
*/ */
if (go->go_optptr[1] != '\0') if (go->go_optptr[1] != '\0')
@ -449,7 +465,7 @@ int getopt_common(int argc, FAR char * const argv[],
return *optchar; return *optchar;
} }
/* No.. is the optional argument the next argument in argv[] ? */ /* No.. is there an argument in the next value of argv[] ? */
if (argv[go->go_optind + 1] && *argv[go->go_optind + 1] != '-') if (argv[go->go_optind + 1] && *argv[go->go_optind + 1] != '-')
{ {
@ -467,7 +483,10 @@ int getopt_common(int argc, FAR char * const argv[],
go->go_optarg = NULL; go->go_optarg = NULL;
go->go_optopt = *optchar; go->go_optopt = *optchar;
go->go_optind++; go->go_optind++;
return noarg_ret;
/* Two colons means that the argument is optional. */
return (optchar[2] == ':') ? *optchar : noarg_ret;
} }
errout: errout: