Cpp-Argument-Parser/Src/argparser.cpp

373 lines
7.6 KiB
C++
Raw Normal View History

2022-07-26 02:57:57 +00:00
#include <iostream>
2024-07-08 20:21:09 +00:00
#include <cctype>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <vector>
#include "argparser.h"
2022-07-26 02:57:57 +00:00
namespace Arguments
{
2024-07-08 20:21:09 +00:00
static std::vector<Argument*> arguments;
static std::vector<PositionalArgument*> positionalArguments;
static size_t posArgIdx = 0;
2022-07-26 02:57:57 +00:00
2024-07-08 20:21:09 +00:00
Parser::Parser(int argc, char** argv)
: m_ArgCount(argc), m_ArgValues(argv), m_Index(0) { };
2022-07-26 02:57:57 +00:00
2024-07-08 20:21:09 +00:00
const char* Parser::value()
2022-07-26 02:57:57 +00:00
{
2024-07-08 20:21:09 +00:00
if (m_Index >= m_ArgCount) {
return nullptr;
} else {
return m_ArgValues[m_Index];
}
2022-07-26 02:57:57 +00:00
}
2024-07-08 20:21:09 +00:00
bool Parser::hasNext()
2022-07-26 02:57:57 +00:00
{
2024-07-08 20:21:09 +00:00
return m_Index + 1 < m_ArgCount;
2022-07-26 02:57:57 +00:00
}
2024-07-08 20:21:09 +00:00
void Parser::next()
2022-07-26 02:57:57 +00:00
{
2024-07-08 20:21:09 +00:00
m_Index++;
2022-07-26 02:57:57 +00:00
}
2024-07-08 20:21:09 +00:00
Argument::Argument(const char* name)
: found(false), m_Name(name), m_ShortName(0)
{
arguments.push_back(this);
};
Argument::Argument(char name)
: m_Name(nullptr), m_ShortName(name)
{
arguments.push_back(this);
};
Argument& Argument::alias(const char* alias)
2022-07-26 02:57:57 +00:00
{
2024-07-08 20:21:09 +00:00
m_Aliases.push_back(alias);
return *this;
2022-07-26 02:57:57 +00:00
}
2024-07-08 20:21:09 +00:00
Argument& Argument::alias(char alias)
2022-07-26 02:57:57 +00:00
{
2024-07-08 20:21:09 +00:00
m_ShortAliases.push_back(alias);
return *this;
2022-07-26 02:57:57 +00:00
}
2024-07-08 20:21:09 +00:00
bool Argument::matches(const char* arg)
2022-07-26 02:57:57 +00:00
{
2024-07-08 20:21:09 +00:00
if (m_Name != nullptr) {
if (std::strlen(arg) == std::strlen(m_Name)) {
if (std::strcmp(arg, m_Name) == 0) {
return true;
}
}
}
for (const char* alias: m_Aliases) {
if (std::strlen(arg) == std::strlen(alias)) {
if (std::strcmp(arg, alias) == 0) {
return true;
}
}
}
return false;
}
2024-07-08 20:21:09 +00:00
bool Argument::matchesShort(char arg)
{
2024-07-08 20:21:09 +00:00
if (arg == 0) {
return false;
}
2024-07-08 20:21:09 +00:00
if (arg == m_ShortName) {
return true;
}
2022-07-26 02:57:57 +00:00
2024-07-08 20:21:09 +00:00
for (char alias: m_ShortAliases) {
if (arg == alias) {
return true;
}
}
2024-07-08 20:21:09 +00:00
return false;
}
2024-07-08 20:21:09 +00:00
Int::Int(const char* name, int64_t defaultValue)
: Argument(name), value(defaultValue) { }
2024-07-08 20:21:09 +00:00
Int::Int(char name, int64_t defaultValue)
: Argument(name), value(defaultValue) { }
2024-07-08 20:21:09 +00:00
ERROR Int::noValue()
{
printMissingValueError();
return ERROR_MISSING_VALUE;
}
2024-07-08 20:21:09 +00:00
ERROR Int::parseValue(Parser& state)
{
2024-07-08 20:21:09 +00:00
if (!state.hasNext()) {
printMissingValueError();
return ERROR_MISSING_VALUE;
}
state.next();
const char* val = state.value();
if (*val == '-' || *val == '+') {
val++;
}
while (*val != '\0') {
if (*val < '0' || *val > '9') {
printInvalidIntError(state.value());
return ERROR_INVALID_INT;
}
val++;
}
found = true;
value = std::atoi(state.value());
return NO_ERROR;
}
2024-07-08 20:21:09 +00:00
void Int::printMissingValueError()
{
std::cerr << "Missing integer value for argument ";
if (m_Name != nullptr) {
std::cerr << "--" << m_Name << std::endl;
} else {
std::cerr << "-" << m_ShortName << std::endl;
}
}
2024-07-08 20:21:09 +00:00
String::String(const char* name, const char* defaultValue)
: Argument(name), value(defaultValue) { }
2024-07-08 20:21:09 +00:00
String::String(char name, const char* defaultValue)
: Argument(name), value(defaultValue) { }
2024-07-08 20:21:09 +00:00
ERROR String::noValue()
{
printMissingValueError();
return ERROR_MISSING_VALUE;
}
ERROR String::parseValue(Parser& state)
{
if (!state.hasNext()) {
printMissingValueError();
return ERROR_MISSING_VALUE;
}
2024-07-08 20:21:09 +00:00
found = true;
2024-07-08 20:21:09 +00:00
state.next();
value = state.value();
return NO_ERROR;
}
2024-07-08 20:21:09 +00:00
void String::printMissingValueError()
{
2024-07-08 20:21:09 +00:00
std::cerr << "Missing value for argument ";
if (m_Name != nullptr) {
std::cerr << "--" << m_Name << std::endl;
} else {
std::cerr << "-" << m_ShortName << std::endl;
}
}
2024-07-08 20:21:09 +00:00
Bool::Bool(const char* name)
: Argument(name) { }
2024-07-08 20:21:09 +00:00
Bool::Bool(char name)
: Argument(name) { }
2024-07-08 20:21:09 +00:00
ERROR Bool::noValue()
{
found = true;
return NO_ERROR;
}
2024-07-08 20:21:09 +00:00
ERROR Bool::parseValue(Parser& state)
{
2024-07-08 20:21:09 +00:00
(void) state;
found = true;
return NO_ERROR;
2022-07-26 02:57:57 +00:00
}
2024-07-08 20:21:09 +00:00
PositionalArgument::PositionalArgument(bool required)
2024-07-14 00:56:16 +00:00
: found(false), required(required)
{
2024-07-08 20:21:09 +00:00
if (required) {
for (PositionalArgument* arg: positionalArguments) {
if (!arg->required) {
std::cerr << "All required positional arguments must be registered before any optional positional arguments" << std::endl;
exit(-1);
}
}
}
positionalArguments.push_back(this);
}
2024-07-08 20:21:09 +00:00
PositionalInt::PositionalInt(int64_t defaultValue, bool required)
: PositionalArgument(required), value(defaultValue) { }
ERROR PositionalInt::parseValue(Parser& state)
{
const char* val = state.value();
if (*val == '-' || *val == '+') {
val++;
}
while (*val != '\0') {
if (*val < '0' || *val > '9') {
printInvalidIntError(state.value());
return ERROR_INVALID_INT;
}
val++;
}
found = true;
value = std::atoi(state.value());
return NO_ERROR;
}
2024-07-08 20:21:09 +00:00
PositionalString::PositionalString(const char* defaultValue, bool required)
: PositionalArgument(required), value(defaultValue) { }
ERROR PositionalString::parseValue(Parser& state)
{
2024-07-08 20:21:09 +00:00
found = true;
value = state.value();
2024-07-08 20:21:09 +00:00
return NO_ERROR;
}
2024-07-08 20:21:09 +00:00
ERROR parse(int argc, char** argv)
{
Parser parser(argc, argv);
while (parser.hasNext()) {
parser.next(); // This is OK on the first iteration because it skips the executable name
const char* value = parser.value();
bool firstDash = value[0] == '-';
bool secondDash = value[1] == '-';
if (firstDash && secondDash) {
ERROR err = findMatch(parser, value + 2);
if (err != NO_ERROR) {
return err;
}
} else if (firstDash) {
size_t len = std::strlen(value);
for (size_t i = 1; i < len; i++) {
ERROR err = findShortMatch(parser, value[i], i == len - 1);
if (err != NO_ERROR) {
return err;
}
}
} else {
ERROR err = matchPositional(parser);
if (err != NO_ERROR) {
return err;
}
}
}
if (posArgIdx < positionalArguments.size()) {
if (positionalArguments[posArgIdx]->required) {
std::cerr << "Missing required positional argument" << std::endl;
return ERROR_MISSING_POSITIONAL_ARGUMENT;
}
}
return NO_ERROR;
}
2024-07-08 20:21:09 +00:00
ERROR findMatch(Parser& parser, const char* name)
{
2024-07-08 20:21:09 +00:00
for (Argument* arg: arguments) {
if (arg->matches(name)) {
return arg->parseValue(parser);
}
}
printUnknownArgError(name);
return ERROR_UNKNOWN_ARGUMENT;
}
2024-07-08 20:21:09 +00:00
ERROR findShortMatch(Parser &parser, char name, bool last)
{
for (Argument* arg: arguments) {
if (arg->matchesShort(name)) {
if (last) {
return arg->parseValue(parser);
}
return arg->noValue();
}
}
printUnknownArgError(name);
return ERROR_UNKNOWN_ARGUMENT;
}
2024-07-08 20:21:09 +00:00
ERROR matchPositional(Parser &parser)
{
2024-07-08 20:21:09 +00:00
if (posArgIdx >= positionalArguments.size()) {
printUnknownPosArgError(parser.value());
return ERROR_UNKNOWN_POSITIONAL_ARGUMENT;
}
return positionalArguments[posArgIdx++]->parseValue(parser);
}
2024-07-08 20:21:09 +00:00
void printInvalidIntError(const char* value)
{
std::cerr << value << " is not an integer" << std::endl;
}
2024-07-08 20:21:09 +00:00
void printUnknownArgError(const char* argument)
{
2024-07-08 20:21:09 +00:00
std::cerr << "Unknown argument --" << argument << std::endl;
}
2024-07-08 20:21:09 +00:00
void printUnknownArgError(char argument)
{
std::cerr << "Unknown argument -" << argument << std::endl;
}
2024-07-08 20:21:09 +00:00
void printUnknownPosArgError(const char* value)
2022-07-26 02:57:57 +00:00
{
2024-07-08 20:21:09 +00:00
std::cerr << "Extra positional argument " << value << std::endl;
2022-07-26 02:57:57 +00:00
}
} // namespace Arguments
2024-07-08 20:21:09 +00:00