refactor wgetopt.cpp to reduce complexity

This commit is contained in:
Kurtis Rader 2017-06-17 17:09:01 -07:00
parent a312abdeb8
commit 1e9c9e01e5
2 changed files with 203 additions and 156 deletions

View file

@ -170,24 +170,27 @@ void wgetopter_t::_wgetopt_initialize(const wchar_t *optstring) {
++optstring; ++optstring;
} }
initialized = true;
shortopts = optstring; shortopts = optstring;
initialized = true;
} }
// Advance to the next ARGV-element. // Advance to the next ARGV-element.
int wgetopter_t::_advance_to_next_argv(int argc, wchar_t **argv, const struct woption *longopts) { int wgetopter_t::_advance_to_next_argv( //!OCLINT(high cyclomatic complexity)
int argc, wchar_t **argv, const struct woption *longopts) {
if (ordering == PERMUTE) { if (ordering == PERMUTE) {
// If we have just processed some options following some non-options, exchange them so // If we have just processed some options following some non-options, exchange them so
// that the options come first. // that the options come first.
if (first_nonopt != last_nonopt && last_nonopt != woptind) if (first_nonopt != last_nonopt && last_nonopt != woptind) {
exchange(argv); exchange(argv);
else if (last_nonopt != woptind) } else if (last_nonopt != woptind) {
first_nonopt = woptind; first_nonopt = woptind;
}
// Skip any additional non-options and extend the range of non-options previously // Skip any additional non-options and extend the range of non-options previously
// skipped. // skipped.
while (woptind < argc && (argv[woptind][0] != '-' || argv[woptind][1] == '\0')) while (woptind < argc && (argv[woptind][0] != '-' || argv[woptind][1] == '\0')) {
woptind++; woptind++;
}
last_nonopt = woptind; last_nonopt = woptind;
} }
@ -197,12 +200,12 @@ int wgetopter_t::_advance_to_next_argv(int argc, wchar_t **argv, const struct wo
if (woptind != argc && !wcscmp(argv[woptind], L"--")) { if (woptind != argc && !wcscmp(argv[woptind], L"--")) {
woptind++; woptind++;
if (first_nonopt != last_nonopt && last_nonopt != woptind) if (first_nonopt != last_nonopt && last_nonopt != woptind) {
exchange(argv); exchange(argv);
else if (first_nonopt == last_nonopt) } else if (first_nonopt == last_nonopt) {
first_nonopt = woptind; first_nonopt = woptind;
}
last_nonopt = argc; last_nonopt = argc;
woptind = argc; woptind = argc;
} }
@ -229,6 +232,188 @@ int wgetopter_t::_advance_to_next_argv(int argc, wchar_t **argv, const struct wo
return 0; return 0;
} }
// Check for a matching short opt.
int wgetopter_t::_handle_short_opt(int argc, wchar_t **argv) {
// Look at and handle the next short option-character.
wchar_t c = *nextchar++;
wchar_t *temp = const_cast<wchar_t *>(my_index(shortopts, c));
// Increment `woptind' when we start to process its last character.
if (*nextchar == '\0') ++woptind;
if (temp == NULL || c == ':') {
if (wopterr) {
fwprintf(stderr, _(L"%ls: Invalid option -- %lc\n"), argv[0], (wint_t)c);
}
woptopt = c;
if (*nextchar != '\0') woptind++;
return '?';
}
if (temp[1] != ':') {
return c;
}
if (temp[2] == ':') {
// This is an option that accepts an argument optionally.
if (*nextchar != '\0') {
woptarg = nextchar;
woptind++;
} else {
woptarg = NULL;
}
nextchar = NULL;
} else {
// This is an option that requires an argument.
if (*nextchar != '\0') {
woptarg = nextchar;
// If we end this ARGV-element by taking the rest as an arg, we must advance to
// the next element now.
woptind++;
} else if (woptind == argc) {
if (wopterr) {
// 1003.2 specifies the format of this message.
fwprintf(stderr, _(L"%ls: Option requires an argument -- %lc\n"), argv[0],
(wint_t)c);
}
woptopt = c;
c = missing_arg_return_colon ? ':' : '?';
} else {
// We already incremented `woptind' once; increment it again when taking next
// ARGV-elt as argument.
woptarg = argv[woptind++];
}
nextchar = NULL;
}
return c;
}
void wgetopter_t::_update_long_opt(int argc, wchar_t **argv, const struct woption *pfound,
wchar_t *nameend, int *longind, int option_index, int *retval) {
woptind++;
if (*nameend) {
// Don't test has_arg with >, because some C compilers don't allow it to be used on
// enums.
if (pfound->has_arg)
woptarg = nameend + 1;
else {
if (wopterr) {
if (argv[woptind - 1][1] == '-') // --option
fwprintf(stderr, _(L"%ls: Option '--%ls' doesn't allow an argument\n"), argv[0],
pfound->name);
else
// +option or -option
fwprintf(stderr, _(L"%ls: Option '%lc%ls' doesn't allow an argument\n"),
argv[0], argv[woptind - 1][0], pfound->name);
}
nextchar += wcslen(nextchar);
*retval = '?';
return;
}
} else if (pfound->has_arg == 1) {
if (woptind < argc)
woptarg = argv[woptind++];
else {
if (wopterr)
fwprintf(stderr, _(L"%ls: Option '%ls' requires an argument\n"), argv[0],
argv[woptind - 1]);
nextchar += wcslen(nextchar);
*retval = missing_arg_return_colon ? ':' : '?';
return;
}
}
nextchar += wcslen(nextchar);
if (longind != NULL) *longind = option_index;
if (pfound->flag) {
*(pfound->flag) = pfound->val;
*retval = 0;
} else {
*retval = pfound->val;
}
}
// Find a matching long opt.
const struct woption *wgetopter_t::_find_matching_long_opt(const struct woption *longopts,
wchar_t *nameend, int *exact, int *ambig,
int *indfound) {
const struct woption *pfound = NULL;
int option_index = 0;
// Test all long options for either exact match or abbreviated matches.
for (const struct woption *p = longopts; p->name; p++, option_index++) {
if (!wcsncmp(p->name, nextchar, nameend - nextchar)) {
if ((unsigned int)(nameend - nextchar) == (unsigned int)wcslen(p->name)) {
// Exact match found.
pfound = p;
*indfound = option_index;
*exact = 1;
break;
} else if (pfound == NULL) {
// First nonexact match found.
pfound = p;
*indfound = option_index;
} else
// Second or later nonexact match found.
*ambig = 1;
}
}
return pfound;
}
// Check for a matching long opt.
bool wgetopter_t::_handle_long_opt(int argc, wchar_t **argv, const struct woption *longopts,
int *longind, int long_only, int *retval) {
int exact = 0;
int ambig = 0;
int indfound = 0;
wchar_t *nameend;
for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
; //!OCLINT(empty body)
const struct woption *pfound =
_find_matching_long_opt(longopts, nameend, &exact, &ambig, &indfound);
if (ambig && !exact) {
if (wopterr) {
fwprintf(stderr, _(L"%ls: Option '%ls' is ambiguous\n"), argv[0], argv[woptind]);
}
nextchar += wcslen(nextchar);
woptind++;
*retval = '?';
return true;
}
if (pfound) {
_update_long_opt(argc, argv, pfound, nameend, longind, indfound, retval);
return true;
}
// Can't find it as a long option. If this is not getopt_long_only, or the option starts
// with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a
// short option.
if (!long_only || argv[woptind][1] == '-' || my_index(shortopts, *nextchar) == NULL) {
if (wopterr) {
if (argv[woptind][1] == '-') // --option
fwprintf(stderr, _(L"%ls: Unrecognized option '--%ls'\n"), argv[0], nextchar);
else
// +option or -option
fwprintf(stderr, _(L"%ls: Unrecognized option '%lc%ls'\n"), argv[0],
argv[woptind][0], nextchar);
}
nextchar = (wchar_t *)L"";
woptind++;
*retval = '?';
return true;
}
return false;
}
// Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. // Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING.
// //
// If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option // If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option
@ -295,156 +480,11 @@ int wgetopter_t::_wgetopt_internal(int argc, wchar_t **argv, const wchar_t *opts
if (longopts != NULL && if (longopts != NULL &&
(argv[woptind][1] == '-' || (argv[woptind][1] == '-' ||
(long_only && (argv[woptind][2] || !my_index(shortopts, argv[woptind][1]))))) { (long_only && (argv[woptind][2] || !my_index(shortopts, argv[woptind][1]))))) {
wchar_t *nameend; int retval;
const struct woption *p; if (_handle_long_opt(argc, argv, longopts, longind, long_only, &retval)) return retval;
const struct woption *pfound = NULL;
int exact = 0;
int ambig = 0;
int indfound = 0; // set to zero by Anton
int option_index;
for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
; //!OCLINT(empty body)
// Test all long options for either exact match or abbreviated matches.
for (p = longopts, option_index = 0; p->name; p++, option_index++)
if (!wcsncmp(p->name, nextchar, nameend - nextchar)) {
if ((unsigned int)(nameend - nextchar) == (unsigned int)wcslen(p->name)) {
// Exact match found.
pfound = p;
indfound = option_index;
exact = 1;
break;
} else if (pfound == NULL) {
// First nonexact match found.
pfound = p;
indfound = option_index;
} else
// Second or later nonexact match found.
ambig = 1;
}
if (ambig && !exact) {
if (wopterr)
fwprintf(stderr, _(L"%ls: Option '%ls' is ambiguous\n"), argv[0], argv[woptind]);
nextchar += wcslen(nextchar);
woptind++;
return '?';
}
if (pfound != NULL) {
option_index = indfound;
woptind++;
if (*nameend) {
// Don't test has_arg with >, because some C compilers don't allow it to be used on
// enums.
if (pfound->has_arg)
woptarg = nameend + 1;
else {
if (wopterr) {
if (argv[woptind - 1][1] == '-') // --option
fwprintf(stderr, _(L"%ls: Option '--%ls' doesn't allow an argument\n"),
argv[0], pfound->name);
else
// +option or -option
fwprintf(stderr, _(L"%ls: Option '%lc%ls' doesn't allow an argument\n"),
argv[0], argv[woptind - 1][0], pfound->name);
}
nextchar += wcslen(nextchar);
return '?';
}
} else if (pfound->has_arg == 1) {
if (woptind < argc)
woptarg = argv[woptind++];
else {
if (wopterr)
fwprintf(stderr, _(L"%ls: Option '%ls' requires an argument\n"), argv[0],
argv[woptind - 1]);
nextchar += wcslen(nextchar);
return missing_arg_return_colon ? ':' : '?';
}
}
nextchar += wcslen(nextchar);
if (longind != NULL) *longind = option_index;
if (pfound->flag) {
*(pfound->flag) = pfound->val;
return 0;
}
return pfound->val;
}
// Can't find it as a long option. If this is not getopt_long_only, or the option starts
// with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a
// short option.
if (!long_only || argv[woptind][1] == '-' || my_index(shortopts, *nextchar) == NULL) {
if (wopterr) {
if (argv[woptind][1] == '-') // --option
fwprintf(stderr, _(L"%ls: Unrecognized option '--%ls'\n"), argv[0], nextchar);
else
// +option or -option
fwprintf(stderr, _(L"%ls: Unrecognized option '%lc%ls'\n"), argv[0],
argv[woptind][0], nextchar);
}
nextchar = (wchar_t *)L"";
woptind++;
return '?';
}
} }
// Look at and handle the next short option-character. return _handle_short_opt(argc, argv);
wchar_t c = *nextchar++;
wchar_t *temp = const_cast<wchar_t *>(my_index(shortopts, c));
// Increment `woptind' when we start to process its last character.
if (*nextchar == '\0') ++woptind;
if (temp == NULL || c == ':') {
if (wopterr) {
fwprintf(stderr, _(L"%ls: Invalid option -- %lc\n"), argv[0], (wint_t)c);
}
woptopt = c;
if (*nextchar != '\0') woptind++;
return '?';
}
if (temp[1] != ':') {
return c;
}
if (temp[2] == ':') {
// This is an option that accepts an argument optionally.
if (*nextchar != '\0') {
woptarg = nextchar;
woptind++;
} else {
woptarg = NULL;
}
nextchar = NULL;
} else {
// This is an option that requires an argument.
if (*nextchar != '\0') {
woptarg = nextchar;
// If we end this ARGV-element by taking the rest as an arg, we must advance to
// the next element now.
woptind++;
} else if (woptind == argc) {
if (wopterr) {
// 1003.2 specifies the format of this message.
fwprintf(stderr, _(L"%ls: Option requires an argument -- %lc\n"), argv[0],
(wint_t)c);
}
woptopt = c;
c = missing_arg_return_colon ? ':' : '?';
} else {
// We already incremented `woptind' once; increment it again when taking next
// ARGV-elt as argument.
woptarg = argv[woptind++];
}
nextchar = NULL;
}
return c;
} }
int wgetopter_t::wgetopt_long(int argc, wchar_t **argv, const wchar_t *options, int wgetopter_t::wgetopt_long(int argc, wchar_t **argv, const wchar_t *options,

View file

@ -53,6 +53,13 @@ class wgetopter_t {
int _wgetopt_internal(int argc, wchar_t **argv, const wchar_t *optstring, int _wgetopt_internal(int argc, wchar_t **argv, const wchar_t *optstring,
const struct woption *longopts, int *longind, int long_only); const struct woption *longopts, int *longind, int long_only);
int _advance_to_next_argv(int argc, wchar_t **argv, const struct woption *longopts); int _advance_to_next_argv(int argc, wchar_t **argv, const struct woption *longopts);
int _handle_short_opt(int argc, wchar_t **argv);
bool _handle_long_opt(int argc, wchar_t **argv, const struct woption *longopts, int *longind,
int long_only, int *retval);
const struct woption *_find_matching_long_opt(const struct woption *longopts, wchar_t *nameend,
int *exact, int *ambig, int *indfound);
void _update_long_opt(int argc, wchar_t **argv, const struct woption *pfound, wchar_t *nameend,
int *longind, int option_index, int *retval);
public: public:
// For communication from `getopt' to the caller. When `getopt' finds an option that takes an // For communication from `getopt' to the caller. When `getopt' finds an option that takes an