Add some infrastructure for options which may appear with multiple prefixes.
Add an implementation of paired boolean options like '--enable-foo' and '--disable-foo', or '--bar' and '--no-bar'. Add/fix tests --- libgetopt++/include/getopt++/BoolOption.h | 17 +++++-- libgetopt++/include/getopt++/Option.h | 6 ++- libgetopt++/include/getopt++/OptionSet.h | 3 +- .../include/getopt++/StringArrayOption.h | 2 +- libgetopt++/include/getopt++/StringOption.h | 2 +- libgetopt++/src/BoolOption.cc | 40 ++++++++++++--- libgetopt++/src/Option.cc | 7 +++ libgetopt++/src/OptionSet.cc | 50 ++++++++++++------- libgetopt++/src/StringArrayOption.cc | 2 +- libgetopt++/src/StringOption.cc | 2 +- libgetopt++/tests/BoolOptionTest.cc | 26 ++++++++-- libgetopt++/tests/OptionSet.cc | 4 +- 12 files changed, 118 insertions(+), 43 deletions(-) diff --git a/libgetopt++/include/getopt++/BoolOption.h b/libgetopt++/include/getopt++/BoolOption.h index 35227cb..8b20e1c 100644 --- a/libgetopt++/include/getopt++/BoolOption.h +++ b/libgetopt++/include/getopt++/BoolOption.h @@ -23,17 +23,25 @@ class BoolOption : public Option { public: + enum class BoolOptionType + { + simple, + pairedAble, + pairedNo, + }; + BoolOption(bool const defaultvalue, char shortopt, char const *longopt = 0, - std::string const &shorthelp = std::string(), - OptionSet &owner=GetOption::GetInstance()); + std::string const &shorthelp = std::string(), + BoolOptionType type = BoolOptionType::simple, + OptionSet &owner=GetOption::GetInstance()); virtual ~ BoolOption (); virtual std::string const shortOption () const; virtual std::string const longOption () const; + virtual std::vector<std::string> const & longOptionPrefixes () const; virtual std::string const shortHelp () const; - virtual Result Process (char const *); + virtual Result Process (char const *, int); virtual Argument argument () const; operator bool () const; - private: bool _value; @@ -41,6 +49,7 @@ private: char _shortopt; char const *_longopt; std::string _shorthelp; + BoolOptionType _type; }; #endif // _BOOLOPTION_H_ diff --git a/libgetopt++/include/getopt++/Option.h b/libgetopt++/include/getopt++/Option.h index a32f949..b3b140f 100644 --- a/libgetopt++/include/getopt++/Option.h +++ b/libgetopt++/include/getopt++/Option.h @@ -21,9 +21,10 @@ #endif #if HAVE_STRING_H #include <string> -#else +#else #error "<string> required" #endif +#include <vector> // Each registered option must implement this class. class Option @@ -32,13 +33,14 @@ public: virtual ~ Option (); virtual std::string const shortOption () const = 0; virtual std::string const longOption () const = 0; + virtual std::vector<std::string> const & longOptionPrefixes () const; virtual std::string const shortHelp () const = 0; enum Result { Failed, Ok, Stop }; - virtual Result Process (char const *) = 0; + virtual Result Process (char const *, int) = 0; enum Argument { None, Optional, diff --git a/libgetopt++/include/getopt++/OptionSet.h b/libgetopt++/include/getopt++/OptionSet.h index 4ccadda..dbd8046 100644 --- a/libgetopt++/include/getopt++/OptionSet.h +++ b/libgetopt++/include/getopt++/OptionSet.h @@ -44,7 +44,8 @@ private: bool isOption(std::string::size_type) const; void doOption(std::string &option, std::string::size_type const &pos); bool doNoArgumentOption(std::string &option, std::string::size_type const &pos); - Option * findOption(std::string &option, std::string::size_type const &pos) const; + void findOption(std::string &option, std::string::size_type const &pos, + Option *&theOption, int & prefixIndex) const; std::vector<Option *> options; std::vector<std::string> argv; std::vector<std::string> nonoptions; diff --git a/libgetopt++/include/getopt++/StringArrayOption.h b/libgetopt++/include/getopt++/StringArrayOption.h index d3f87c0..b589d28 100644 --- a/libgetopt++/include/getopt++/StringArrayOption.h +++ b/libgetopt++/include/getopt++/StringArrayOption.h @@ -29,7 +29,7 @@ public: virtual std::string const shortOption () const; virtual std::string const longOption () const; virtual std::string const shortHelp () const; - virtual Result Process (char const *); + virtual Result Process (char const *, int); virtual Argument argument () const; operator std::vector<std::string> () const; diff --git a/libgetopt++/include/getopt++/StringOption.h b/libgetopt++/include/getopt++/StringOption.h index f13be8c..37e723a 100644 --- a/libgetopt++/include/getopt++/StringOption.h +++ b/libgetopt++/include/getopt++/StringOption.h @@ -30,7 +30,7 @@ public: virtual std::string const shortOption () const; virtual std::string const longOption () const; virtual std::string const shortHelp () const; - virtual Result Process (char const *); + virtual Result Process (char const *, int); virtual Argument argument () const; operator const std::string& () const; diff --git a/libgetopt++/src/BoolOption.cc b/libgetopt++/src/BoolOption.cc index 4b26553..37d9a22 100644 --- a/libgetopt++/src/BoolOption.cc +++ b/libgetopt++/src/BoolOption.cc @@ -16,10 +16,10 @@ #include <getopt++/BoolOption.h> BoolOption::BoolOption(bool const defaultvalue, char shortopt, - char const *longopt, std::string const &shorthelp, - OptionSet &owner) : _value (defaultvalue) , - _ovalue (defaultvalue), _shortopt(shortopt), - _longopt (longopt), _shorthelp (shorthelp) + char const *longopt, std::string const &shorthelp, + BoolOptionType type, OptionSet &owner) : + _value (defaultvalue), _ovalue (defaultvalue), _shortopt(shortopt), + _longopt (longopt), _shorthelp (shorthelp), _type(type) { owner.Register (this); }; @@ -38,6 +38,24 @@ BoolOption::longOption () const return _longopt; } +std::vector<std::string> const & +BoolOption::longOptionPrefixes () const +{ + switch (_type) + { + default: + case BoolOption::BoolOptionType::simple: + static std::vector<std::string> simple = {""}; + return simple; + case BoolOption::BoolOptionType::pairedAble: + static std::vector<std::string> able = {"enable-", "disable-"}; + return able; + case BoolOption::BoolOptionType::pairedNo: + static std::vector<std::string> no = {"", "no-"}; + return no; + } +} + std::string const BoolOption::shortHelp () const { @@ -45,9 +63,19 @@ BoolOption::shortHelp () const } Option::Result -BoolOption::Process (char const *) +BoolOption::Process (char const *, int prefixIndex) { - _value = !_ovalue; + switch (_type) + { + default: + case BoolOption::BoolOptionType::simple: + _value = !_ovalue; + case BoolOption::BoolOptionType::pairedAble: + _value = (prefixIndex == 0); + case BoolOption::BoolOptionType::pairedNo: + _value = (prefixIndex == 0); + } + return Ok; } diff --git a/libgetopt++/src/Option.cc b/libgetopt++/src/Option.cc index 7c61eba..2e6e9d8 100644 --- a/libgetopt++/src/Option.cc +++ b/libgetopt++/src/Option.cc @@ -22,3 +22,10 @@ Option::Option () : present(false) Option::~Option () { } + +std::vector<std::string> const & +Option::longOptionPrefixes () const +{ + static std::vector<std::string> noprefix = {""}; + return noprefix; +} diff --git a/libgetopt++/src/OptionSet.cc b/libgetopt++/src/OptionSet.cc index a8f02e8..5a8ddee 100644 --- a/libgetopt++/src/OptionSet.cc +++ b/libgetopt++/src/OptionSet.cc @@ -38,37 +38,47 @@ OptionSet::processOne() if (!isOption(pos)) { /* Push the non option into storage */ - if (nonOptionHandler) { - lastResult = nonOptionHandler->Process(option.c_str()); - } else { - nonoptions.push_back(option); - lastResult = Option::Ok; - } + if (nonOptionHandler) { + lastResult = nonOptionHandler->Process(option.c_str(), 0); + } else { + nonoptions.push_back(option); + lastResult = Option::Ok; + } } else { - doOption(option, pos); + doOption(option, pos); } } -Option * -OptionSet::findOption(std::string &option, std::string::size_type const &pos) const +void +OptionSet::findOption(std::string &option, std::string::size_type const &pos, + Option *&theOption, int & prefixIndex) const { - Option *theOption = NULL; + theOption = NULL; + prefixIndex = 0; for (std::vector<Option *>::const_iterator i = options.begin(); i != options.end(); ++i) { if (pos == 1) { if (option[0] == (*i)->shortOption()[0]) { theOption = (*i); + return; } } else { - /* pos == 2 : todo - prefix matches */ - - if (option.find((*i)->longOption()) == 0) { + /* pos == 2 */ + std::vector<std::string> prefixes = (*i)->longOptionPrefixes(); + for (std::vector<std::string>::const_iterator j = prefixes.begin(); + j != prefixes.end(); + j++) + { + std::string prefixedOption = *j + (*i)->longOption(); + if (option.find(prefixedOption) == 0) { theOption = (*i); + prefixIndex = j - prefixes.begin(); + return; + } } } } - return theOption; } bool @@ -98,7 +108,9 @@ OptionSet::doOption(std::string &option, std::string::size_type const &pos) { lastResult = Option::Failed; option.erase(0, pos); - Option *theOption = findOption(option, pos); + Option *theOption = NULL; + int prefixIndex = 0; + findOption(option, pos, theOption, prefixIndex); char const *optionValue = NULL; std::string value; @@ -108,9 +120,9 @@ OptionSet::doOption(std::string &option, std::string::size_type const &pos) switch (theOption->argument()) { case Option::None: - if (!doNoArgumentOption (option, pos)) - return; - break; + if (!doNoArgumentOption (option, pos)) + return; + break; case Option::Optional: { if (pos == 1) { @@ -231,7 +243,7 @@ OptionSet::doOption(std::string &option, std::string::size_type const &pos) break; } theOption->setPresent(true); - lastResult = theOption->Process(optionValue); + lastResult = theOption->Process(optionValue, prefixIndex); } OptionSet::OptionSet () {} diff --git a/libgetopt++/src/StringArrayOption.cc b/libgetopt++/src/StringArrayOption.cc index 7cbee20..fe6f613 100644 --- a/libgetopt++/src/StringArrayOption.cc +++ b/libgetopt++/src/StringArrayOption.cc @@ -44,7 +44,7 @@ StringArrayOption::shortHelp () const } Option::Result -StringArrayOption::Process (char const *optarg) +StringArrayOption::Process (char const *optarg, int prefixIndex) { if (optarg) { diff --git a/libgetopt++/src/StringOption.cc b/libgetopt++/src/StringOption.cc index d359236..e3c4af6 100644 --- a/libgetopt++/src/StringOption.cc +++ b/libgetopt++/src/StringOption.cc @@ -49,7 +49,7 @@ StringOption::shortHelp () const } Option::Result -StringOption::Process (char const *optarg) +StringOption::Process (char const *optarg, int prefixIndex) { if (optarg) _value = optarg; diff --git a/libgetopt++/tests/BoolOptionTest.cc b/libgetopt++/tests/BoolOptionTest.cc index d2c5190..7fdd259 100644 --- a/libgetopt++/tests/BoolOptionTest.cc +++ b/libgetopt++/tests/BoolOptionTest.cc @@ -20,16 +20,18 @@ #include <iostream> #include <string.h> -static BoolOption testoption (false, 't', "testoption", "Tests the use of boolean options"); int main (int anargc, char **anargv) { + BoolOption helpoption (false, 'h', "help", "Tests the use of help output."); + BoolOption helpoption2 (false, 'o', "help2", "Tests the use of help output."); + BoolOption ableoption (false, '\0', "foo", "Tests the use of paired option.", BoolOption::BoolOptionType::pairedAble); + int argc=2; char *argv[4]; argv[0] = strdup("BoolOptionTest"); argv[1] = strdup("-h"); { - BoolOption helpoption (false, 'h', "help", "Tests the use of help output."); if (!GetOption::GetInstance().Process (argc, argv, NULL)) { std::cout << "Failed to process options" << std::endl; @@ -44,17 +46,31 @@ main (int anargc, char **anargv) argc = 0; } { - BoolOption helpoption (false, 'h', "help", "Tests the use of help output."); if (!GetOption::GetInstance().Process (argc, argv, NULL)) { std::cout << "Failed to process options (2) " << std::endl; return 1; } - if (helpoption) + if (helpoption2) { - std::cout << "Recieved unexpected help option" << std::endl; + std::cout << "Received unexpected help option" << std::endl; return 1; } } + argc=2; + argv[0] = strdup("BoolOptionTest"); + argv[1] = strdup("--enable-foo"); + { + if (!GetOption::GetInstance().Process (argc, argv, NULL)) + { + std::cout << "Failed to process options (3) " << std::endl; + return 1; + } + if (!ableoption) + { + std::cout << "Did not receive expected enable-foo option" << std::endl; + return 1; + } + } return 0; } diff --git a/libgetopt++/tests/OptionSet.cc b/libgetopt++/tests/OptionSet.cc index ff63e39..4eac880 100644 --- a/libgetopt++/tests/OptionSet.cc +++ b/libgetopt++/tests/OptionSet.cc @@ -36,7 +36,7 @@ class StringCollector : public Option { return ""; } - virtual Option::Result Process(const char * value); + virtual Option::Result Process(const char * value, int index); virtual Option::Argument argument() const { return Required; @@ -46,7 +46,7 @@ class StringCollector : public Option }; Option::Result -StringCollector::Process(const char * value) +StringCollector::Process(const char * value, int index) { values.push_back(value); if (values.size() == 1) -- 2.21.0