From 1e9c9e01e50cf00f074473e2833e380e3aa0b155 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sat, 17 Jun 2017 17:09:01 -0700 Subject: [PATCH] refactor wgetopt.cpp to reduce complexity --- src/wgetopt.cpp | 352 +++++++++++++++++++++++++++--------------------- src/wgetopt.h | 7 + 2 files changed, 203 insertions(+), 156 deletions(-) diff --git a/src/wgetopt.cpp b/src/wgetopt.cpp index a19116a35..6effae164 100644 --- a/src/wgetopt.cpp +++ b/src/wgetopt.cpp @@ -170,24 +170,27 @@ void wgetopter_t::_wgetopt_initialize(const wchar_t *optstring) { ++optstring; } - initialized = true; shortopts = optstring; + initialized = true; } // 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 we have just processed some options following some non-options, exchange them so // that the options come first. - if (first_nonopt != last_nonopt && last_nonopt != woptind) + if (first_nonopt != last_nonopt && last_nonopt != woptind) { exchange(argv); - else if (last_nonopt != woptind) + } else if (last_nonopt != woptind) { first_nonopt = woptind; + } // Skip any additional non-options and extend the range of non-options previously // skipped. - while (woptind < argc && (argv[woptind][0] != '-' || argv[woptind][1] == '\0')) + while (woptind < argc && (argv[woptind][0] != '-' || argv[woptind][1] == '\0')) { 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"--")) { woptind++; - if (first_nonopt != last_nonopt && last_nonopt != woptind) + if (first_nonopt != last_nonopt && last_nonopt != woptind) { exchange(argv); - else if (first_nonopt == last_nonopt) + } else if (first_nonopt == last_nonopt) { first_nonopt = woptind; + } last_nonopt = argc; - woptind = argc; } @@ -229,6 +232,188 @@ int wgetopter_t::_advance_to_next_argv(int argc, wchar_t **argv, const struct wo 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(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. // // 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 && (argv[woptind][1] == '-' || (long_only && (argv[woptind][2] || !my_index(shortopts, argv[woptind][1]))))) { - wchar_t *nameend; - const struct woption *p; - 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 '?'; - } + int retval; + if (_handle_long_opt(argc, argv, longopts, longind, long_only, &retval)) return retval; } - // Look at and handle the next short option-character. - wchar_t c = *nextchar++; - wchar_t *temp = const_cast(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; + return _handle_short_opt(argc, argv); } int wgetopter_t::wgetopt_long(int argc, wchar_t **argv, const wchar_t *options, diff --git a/src/wgetopt.h b/src/wgetopt.h index 6d271a3c1..90b0ba895 100644 --- a/src/wgetopt.h +++ b/src/wgetopt.h @@ -53,6 +53,13 @@ class wgetopter_t { int _wgetopt_internal(int argc, wchar_t **argv, const wchar_t *optstring, const struct woption *longopts, int *longind, int long_only); 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: // For communication from `getopt' to the caller. When `getopt' finds an option that takes an