mirror of
https://github.com/Cameron-Reed1/Cpp-Argument-Parser.git
synced 2025-01-11 00:06:47 +00:00
v2
This commit is contained in:
parent
9cc2348ded
commit
f8f5b4eafc
213
Inc/argparser.h
213
Inc/argparser.h
@ -2,88 +2,161 @@
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Cam
|
||||
{
|
||||
namespace Arguments
|
||||
{
|
||||
|
||||
enum ERROR {
|
||||
NO_ERROR = 0,
|
||||
ERROR_UNKNOWN_OPTION,
|
||||
ERROR_INCORRECT_TYPE,
|
||||
ERROR_MISSING_ARGUMENT,
|
||||
ERROR_UNKNOWN_ARGUMENT,
|
||||
ERROR_UNKNOWN_POSITIONAL_ARGUMENT,
|
||||
ERROR_MISSING_VALUE,
|
||||
ERROR_MISSING_POSITIONAL_ARGUMENT,
|
||||
SPECIAL_CASE_HELP
|
||||
};
|
||||
|
||||
enum OPTTYPE {
|
||||
FLAG = 0,
|
||||
STRING,
|
||||
INT
|
||||
};
|
||||
|
||||
class Option
|
||||
{
|
||||
public:
|
||||
Option(const char* name, const char* description, OPTTYPE type);
|
||||
Option(const char* name, const char short_name, const char* description, OPTTYPE type);
|
||||
Option(const char short_name, const char* description, OPTTYPE type);
|
||||
bool found();
|
||||
void* data;
|
||||
private:
|
||||
const char* m_name;
|
||||
const char m_short_name;
|
||||
const char* m_description;
|
||||
OPTTYPE m_type;
|
||||
bool m_found;
|
||||
|
||||
friend class Parser;
|
||||
};
|
||||
|
||||
class PositionalArgument
|
||||
{
|
||||
public:
|
||||
PositionalArgument(const char* name, bool required, OPTTYPE type = STRING);
|
||||
bool found();
|
||||
void* data;
|
||||
private:
|
||||
const char* m_name;
|
||||
OPTTYPE m_type;
|
||||
bool m_req;
|
||||
bool m_found;
|
||||
|
||||
friend class Parser;
|
||||
ERROR_INVALID_INT,
|
||||
SPECIAL_CASE_HELP,
|
||||
};
|
||||
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
Parser(const char* program_name);
|
||||
void set_description(const char* description);
|
||||
void add_option(Option* opt);
|
||||
void add_positional_argument(PositionalArgument* arg);
|
||||
ERROR parse(int argc, char** argv);
|
||||
Parser(int argc, char** argv);
|
||||
|
||||
bool hasNext();
|
||||
void next();
|
||||
const char* value();
|
||||
|
||||
private:
|
||||
ERROR print_help_message();
|
||||
ERROR handle_long_option(const char* option, const char* next_value);
|
||||
ERROR handle_short_option(const char option, const char* next_value);
|
||||
ERROR handle_positional_argument(const char* arg);
|
||||
ERROR get_option_data(Option* opt, const char* option_name, const char* data_str);
|
||||
ERROR get_option_data(Option* opt, const char option_name, const char* data_str);
|
||||
ERROR unknown_option(const char* option);
|
||||
ERROR unknown_option(const char option);
|
||||
ERROR incorrect_type(const char* option, const char* got);
|
||||
ERROR missing_argument(const char* option);
|
||||
ERROR missing_argument(const char option);
|
||||
ERROR missing_positional_argument(const char* arg);
|
||||
private:
|
||||
const char* m_program_name;
|
||||
const char* m_description;
|
||||
uint16_t m_opt_index;
|
||||
uint16_t m_pos_index;
|
||||
std::vector<Option*> m_options;
|
||||
std::vector<PositionalArgument*> m_positional_args;
|
||||
int m_ArgCount;
|
||||
char** m_ArgValues;
|
||||
|
||||
int m_Index;
|
||||
};
|
||||
|
||||
|
||||
class Argument
|
||||
{
|
||||
public:
|
||||
Argument(const char* name);
|
||||
Argument(char name);
|
||||
|
||||
Argument& description(const char* desc);
|
||||
|
||||
Argument& alias(const char* alias);
|
||||
Argument& alias(char alias);
|
||||
|
||||
bool matches(const char*);
|
||||
bool matchesShort(char);
|
||||
|
||||
virtual ERROR noValue() = 0;
|
||||
virtual ERROR parseValue(Parser& state) = 0;
|
||||
|
||||
public:
|
||||
bool found;
|
||||
|
||||
protected:
|
||||
const char* m_Name;
|
||||
char m_ShortName;
|
||||
|
||||
const char* m_Description;
|
||||
|
||||
private:
|
||||
std::vector<const char*> m_Aliases;
|
||||
std::vector<char> m_ShortAliases;
|
||||
};
|
||||
|
||||
|
||||
class Int: public Argument
|
||||
{
|
||||
public:
|
||||
Int(const char* name, int64_t defaultValue);
|
||||
Int(char name, int64_t defaultValue);
|
||||
|
||||
ERROR noValue() override;
|
||||
ERROR parseValue(Parser& state) override;
|
||||
|
||||
public:
|
||||
int64_t value;
|
||||
|
||||
private:
|
||||
void printMissingValueError();
|
||||
};
|
||||
|
||||
class String: public Argument
|
||||
{
|
||||
public:
|
||||
String(const char* name, const char* defaultValue);
|
||||
String(char name, const char* defaultValue);
|
||||
|
||||
ERROR noValue() override;
|
||||
ERROR parseValue(Parser& state) override;
|
||||
|
||||
public:
|
||||
const char* value;
|
||||
|
||||
private:
|
||||
void printMissingValueError();
|
||||
};
|
||||
|
||||
class Bool: public Argument
|
||||
{
|
||||
public:
|
||||
Bool(const char* name);
|
||||
Bool(char name);
|
||||
|
||||
ERROR noValue() override;
|
||||
ERROR parseValue(Parser& state) override;
|
||||
};
|
||||
|
||||
|
||||
class PositionalArgument
|
||||
{
|
||||
public:
|
||||
PositionalArgument(bool required);
|
||||
|
||||
void description(const char* desc);
|
||||
|
||||
virtual ERROR parseValue(Parser& state) = 0;
|
||||
|
||||
public:
|
||||
bool found;
|
||||
bool required;
|
||||
|
||||
private:
|
||||
const char* m_Description;
|
||||
};
|
||||
|
||||
|
||||
class PositionalInt: public PositionalArgument
|
||||
{
|
||||
public:
|
||||
PositionalInt(int64_t defaultValue, bool required);
|
||||
|
||||
ERROR parseValue(Parser& state) override;
|
||||
|
||||
public:
|
||||
int64_t value;
|
||||
};
|
||||
|
||||
class PositionalString: public PositionalArgument
|
||||
{
|
||||
public:
|
||||
PositionalString(const char* defaultValue, bool required);
|
||||
|
||||
ERROR parseValue(Parser& state) override;
|
||||
|
||||
public:
|
||||
const char* value;
|
||||
};
|
||||
|
||||
|
||||
ERROR parse(int argc, char** argv);
|
||||
ERROR findMatch(Parser& parser, const char* name);
|
||||
ERROR findShortMatch(Parser& parser, char name, bool last);
|
||||
ERROR matchPositional(Parser& parser);
|
||||
|
||||
void printInvalidIntError(const char* value);
|
||||
void printUnknownArgError(const char* argument);
|
||||
void printUnknownArgError(char argument);
|
||||
void printUnknownPosArgError(const char* value);
|
||||
|
||||
} // namespace Arguments
|
||||
} // namespace Cam
|
||||
|
||||
|
35
Makefile
35
Makefile
@ -2,9 +2,10 @@ TYPE = STATIC
|
||||
SHARED_TYPE = SHARED
|
||||
STATIC_TYPE = STATIC
|
||||
|
||||
BUILD_DIR = bin
|
||||
SOURCE_DIR = Src
|
||||
HEADER_DIR = Inc
|
||||
BUILD_DIR = bin
|
||||
|
||||
HEADER_INSTALL_DIR = /usr/local/include
|
||||
INSTALL_DIR = /usr/local/lib
|
||||
|
||||
@ -17,8 +18,7 @@ endif
|
||||
|
||||
OPT = -O2
|
||||
|
||||
INCS = \
|
||||
-IInc
|
||||
INCS = -IInc
|
||||
|
||||
C_SOURCES = $(wildcard $(SOURCE_DIR)/*.c)
|
||||
CXX_SOURCES = $(wildcard $(SOURCE_DIR)/*.cpp)
|
||||
@ -38,25 +38,20 @@ endif
|
||||
|
||||
HEADERS = $(wildcard $(HEADER_DIR)/*.h)
|
||||
|
||||
CP = cp
|
||||
MKDIR = mkdir -p
|
||||
ARFLAGS = rvc
|
||||
SED = sed -i -e
|
||||
CHMOD = chmod
|
||||
LDCONFIG = ldconfig
|
||||
|
||||
all: $(BUILD_DIR)/$(LIB_FILE_NAME)
|
||||
|
||||
ifneq ($(TYPE), $(SHARED_TYPE))
|
||||
shared: clean
|
||||
@$(SED) "s/^TYPE = .*$$/TYPE = $(SHARED_TYPE)/" Makefile
|
||||
@sed -i -e "s/^TYPE = .*$$/TYPE = $(SHARED_TYPE)/" Makefile
|
||||
|
||||
.PHONY: shared
|
||||
endif
|
||||
|
||||
ifneq ($(TYPE), $(STATIC_TYPE))
|
||||
static: clean
|
||||
@$(SED) "s/^TYPE = .*$$/TYPE = $(STATIC_TYPE)/" Makefile
|
||||
@sed -i -e "s/^TYPE = .*$$/TYPE = $(STATIC_TYPE)/" Makefile
|
||||
|
||||
.PHONY: static
|
||||
endif
|
||||
@ -75,29 +70,29 @@ $(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.cpp | $(BUILD_DIR)
|
||||
$(CXX) -c $< $(CXXFLAGS) -o $@
|
||||
|
||||
$(BUILD_DIR):
|
||||
$(MKDIR) $@
|
||||
mkdir -p $@
|
||||
|
||||
test: all
|
||||
$(CXX) test.cpp $(CXXFLAGS) -l$(LIB_NAME) -L$(BUILD_DIR) $(TESTFLAGS) -o $(BUILD_DIR)/test
|
||||
test: test.cpp all | $(BUILD_DIR)
|
||||
$(CXX) $< $(CXXFLAGS) -l$(LIB_NAME) -L$(BUILD_DIR) $(TESTFLAGS) -o $(BUILD_DIR)/test
|
||||
-$(BUILD_DIR)/test
|
||||
|
||||
test_installed:
|
||||
$(CXX) test.cpp $(CXXFLAGS) -l$(LIB_NAME) -o $(BUILD_DIR)/test
|
||||
test_installed: test.cpp | $(BUILD_DIR)
|
||||
$(CXX) $< $(CXXFLAGS) -l$(LIB_NAME) -o $(BUILD_DIR)/test
|
||||
-$(BUILD_DIR)/test
|
||||
|
||||
install: all
|
||||
$(CP) $(BUILD_DIR)/$(LIB_FILE_NAME) $(INSTALL_DIR)
|
||||
$(CP) $(HEADERS) $(HEADER_INSTALL_DIR)
|
||||
@$(CHMOD) 0755 $(INSTALL_DIR)/$(LIB_FILE_NAME)
|
||||
cp $(BUILD_DIR)/$(LIB_FILE_NAME) $(INSTALL_DIR)
|
||||
cp $(HEADERS) $(HEADER_INSTALL_DIR)
|
||||
@chmod 0755 $(INSTALL_DIR)/$(LIB_FILE_NAME)
|
||||
ifeq ($(TYPE), $(SHARED_TYPE))
|
||||
@$(LDCONFIG)
|
||||
@ldconfig
|
||||
endif
|
||||
|
||||
uninstall:
|
||||
$(RM) $(INSTALL_DIR)/$(LIB_FILE_NAME)
|
||||
$(RM) $(addprefix $(HEADER_INSTALL_DIR)/, $(notdir $(HEADERS)))
|
||||
ifeq ($(TYPE), $(SHARED_TYPE))
|
||||
@$(LDCONFIG)
|
||||
@ldconfig
|
||||
endif
|
||||
|
||||
clean:
|
||||
|
@ -1,315 +1,384 @@
|
||||
#include "argparser.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
int stringToInt(const char* input)
|
||||
{
|
||||
int value = 0;
|
||||
for (int i = 0; input[i] != 0; i++)
|
||||
{
|
||||
if (input[i] < '0' || input[i] > '9')
|
||||
return -1;
|
||||
value = (value * 10) + input[i] - '0';
|
||||
}
|
||||
#include "argparser.h"
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
namespace Cam
|
||||
{
|
||||
namespace Arguments
|
||||
{
|
||||
static std::vector<Argument*> arguments;
|
||||
static std::vector<PositionalArgument*> positionalArguments;
|
||||
static size_t posArgIdx = 0;
|
||||
|
||||
Option::Option(const char* name, const char* description, OPTTYPE type)
|
||||
: m_name(name), m_short_name(0), m_description(description), m_type(type), m_found(false) {}
|
||||
|
||||
Option::Option(const char* name, const char short_name, const char* description, OPTTYPE type)
|
||||
: m_name(name), m_short_name(short_name), m_description(description), m_type(type), m_found(false) {}
|
||||
Parser::Parser(int argc, char** argv)
|
||||
: m_ArgCount(argc), m_ArgValues(argv), m_Index(0) { };
|
||||
|
||||
Option::Option(const char short_name, const char* description, OPTTYPE type)
|
||||
: m_name(nullptr), m_short_name(short_name), m_description(description), m_type(type), m_found(false) {}
|
||||
|
||||
bool Option::found()
|
||||
const char* Parser::value()
|
||||
{
|
||||
return m_found;
|
||||
}
|
||||
|
||||
PositionalArgument::PositionalArgument(const char* name, bool required, OPTTYPE type /* = STRING */)
|
||||
: m_name(name), m_type(type), m_req(required), m_found(false)
|
||||
{
|
||||
if (type == FLAG) {
|
||||
std::cout << "Warning OPTTYPE 'FLAG' is not meant to be used with positional arguments" << std::endl;
|
||||
std::cout << "Assuming type 'STRING' instead" << std::endl;
|
||||
m_type = STRING;
|
||||
}
|
||||
}
|
||||
|
||||
bool PositionalArgument::found()
|
||||
{
|
||||
return m_found;
|
||||
}
|
||||
|
||||
Parser::Parser(const char* program_name)
|
||||
: m_program_name(program_name), m_description(nullptr), m_options(), m_positional_args() {}
|
||||
|
||||
void Parser::set_description(const char* description)
|
||||
{
|
||||
m_description = description;
|
||||
}
|
||||
|
||||
void Parser::add_option(Option* opt)
|
||||
{
|
||||
m_options.push_back(opt);
|
||||
}
|
||||
|
||||
void Parser::add_positional_argument(PositionalArgument* arg)
|
||||
{
|
||||
m_positional_args.push_back(arg);
|
||||
}
|
||||
|
||||
ERROR Parser::parse(int argc, char** argv)
|
||||
{
|
||||
m_pos_index = 0;
|
||||
for (m_opt_index = 1; m_opt_index < argc; m_opt_index++)
|
||||
{
|
||||
if (argv[m_opt_index][0] == '-' && argv[m_opt_index][1] == '-') {
|
||||
|
||||
ERROR ret = handle_long_option(argv[m_opt_index] + 2, m_opt_index + 1 < argc ? argv[m_opt_index + 1] : nullptr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
} else if (argv[m_opt_index][0] == '-' && argv[m_opt_index][1] != '-') {
|
||||
|
||||
for (uint8_t i = 1; argv[m_opt_index][i] != 0; i++) {
|
||||
bool last = argv[m_opt_index][i + 1] == 0;
|
||||
ERROR ret = handle_short_option(argv[m_opt_index][i], m_opt_index + 1 < argc && last ? argv[m_opt_index + 1] : nullptr);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (last)
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_Index >= m_ArgCount) {
|
||||
return nullptr;
|
||||
} else {
|
||||
|
||||
ERROR ret = handle_positional_argument(argv[m_opt_index]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return m_ArgValues[m_Index];
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pos_index < m_positional_args.size()) {
|
||||
for (; m_pos_index < m_positional_args.size(); m_pos_index++) {
|
||||
if (m_positional_args[m_pos_index]->m_req)
|
||||
return missing_positional_argument(m_positional_args[m_pos_index]->m_name);
|
||||
bool Parser::hasNext()
|
||||
{
|
||||
return m_Index + 1 < m_ArgCount;
|
||||
}
|
||||
|
||||
void Parser::next()
|
||||
{
|
||||
m_Index++;
|
||||
}
|
||||
|
||||
|
||||
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::description(const char* desc)
|
||||
{
|
||||
m_Description = desc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Argument& Argument::alias(const char* alias)
|
||||
{
|
||||
m_Aliases.push_back(alias);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Argument& Argument::alias(char alias)
|
||||
{
|
||||
m_ShortAliases.push_back(alias);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Argument::matches(const char* arg)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
bool Argument::matchesShort(char arg)
|
||||
{
|
||||
if (arg == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arg == m_ShortName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (char alias: m_ShortAliases) {
|
||||
if (arg == alias) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Int::Int(const char* name, int64_t defaultValue)
|
||||
: Argument(name), value(defaultValue) { }
|
||||
|
||||
Int::Int(char name, int64_t defaultValue)
|
||||
: Argument(name), value(defaultValue) { }
|
||||
|
||||
ERROR Int::noValue()
|
||||
{
|
||||
printMissingValueError();
|
||||
return ERROR_MISSING_VALUE;
|
||||
}
|
||||
|
||||
ERROR Int::parseValue(Parser& state)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
ERROR Parser::handle_long_option(const char* option, const char* next_value)
|
||||
void Int::printMissingValueError()
|
||||
{
|
||||
Option* matched_option = nullptr;
|
||||
|
||||
for (Option* opt: m_options) {
|
||||
if (opt->m_name == nullptr)
|
||||
continue;
|
||||
if (strcmp(opt->m_name, option) == 0) {
|
||||
matched_option = opt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matched_option == nullptr) {
|
||||
|
||||
if (strcmp("help", option) == 0)
|
||||
return print_help_message();
|
||||
|
||||
return unknown_option(option);
|
||||
|
||||
}
|
||||
|
||||
|
||||
matched_option->m_found = true;
|
||||
return get_option_data(matched_option, option, next_value);
|
||||
}
|
||||
|
||||
ERROR Parser::handle_short_option(const char option, const char* next_value)
|
||||
{
|
||||
Option* matched_option = nullptr;
|
||||
|
||||
for (Option* opt: m_options) {
|
||||
if (opt->m_short_name == option) {
|
||||
matched_option = opt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matched_option == nullptr) {
|
||||
|
||||
if ('h' == option)
|
||||
return print_help_message();
|
||||
|
||||
return unknown_option(option);
|
||||
|
||||
}
|
||||
|
||||
|
||||
matched_option->m_found = true;
|
||||
return get_option_data(matched_option, option, next_value);
|
||||
}
|
||||
|
||||
ERROR Parser::handle_positional_argument(const char* arg)
|
||||
{
|
||||
if (m_pos_index >= m_positional_args.size())
|
||||
return unknown_option(arg);
|
||||
|
||||
PositionalArgument* pos_arg = m_positional_args[m_pos_index];
|
||||
|
||||
if (pos_arg->m_type == INT) {
|
||||
int* val = new int;
|
||||
if ((*val = stringToInt(arg)) == -1)
|
||||
return incorrect_type(pos_arg->m_name, arg);
|
||||
|
||||
pos_arg->data = val;
|
||||
std::cerr << "Missing integer value for argument ";
|
||||
if (m_Name != nullptr) {
|
||||
std::cerr << "--" << m_Name << std::endl;
|
||||
} else {
|
||||
pos_arg->data = (void*) arg;
|
||||
std::cerr << "-" << m_ShortName << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
pos_arg->m_found = true;
|
||||
m_pos_index++;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
ERROR Parser::get_option_data(Option* opt, const char* option_name, const char* data_str)
|
||||
String::String(const char* name, const char* defaultValue)
|
||||
: Argument(name), value(defaultValue) { }
|
||||
|
||||
String::String(char name, const char* defaultValue)
|
||||
: Argument(name), value(defaultValue) { }
|
||||
|
||||
ERROR String::noValue()
|
||||
{
|
||||
if (opt->m_type == FLAG)
|
||||
printMissingValueError();
|
||||
return ERROR_MISSING_VALUE;
|
||||
}
|
||||
|
||||
ERROR String::parseValue(Parser& state)
|
||||
{
|
||||
if (!state.hasNext()) {
|
||||
printMissingValueError();
|
||||
return ERROR_MISSING_VALUE;
|
||||
}
|
||||
|
||||
found = true;
|
||||
|
||||
state.next();
|
||||
value = state.value();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
if (data_str == nullptr || data_str[0] == '-')
|
||||
return missing_argument(option_name);
|
||||
|
||||
if (opt->m_type == INT) {
|
||||
int* val = new int;
|
||||
if ((*val = stringToInt(data_str)) == -1)
|
||||
return incorrect_type(opt->m_name, data_str);
|
||||
opt->data = val;
|
||||
void String::printMissingValueError()
|
||||
{
|
||||
std::cerr << "Missing value for argument ";
|
||||
if (m_Name != nullptr) {
|
||||
std::cerr << "--" << m_Name << std::endl;
|
||||
} else {
|
||||
opt->data = (void*) data_str;
|
||||
std::cerr << "-" << m_ShortName << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
m_opt_index++;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
ERROR Parser::get_option_data(Option* opt, const char option_name, const char* data_str)
|
||||
Bool::Bool(const char* name)
|
||||
: Argument(name) { }
|
||||
|
||||
Bool::Bool(char name)
|
||||
: Argument(name) { }
|
||||
|
||||
ERROR Bool::noValue()
|
||||
{
|
||||
if (opt->m_type == FLAG)
|
||||
found = true;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
if (data_str == nullptr || data_str[0] == '-')
|
||||
return missing_argument(option_name);
|
||||
ERROR Bool::parseValue(Parser& state)
|
||||
{
|
||||
(void) state;
|
||||
|
||||
if (opt->m_type == INT) {
|
||||
int* val = new int;
|
||||
if ((*val = stringToInt(data_str)) == -1)
|
||||
return incorrect_type(opt->m_name, data_str);
|
||||
opt->data = val;
|
||||
found = true;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
PositionalArgument::PositionalArgument(bool required)
|
||||
: found(false), required(required), m_Description(nullptr)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
void PositionalArgument::description(const char* desc)
|
||||
{
|
||||
m_Description = desc;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
PositionalString::PositionalString(const char* defaultValue, bool required)
|
||||
: PositionalArgument(required), value(defaultValue) { }
|
||||
|
||||
ERROR PositionalString::parseValue(Parser& state)
|
||||
{
|
||||
found = true;
|
||||
value = state.value();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
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 {
|
||||
opt->data = (void*) data_str;
|
||||
ERROR err = matchPositional(parser);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_opt_index++;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
ERROR Parser::unknown_option(const char* option)
|
||||
{
|
||||
std::cout << m_program_name << ": invalid option '" << option << '\'' << std::endl << std::endl;
|
||||
std::cout << "See " << m_program_name << " --help" << std::endl;
|
||||
|
||||
return ERROR_UNKNOWN_OPTION;
|
||||
}
|
||||
|
||||
ERROR Parser::unknown_option(const char option)
|
||||
{
|
||||
std::cout << m_program_name << ": invalid option: " << option << std::endl << std::endl;
|
||||
std::cout << "See " << m_program_name << " --help" << std::endl;
|
||||
|
||||
return ERROR_UNKNOWN_OPTION;
|
||||
}
|
||||
|
||||
ERROR Parser::incorrect_type(const char* option, const char* got)
|
||||
{
|
||||
std::cout << m_program_name << ": argument '" << option << "' expects an integer as an argument, got " << got << " instead" << std::endl;
|
||||
|
||||
return ERROR_INCORRECT_TYPE;
|
||||
}
|
||||
|
||||
ERROR Parser::missing_argument(const char* option)
|
||||
{
|
||||
std::cout << m_program_name << ": Missing argument for option '" << option << '\'' << std::endl;
|
||||
|
||||
return ERROR_MISSING_ARGUMENT;
|
||||
}
|
||||
|
||||
ERROR Parser::missing_argument(const char option)
|
||||
{
|
||||
std::cout << m_program_name << ": Missing argument for option '" << option << '\'' << std::endl;
|
||||
|
||||
return ERROR_MISSING_ARGUMENT;
|
||||
}
|
||||
|
||||
ERROR Parser::missing_positional_argument(const char* arg)
|
||||
{
|
||||
std::cout << m_program_name << ": Missing required positional argument '" << arg << "'" << std::endl << std::endl;
|
||||
std::cout << "See " << m_program_name << " --help" << std::endl;
|
||||
|
||||
if (posArgIdx < positionalArguments.size()) {
|
||||
if (positionalArguments[posArgIdx]->required) {
|
||||
std::cerr << "Missing required positional argument" << std::endl;
|
||||
return ERROR_MISSING_POSITIONAL_ARGUMENT;
|
||||
}
|
||||
|
||||
ERROR Parser::print_help_message()
|
||||
{
|
||||
std::cout << "Usage: " << m_program_name << (m_options.size() > 0 ? " [OPTIONS]" : "");
|
||||
for (PositionalArgument* arg: m_positional_args)
|
||||
{
|
||||
std::cout << " " << arg->m_name;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
|
||||
if (m_options.size() > 0)
|
||||
std::cout << std::endl << std::endl << "Options:" << std::endl;
|
||||
for (Option* opt: m_options)
|
||||
{
|
||||
std::cout << " ";
|
||||
if (opt->m_short_name != 0)
|
||||
std::cout << '-' << opt->m_short_name;
|
||||
|
||||
if (opt->m_short_name != 0 && opt->m_name != nullptr)
|
||||
std::cout << ", ";
|
||||
|
||||
if (opt->m_name != nullptr)
|
||||
std::cout << "--" << opt->m_name;
|
||||
|
||||
if (opt->m_name != nullptr) {
|
||||
for (uint8_t i = 0; i < 20 - strlen(opt->m_name) - ((opt->m_short_name != 0) * 4); i++)
|
||||
std::cout << ' ';
|
||||
} else {
|
||||
for (uint8_t i = 0; i < 24 - ((opt->m_short_name != 0) * 4); i++)
|
||||
std::cout << ' ';
|
||||
}
|
||||
|
||||
std::cout << opt->m_description << std::endl;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
if (m_description)
|
||||
std::cout << std::endl << std::endl << m_description << std::endl;
|
||||
ERROR findMatch(Parser& parser, const char* name)
|
||||
{
|
||||
for (Argument* arg: arguments) {
|
||||
if (arg->matches(name)) {
|
||||
return arg->parseValue(parser);
|
||||
}
|
||||
}
|
||||
|
||||
return SPECIAL_CASE_HELP;
|
||||
printUnknownArgError(name);
|
||||
return ERROR_UNKNOWN_ARGUMENT;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ERROR matchPositional(Parser &parser)
|
||||
{
|
||||
if (posArgIdx >= positionalArguments.size()) {
|
||||
printUnknownPosArgError(parser.value());
|
||||
return ERROR_UNKNOWN_POSITIONAL_ARGUMENT;
|
||||
}
|
||||
|
||||
return positionalArguments[posArgIdx++]->parseValue(parser);
|
||||
}
|
||||
|
||||
void printInvalidIntError(const char* value)
|
||||
{
|
||||
std::cerr << value << " is not an integer" << std::endl;
|
||||
}
|
||||
|
||||
void printUnknownArgError(const char* argument)
|
||||
{
|
||||
std::cerr << "Unknown argument --" << argument << std::endl;
|
||||
}
|
||||
|
||||
void printUnknownArgError(char argument)
|
||||
{
|
||||
std::cerr << "Unknown argument -" << argument << std::endl;
|
||||
}
|
||||
|
||||
void printUnknownPosArgError(const char* value)
|
||||
{
|
||||
std::cerr << "Extra positional argument " << value << std::endl;
|
||||
}
|
||||
|
||||
} // namespace Arguments
|
||||
} // namespace Cam
|
||||
|
||||
|
49
test.cpp
49
test.cpp
@ -1,48 +1,37 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "argparser.h"
|
||||
|
||||
using namespace Cam;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
Arguments::Parser parser = Arguments::Parser("test");
|
||||
Arguments::String first('f', "none");
|
||||
first.description("First arg");
|
||||
|
||||
parser.set_description("A test of argument parsing :)");
|
||||
Arguments::Int second("second", 0);
|
||||
second.description("Second arg").alias('s');
|
||||
|
||||
Arguments::Option first('f', "First arg", Arguments::STRING);
|
||||
Arguments::Option second("second", 's', "Second arg", Arguments::INT);
|
||||
Arguments::Option third("third", "Third arg", Arguments::FLAG);
|
||||
Arguments::Bool third("third");
|
||||
third.description("Third arg").alias("abc").alias('t');
|
||||
|
||||
Arguments::PositionalArgument name("name", true, Arguments::STRING);
|
||||
Arguments::PositionalArgument age("age", false, Arguments::INT);
|
||||
Arguments::PositionalString name("", true);
|
||||
Arguments::PositionalInt age(0, false);
|
||||
|
||||
parser.add_option(&first);
|
||||
parser.add_option(&second);
|
||||
parser.add_option(&third);
|
||||
|
||||
parser.add_positional_argument(&name);
|
||||
parser.add_positional_argument(&age);
|
||||
|
||||
if (parser.parse(argc, argv) != 0) {
|
||||
if (Arguments::parse(argc, argv) != Arguments::NO_ERROR) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << "Found:" << std::endl;
|
||||
std::cout << "\tFirst: " << first.found() << std::endl;
|
||||
std::cout << "\tSecond: " << second.found() << std::endl;
|
||||
std::cout << "\tThird: " << third.found() << std::endl;
|
||||
std::cout << "\tName: " << name.found() << std::endl;
|
||||
std::cout << "\tAge: " << age.found() << std::endl;
|
||||
std::cout << "\tFirst: " << first.found << std::endl;
|
||||
std::cout << "\tSecond: " << second.found << std::endl;
|
||||
std::cout << "\tThird: " << third.found << std::endl;
|
||||
std::cout << "\tName: " << name.found << std::endl;
|
||||
std::cout << "\tAge: " << age.found << std::endl;
|
||||
|
||||
if (first.found() || second.found() || name.found() || age.found())
|
||||
std::cout << std::endl << "Values:" << std::endl;
|
||||
if (first.found())
|
||||
std::cout << "\tFirst: " << (char*) first.data << std::endl;
|
||||
if (second.found())
|
||||
std::cout << "\tSecond: " << *((int*) second.data) << std::endl;
|
||||
if (name.found())
|
||||
std::cout << "\tName: " << (char*) name.data << std::endl;
|
||||
if (age.found())
|
||||
std::cout << "\tAge: " << *((int*) age.data) << std::endl;
|
||||
std::cout << "\tFirst: " << first.value << std::endl;
|
||||
std::cout << "\tSecond: " << second.value << std::endl;
|
||||
std::cout << "\tName: " << name.value << std::endl;
|
||||
std::cout << "\tAge: " << age.value << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user