Make getopt function family more posix complaint

by porting Gregory Pietsch's version

Change-Id: I9a792734b45278863630f060a20589b0d9ab8bed
This commit is contained in:
Xiang Xiao 2020-11-10 16:21:28 +08:00 committed by chao.an
parent 865661585e
commit dc176e2fb3
3 changed files with 809 additions and 255 deletions

233
include/getopt.h Normal file
View file

@ -0,0 +1,233 @@
/****************************************************************************
* include/getopt.h
*
* Copyright (C) 2018 Pinecone Inc. All rights reserved.
* Author: Xiang Xiao <xiaoxiang@pinecone.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* AUTHOR: Gregory Pietsch
* CREATED Thu Jan 09 22:37:00 1997
*
* COPYRIGHT NOTICE AND DISCLAIMER:
*
* Copyright (C) 1997 Gregory Pietsch
*
* This file and the accompanying getopt.c implementation file are hereby
* placed in the public domain without restrictions. Just give the author
* credit, don't claim you wrote it or prevent anyone else from using it.
*
* Gregory Pietsch's current e-mail address:
* gpietsch@comcast.net
***************************************************************************/
/* This is a glibc-extension header file. */
#ifndef __INCLUDE_GETOPT_H
#define __INCLUDE_GETOPT_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/compiler.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Names for the values of the `has_arg' field of `struct option'. */
#define no_argument 0
#define required_argument 1
#define optional_argument 2
#define NO_ARG no_argument
#define REQUIRED_ARG required_argument
#define OPTIONAL_ARG optional_argument
/* The GETOPT_DATA_INITIALIZER macro is used to initialize a statically-
* allocated variable of type struct getopt_data.
*/
#define GETOPT_DATA_INITIALIZER {0, 0, 1, 0, 0, 0, 0}
/****************************************************************************
* Public Data
****************************************************************************/
#undef EXTERN
#if defined(__cplusplus)
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/* Used by getopt (obviously NOT thread safe!). These variables cannot be
* accessed directly by an external NXFLAT module. In that case, accessor
* functions must be used.
*/
#ifndef __NXFLAT__
/* For communication from `getopt' to the caller.
* When `getopt' finds an option that takes an argument,
* the argument value is returned here.
* Also, when `ordering' is RETURN_IN_ORDER,
* each non-option ARGV-element is returned here.
*/
EXTERN FAR char *optarg;
/* Index in ARGV of the next element to be scanned.
* This is used for communication to and from the caller
* and for communication between successive calls to `getopt'.
*
* On entry to `getopt', zero means this is the first call; initialize.
*
* When `getopt' returns -1, this is the index of the first of the
* non-option elements that the caller should itself scan.
*
* Otherwise, `optind' communicates from one call to the next
* how much of ARGV has been scanned so far.
*/
EXTERN int optind;
/* Callers store zero here to inhibit the error message `getopt' prints
* for unrecognized options.
*/
EXTERN int opterr;
/* Set to an option character which was unrecognized. */
EXTERN int optopt;
#else
# define optarg (*(getoptargp()))
# define optind (*(getoptindp()))
# define optopt (*(getoptoptp()))
#endif
/****************************************************************************
* Public Type Definitions
****************************************************************************/
/* Describe the long-named options requested by the application.
* The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
* of `struct option' terminated by an element containing a name which is
* zero.
*
* The field `has_arg' is:
* no_argument (or 0) if the option does not take an argument,
* required_argument (or 1) if the option requires an argument,
* optional_argument (or 2) if the option takes an optional argument.
*
* If the field `flag' is not NULL, it points to a variable that is set
* to the value given in the field `val' when the option is found, but
* left unchanged if the option is not found.
*
* To have a long-named option do something other than set an `int' to
* a compiled-in constant, such as set a value from `optarg', set the
* option's `flag' field to zero and its `val' field to a nonzero
* value (the equivalent single-letter option character, if there is
* one). For long options that have a zero `flag' field, `getopt'
* returns the contents of the `val' field.
*/
struct option
{
FAR const char *name; /* The name of the long option */
int has_arg; /* One of the above macros */
FAR int *flag; /* Determines if getopt_long() returns a
* value for a long option; if it is
* non-NULL, 0 is returned as a function
* value and the value of val is stored in
* the area pointed to by flag. Otherwise,
* val is returned.
*/
int val; /* Determines the value to return if flag is
* NULL.
*/
};
/* The getopt_data structure is for reentrancy. Its members are similar to
* the externally-defined variables.
*/
typedef struct getopt_data
{
FAR char *optarg;
int optind, opterr, optopt, optwhere;
int permute_from, num_nonopts;
} getopt_data;
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
int getopt(int argc, FAR char *const argv[], FAR const char *optstring);
int getopt_long(int argc, FAR char *const argv[], FAR const char *shortopts,
FAR const struct option *longopts, FAR int *longind);
int getopt_long_only(int argc, FAR char *const argv[], FAR const char *shortopts,
FAR const struct option *longopts, FAR int *longind);
int getopt_r(int argc, FAR char *const argv[], FAR const char *optstring,
FAR struct getopt_data *data);
int getopt_long_r(int argc, FAR char *const argv[], FAR const char *shortopts,
FAR const struct option *longopts, FAR int *longind,
FAR struct getopt_data *data);
int getopt_long_only_r(int argc, FAR char *const argv[], FAR const char *shortopts,
FAR const struct option *longopts, FAR int *longind,
FAR struct getopt_data *data);
/* Accessor functions intended for use only by external NXFLAT
* modules. The global variables optarg, optind, and optopt cannot
* be referenced directly from external modules.
*/
FAR char **getoptargp(void); /* Optional argument following option */
FAR int *getoptindp(void); /* Index into argv */
FAR int *getoptoptp(void); /* Unrecognized option character */
#undef EXTERN
#if defined(__cplusplus)
}
#endif
#endif /* __INCLUDE_GETOPT_H */

View file

@ -44,6 +44,8 @@
#include <sys/types.h>
#include <nuttx/compiler.h>
#include <getopt.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@ -289,21 +291,6 @@ extern "C"
#define EXTERN extern
#endif
/* Used by getopt (obviously NOT thread safe!). These variables cannot be
* accessed directly by an external NXFLAT module. In that case, accessor
* functions must be used.
*/
#ifndef __NXFLAT__
EXTERN FAR char *optarg; /* Optional argument following option */
EXTERN int optind; /* Index into argv */
EXTERN int optopt; /* Unrecognized option character */
#else
# define optarg (*(getoptargp()))
# define optind (*(getoptindp()))
# define optopt (*(getoptoptp()))
#endif
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
@ -379,19 +366,6 @@ int execv(FAR const char *path, FAR char * const argv[]);
void swab(FAR const void *src, FAR void *dest, ssize_t nbytes);
/* getopt and friends */
int getopt(int argc, FAR char * const argv[], FAR const char *optstring);
/* Accessor functions intended for use only by external NXFLAT
* modules. The global variables optarg, optind, and optopt cannot
* be referenced directly from external modules.
*/
FAR char **getoptargp(void); /* Optional argument following option */
FAR int *getoptindp(void); /* Index into argv */
FAR int *getoptoptp(void); /* Unrecognized option character */
int gethostname(FAR char *name, size_t size);
int sethostname(FAR const char *name, size_t size);

View file

@ -1,8 +1,9 @@
/****************************************************************************
* libs/libc/unistd/lib_getopt.c
*
* Copyright (C) 2007-2009, 2011, 2019 Gregory Nutt. All rights reserved.
* Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
* Author: Xiang Xiao <xiaoxiang@pinecone.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -33,263 +34,609 @@
*
****************************************************************************/
/****************************************************************************
* AUTHOR: Gregory Pietsch
* CREATED Fri Jan 10 21:13:05 1997
*
* Copyright (C) 1997 Gregory Pietsch
*
* This file and the accompanying getopt.h header file are hereby placed in the
* public domain without restrictions. Just give the author credit, don't
* claim you wrote it or prevent anyone else from using it.
*
* Gregory Pietsch's current e-mail address:
* gpietsch@comcast.net
***************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define PERMUTE 0
#define RETURN_IN_ORDER 1
#define REQUIRE_ORDER 2
/****************************************************************************
* Public Data
****************************************************************************/
FAR char *optarg; /* Optional argument following option */
int optind = 1; /* Index into argv */
int opterr = 1;
int optopt = '?'; /* unrecognized option character */
/****************************************************************************
* Private Data
****************************************************************************/
static FAR char *g_optptr = NULL;
static FAR char * const *g_argv = NULL;
static int g_argc = 0;
static bool g_binitialized = false;
static int optwhere = 1;
static int permute_from = 0;
static int num_nonopts = 0;
static int g_argc;
static FAR char *const *g_argv;
/****************************************************************************
* Private Functions
****************************************************************************/
/* reverse_argv_elements: reverses num elements starting at argv */
static void
reverse_argv_elements(FAR char **argv, int num)
{
int i;
FAR char *tmp;
for (i = 0; i < (num >> 1); i++)
{
tmp = argv[i];
argv[i] = argv[num - i - 1];
argv[num - i - 1] = tmp;
}
}
/* permute: swap two blocks of argv-elements given their lengths */
static void
permute(FAR char *const argv[], int len1, int len2)
{
reverse_argv_elements((FAR char **)argv, len1);
reverse_argv_elements((FAR char **)argv, len1 + len2);
reverse_argv_elements((FAR char **)argv, len2);
}
/* is_option: is this argv-element an option or the end of the option list? */
static int
is_option(FAR char *argv_element, int only)
{
return ((argv_element == 0) ||
(argv_element[0] == '-') || (only && argv_element[0] == '+'));
}
/* read_globals: read the values from the globals into a getopt_data
* structure
*/
static void
read_globals(int argc, FAR char *const argv[],
FAR struct getopt_data *data)
{
bool reinit = false;
if (g_argc != argc || g_argv != argv)
{
g_argc = argc;
g_argv = argv;
reinit = true;
}
data->optarg = optarg;
data->optind = reinit ? 0 : optind;
data->opterr = opterr;
data->optopt = optopt;
data->optwhere = optwhere;
data->permute_from = permute_from;
data->num_nonopts = num_nonopts;
}
/* write_globals: write the values into the globals from a getopt_data
* structure
*/
static void
write_globals(int r, FAR struct getopt_data *data)
{
if (r == EOF)
{
g_argc = 0;
g_argv = NULL;
}
optarg = data->optarg;
optind = data->optind;
opterr = data->opterr;
optopt = data->optopt;
optwhere = data->optwhere;
permute_from = data->permute_from;
num_nonopts = data->num_nonopts;
}
/* getopt_internal: the function that does all the dirty work
* NOTE: to reduce the code and RAM footprint this function uses
* fputs()/fputc() to do output to stderr instead of fprintf().
*/
static int
getopt_internal(int argc, FAR char *const argv[], FAR const char *shortopts,
FAR const struct option *longopts, FAR int *longind, int only,
FAR struct getopt_data *data)
{
int ordering = PERMUTE;
int optindex = 0;
size_t match_chars = 0;
FAR char *possible_arg = 0;
int longopt_match = -1;
int has_arg = -1;
FAR char *cp = 0;
int arg_next = 0;
int initial_colon = 0;
/* If this is our first time through */
if (data->optind < 1)
{
data->optind = 1;
data->optwhere = 1;
data->permute_from = 0;
data->num_nonopts = 0;
}
/* First, deal with silly parameters and easy stuff */
if (argc == 0 || argv == 0 || (shortopts == 0 && longopts == 0)
|| data->optind >= argc || argv[data->optind] == 0)
{
return EOF;
}
if (strcmp(argv[data->optind], "--") == 0)
{
data->optind++;
return EOF;
}
/* Define ordering */
if (shortopts != 0 && (*shortopts == '-' || *shortopts == '+'))
{
ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;
shortopts++;
}
#ifndef CONFIG_DISABLE_ENVIRON
else
{
ordering = (getenv("POSIXLY_CORRECT") != 0) ? REQUIRE_ORDER : PERMUTE;
}
#endif
/* Check for initial colon in shortopts */
if (shortopts != 0 && *shortopts == ':')
{
++shortopts;
initial_colon = 1;
}
/* Based on ordering, find our next option, if we're at the beginning of
* one
*/
if (data->optwhere == 1)
{
switch (ordering)
{
default: /* Shouldn't happen */
case PERMUTE:
data->permute_from = data->optind;
data->num_nonopts = 0;
while (!is_option(argv[data->optind], only))
{
data->optind++;
data->num_nonopts++;
}
if (argv[data->optind] == 0)
{
/* No more options */
data->optind = data->permute_from;
return EOF;
}
else if (strcmp(argv[data->optind], "--") == 0)
{
/* No more options, but have to get `--' out of the way */
permute(argv + data->permute_from, data->num_nonopts, 1);
data->optind = data->permute_from + 1;
return EOF;
}
break;
case RETURN_IN_ORDER:
if (!is_option(argv[data->optind], only))
{
data->optarg = argv[data->optind++];
return (data->optopt = 1);
}
break;
case REQUIRE_ORDER:
if (!is_option(argv[data->optind], only))
{
return EOF;
}
break;
}
}
/* We've got an option, so parse it */
/* First, is it a long option? */
if (longopts != 0 &&
(memcmp(argv[data->optind], "--", 2) == 0 ||
(only && argv[data->optind][0] == '+')) && data->optwhere == 1)
{
/* Handle long options */
if (memcmp(argv[data->optind], "--", 2) == 0)
{
data->optwhere = 2;
}
possible_arg = strchr(argv[data->optind] + data->optwhere, '=');
if (possible_arg == 0)
{
/* No =, so next argv might be arg */
match_chars = strlen(argv[data->optind]);
possible_arg = argv[data->optind] + match_chars;
match_chars = match_chars - data->optwhere;
}
else
{
match_chars = (possible_arg - argv[data->optind]) - data->optwhere;
}
for (optindex = 0; longopts[optindex].name != 0; ++optindex)
{
if (memcmp(argv[data->optind] + data->optwhere,
longopts[optindex].name, match_chars) == 0)
{
/* Do we have an exact match? */
if (match_chars == strlen(longopts[optindex].name))
{
longopt_match = optindex;
break;
}
/* Do any characters match? */
else
{
if (longopt_match < 0)
{
longopt_match = optindex;
}
else
{
/* We have ambiguous options */
if (data->opterr)
{
fputs(argv[0], stderr);
fputs(": option `", stderr);
fputs(argv[data->optind], stderr);
fputs("' is ambiguous (could be `--", stderr);
fputs(longopts[longopt_match].name, stderr);
fputs("' or `--", stderr);
fputs(longopts[optindex].name, stderr);
fputs("')\n", stderr);
}
data->optind++;
return (data->optopt = '?');
}
}
}
}
if (longopt_match >= 0)
{
has_arg = longopts[longopt_match].has_arg;
}
}
/* If we didn't find a long option, is it a short option? */
if (longopt_match < 0 && shortopts != 0)
{
cp = strchr(shortopts, argv[data->optind][data->optwhere]);
if (cp == 0)
{
/* Couldn't find option in shortopts */
if (data->opterr)
{
fputs(argv[0], stderr);
fputs(": invalid option -- `-", stderr);
fputc(argv[data->optind][data->optwhere], stderr);
fputs("'\n", stderr);
}
data->optwhere++;
if (argv[data->optind][data->optwhere] == '\0')
{
data->optind++;
data->optwhere = 1;
}
return (data->optopt = '?');
}
has_arg = ((cp[1] == ':') ?
((cp[2] == ':') ? OPTIONAL_ARG : REQUIRED_ARG) : NO_ARG);
possible_arg = argv[data->optind] + data->optwhere + 1;
data->optopt = *cp;
}
/* Get argument and reset data->optwhere */
switch (has_arg)
{
case OPTIONAL_ARG:
if (*possible_arg == '=')
{
possible_arg++;
}
data->optarg = (*possible_arg != '\0') ? possible_arg : 0;
data->optwhere = 1;
break;
case REQUIRED_ARG:
if (*possible_arg == '=')
{
possible_arg++;
}
if (*possible_arg != '\0')
{
data->optarg = possible_arg;
data->optwhere = 1;
}
else if (data->optind + 1 >= argc)
{
if (data->opterr)
{
fputs(argv[0], stderr);
fputs(": argument required for option `-", stderr);
if (longopt_match >= 0)
{
fputc('-', stderr);
fputs(longopts[longopt_match].name, stderr);
data->optopt = initial_colon ? ':' : '\?';
}
else
{
fputc(*cp, stderr);
data->optopt = *cp;
}
fputs("'\n", stderr);
}
data->optind++;
return initial_colon ? ':' : '\?';
}
else
{
data->optarg = argv[data->optind + 1];
arg_next = 1;
data->optwhere = 1;
}
break;
default: /* Shouldn't happen */
case NO_ARG:
if (longopt_match < 0)
{
data->optwhere++;
if (argv[data->optind][data->optwhere] == '\0')
{
data->optwhere = 1;
}
}
else
{
data->optwhere = 1;
}
data->optarg = 0;
break;
}
/* Do we have to permute or otherwise modify data->optind? */
if (ordering == PERMUTE && data->optwhere == 1 && data->num_nonopts != 0)
{
permute(argv + data->permute_from, data->num_nonopts, 1 + arg_next);
data->optind = data->permute_from + 1 + arg_next;
}
else if (data->optwhere == 1)
{
data->optind = data->optind + 1 + arg_next;
}
/* Finally return */
if (longopt_match >= 0)
{
if (longind != 0)
{
*longind = longopt_match;
}
if (longopts[longopt_match].flag != 0)
{
*(longopts[longopt_match].flag) = longopts[longopt_match].val;
return 0;
}
else
{
return longopts[longopt_match].val;
}
}
else
{
return data->optopt;
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: getopt
*
* Description:
* getopt() parses command-line arguments. Its arguments argc and argv
* are the argument count and array as passed to the main() function on
* program invocation. An element of argv that starts with '-' is an
* option element. The characters of this element (aside from the initial
* '-') are option characters. If getopt() is called repeatedly, it
* returns successively each of the option characters from each of the
* option elements.
*
* If getopt() finds another option character, it returns that character,
* updating the external variable optind and a static variable nextchar so
* that the next call to getopt() can resume the scan with the following
* option character or argv-element.
*
* If there are no more option characters, getopt() returns -1. Then optind
* is the index in argv of the first argv-element that is not an option.
*
* The 'optstring' argument is a string containing the legitimate option
* characters. If such a character is followed by a colon, this indicates
* that the option requires an argument. If an argument is required for an
* option so getopt() places a pointer to the following text in the same
* argv-element, or the text of the following argv-element, in optarg.
*
* NOTES:
* 1. opterr is not supported and this implementation of getopt() never
* printfs error messages.
* 2. getopt is NOT threadsafe!
* 3. This version of getopt() does not reset global variables until
* -1 is returned. As a result, your command line parsing loops
* must call getopt() repeatedly and continue to parse if other
* errors are returned ('?' or ':') until getopt() finally returns -1.
* (You can also set optind to -1 to force a reset).
*
* Returned Value:
* If an option was successfully found, then getopt() returns the option
* character. If all command-line options have been parsed, then getopt()
* returns -1. If getopt() encounters an option character that was not
* in optstring, then '?' is returned. If getopt() encounters an option
* with a missing argument, then the return value depends on the first
* character in optstring: if it is ':', then ':' is returned; otherwise
* '?' is returned.
*
****************************************************************************/
The getopt() function parses the command line arguments. Its arguments argc
and argv are the argument count and array as passed to the main() function
on program invocation. The argument optstring is a list of available option
characters. If such a character is followed by a colon (`:'), the option
takes an argument, which is placed in optarg. If such a character is
followed by two colons, the option takes an optional argument, which is
placed in optarg. If the option does not take an argument, optarg is NULL.
int getopt(int argc, FAR char * const argv[], FAR const char *optstring)
The external variable optind is the index of the next array element of argv
to be processed; it communicates from one call to the next which element to
process.
The getopt_long() function works like getopt() except that it also accepts
long options started by two dashes `--'. If these take values, it is either
in the form
--arg=value
or
--arg value
It takes the additional arguments longopts which is a pointer to the first
element of an array of type struct option. The last element of the array
has to be filled with NULL for the name field.
The longind pointer points to the index of the current long option relative
to longopts if it is non-NULL.
The getopt() function returns the option character if the option was found
successfully, `:' if there was a missing parameter for one of the options,
`?' for an unknown option character, and EOF for the end of the option list.
The getopt_long() function's return value is described in the header file.
The function getopt_long_only() is identical to getopt_long(), except that a
plus sign `+' can introduce long options as well as `--'.
The following describes how to deal with options that follow non-option
argv-elements.
If the caller did not specify anything, the default is REQUIRE_ORDER if the
environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.
REQUIRE_ORDER means don't recognize them as options; stop option processing
when the first non-option is seen. This is what Unix does. This mode of
operation is selected by either setting the environment variable
POSIXLY_CORRECT, or using `+' as the first character of the optstring
parameter.
PERMUTE is the default. We permute the contents of ARGV as we scan, so that
eventually all the non-options are at the end. This allows options to be
given in any order, even with programs that were not written to expect this.
RETURN_IN_ORDER is an option available to programs that were written to
expect options and other argv-elements in any order and that care about the
ordering of the two. We describe each non-option argv-element as if it were
the argument of an option with character code 1. Using `-' as the first
character of the optstring parameter selects this mode of operation.
The special argument `--' forces an end of option-scanning regardless of the
value of ordering. In the case of RETURN_IN_ORDER, only `--' can cause
getopt() and friends to return EOF with optind != argc.
2012-08-26: Tried to make the error handling more sus4-like. The functions
return a colon if getopt() and friends detect a missing argument and the
first character of shortopts/optstring starts with a colon (`:'). If getopt()
and friends detect a missing argument and shortopts/optstring does not start
with a colon, the function returns a question mark (`?'). If it was a missing
argument to a short option, optopt is set to the character in question. The
colon goes after the ordering character (`+' or `-').
****************************************************************************/
int
getopt(int argc, FAR char *const argv[], FAR const char *optstring)
{
/* Were new argc or argv passed in? This detects misuse of getopt() by
* applications that break out of the getopt() loop before getop() returns
* -1.
*/
struct getopt_data data;
int r;
if (argc != g_argc || argv != g_argv)
{
/* Yes, clear the internal state */
g_binitialized = false;
g_argc = argc;
g_argv = argv;
}
/* Verify input parameters. */
if (argv != NULL && optstring != NULL && argc > 1)
{
FAR char *optchar;
int noarg_ret = '?';
/* The initial value of optind is 1. If getopt() is called again in
* the program, optind must be reset to some value <= 1.
*/
if (optind < 1 || !g_binitialized)
{
optarg = NULL;
optind = 1; /* Skip over the program name */
optopt = '?';
g_optptr = NULL; /* Start at the beginning of the first argument */
g_binitialized = true; /* Now we are initialized */
}
/* If the first character of opstring s ':', then ':' is in the event
* of a missing argument. Otherwise '?' is returned.
*/
if (*optstring == ':')
{
noarg_ret = ':';
optstring++;
}
/* Are we resuming in the middle, or at the end of a string of
* arguments? g_optptr == NULL means that we are started at the
* beginning of argv[optind]; *g_optptr == \0 means that we are
* starting at the beginning of optind+1
*/
while (!g_optptr || !*g_optptr)
{
/* We need to start at the beginning of the next argv. Check if we
* need to increment optind
*/
if (g_optptr)
{
/* Yes.. Increment it and check for the case where where we
* have processed everything in the argv[] array.
*/
optind++;
}
/* Check for the end of the argument list */
g_optptr = argv[optind];
if (!g_optptr)
{
/* There are no more arguments, we are finished */
g_binitialized = false;
return ERROR;
}
/* We are starting at the beginning of argv[optind]. In this case,
* the first character must be '-'
*/
if (*g_optptr != '-')
{
/* The argument does not start with '-', we are finished */
g_binitialized = false;
return ERROR;
}
/* Skip over the '-' */
g_optptr++;
}
/* Special case handling of "-" and "-:" */
if (!*g_optptr)
{
optopt = '\0'; /* We'll fix up g_optptr the next time we are called */
return '?';
}
/* Handle the case of "-:" */
if (*g_optptr == ':')
{
optopt = ':';
g_optptr++;
return '?';
}
/* g_optptr now points at the next option and it is not something
* crazy. check if the option is in the list of valid options.
*/
optchar = strchr(optstring, *g_optptr);
if (!optchar)
{
/* No this character is not in the list of valid options */
optopt = *g_optptr;
g_optptr++;
return '?';
}
/* Yes, the character is in the list of valid options. Does it have an
* required argument?
*/
if (optchar[1] != ':')
{
/* No, no arguments. Just return the character that we found */
g_optptr++;
return *optchar;
}
/* Yes, it has a required argument. Is the required argument
* immediately after the command in this same argument?
*/
if (g_optptr[1] != '\0')
{
/* Yes, return a pointer into the current argument */
optarg = &g_optptr[1];
optind++;
g_optptr = NULL;
return *optchar;
}
/* No.. is the optional argument the next argument in argv[] ? */
if (argv[optind + 1] && *argv[optind + 1] != '-')
{
/* Yes.. return that */
optarg = argv[optind + 1];
optind += 2;
g_optptr = NULL;
return *optchar;
}
/* No argument was supplied */
g_optptr = NULL;
optarg = NULL;
optopt = *optchar;
optind++;
return noarg_ret;
}
/* Restore the initial, uninitialized state */
g_binitialized = false;
return ERROR;
read_globals(argc, argv, &data);
r = getopt_internal(argc, argv, optstring, 0, 0, 0, &data);
write_globals(r, &data);
return r;
}
int
getopt_long(int argc, FAR char *const argv[], FAR const char *shortopts,
FAR const struct option *longopts, FAR int *longind)
{
struct getopt_data data;
int r;
read_globals(argc, argv, &data);
r = getopt_internal(argc, argv, shortopts, longopts, longind, 0, &data);
write_globals(r, &data);
return r;
}
int
getopt_long_only(int argc, FAR char *const argv[], FAR const char *shortopts,
FAR const struct option *longopts, FAR int *longind)
{
struct getopt_data data;
int r;
read_globals(argc, argv, &data);
r = getopt_internal(argc, argv, shortopts, longopts, longind, 1, &data);
write_globals(r, &data);
return r;
}
int
getopt_r(int argc, FAR char *const argv[], FAR const char *optstring,
FAR struct getopt_data *data)
{
return getopt_internal(argc, argv, optstring, 0, 0, 0, data);
}
int
getopt_long_r(int argc, FAR char *const argv[], FAR const char *shortopts,
FAR const struct option *longopts, FAR int *longind,
FAR struct getopt_data *data)
{
return getopt_internal(argc, argv, shortopts, longopts, longind, 0, data);
}
int
getopt_long_only_r(int argc, FAR char *const argv[], FAR const char *shortopts,
FAR const struct option *longopts, FAR int *longind,
FAR struct getopt_data *data)
{
return getopt_internal(argc, argv, shortopts, longopts, longind, 1, data);
}