# HG changeset patch # User David Demelier # Date 1425675878 -3600 # Node ID 9e223d1de96fdc7ef153a2a14c387fd728fcab55 # Parent 14d9c7a4f358df071430301f108c13428ae165c0# Parent 99e83685d4da7cc1b3cd7c6ff387d7992de2f4dd Merge optionparser to default diff -r 14d9c7a4f358 -r 9e223d1de96f C++/OptionParser.cpp --- a/C++/OptionParser.cpp Fri Mar 06 22:01:40 2015 +0100 +++ b/C++/OptionParser.cpp Fri Mar 06 22:04:38 2015 +0100 @@ -1,5 +1,5 @@ /* - * OptionParser.cpp -- safe getopt(3) replacement + * OptionParser.cpp -- command line option parser * * Copyright (c) 2013, 2014 David Demelier * @@ -16,190 +16,188 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include +#include #include "OptionParser.h" -/* -------------------------------------------------------- - * Option class - * -------------------------------------------------------- */ +bool OptionParser::isShort(const std::string &arg) const +{ + return arg.size() >= 2 && arg[0] == '-' && arg[1] != '-'; +} + +bool OptionParser::isLong(const std::string &arg) const +{ + return arg.size() >= 3 && arg[0] == '-' && arg[1] == '-' && arg[2] != '-'; +} -Option::Option(const std::string &name, const std::string &help, Type type, int flags) - : m_name(name) - , m_help(help) - , m_flags(flags) - , m_type(type) +bool OptionParser::isOption(const std::string &arg) const { + return isShort(arg) || isLong(arg); +} + +std::string OptionParser::key(const std::string &arg) const +{ + if (isShort(arg)) + return arg.substr(1, 1); + + return arg.substr(2); } -const std::string &Option::name() const +bool OptionParser::isShortCompacted(const std::string &arg) const { - return m_name; + return arg.size() >= 3; } -const std::string &Option::token() const +bool OptionParser::isDefined(const std::string &arg) const { - return m_token; + auto n = key(arg); + auto it = std::find_if(m_options.begin(), m_options.end(), [&] (const Option &o) -> bool { + return o.key() == n || o.full() == n; + }); + + return it != m_options.end(); +} + +const Option &OptionParser::get(const std::string &arg) const +{ + std::string n = key(arg); + + return *std::find_if(m_options.begin(), m_options.end(), [&] (const Option &o) -> bool { + return o.key() == n || o.full() == n; + }); +} + +bool OptionParser::isToggle(const std::string &arg) const +{ + return (get(arg).flags() & Option::NoArg); } -const std::string &Option::help() const +void OptionParser::readShort(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const { - return m_help; -} + /* + * There are many options when passing a short option: + * + * 1. -cmyconfig Takes on argument but parsed as unique, + * 2. -c myconfig Takes on argument but parsed as two strings + * 3. -abc If a is not a toggle option, its argument is `bc' + * 4. -abc If a is a toggle option and b, c are toggle, they are added + */ + + std::string v = it->substr(2); + std::string k = key(*it); + const Option &option = get(std::string("-") + k); + + if (isToggle(*it)) { + // 3. and optionally 4. + pack.push_back(OptionValue(option, "")); + pack.m_argsParsed += 1; -Option::Type Option::type() const -{ - return m_type; + if (isShortCompacted(*it)) { + for (char c : v) { + if (!isDefined("-" + std::string(1, c))) { + pack.m_error = "-" + std::string(1, c) + " is not a valid option"; + break; + } + + const Option &sub = get("-" + std::string(1, c)); + + pack.push_back(OptionValue(sub, "")); + } + } + + ++ it; + } else { + // 1. + if (isShortCompacted(*it++)) { + pack.push_back(OptionValue(option, v)); + pack.m_argsParsed += 1; + } else { + // 2. + if (it == end) { + pack.m_error = option.key() + " requires an option"; + } else { + pack.push_back(OptionValue(option, *it++)); + pack.m_argsParsed += 2; + } + } + } } -int Option::flags() const +void OptionParser::readFull(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const { - return m_flags; + /* + * Long options can't be compacted, there are only two possibilities: + * + * 1. --fullscreen No argument + * 2. --config foo One argument + */ + const Option &option = get(*it); + + if (!isToggle(*it)) { + // 2. + if (++it == end) { + pack.m_error = "--" + option.full() + " requires an option"; + } else { + pack.push_back(OptionValue(option, *it++)); + pack.m_argsParsed += 2; + } + } else { + pack.push_back(OptionValue(option, "")); + pack.m_argsParsed ++; + + ++ it; + } } -/* -------------------------------------------------------- - * OptionValue class - * -------------------------------------------------------- */ - -OptionValue::OptionValue(const std::string &name) - : m_name(name) - , m_enabled(true) -{ -} - -OptionValue::OptionValue(const std::string &name, const std::string &value) - : m_name(name) - , m_value(value) +OptionParser::OptionParser(std::initializer_list