diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4dd82f4..3f24779 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,14 +22,4 @@ add_executable(example example.cpp) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") -add_library(cxxopts SHARED cxxopts.cpp) - -target_link_libraries(example cxxopts) - -set_target_properties(cxxopts PROPERTIES - SOVERSION 0 - VERSION 0.0.1 -) - -install(TARGETS cxxopts LIBRARY DESTINATION lib) install(FILES cxxopts.hpp DESTINATION include) diff --git a/src/cxxopts.cpp b/src/cxxopts.cpp deleted file mode 100644 index ea8d0d2..0000000 --- a/src/cxxopts.cpp +++ /dev/null @@ -1,459 +0,0 @@ -/* - -Copyright (c) 2014 Jarryd Beck - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -*/ - -#include "cxxopts.hpp" - -#include - -#define OPTION_LONGEST 30 -#define OPTION_DESC_GAP 2 - -namespace cxxopts -{ - - namespace - { - - std::basic_regex option_matcher - ("--([[:alpha:]][-_[:alpha:]]+)(=(.*))?|-([a-zA-Z]+)"); - - std::basic_regex option_specifier - ("(([a-zA-Z]),)?([a-zA-Z][-_a-zA-Z]+)"); - - std::string - format_option - ( - const std::string& s, - const std::string& l, - bool has_arg - ) - { - std::string result = " "; - - if (s.size() > 0) - { - result += "-" + s + ","; - } - else - { - result += " "; - } - - if (l.size() > 0) - { - result += " --" + l; - } - - if (has_arg) - { - result += " arg"; - } - - return result; - } - - std::string - format_description - ( - const std::string& text, - int start, - int width - ) - { - std::string result; - - auto current = text.begin(); - auto startLine = current; - auto lastSpace = current; - - int size = 0; - - while (current != text.end()) - { - if (*current == ' ') - { - lastSpace = current; - } - - if (size > width) - { - if (lastSpace == startLine) - { - result.append(startLine, current + 1); - result.append("\n"); - result.append(start, ' '); - startLine = current + 1; - lastSpace = startLine; - } - else - { - result.append(startLine, lastSpace); - result.append("\n"); - result.append(start, ' '); - startLine = lastSpace + 1; - } - size = 0; - } - else - { - ++size; - } - - ++current; - } - - //append whatever is left - result.append(startLine, current); - - return result; - } - } - -OptionAdder -Options::add_options(std::string group) -{ - return OptionAdder(*this, std::move(group)); -} - -OptionAdder& -OptionAdder::operator() -( - const std::string& opts, - const std::string& desc, - std::shared_ptr value -) -{ - std::match_results result; - std::regex_match(opts.c_str(), result, option_specifier); - - if (result.empty()) - { - throw invalid_option_format_error(opts); - } - - const auto& s = result[2]; - const auto& l = result[3]; - - m_options.add_option(m_group, s.str(), l.str(), desc, value); - - return *this; -} - -void -Options::parse_option -( - std::shared_ptr value, - const std::string& name, - const std::string& arg -) -{ - value->parse(arg); -} - -void -Options::checked_parse_arg -( - int argc, - char* argv[], - int argPos, - std::shared_ptr value, - const std::string& name -) -{ - if (argPos >= argc) - { - throw missing_argument_exception(name); - } - - parse_option(value, name, argv[argPos]); -} - -void -Options::add_to_option(const std::string& option, const std::string& arg) -{ - auto iter = m_options.find(option); - - if (iter == m_options.end()) - { - throw option_not_exists_exception(option); - } - - parse_option(iter->second, option, arg); -} - -bool -Options::consume_positional(std::string a) -{ - if (m_positional.size() > 0) - { - add_to_option(m_positional, a); - return true; - } - else - { - return false; - } -} - -void -Options::parse_positional(std::string option) -{ - m_positional = std::move(option); -} - -void -Options::parse(int& argc, char**& argv) -{ - int current = 1; - - int nextKeep = 1; - - while (current != argc) - { - std::match_results result; - std::regex_match(argv[current], result, option_matcher); - - if (result.empty()) - { - //not a flag - - //if true is returned here then it was consumed, otherwise it is - //ignored - if (consume_positional(argv[current])) - { - } - else - { - argv[nextKeep] = argv[current]; - ++nextKeep; - } - //if we return from here then it was parsed successfully, so continue - } - else - { - //short or long option? - if (result[4].length() != 0) - { - const std::string& s = result[4]; - - for (int i = 0; i != s.size(); ++i) - { - std::string name(1, s[i]); - auto iter = m_options.find(name); - - if (iter == m_options.end()) - { - throw option_not_exists_exception(name); - } - - auto value = iter->second; - - //if no argument then just add it - if (!value->has_arg()) - { - parse_option(value, name); - } - else - { - //it must be the last argument - if (i + 1 == s.size()) - { - checked_parse_arg(argc, argv, current+1, value, name); - ++current; - } - else - { - //error - throw option_requires_argument_exception(name); - } - } - } - } - else if (result[1].length() != 0) - { - const std::string& name = result[1]; - - auto iter = m_options.find(name); - - if (iter == m_options.end()) - { - throw option_not_exists_exception(name); - } - - auto opt = iter->second; - - //equals provided for long option? - if (result[3].length() != 0) - { - //parse the option given - - //but if it doesn't take an argument, this is an error - if (!opt->has_arg()) - { - throw option_not_has_argument_exception(name, result[3]); - } - - parse_option(opt, name, result[3]); - } - else - { - if (opt->has_arg()) - { - //parse the next argument - checked_parse_arg(argc, argv, current + 1, opt, name); - - ++current; - } - else - { - //parse with empty argument - parse_option(opt, name); - } - } - } - - } - - ++current; - } - - argc = nextKeep; -} - -void -Options::add_option -( - const std::string& group, - const std::string& s, - const std::string& l, - const std::string& desc, - std::shared_ptr value -) -{ - auto option = std::make_shared(desc, value); - - if (s.size() > 0) - { - add_one_option(s, option); - } - - if (l.size() > 0) - { - add_one_option(l, option); - } - - //add the help details - auto& options = m_help[group]; - options.options.emplace_back(HelpOptionDetails{s, l, desc, value->has_arg()}); -} - -void -Options::add_one_option -( - const std::string& option, - std::shared_ptr details -) -{ - auto in = m_options.emplace(option, details); - - if (!in.second) - { - throw option_exists_error(option); - } -} - -std::string -Options::help_one_group(const std::string& g) const -{ - typedef std::vector> OptionHelp; - - auto group = m_help.find(g); - if (group == m_help.end()) - { - return ""; - } - - OptionHelp format; - - size_t longest = 0; - - std::string result; - - if (!g.empty()) - { - result += " " + g + " options:\n\n"; - } - - for (const auto& o : group->second.options) - { - auto s = format_option(o.s, o.l, o.has_arg); - longest = std::max(longest, s.size()); - format.push_back(std::make_pair(s, std::string())); - } - - longest = std::min(longest, static_cast(OPTION_LONGEST)); - - //widest allowed description - int allowed = 76 - longest - OPTION_DESC_GAP; - - auto fiter = format.begin(); - for (const auto& o : group->second.options) - { - auto d = format_description(o.desc, longest + OPTION_DESC_GAP, allowed); - - result += fiter->first; - if (fiter->first.size() > longest) - { - result += "\n"; - result += std::string(longest + OPTION_DESC_GAP, ' '); - } - else - { - result += std::string(longest + OPTION_DESC_GAP - fiter->first.size(), - ' '); - } - result += d; - result += "\n"; - - ++fiter; - } - - return result; -} - -std::string -Options::help(const std::vector& groups) const -{ - std::string result = "Usage:\n " + m_program + " [OPTION...] " - + m_help_string + "\n\n"; - - for (const auto& g : groups) - { - result += help_one_group(g); - } - - return result; -} - -} diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index a620ef3..b1d456a 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -29,6 +29,7 @@ THE SOFTWARE. #include #include #include +#include #include #include #include @@ -194,6 +195,7 @@ namespace cxxopts value.push_back(v); } + inline void parse_value(const std::string& text, bool& value) { @@ -354,12 +356,15 @@ namespace cxxopts { } + inline void parse(int& argc, char**& argv); + inline OptionAdder add_options(std::string group = ""); + inline void add_option ( @@ -396,14 +401,17 @@ namespace cxxopts } //parse positional arguments into the given option + inline void parse_positional(std::string option); + inline std::string help(const std::vector& groups = {""}) const; private: + inline void add_one_option ( @@ -411,12 +419,15 @@ namespace cxxopts std::shared_ptr details ); + inline bool consume_positional(std::string a); + inline void add_to_option(const std::string& option, const std::string& arg); + inline void parse_option ( @@ -425,6 +436,7 @@ namespace cxxopts const std::string& arg = "" ); + inline void checked_parse_arg ( @@ -435,6 +447,7 @@ namespace cxxopts const std::string& name ); + inline std::string help_one_group(const std::string& group) const; @@ -457,6 +470,7 @@ namespace cxxopts { } + inline OptionAdder& operator() ( @@ -473,4 +487,435 @@ namespace cxxopts } +namespace cxxopts +{ + + namespace + { + + constexpr int OPTION_LONGEST = 30; + constexpr int OPTION_DESC_GAP = 2; + + std::basic_regex option_matcher + ("--([[:alpha:]][-_[:alpha:]]+)(=(.*))?|-([a-zA-Z]+)"); + + std::basic_regex option_specifier + ("(([a-zA-Z]),)?([a-zA-Z][-_a-zA-Z]+)"); + + std::string + format_option + ( + const std::string& s, + const std::string& l, + bool has_arg + ) + { + std::string result = " "; + + if (s.size() > 0) + { + result += "-" + s + ","; + } + else + { + result += " "; + } + + if (l.size() > 0) + { + result += " --" + l; + } + + if (has_arg) + { + result += " arg"; + } + + return result; + } + + std::string + format_description + ( + const std::string& text, + int start, + int width + ) + { + std::string result; + + auto current = text.begin(); + auto startLine = current; + auto lastSpace = current; + + int size = 0; + + while (current != text.end()) + { + if (*current == ' ') + { + lastSpace = current; + } + + if (size > width) + { + if (lastSpace == startLine) + { + result.append(startLine, current + 1); + result.append("\n"); + result.append(start, ' '); + startLine = current + 1; + lastSpace = startLine; + } + else + { + result.append(startLine, lastSpace); + result.append("\n"); + result.append(start, ' '); + startLine = lastSpace + 1; + } + size = 0; + } + else + { + ++size; + } + + ++current; + } + + //append whatever is left + result.append(startLine, current); + + return result; + } + } + +OptionAdder +Options::add_options(std::string group) +{ + return OptionAdder(*this, std::move(group)); +} + +OptionAdder& +OptionAdder::operator() +( + const std::string& opts, + const std::string& desc, + std::shared_ptr value +) +{ + std::match_results result; + std::regex_match(opts.c_str(), result, option_specifier); + + if (result.empty()) + { + throw invalid_option_format_error(opts); + } + + const auto& s = result[2]; + const auto& l = result[3]; + + m_options.add_option(m_group, s.str(), l.str(), desc, value); + + return *this; +} + +void +Options::parse_option +( + std::shared_ptr value, + const std::string& name, + const std::string& arg +) +{ + value->parse(arg); +} + +void +Options::checked_parse_arg +( + int argc, + char* argv[], + int argPos, + std::shared_ptr value, + const std::string& name +) +{ + if (argPos >= argc) + { + throw missing_argument_exception(name); + } + + parse_option(value, name, argv[argPos]); +} + +void +Options::add_to_option(const std::string& option, const std::string& arg) +{ + auto iter = m_options.find(option); + + if (iter == m_options.end()) + { + throw option_not_exists_exception(option); + } + + parse_option(iter->second, option, arg); +} + +bool +Options::consume_positional(std::string a) +{ + if (m_positional.size() > 0) + { + add_to_option(m_positional, a); + return true; + } + else + { + return false; + } +} + +void +Options::parse_positional(std::string option) +{ + m_positional = std::move(option); +} + +void +Options::parse(int& argc, char**& argv) +{ + int current = 1; + + int nextKeep = 1; + + while (current != argc) + { + std::match_results result; + std::regex_match(argv[current], result, option_matcher); + + if (result.empty()) + { + //not a flag + + //if true is returned here then it was consumed, otherwise it is + //ignored + if (consume_positional(argv[current])) + { + } + else + { + argv[nextKeep] = argv[current]; + ++nextKeep; + } + //if we return from here then it was parsed successfully, so continue + } + else + { + //short or long option? + if (result[4].length() != 0) + { + const std::string& s = result[4]; + + for (int i = 0; i != s.size(); ++i) + { + std::string name(1, s[i]); + auto iter = m_options.find(name); + + if (iter == m_options.end()) + { + throw option_not_exists_exception(name); + } + + auto value = iter->second; + + //if no argument then just add it + if (!value->has_arg()) + { + parse_option(value, name); + } + else + { + //it must be the last argument + if (i + 1 == s.size()) + { + checked_parse_arg(argc, argv, current+1, value, name); + ++current; + } + else + { + //error + throw option_requires_argument_exception(name); + } + } + } + } + else if (result[1].length() != 0) + { + const std::string& name = result[1]; + + auto iter = m_options.find(name); + + if (iter == m_options.end()) + { + throw option_not_exists_exception(name); + } + + auto opt = iter->second; + + //equals provided for long option? + if (result[3].length() != 0) + { + //parse the option given + + //but if it doesn't take an argument, this is an error + if (!opt->has_arg()) + { + throw option_not_has_argument_exception(name, result[3]); + } + + parse_option(opt, name, result[3]); + } + else + { + if (opt->has_arg()) + { + //parse the next argument + checked_parse_arg(argc, argv, current + 1, opt, name); + + ++current; + } + else + { + //parse with empty argument + parse_option(opt, name); + } + } + } + + } + + ++current; + } + + argc = nextKeep; +} + +void +Options::add_option +( + const std::string& group, + const std::string& s, + const std::string& l, + const std::string& desc, + std::shared_ptr value +) +{ + auto option = std::make_shared(desc, value); + + if (s.size() > 0) + { + add_one_option(s, option); + } + + if (l.size() > 0) + { + add_one_option(l, option); + } + + //add the help details + auto& options = m_help[group]; + options.options.emplace_back(HelpOptionDetails{s, l, desc, value->has_arg()}); +} + +void +Options::add_one_option +( + const std::string& option, + std::shared_ptr details +) +{ + auto in = m_options.emplace(option, details); + + if (!in.second) + { + throw option_exists_error(option); + } +} + +std::string +Options::help_one_group(const std::string& g) const +{ + typedef std::vector> OptionHelp; + + auto group = m_help.find(g); + if (group == m_help.end()) + { + return ""; + } + + OptionHelp format; + + size_t longest = 0; + + std::string result; + + if (!g.empty()) + { + result += " " + g + " options:\n\n"; + } + + for (const auto& o : group->second.options) + { + auto s = format_option(o.s, o.l, o.has_arg); + longest = std::max(longest, s.size()); + format.push_back(std::make_pair(s, std::string())); + } + + longest = std::min(longest, static_cast(OPTION_LONGEST)); + + //widest allowed description + int allowed = 76 - longest - OPTION_DESC_GAP; + + auto fiter = format.begin(); + for (const auto& o : group->second.options) + { + auto d = format_description(o.desc, longest + OPTION_DESC_GAP, allowed); + + result += fiter->first; + if (fiter->first.size() > longest) + { + result += "\n"; + result += std::string(longest + OPTION_DESC_GAP, ' '); + } + else + { + result += std::string(longest + OPTION_DESC_GAP - fiter->first.size(), + ' '); + } + result += d; + result += "\n"; + + ++fiter; + } + + return result; +} + +std::string +Options::help(const std::vector& groups) const +{ + std::string result = "Usage:\n " + m_program + " [OPTION...] " + + m_help_string + "\n\n"; + + for (const auto& g : groups) + { + result += help_one_group(g); + } + + return result; +} + +} #endif //CXX_OPTS_HPP