Added getopt_long for Windows (resolves issue #52).

getopt, getopt_long implementation by Kim Gräsman.
This commit is contained in:
Volodymyr Sapsai 2012-08-11 18:15:00 +00:00
parent 19f89b4cfe
commit 3a3b25260d
3 changed files with 203 additions and 86 deletions

View File

@ -5,93 +5,216 @@
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// This code is used with permission from Kim Gräsman. The original is
// maintained with tests at: https://github.com/kimgr/getopt_port
//
//===----------------------------------------------------------------------===//
#include "iwyu_getopt.h"
#include <string.h>
namespace include_what_you_use {
#if defined(_MSC_VER)
// This code is derived from http://www.codeproject.com/KB/cpp/xgetopt.aspx
// Originally written by Hans Dietrich, and released into the public domain.
#include <stddef.h>
#include <string.h>
const int no_argument = 0;
const int required_argument = 1;
const int optional_argument = 2;
char* optarg;
int optopt;
/* The variable optind [...] shall be initialized to 1 by the system. */
int optind = 1;
int opterr;
static char* optcursor = NULL;
/* Implemented based on [1] and [2] for optional arguments.
optopt is handled FreeBSD-style, per [3].
Other GNU and FreeBSD extensions are purely accidental.
[1] http://pubs.opengroup.org/onlinepubs/000095399/functions/getopt.html
[2] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html
[3] http://www.freebsd.org/cgi/man.cgi?query=getopt&sektion=3&manpath=FreeBSD+9.0-RELEASE
*/
int getopt(int argc, char* const argv[], const char* optstring) {
int optchar = -1;
const char* optdecl = NULL;
int GetOptLong(int argc, char* const* argv, const char* shortopts,
const option* longopts,
const char** optarg_ptr, int* optind_ptr) {
const char*& optarg = *optarg_ptr;
int& optind = *optind_ptr;
static char * next = NULL;
if(optind == 0)
next = NULL;
optarg = NULL;
if (next == NULL || *next == 0)
{
if (optind == 0)
optind++;
opterr = 0;
optopt = 0;
if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == 0)
{
optarg = NULL;
if (optind < argc)
optarg = argv[optind];
return -1;
}
/* Unspecified, but we need it to avoid overrunning the argv bounds. */
if (optind >= argc)
goto no_more_optchars;
if (strcmp(argv[optind], "--") == 0)
{
optind++;
optarg = NULL;
if (optind < argc)
optarg = argv[optind];
return -1;
}
/* If, when getopt() is called argv[optind] is a null pointer, getopt()
shall return -1 without changing optind. */
if (argv[optind] == NULL)
goto no_more_optchars;
next = argv[optind];
next++; // skip past -
optind++;
/* If, when getopt() is called *argv[optind] is not the character '-',
getopt() shall return -1 without changing optind. */
if (*argv[optind] != '-')
goto no_more_optchars;
/* If, when getopt() is called argv[optind] points to the string "-",
getopt() shall return -1 without changing optind. */
if (strcmp(argv[optind], "-") == 0)
goto no_more_optchars;
/* If, when getopt() is called argv[optind] points to the string "--",
getopt() shall return -1 after incrementing optind. */
if (strcmp(argv[optind], "--") == 0) {
++optind;
goto no_more_optchars;
}
char c = *next++;
const char *cp = strchr(shortopts, c);
if (optcursor == NULL || *optcursor == '\0')
optcursor = argv[optind] + 1;
if (cp == NULL || c == ':')
return '?';
optchar = *optcursor;
cp++;
if (*cp == ':')
{
if (*next != 0)
{
optarg = next;
next = NULL;
}
else if (optind < argc)
{
optarg = argv[optind];
optind++;
}
else
{
return '?';
/* FreeBSD: The variable optopt saves the last known option character
returned by getopt(). */
optopt = optchar;
/* The getopt() function shall return the next option character (if one is
found) from argv that matches a character in optstring, if there is
one that matches. */
optdecl = strchr(optstring, optchar);
if (optdecl) {
/* [I]f a character is followed by a colon, the option takes an
argument. */
if (optdecl[1] == ':') {
optarg = ++optcursor;
if (*optarg == '\0') {
/* GNU extension: Two colons mean an option takes an
optional arg; if there is text in the current argv-element
(i.e., in the same word as the option name itself, for example,
"-oarg"), then it is returned in optarg, otherwise optarg is set
to zero. */
if (optdecl[2] != ':') {
/* If the option was the last character in the string pointed to by
an element of argv, then optarg shall contain the next element
of argv, and optind shall be incremented by 2. If the resulting
value of optind is greater than argc, this indicates a missing
option-argument, and getopt() shall return an error indication.
Otherwise, optarg shall point to the string following the
option character in that element of argv, and optind shall be
incremented by 1.
*/
if (++optind < argc) {
optarg = argv[optind];
} else {
/* If it detects a missing option-argument, it shall return the
colon character ( ':' ) if the first character of optstring
was a colon, or a question-mark character ( '?' ) otherwise.
*/
optarg = NULL;
optchar = (optstring[0] == ':') ? ':' : '?';
}
} else {
optarg = NULL;
}
}
optcursor = NULL;
}
} else {
/* If getopt() encounters an option character that is not contained in
optstring, it shall return the question-mark ( '?' ) character. */
optchar = '?';
}
return c;
if (optcursor == NULL || *++optcursor == '\0')
++optind;
return optchar;
no_more_optchars:
optcursor = NULL;
return -1;
}
#else // #if defined(_MSC_VER)
/* Implementation based on [1].
int GetOptLong(int argc, char* const* argv, const char* shortopts,
const option* longopts, const char** optarg, int* optind) {
const int ret = getopt_long(argc, argv, shortopts, longopts, NULL);
*optarg = ::optarg;
*optind = ::optind;
return ret;
[1] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html
*/
int getopt_long(int argc, char* const argv[], const char* optstring,
const struct option* longopts, int* longindex) {
const struct option* o = longopts;
const struct option* match = NULL;
int num_matches = 0;
size_t argument_name_length = 0;
const char* current_argument = NULL;
int retval = -1;
optarg = NULL;
optopt = 0;
if (optind >= argc)
return -1;
if (strlen(argv[optind]) < 3 || strncmp(argv[optind], "--", 2) != 0)
return getopt(argc, argv, optstring);
/* It's an option; starts with -- and is longer than two chars. */
current_argument = argv[optind] + 2;
argument_name_length = strcspn(current_argument, "=");
for (; o->name; ++o) {
if (strncmp(o->name, current_argument, argument_name_length) == 0) {
match = o;
++num_matches;
}
}
if (num_matches == 1) {
/* If longindex is not NULL, it points to a variable which is set to the
index of the long option relative to longopts. */
if (longindex)
*longindex = (match - longopts);
/* If flag is NULL, then getopt_long() shall return val.
Otherwise, getopt_long() returns 0, and flag shall point to a variable
which shall be set to val if the option is found, but left unchanged if
the option is not found. */
if (match->flag)
*(match->flag) = match->val;
retval = match->flag ? 0 : match->val;
if (match->has_arg != no_argument) {
optarg = strchr(argv[optind], '=');
if (optarg != NULL)
++optarg;
if (match->has_arg == required_argument) {
/* Only scan the next argv for required arguments. Behavior is not
specified, but has been observed with Ubuntu and Mac OSX. */
if (optarg == NULL && ++optind < argc) {
optarg = argv[optind];
}
if (optarg == NULL)
retval = ':';
}
} else if (strchr(argv[optind], '=')) {
/* An argument was provided to a non-argument option.
I haven't seen this specified explicitly, but both GNU and BSD-based
implementations show this behavior.
*/
retval = '?';
}
} else {
/* Unknown option or ambiguous match. */
retval = '?';
}
++optind;
return retval;
}
#endif // #if defined(_MSC_VER)
} // namespace include_what_you_use

View File

@ -12,21 +12,25 @@
#if defined(_MSC_VER)
// We provide a partial implementation of getopt_long for Windows.
// For now the longopts struct is ignored, and only shortopts are parsed.
// This would all normally be defined in getopt.h.
// Hand-rolled implementation of getopt/getopt_long for Visual C++.
extern const int no_argument;
extern const int required_argument;
extern const int optional_argument;
extern char* optarg;
extern int optind, opterr, optopt;
struct option {
const char *name;
const char* name;
int has_arg;
int *flag;
int* flag;
int val;
};
// Valid values for the 'has_arg' field of struct 'option'.
const int no_argument = 0;
const int required_argument = 1;
const int optional_argument = 2;
int getopt(int argc, char* const argv[], const char* optstring);
int getopt_long(int argc, char* const argv[],
const char* optstring, const struct option* longopts, int* longindex);
#else // #if defined(_MSC_VER)
@ -34,12 +38,4 @@ const int optional_argument = 2;
#endif // #if defined(_MSC_VER)
namespace include_what_you_use {
// Like getopt_long(), except that it outputs optarg and optind via
// output parameters instead of global variables.
int GetOptLong(int argc, char* const* argv, const char* shortopts,
const option* longopts, const char** optarg, int* optind);
} // namespace include_what_you_use
#endif // DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_GETOPT_H_

View File

@ -90,10 +90,8 @@ int CommandlineFlags::ParseArgv(int argc, char** argv) {
{0, 0, 0, 0}
};
static const char shortopts[] = "d::p:v:c:";
const char* optarg = NULL;
int optind = 0;
while (true) {
switch (GetOptLong(argc, argv, shortopts, longopts, &optarg, &optind)) {
switch (getopt_long(argc, argv, shortopts, longopts, NULL)) {
case 'c': AddGlobToReportIWYUViolationsFor(optarg); break;
case 'd': howtodebug = optarg ? optarg : ""; break;
case 'h': PrintHelp(""); exit(0); break;