Config file and package tracking utility
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

281 lines
6.6 KiB

#include <algorithm> // std::find_if
#include <cstdio> // printf
#include <string_view>
#include "util/argparser.h"
namespace Util {
ArgParser::ArgParser()
{
}
ArgParser::~ArgParser()
{
}
void ArgParser::printOptionError(char name, Error error)
{
char tmp[] { name, '\0' };
printOptionError(tmp, error, false);
}
void ArgParser::printOptionError(const char* name, Error error, bool longName)
{
if (!m_errorMessages) {
return;
}
if (error == Error::InvalidOption) {
printf("%s: invalid option -- '%s'\n", m_name, name);
}
else if (error == Error::UnrecognizedOption) {
printf("%s: unrecognized option -- '%s'\n", m_name, name);
}
else if (error == Error::DoesntAllowArgument) {
printf("%s: option '--%s' doesn't allow an argument\n", m_name, name);
}
else if (error == Error::RequiresArgument) {
if (longName) {
printf("%s: option '--%s' requires an argument", m_name, name);
}
else {
printf("%s: option requires an argument -- '%s'\n", m_name, name);
}
}
// TODO: Print command usage, if it's enabled.
}
// Required: directly after || separated by space
// Optional: directly after
bool ArgParser::parseShortOption(std::string_view option, std::string_view next)
{
bool result = true;
printf("Parsing short option: '%s'\n", option.data());
char c;
std::string_view value;
for (std::string_view::size_type i = 0; i < option.size(); ++i) {
c = option.at(i);
printf("short '%c'\n", c);
auto foundOption = std::find_if(m_options.begin(), m_options.end(), [&c](Option& it) -> bool {
return it.shortName == c;
});
// Option does not exist
if (foundOption == m_options.cend()) {
printOptionError(c, Error::InvalidOption);
result = false;
if (m_exitOnFirstError) {
return result;
}
}
if (foundOption->requiresArgument == Required::No) {
// FIXME: Figure out why providing a nullptr breaks the lambda here.
foundOption->acceptValue("");
}
else if (foundOption->requiresArgument == Required::Yes) {
value = option.substr(i + 1);
if (value.empty() && next.empty()) {
foundOption->error = Error::RequiresArgument;
printOptionError(c, Error::RequiresArgument);
result = false;
if (m_exitOnFirstError) {
return result;
}
}
else if (!value.empty()) {
foundOption->acceptValue(value.data());
}
else if (next[0] == '-') {
foundOption->error = Error::RequiresArgument;
printOptionError(c, Error::RequiresArgument);
result = false;
if (m_exitOnFirstError) {
return result;
}
}
else {
foundOption->acceptValue(next.data());
m_optionIndex++;
}
break;
}
else if (foundOption->requiresArgument == Required::Optional) {
value = option.substr(i + 1);
if (!value.empty()) {
foundOption->acceptValue(value.data());
break;
}
}
}
return result;
}
// Required: directly after, separated by '=' || separated by space
// Optional: directly after, separated by '='
bool ArgParser::parseLongOption(std::string_view option, std::string_view next)
{
bool result = true;
std::string_view name = option.substr(0, option.find_first_of('='));
std::string_view value = option.substr(option.find_first_of('=') + 1);
bool argumentProvided = true;
if (name.compare(value) == 0 && option.find('=') == std::string_view::npos) {
argumentProvided = false;
}
printf("Parsing long option: '%s' with value '%s'\n", name.data(), argumentProvided ? value.data() : "");
auto foundOption = std::find_if(m_options.begin(), m_options.end(), [&name](Option& it) -> bool {
return it.longName == name;
});
if (foundOption == m_options.cend()) {
foundOption->error = Error::UnrecognizedOption;
printOptionError(name.data(), Error::UnrecognizedOption);
result = false;
if (m_exitOnFirstError) {
return result;
}
}
if (argumentProvided) {
if (foundOption->requiresArgument == Required::No) {
foundOption->error = Error::DoesntAllowArgument;
printOptionError(name.data(), Error::DoesntAllowArgument);
result = false;
if (m_exitOnFirstError) {
return result;
}
}
else if (foundOption->requiresArgument == Required::Yes) {
foundOption->acceptValue(value.data());
}
else if (foundOption->requiresArgument == Required::Optional) {
foundOption->acceptValue(value.data());
}
}
else if (!next.empty() && foundOption->requiresArgument == Required::Yes) {
if (next[0] == '-') {
foundOption->error = Error::RequiresArgument;
printOptionError(name.data(), Error::RequiresArgument);
result = false;
if (m_exitOnFirstError) {
return result;
}
}
else {
foundOption->acceptValue(next.data());
m_optionIndex++;
}
}
else if (foundOption->requiresArgument == Required::Yes) {
foundOption->error = Error::RequiresArgument;
printOptionError(name.data(), Error::RequiresArgument);
result = false;
if (m_exitOnFirstError) {
return result;
}
}
else {
// FIXME: Figure out why providing a nullptr breaks the lambda here.
foundOption->acceptValue("");
}
return result;
}
bool ArgParser::parse(int argc, const char* argv[])
{
// Get program name
m_name = argv[0] + std::string_view(argv[0]).find_last_of('/') + 1;
printf("name: %s\n", m_name);
std::string_view argument;
std::string_view next;
for (; m_optionIndex < argc; ++m_optionIndex) {
printf("argv[%d]: %s\n", m_optionIndex, argv[m_optionIndex]);
argument = argv[m_optionIndex];
if (m_optionIndex + 1 < argc && argv[m_optionIndex + 1][0] != '-') {
next = argv[m_optionIndex + 1];
}
else {
next = {};
}
// Long Option
if (argument[0] == '-' && argument[1] == '-') {
argument = argument.substr(argument.find_first_not_of('-'));
parseLongOption(argument, next);
}
// Short Option
else if (argument[0] == '-') {
argument = argument.substr(argument.find_first_not_of('-'));
parseShortOption(argument, next);
}
// Argument
else {
printf("-> argu: '%s'", argument.data());
}
}
return true;
}
void ArgParser::addOption(Option&& option)
{
m_options.push_back(option);
}
void ArgParser::addOption(bool& value, char shortName, const char* longName, const char* usageString, const char* manString)
{
Option option {
shortName,
longName,
nullptr,
usageString,
manString,
Required::No,
[&value](const char*) -> bool {
value = true;
return true;
}
};
addOption(std::move(option));
}
void ArgParser::addOption(std::string& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument)
{
Option option {
shortName,
longName,
argumentName,
usageString,
manString,
requiresArgument,
[&value](const char* a) -> bool {
value = a;
return true;
}
};
addOption(std::move(option));
}
} // namespace Util