2022-03-16 14:20:26 +11:00
|
|
|
#include "./parser.hpp"
|
|
|
|
|
2022-03-18 13:26:10 +11:00
|
|
|
#include "./args.hpp"
|
2022-03-18 12:38:30 +11:00
|
|
|
|
2022-03-18 14:03:56 +11:00
|
|
|
#include <cruft/util/container.hpp>
|
|
|
|
|
2022-03-16 14:20:26 +11:00
|
|
|
using cruft::cmdopt2::parser;
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
cruft::cmdopt2::positional&
|
|
|
|
parser::add (positional const &arg)&
|
|
|
|
{
|
|
|
|
return m_positional.emplace_back (std::move (arg));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
cruft::cmdopt2::keyword&
|
|
|
|
parser::add (keyword const &arg)&
|
|
|
|
{
|
|
|
|
return m_keyword.emplace_back (std::move (arg));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
int
|
|
|
|
parser::parse_named (int argc, const char *const *argv) const
|
|
|
|
{
|
|
|
|
auto cursor = argv[0];
|
|
|
|
if (!cursor or !*cursor)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (*cursor++ != '-')
|
|
|
|
return 0;
|
|
|
|
if (!*cursor)
|
|
|
|
throw std::runtime_error ("missing argument name");
|
|
|
|
|
|
|
|
if (*cursor == '-') {
|
|
|
|
++cursor;
|
|
|
|
if (!*cursor)
|
|
|
|
throw std::runtime_error ("missing long argument name");
|
|
|
|
return parse_long (argc, argv);
|
|
|
|
} else {
|
|
|
|
return parse_short (argc, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
unreachable ();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int
|
|
|
|
parser::parse_long (int argc, const char *const *argv) const
|
|
|
|
{
|
|
|
|
CHECK (argc >= 1);
|
|
|
|
CHECK (strlen (argv[0]) >= 2);
|
|
|
|
CHECK (argv[0][0] == '-');
|
|
|
|
CHECK (argv[0][1] == '-');
|
|
|
|
|
|
|
|
auto const eq = strchr (argv[0], '=');
|
|
|
|
std::string_view const key (argv[0] + 2, eq ?: strlen (argv[0]) + argv[0]);
|
|
|
|
|
|
|
|
auto const pos = std::find_if (
|
|
|
|
m_keyword.begin (),
|
|
|
|
m_keyword.end (),
|
|
|
|
[&] (auto const &arg)
|
|
|
|
{
|
|
|
|
return arg.long_ and *arg.long_ == key;
|
|
|
|
});
|
|
|
|
if (pos == m_keyword.end ())
|
|
|
|
throw std::runtime_error ("Unknown long argument");
|
|
|
|
|
|
|
|
if (eq) {
|
|
|
|
(*pos->acceptor1) (eq + 1);
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
if (pos->acceptor0) {
|
|
|
|
CHECK (!pos->acceptor1);
|
|
|
|
(*pos->acceptor0) ();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECK (pos->acceptor1);
|
|
|
|
if (argc < 2)
|
|
|
|
throw std::runtime_error ("Missing long arg value");
|
|
|
|
|
|
|
|
if (not argv[1] or not argv[1][0] or argv[1][0] == '-')
|
|
|
|
throw std::runtime_error ("Missing long arg value");
|
|
|
|
|
|
|
|
(*pos->acceptor1) (argv[1]);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
unreachable ();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int
|
|
|
|
parser::parse_short (int argc, const char *const *argv) const
|
|
|
|
{
|
|
|
|
(void)argc;
|
|
|
|
CHECK (argc >= 1);
|
|
|
|
CHECK (strlen (argv[0]) >= 2);
|
|
|
|
CHECK (argv[0][0] == '-');
|
|
|
|
CHECK (argv[0][1] != '-');
|
|
|
|
|
|
|
|
auto const len = strlen (argv[0]);
|
|
|
|
if (len < 1)
|
|
|
|
throw std::runtime_error ("Missing short arguments");
|
|
|
|
|
|
|
|
// Handle single short args with a potential for an associated value
|
|
|
|
if (len == 2) {
|
|
|
|
auto const pos = std::find_if (
|
|
|
|
m_keyword.begin (),
|
|
|
|
m_keyword.end (),
|
|
|
|
[&] (auto const &arg)
|
|
|
|
{
|
|
|
|
return arg.short_ and *arg.short_ == argv[0][1];
|
|
|
|
});
|
|
|
|
|
|
|
|
if (pos == m_keyword.end ())
|
|
|
|
throw std::runtime_error ("Unknown short argument");
|
|
|
|
|
|
|
|
if (pos->acceptor0) {
|
|
|
|
CHECK (!pos->acceptor1);
|
|
|
|
(*pos->acceptor0) ();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECK (!pos->acceptor0);
|
|
|
|
if (argc < 2)
|
|
|
|
throw std::runtime_error ("Missing short argument value");
|
|
|
|
(*pos->acceptor1) (argv[1]);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle strings of short arguments, eg: "-vvv"
|
|
|
|
for (std::size_t i = 1; i < len; ++i) {
|
|
|
|
auto const pos = std::find_if (
|
|
|
|
m_keyword.begin (),
|
|
|
|
m_keyword.end (),
|
|
|
|
[&] (auto const &arg)
|
|
|
|
{
|
|
|
|
return arg.short_ and *arg.short_ == argv[0][1];
|
|
|
|
});
|
|
|
|
if (pos == m_keyword.end ())
|
|
|
|
throw std::runtime_error ("Unknown short argument");
|
|
|
|
|
|
|
|
if (pos->acceptor1)
|
|
|
|
throw std::runtime_error ("Missing short argument value");
|
|
|
|
|
|
|
|
(*pos->acceptor0) ();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int
|
|
|
|
parser::parse (int const argc, const char *const *argv)
|
|
|
|
{
|
|
|
|
if (argc <= 1)
|
|
|
|
return argc;
|
|
|
|
|
2022-03-18 14:03:56 +11:00
|
|
|
using namespace std::string_view_literals;
|
|
|
|
if (contains ("--help"sv, cruft::view (argv, argc))) {
|
|
|
|
usage (argc, argv, stdout);
|
|
|
|
exit (EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2022-03-16 14:20:26 +11:00
|
|
|
int arg_cursor = 1;
|
|
|
|
int pos_cursor = 0;
|
|
|
|
|
|
|
|
while (arg_cursor != argc) {
|
|
|
|
auto const arg = argv[arg_cursor];
|
|
|
|
if (!arg or !*arg)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (*arg == '-') {
|
|
|
|
arg_cursor += parse_named (argc - arg_cursor, argv + arg_cursor);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*m_positional[pos_cursor].acceptor1) (argv[arg_cursor++]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return arg_cursor;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2022-03-18 13:18:22 +11:00
|
|
|
template <typename OutputT>
|
|
|
|
static void
|
|
|
|
usage (
|
|
|
|
int argc,
|
|
|
|
char const * const* argv,
|
|
|
|
std::vector<cruft::cmdopt2::positional> const &positional,
|
|
|
|
std::vector<cruft::cmdopt2::keyword> const &keyword,
|
|
|
|
OutputT &output
|
|
|
|
) {
|
|
|
|
fmt::print (output, "Usage:");
|
2022-03-16 14:20:26 +11:00
|
|
|
if (argc > 0)
|
2022-03-18 13:18:22 +11:00
|
|
|
fmt::print (output, " {}", argv[0]);
|
2022-03-16 14:20:26 +11:00
|
|
|
|
|
|
|
static char constexpr OPTIONAL[2] { '[', ']' };
|
|
|
|
static char constexpr REQUIRED[2] { '<', '>' };
|
|
|
|
|
2022-03-18 13:18:22 +11:00
|
|
|
for (auto const &arg: keyword) {
|
2022-03-16 14:20:26 +11:00
|
|
|
auto const &delimiters = arg.required ? REQUIRED : OPTIONAL;
|
|
|
|
|
2022-03-18 14:04:09 +11:00
|
|
|
if (arg.short_) {
|
|
|
|
fmt::print (output, FMT_STRING (" {}-{}"), delimiters[0], *arg.short_);
|
|
|
|
if (arg.acceptor1)
|
|
|
|
fmt::print (output, FMT_STRING (" <{}>"), arg.name);
|
|
|
|
fmt::print (output, FMT_STRING ("{}"), delimiters[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arg.long_) {
|
|
|
|
fmt::print (output, FMT_STRING (" {}--{}"), delimiters[0], *arg.long_);
|
|
|
|
if (arg.acceptor1)
|
|
|
|
fmt::print (output, FMT_STRING (" <{}>"), arg.name);
|
|
|
|
fmt::print (output, FMT_STRING ("{}"), delimiters[1]);
|
|
|
|
}
|
2022-03-16 14:20:26 +11:00
|
|
|
}
|
|
|
|
|
2022-03-18 13:18:22 +11:00
|
|
|
for (auto const &arg: positional) {
|
2022-03-16 14:20:26 +11:00
|
|
|
auto const &delimiters = arg.required ? REQUIRED : OPTIONAL;
|
2022-03-18 14:04:09 +11:00
|
|
|
fmt::print (output, FMT_STRING (" {}{}{}"), delimiters[0], arg.name, delimiters[1]);
|
2022-03-16 14:20:26 +11:00
|
|
|
}
|
|
|
|
|
2022-03-18 13:18:22 +11:00
|
|
|
fmt::print (output, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void
|
|
|
|
parser::usage (int argc, char const * const* argv, std::ostream &os) const
|
|
|
|
{
|
|
|
|
::usage (argc, argv, m_positional, m_keyword, os);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void
|
|
|
|
parser::usage (int argc, char const * const* argv, FILE *fp) const
|
|
|
|
{
|
|
|
|
::usage (argc, argv, m_positional, m_keyword, fp);
|
2022-03-16 14:20:26 +11:00
|
|
|
}
|