2008-02-29 Gregory Pietsch <gpietsch@comcast.net>

* libc/stdlib/getopt.c (getopt_internal): Rewrite to accept
        data area so as to support reentrant calls.  Change all callers
        to fill in data area with global values and restore any changes
        to the global values after call.
        (__getopt_r, __getopt_long_r, __getopt_long_only_r): New routines
        to support reentrancy that add a data area argument.
        * libc/include/getopt.h: Add new _r routines and provide macros
        so they can be called with using double-underscores.
This commit is contained in:
Jeff Johnston 2008-02-29 21:11:57 +00:00
parent 86a4b0c733
commit 76ff710cfa
3 changed files with 322 additions and 199 deletions

View File

@ -1,3 +1,14 @@
2008-02-29 Gregory Pietsch <gpietsch@comcast.net>
* libc/stdlib/getopt.c (getopt_internal): Rewrite to accept
data area so as to support reentrant calls. Change all callers
to fill in data area with global values and restore any changes
to the global values after call.
(__getopt_r, __getopt_long_r, __getopt_long_only_r): New routines
to support reentrancy that add a data area argument.
* libc/include/getopt.h: Add new _r routines and provide macros
so they can be called with using double-underscores.
2008-02-21 Eric Blake <ebb9@byu.net>
Fix strtod("-0x", NULL).

View File

@ -82,6 +82,7 @@ Gregory Pietsch's current e-mail address:
gpietsch@comcast.net
****************************************************************************/
#ifndef GETOPT_H
#define GETOPT_H
@ -90,30 +91,46 @@ gpietsch@comcast.net
/* include files needed by this include file */
/* macros defined by this include file */
#define NO_ARG 0
#define REQUIRED_ARG 1
#define OPTIONAL_ARG 2
/* types defined by this include file */
struct option
{
char *name; /* the name of the long option */
int has_arg; /* one of the above macros */
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. */
};
#define NO_ARG 0
#define REQUIRED_ARG 1
#define OPTIONAL_ARG 2
/* The GETOPT_DATA_INITIALIZER macro is used to initialize a statically-
allocated variable of type struct getopt_data. */
#define GETOPT_DATA_INITIALIZER {0,0,0,0,0}
/* These #defines are to keep the namespace clear... */
#define getopt_r __getopt_r
#define getopt_long_r __getopt_long_r
#define getopt_long_only_r __getopt_long_only_r
#ifdef __cplusplus
extern "C"
{
#endif
#endif /* __cplusplus */
/* types defined by this include file */
struct option
{
char *name; /* the name of the long option */
int has_arg; /* one of the above macros */
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
{
char *optarg;
int optind, opterr, optopt, optwhere;
} getopt_data;
/* externally-defined variables */
extern char *optarg;
@ -122,14 +139,35 @@ extern "C"
extern int optopt;
/* function prototypes */
int _EXFUN (getopt, (int __argc, char *const __argv[], const char *__optstring));
int _EXFUN (getopt_long, (int __argc, char *const __argv[], const char *__shortopts, const struct option *__longopts, int *__longind));
int _EXFUN (getopt_long_only, (int __argc, char *const __argv[], const char *__shortopts, const struct option *__longopts, int *__longind));
int _EXFUN (getopt,
(int __argc, char *const __argv[], const char *__optstring));
int _EXFUN (getopt_long,
(int __argc, char *const __argv[], const char *__shortopts,
const struct option * __longopts, int *__longind));
int _EXFUN (getopt_long_only,
(int __argc, char *const __argv[], const char *__shortopts,
const struct option * __longopts, int *__longind));
int _EXFUN (__getopt_r,
(int __argc, char *const __argv[], const char *__optstring,
struct getopt_data * __data));
int _EXFUN (__getopt_long_r,
(int __argc, char *const __argv[], const char *__shortopts,
const struct option * __longopts, int *__longind,
struct getopt_data * __data));
int _EXFUN (__getopt_long_only_r,
(int __argc, char *const __argv[], const char *__shortopts,
const struct option * __longopts, int *__longind,
struct getopt_data * __data));
#ifdef __cplusplus
};
#endif
#endif /* __cplusplus */
#endif /* GETOPT_H */

View File

@ -83,6 +83,7 @@ Gregory Pietsch's current e-mail address:
gpietsch@comcast.net
****************************************************************************/
/* include files */
#include <stdio.h>
#include <stdlib.h>
@ -100,16 +101,19 @@ typedef enum GETOPT_ORDERING_T
} GETOPT_ORDERING_T;
/* globally-defined variables */
char *optarg = NULL;
char *optarg = 0;
int optind = 0;
int opterr = 1;
int optopt = '?';
/* static variables */
static int optwhere = 0;
/* functions */
/* reverse_argv_elements: reverses num elements starting at argv */
static void
reverse_argv_elements (char ** argv, int num)
reverse_argv_elements (char **argv, int num)
{
int i;
char *tmp;
@ -126,276 +130,346 @@ reverse_argv_elements (char ** argv, int num)
static void
permute (char *const argv[], int len1, int len2)
{
reverse_argv_elements ((char **)argv, len1);
reverse_argv_elements ((char **)argv, len1 + len2);
reverse_argv_elements ((char **)argv, len2);
reverse_argv_elements ((char **) argv, len1);
reverse_argv_elements ((char **) argv, len1 + len2);
reverse_argv_elements ((char **) argv, len2);
}
/* is_option: is this argv-element an option or the end of the option list? */
static int
is_option (char *argv_element, int only)
{
return ((argv_element == NULL)
|| (argv_element[0] == '-') || (only && argv_element[0] == '+'));
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 (struct getopt_data *data)
{
data->optarg = optarg;
data->optind = optind;
data->opterr = opterr;
data->optopt = optopt;
data->optwhere = optwhere;
}
/* write_globals: write the values into the globals from a getopt_data
structure */
static void
write_globals (struct getopt_data *data)
{
optarg = data->optarg;
optind = data->optind;
opterr = data->opterr;
optopt = data->optopt;
optwhere = data->optwhere;
}
/* getopt_internal: the function that does all the dirty work */
static int
getopt_internal (int argc, char *const argv[], const char *shortopts,
const struct option *longopts, int *longind, int only)
const struct option *longopts, int *longind, int only,
struct getopt_data *data)
{
GETOPT_ORDERING_T ordering = PERMUTE;
static size_t optwhere = 0;
size_t permute_from = 0;
int num_nonopts = 0;
int optindex = 0;
size_t match_chars = 0;
char *possible_arg = NULL;
char *possible_arg = 0;
int longopt_match = -1;
int has_arg = -1;
char *cp = NULL;
char *cp = 0;
int arg_next = 0;
/* first, deal with silly parameters and easy stuff */
if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL))
if (argc == 0 || argv == 0 || (shortopts == 0 && longopts == 0)
|| data->optind >= argc || argv[data->optind] == 0)
return EOF;
if (optind >= argc || argv[optind] == NULL)
return EOF;
if (strcmp (argv[optind], "--") == 0)
if (strcmp (argv[data->optind], "--") == 0)
{
optind++;
data->optind++;
return EOF;
}
/* if this is our first time through */
if (optind == 0)
optind = optwhere = 1;
if (data->optind == 0)
data->optind = data->optwhere = 1;
/* define ordering */
if (shortopts != NULL && (*shortopts == '-' || *shortopts == '+'))
if (shortopts != 0 && (*shortopts == '-' || *shortopts == '+'))
{
ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;
shortopts++;
}
else
ordering = (getenv ("POSIXLY_CORRECT") != NULL) ? REQUIRE_ORDER : PERMUTE;
ordering = (getenv ("POSIXLY_CORRECT") != 0) ? REQUIRE_ORDER : PERMUTE;
/*
* based on ordering, find our next option, if we're at the beginning of
* one
*/
if (optwhere == 1)
if (data->optwhere == 1)
{
switch (ordering)
{
case PERMUTE:
permute_from = optind;
num_nonopts = 0;
while (!is_option (argv[optind], only))
{
optind++;
num_nonopts++;
}
if (argv[optind] == NULL)
{
/* no more options */
optind = permute_from;
return EOF;
}
else if (strcmp (argv[optind], "--") == 0)
{
/* no more options, but have to get `--' out of the way */
permute (argv + permute_from, num_nonopts, 1);
optind = permute_from + 1;
return EOF;
}
break;
case RETURN_IN_ORDER:
if (!is_option (argv[optind], only))
{
optarg = argv[optind++];
return (optopt = 1);
}
break;
case REQUIRE_ORDER:
if (!is_option (argv[optind], only))
return EOF;
break;
}
{
default: /* shouldn't happen */
case PERMUTE:
permute_from = data->optind;
num_nonopts = 0;
while (!is_option (argv[data->optind], only))
{
data->optind++;
num_nonopts++;
}
if (argv[data->optind] == 0)
{
/* no more options */
data->optind = permute_from;
return EOF;
}
else if (strcmp (argv[data->optind], "--") == 0)
{
/* no more options, but have to get `--' out of the way */
permute (argv + permute_from, num_nonopts, 1);
data->optind = 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 != NULL
&& (memcmp (argv[optind], "--", 2) == 0
|| (only && argv[optind][0] == '+')) && optwhere == 1)
if (longopts != 0
&& (memcmp (argv[data->optind], "--", 2) == 0
|| (only && argv[data->optind][0] == '+')) && data->optwhere == 1)
{
/* handle long options */
if (memcmp (argv[optind], "--", 2) == 0)
optwhere = 2;
if (memcmp (argv[data->optind], "--", 2) == 0)
data->optwhere = 2;
longopt_match = -1;
possible_arg = strchr (argv[optind] + optwhere, '=');
if (possible_arg == NULL)
{
/* no =, so next argv might be arg */
match_chars = strlen (argv[optind]);
possible_arg = argv[optind] + match_chars;
match_chars = match_chars - optwhere;
}
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[optind]) - optwhere;
for (optindex = 0; longopts[optindex].name != NULL; optindex++)
{
if (memcmp (argv[optind] + optwhere,
longopts[optindex].name, match_chars) == 0)
{
/* do we have an exact match? */
if (match_chars == (int) (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 (opterr)
fprintf (stderr, "%s: option `%s' is ambiguous "
"(could be `--%s' or `--%s')\n",
argv[0],
argv[optind],
longopts[longopt_match].name,
longopts[optindex].name);
return (optopt = '?');
}
}
}
}
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 == (int) (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)
fprintf (stderr, "%s: option `%s' is ambiguous "
"(could be `--%s' or `--%s')\n",
argv[0],
argv[data->optind],
longopts[longopt_match].name,
longopts[optindex].name);
return (data->optopt = '?');
}
}
}
}
if (longopt_match >= 0)
has_arg = longopts[longopt_match].has_arg;
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 != NULL)
if (longopt_match < 0 && shortopts != 0)
{
cp = strchr (shortopts, argv[optind][optwhere]);
if (cp == NULL)
{
/* couldn't find option in shortopts */
if (opterr)
fprintf (stderr,
"%s: invalid option -- `-%c'\n",
argv[0], argv[optind][optwhere]);
optwhere++;
if (argv[optind][optwhere] == '\0')
{
optind++;
optwhere = 1;
}
return (optopt = '?');
}
cp = strchr (shortopts, argv[data->optind][data->optwhere]);
if (cp == 0)
{
/* couldn't find option in shortopts */
if (data->opterr)
fprintf (stderr,
"%s: invalid option -- `-%c'\n",
argv[0], argv[data->optind][data->optwhere]);
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[optind] + optwhere + 1;
optopt = *cp;
? ((cp[2] == ':') ? OPTIONAL_ARG : REQUIRED_ARG) : NO_ARG);
possible_arg = argv[data->optind] + data->optwhere + 1;
data->optopt = *cp;
}
/* get argument and reset optwhere */
/* get argument and reset data->optwhere */
arg_next = 0;
switch (has_arg)
{
case OPTIONAL_ARG:
if (*possible_arg == '=')
possible_arg++;
optarg = (*possible_arg != '\0') ? possible_arg : NULL;
optwhere = 1;
possible_arg++;
data->optarg = (*possible_arg != '\0') ? possible_arg : 0;
data->optwhere = 1;
break;
case REQUIRED_ARG:
if (*possible_arg == '=')
possible_arg++;
possible_arg++;
if (*possible_arg != '\0')
{
optarg = possible_arg;
optwhere = 1;
}
else if (optind + 1 >= argc)
{
if (opterr)
{
fprintf (stderr, "%s: argument required for option `", argv[0]);
if (longopt_match >= 0)
fprintf (stderr, "--%s'\n", longopts[longopt_match].name);
else
fprintf (stderr, "-%c'\n", *cp);
}
optind++;
return (optopt = ':');
}
{
data->optarg = possible_arg;
data->optwhere = 1;
}
else if (data->optind + 1 >= argc)
{
if (data->opterr)
{
fprintf (stderr, "%s: argument required for option `", argv[0]);
if (longopt_match >= 0)
fprintf (stderr, "--%s'\n", longopts[longopt_match].name);
else
fprintf (stderr, "-%c'\n", *cp);
}
data->optind++;
return (data->optopt = ':');
}
else
{
optarg = argv[optind + 1];
arg_next = 1;
optwhere = 1;
}
{
data->optarg = argv[data->optind + 1];
arg_next = 1;
data->optwhere = 1;
}
break;
default: /* shouldn't happen */
case NO_ARG:
if (longopt_match < 0)
{
optwhere++;
if (argv[optind][optwhere] == '\0')
optwhere = 1;
}
{
data->optwhere++;
if (argv[data->optind][data->optwhere] == '\0')
data->optwhere = 1;
}
else
optwhere = 1;
optarg = NULL;
data->optwhere = 1;
data->optarg = 0;
break;
}
/* do we have to permute or otherwise modify optind? */
if (ordering == PERMUTE && optwhere == 1 && num_nonopts != 0)
/* do we have to permute or otherwise modify data->optind? */
if (ordering == PERMUTE && data->optwhere == 1 && num_nonopts != 0)
{
permute (argv + permute_from, num_nonopts, 1 + arg_next);
optind = permute_from + 1 + arg_next;
data->optind = permute_from + 1 + arg_next;
}
else if (optwhere == 1)
optind = optind + 1 + arg_next;
else if (data->optwhere == 1)
data->optind = data->optind + 1 + arg_next;
/* finally return */
if (longopt_match >= 0)
{
if (longind != NULL)
*longind = longopt_match;
if (longopts[longopt_match].flag != NULL)
{
*(longopts[longopt_match].flag) = longopts[longopt_match].val;
return 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;
return longopts[longopt_match].val;
}
else
return optopt;
return data->optopt;
}
int
getopt (int argc, char *const argv[], const char *optstring)
{
return getopt_internal (argc, argv, optstring, NULL, NULL, 0);
struct getopt_data data;
int r;
read_globals (&data);
r = getopt_internal (argc, argv, optstring, 0, 0, 0, &data);
write_globals (&data);
return r;
}
int
getopt_long (int argc, char *const argv[], const char *shortopts,
const struct option *longopts, int *longind)
const struct option *longopts, int *longind)
{
return getopt_internal (argc, argv, shortopts, longopts, longind, 0);
struct getopt_data data;
int r;
read_globals (&data);
r = getopt_internal (argc, argv, shortopts, longopts, longind, 0, &data);
write_globals (&data);
return r;
}
int
getopt_long_only (int argc, char *const argv[], const char *shortopts,
const struct option *longopts, int *longind)
const struct option *longopts, int *longind)
{
return getopt_internal (argc, argv, shortopts, longopts, longind, 1);
struct getopt_data data;
int r;
read_globals (&data);
r = getopt_internal (argc, argv, shortopts, longopts, longind, 1, &data);
write_globals (&data);
return r;
}
int
__getopt_r (int argc, char *const argv[], const char *optstring,
struct getopt_data *data)
{
return getopt_internal (argc, argv, optstring, 0, 0, 0, data);
}
int
__getopt_long_r (int argc, char *const argv[], const char *shortopts,
const struct option *longopts, int *longind,
struct getopt_data *data)
{
return getopt_internal (argc, argv, shortopts, longopts, longind, 0, data);
}
int
__getopt_long_only_r (int argc, char *const argv[], const char *shortopts,
const struct option *longopts, int *longind,
struct getopt_data *data)
{
return getopt_internal (argc, argv, shortopts, longopts, longind, 1, data);
}
/* end of file GETOPT.C */