inline everything
This commit is contained in:
parent
2c0e4e8213
commit
9889637790
@ -22,14 +22,4 @@ add_executable(example example.cpp)
|
|||||||
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
|
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)
|
install(FILES cxxopts.hpp DESTINATION include)
|
||||||
|
|||||||
459
src/cxxopts.cpp
459
src/cxxopts.cpp
@ -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 <regex>
|
|
||||||
|
|
||||||
#define OPTION_LONGEST 30
|
|
||||||
#define OPTION_DESC_GAP 2
|
|
||||||
|
|
||||||
namespace cxxopts
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
std::basic_regex<char> option_matcher
|
|
||||||
("--([[:alpha:]][-_[:alpha:]]+)(=(.*))?|-([a-zA-Z]+)");
|
|
||||||
|
|
||||||
std::basic_regex<char> 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<const Value> value
|
|
||||||
)
|
|
||||||
{
|
|
||||||
std::match_results<const char*> 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<OptionDetails> 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<OptionDetails> 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<const char*> 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<const Value> value
|
|
||||||
)
|
|
||||||
{
|
|
||||||
auto option = std::make_shared<OptionDetails>(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<OptionDetails> 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<std::pair<std::string, std::string>> 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<size_t>(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<std::string>& 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
445
src/cxxopts.hpp
445
src/cxxopts.hpp
@ -29,6 +29,7 @@ THE SOFTWARE.
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <regex>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -194,6 +195,7 @@ namespace cxxopts
|
|||||||
value.push_back(v);
|
value.push_back(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
void
|
void
|
||||||
parse_value(const std::string& text, bool& value)
|
parse_value(const std::string& text, bool& value)
|
||||||
{
|
{
|
||||||
@ -354,12 +356,15 @@ namespace cxxopts
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
void
|
void
|
||||||
parse(int& argc, char**& argv);
|
parse(int& argc, char**& argv);
|
||||||
|
|
||||||
|
inline
|
||||||
OptionAdder
|
OptionAdder
|
||||||
add_options(std::string group = "");
|
add_options(std::string group = "");
|
||||||
|
|
||||||
|
inline
|
||||||
void
|
void
|
||||||
add_option
|
add_option
|
||||||
(
|
(
|
||||||
@ -396,14 +401,17 @@ namespace cxxopts
|
|||||||
}
|
}
|
||||||
|
|
||||||
//parse positional arguments into the given option
|
//parse positional arguments into the given option
|
||||||
|
inline
|
||||||
void
|
void
|
||||||
parse_positional(std::string option);
|
parse_positional(std::string option);
|
||||||
|
|
||||||
|
inline
|
||||||
std::string
|
std::string
|
||||||
help(const std::vector<std::string>& groups = {""}) const;
|
help(const std::vector<std::string>& groups = {""}) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
inline
|
||||||
void
|
void
|
||||||
add_one_option
|
add_one_option
|
||||||
(
|
(
|
||||||
@ -411,12 +419,15 @@ namespace cxxopts
|
|||||||
std::shared_ptr<OptionDetails> details
|
std::shared_ptr<OptionDetails> details
|
||||||
);
|
);
|
||||||
|
|
||||||
|
inline
|
||||||
bool
|
bool
|
||||||
consume_positional(std::string a);
|
consume_positional(std::string a);
|
||||||
|
|
||||||
|
inline
|
||||||
void
|
void
|
||||||
add_to_option(const std::string& option, const std::string& arg);
|
add_to_option(const std::string& option, const std::string& arg);
|
||||||
|
|
||||||
|
inline
|
||||||
void
|
void
|
||||||
parse_option
|
parse_option
|
||||||
(
|
(
|
||||||
@ -425,6 +436,7 @@ namespace cxxopts
|
|||||||
const std::string& arg = ""
|
const std::string& arg = ""
|
||||||
);
|
);
|
||||||
|
|
||||||
|
inline
|
||||||
void
|
void
|
||||||
checked_parse_arg
|
checked_parse_arg
|
||||||
(
|
(
|
||||||
@ -435,6 +447,7 @@ namespace cxxopts
|
|||||||
const std::string& name
|
const std::string& name
|
||||||
);
|
);
|
||||||
|
|
||||||
|
inline
|
||||||
std::string
|
std::string
|
||||||
help_one_group(const std::string& group) const;
|
help_one_group(const std::string& group) const;
|
||||||
|
|
||||||
@ -457,6 +470,7 @@ namespace cxxopts
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
OptionAdder&
|
OptionAdder&
|
||||||
operator()
|
operator()
|
||||||
(
|
(
|
||||||
@ -473,4 +487,435 @@ namespace cxxopts
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace cxxopts
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
constexpr int OPTION_LONGEST = 30;
|
||||||
|
constexpr int OPTION_DESC_GAP = 2;
|
||||||
|
|
||||||
|
std::basic_regex<char> option_matcher
|
||||||
|
("--([[:alpha:]][-_[:alpha:]]+)(=(.*))?|-([a-zA-Z]+)");
|
||||||
|
|
||||||
|
std::basic_regex<char> 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<const Value> value
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::match_results<const char*> 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<OptionDetails> 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<OptionDetails> 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<const char*> 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<const Value> value
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto option = std::make_shared<OptionDetails>(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<OptionDetails> 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<std::pair<std::string, std::string>> 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<size_t>(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<std::string>& 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
|
#endif //CXX_OPTS_HPP
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user