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 <vector>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace Cam
|
|
||||||
{
|
|
||||||
namespace Arguments
|
namespace Arguments
|
||||||
{
|
{
|
||||||
|
|
||||||
enum ERROR {
|
enum ERROR {
|
||||||
NO_ERROR = 0,
|
NO_ERROR = 0,
|
||||||
ERROR_UNKNOWN_OPTION,
|
ERROR_UNKNOWN_ARGUMENT,
|
||||||
ERROR_INCORRECT_TYPE,
|
ERROR_UNKNOWN_POSITIONAL_ARGUMENT,
|
||||||
ERROR_MISSING_ARGUMENT,
|
ERROR_MISSING_VALUE,
|
||||||
ERROR_MISSING_POSITIONAL_ARGUMENT,
|
ERROR_MISSING_POSITIONAL_ARGUMENT,
|
||||||
SPECIAL_CASE_HELP
|
ERROR_INVALID_INT,
|
||||||
};
|
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Parser
|
class Parser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Parser(const char* program_name);
|
Parser(int argc, char** argv);
|
||||||
void set_description(const char* description);
|
|
||||||
void add_option(Option* opt);
|
bool hasNext();
|
||||||
void add_positional_argument(PositionalArgument* arg);
|
void next();
|
||||||
ERROR parse(int argc, char** argv);
|
const char* value();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ERROR print_help_message();
|
int m_ArgCount;
|
||||||
ERROR handle_long_option(const char* option, const char* next_value);
|
char** m_ArgValues;
|
||||||
ERROR handle_short_option(const char option, const char* next_value);
|
|
||||||
ERROR handle_positional_argument(const char* arg);
|
int m_Index;
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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 Arguments
|
||||||
} // namespace Cam
|
|
||||||
|
35
Makefile
35
Makefile
@ -2,9 +2,10 @@ TYPE = STATIC
|
|||||||
SHARED_TYPE = SHARED
|
SHARED_TYPE = SHARED
|
||||||
STATIC_TYPE = STATIC
|
STATIC_TYPE = STATIC
|
||||||
|
|
||||||
BUILD_DIR = bin
|
|
||||||
SOURCE_DIR = Src
|
SOURCE_DIR = Src
|
||||||
HEADER_DIR = Inc
|
HEADER_DIR = Inc
|
||||||
|
BUILD_DIR = bin
|
||||||
|
|
||||||
HEADER_INSTALL_DIR = /usr/local/include
|
HEADER_INSTALL_DIR = /usr/local/include
|
||||||
INSTALL_DIR = /usr/local/lib
|
INSTALL_DIR = /usr/local/lib
|
||||||
|
|
||||||
@ -17,8 +18,7 @@ endif
|
|||||||
|
|
||||||
OPT = -O2
|
OPT = -O2
|
||||||
|
|
||||||
INCS = \
|
INCS = -IInc
|
||||||
-IInc
|
|
||||||
|
|
||||||
C_SOURCES = $(wildcard $(SOURCE_DIR)/*.c)
|
C_SOURCES = $(wildcard $(SOURCE_DIR)/*.c)
|
||||||
CXX_SOURCES = $(wildcard $(SOURCE_DIR)/*.cpp)
|
CXX_SOURCES = $(wildcard $(SOURCE_DIR)/*.cpp)
|
||||||
@ -38,25 +38,20 @@ endif
|
|||||||
|
|
||||||
HEADERS = $(wildcard $(HEADER_DIR)/*.h)
|
HEADERS = $(wildcard $(HEADER_DIR)/*.h)
|
||||||
|
|
||||||
CP = cp
|
|
||||||
MKDIR = mkdir -p
|
|
||||||
ARFLAGS = rvc
|
ARFLAGS = rvc
|
||||||
SED = sed -i -e
|
|
||||||
CHMOD = chmod
|
|
||||||
LDCONFIG = ldconfig
|
|
||||||
|
|
||||||
all: $(BUILD_DIR)/$(LIB_FILE_NAME)
|
all: $(BUILD_DIR)/$(LIB_FILE_NAME)
|
||||||
|
|
||||||
ifneq ($(TYPE), $(SHARED_TYPE))
|
ifneq ($(TYPE), $(SHARED_TYPE))
|
||||||
shared: clean
|
shared: clean
|
||||||
@$(SED) "s/^TYPE = .*$$/TYPE = $(SHARED_TYPE)/" Makefile
|
@sed -i -e "s/^TYPE = .*$$/TYPE = $(SHARED_TYPE)/" Makefile
|
||||||
|
|
||||||
.PHONY: shared
|
.PHONY: shared
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(TYPE), $(STATIC_TYPE))
|
ifneq ($(TYPE), $(STATIC_TYPE))
|
||||||
static: clean
|
static: clean
|
||||||
@$(SED) "s/^TYPE = .*$$/TYPE = $(STATIC_TYPE)/" Makefile
|
@sed -i -e "s/^TYPE = .*$$/TYPE = $(STATIC_TYPE)/" Makefile
|
||||||
|
|
||||||
.PHONY: static
|
.PHONY: static
|
||||||
endif
|
endif
|
||||||
@ -75,29 +70,29 @@ $(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.cpp | $(BUILD_DIR)
|
|||||||
$(CXX) -c $< $(CXXFLAGS) -o $@
|
$(CXX) -c $< $(CXXFLAGS) -o $@
|
||||||
|
|
||||||
$(BUILD_DIR):
|
$(BUILD_DIR):
|
||||||
$(MKDIR) $@
|
mkdir -p $@
|
||||||
|
|
||||||
test: all
|
test: test.cpp all | $(BUILD_DIR)
|
||||||
$(CXX) test.cpp $(CXXFLAGS) -l$(LIB_NAME) -L$(BUILD_DIR) $(TESTFLAGS) -o $(BUILD_DIR)/test
|
$(CXX) $< $(CXXFLAGS) -l$(LIB_NAME) -L$(BUILD_DIR) $(TESTFLAGS) -o $(BUILD_DIR)/test
|
||||||
-$(BUILD_DIR)/test
|
-$(BUILD_DIR)/test
|
||||||
|
|
||||||
test_installed:
|
test_installed: test.cpp | $(BUILD_DIR)
|
||||||
$(CXX) test.cpp $(CXXFLAGS) -l$(LIB_NAME) -o $(BUILD_DIR)/test
|
$(CXX) $< $(CXXFLAGS) -l$(LIB_NAME) -o $(BUILD_DIR)/test
|
||||||
-$(BUILD_DIR)/test
|
-$(BUILD_DIR)/test
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
$(CP) $(BUILD_DIR)/$(LIB_FILE_NAME) $(INSTALL_DIR)
|
cp $(BUILD_DIR)/$(LIB_FILE_NAME) $(INSTALL_DIR)
|
||||||
$(CP) $(HEADERS) $(HEADER_INSTALL_DIR)
|
cp $(HEADERS) $(HEADER_INSTALL_DIR)
|
||||||
@$(CHMOD) 0755 $(INSTALL_DIR)/$(LIB_FILE_NAME)
|
@chmod 0755 $(INSTALL_DIR)/$(LIB_FILE_NAME)
|
||||||
ifeq ($(TYPE), $(SHARED_TYPE))
|
ifeq ($(TYPE), $(SHARED_TYPE))
|
||||||
@$(LDCONFIG)
|
@ldconfig
|
||||||
endif
|
endif
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
$(RM) $(INSTALL_DIR)/$(LIB_FILE_NAME)
|
$(RM) $(INSTALL_DIR)/$(LIB_FILE_NAME)
|
||||||
$(RM) $(addprefix $(HEADER_INSTALL_DIR)/, $(notdir $(HEADERS)))
|
$(RM) $(addprefix $(HEADER_INSTALL_DIR)/, $(notdir $(HEADERS)))
|
||||||
ifeq ($(TYPE), $(SHARED_TYPE))
|
ifeq ($(TYPE), $(SHARED_TYPE))
|
||||||
@$(LDCONFIG)
|
@ldconfig
|
||||||
endif
|
endif
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
@ -1,315 +1,384 @@
|
|||||||
#include "argparser.h"
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
int stringToInt(const char* input)
|
#include "argparser.h"
|
||||||
{
|
|
||||||
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';
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Cam
|
|
||||||
{
|
|
||||||
namespace Arguments
|
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)
|
Parser::Parser(int argc, char** argv)
|
||||||
: m_name(name), m_short_name(short_name), m_description(description), m_type(type), m_found(false) {}
|
: m_ArgCount(argc), m_ArgValues(argv), m_Index(0) { };
|
||||||
|
|
||||||
Option::Option(const char short_name, const char* description, OPTTYPE type)
|
const char* Parser::value()
|
||||||
: m_name(nullptr), m_short_name(short_name), m_description(description), m_type(type), m_found(false) {}
|
|
||||||
|
|
||||||
bool Option::found()
|
|
||||||
{
|
{
|
||||||
return m_found;
|
if (m_Index >= m_ArgCount) {
|
||||||
}
|
return nullptr;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
return m_ArgValues[m_Index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ERROR ret = handle_positional_argument(argv[m_opt_index]);
|
bool Parser::hasNext()
|
||||||
if (ret)
|
{
|
||||||
return ret;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_pos_index < m_positional_args.size()) {
|
for (const char* alias: m_Aliases) {
|
||||||
for (; m_pos_index < m_positional_args.size(); m_pos_index++) {
|
if (std::strlen(arg) == std::strlen(alias)) {
|
||||||
if (m_positional_args[m_pos_index]->m_req)
|
if (std::strcmp(arg, alias) == 0) {
|
||||||
return missing_positional_argument(m_positional_args[m_pos_index]->m_name);
|
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;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ERROR Parser::handle_long_option(const char* option, const char* next_value)
|
void Int::printMissingValueError()
|
||||||
{
|
{
|
||||||
Option* matched_option = nullptr;
|
std::cerr << "Missing integer value for argument ";
|
||||||
|
if (m_Name != nullptr) {
|
||||||
for (Option* opt: m_options) {
|
std::cerr << "--" << m_Name << std::endl;
|
||||||
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;
|
|
||||||
} else {
|
} else {
|
||||||
pos_arg->data = (void*) arg;
|
std::cerr << "-" << m_ShortName << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
printMissingValueError();
|
||||||
|
return ERROR_MISSING_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ERROR String::parseValue(Parser& state)
|
||||||
|
{
|
||||||
|
if (!state.hasNext()) {
|
||||||
|
printMissingValueError();
|
||||||
|
return ERROR_MISSING_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
pos_arg->m_found = true;
|
found = true;
|
||||||
m_pos_index++;
|
|
||||||
|
state.next();
|
||||||
|
value = state.value();
|
||||||
|
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ERROR Parser::get_option_data(Option* opt, const char* option_name, const char* data_str)
|
void String::printMissingValueError()
|
||||||
{
|
{
|
||||||
if (opt->m_type == FLAG)
|
std::cerr << "Missing value for argument ";
|
||||||
return NO_ERROR;
|
if (m_Name != nullptr) {
|
||||||
|
std::cerr << "--" << m_Name << std::endl;
|
||||||
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;
|
|
||||||
} else {
|
} else {
|
||||||
opt->data = (void*) data_str;
|
std::cerr << "-" << m_ShortName << std::endl;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Bool::Bool(const char* name)
|
||||||
|
: Argument(name) { }
|
||||||
|
|
||||||
|
Bool::Bool(char name)
|
||||||
|
: Argument(name) { }
|
||||||
|
|
||||||
|
ERROR Bool::noValue()
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
|
||||||
m_opt_index++;
|
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ERROR Parser::get_option_data(Option* opt, const char option_name, const char* data_str)
|
ERROR Bool::parseValue(Parser& state)
|
||||||
{
|
{
|
||||||
if (opt->m_type == FLAG)
|
(void) state;
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
if (data_str == nullptr || data_str[0] == '-')
|
|
||||||
return missing_argument(option_name);
|
|
||||||
|
|
||||||
if (opt->m_type == INT) {
|
PositionalArgument::PositionalArgument(bool required)
|
||||||
int* val = new int;
|
: found(false), required(required), m_Description(nullptr)
|
||||||
if ((*val = stringToInt(data_str)) == -1)
|
{
|
||||||
return incorrect_type(opt->m_name, data_str);
|
if (required) {
|
||||||
opt->data = val;
|
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 {
|
} else {
|
||||||
opt->data = (void*) data_str;
|
ERROR err = matchPositional(parser);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_opt_index++;
|
if (posArgIdx < positionalArguments.size()) {
|
||||||
return NO_ERROR;
|
if (positionalArguments[posArgIdx]->required) {
|
||||||
}
|
std::cerr << "Missing required positional argument" << std::endl;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
return ERROR_MISSING_POSITIONAL_ARGUMENT;
|
return ERROR_MISSING_POSITIONAL_ARGUMENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ERROR Parser::print_help_message()
|
ERROR findMatch(Parser& parser, const char* name)
|
||||||
{
|
{
|
||||||
std::cout << "Usage: " << m_program_name << (m_options.size() > 0 ? " [OPTIONS]" : "");
|
for (Argument* arg: arguments) {
|
||||||
for (PositionalArgument* arg: m_positional_args)
|
if (arg->matches(name)) {
|
||||||
{
|
return arg->parseValue(parser);
|
||||||
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;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_description)
|
return arg->noValue();
|
||||||
std::cout << std::endl << std::endl << m_description << std::endl;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return SPECIAL_CASE_HELP;
|
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 Arguments
|
||||||
} // namespace Cam
|
|
||||||
|
51
test.cpp
51
test.cpp
@ -1,48 +1,37 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "argparser.h"
|
#include "argparser.h"
|
||||||
|
|
||||||
using namespace Cam;
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
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::Bool third("third");
|
||||||
Arguments::Option second("second", 's', "Second arg", Arguments::INT);
|
third.description("Third arg").alias("abc").alias('t');
|
||||||
Arguments::Option third("third", "Third arg", Arguments::FLAG);
|
|
||||||
|
|
||||||
Arguments::PositionalArgument name("name", true, Arguments::STRING);
|
Arguments::PositionalString name("", true);
|
||||||
Arguments::PositionalArgument age("age", false, Arguments::INT);
|
Arguments::PositionalInt age(0, false);
|
||||||
|
|
||||||
parser.add_option(&first);
|
if (Arguments::parse(argc, argv) != Arguments::NO_ERROR) {
|
||||||
parser.add_option(&second);
|
|
||||||
parser.add_option(&third);
|
|
||||||
|
|
||||||
parser.add_positional_argument(&name);
|
|
||||||
parser.add_positional_argument(&age);
|
|
||||||
|
|
||||||
if (parser.parse(argc, argv) != 0) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Found:" << std::endl;
|
std::cout << "Found:" << std::endl;
|
||||||
std::cout << "\tFirst: " << first.found() << std::endl;
|
std::cout << "\tFirst: " << first.found << std::endl;
|
||||||
std::cout << "\tSecond: " << second.found() << std::endl;
|
std::cout << "\tSecond: " << second.found << std::endl;
|
||||||
std::cout << "\tThird: " << third.found() << std::endl;
|
std::cout << "\tThird: " << third.found << std::endl;
|
||||||
std::cout << "\tName: " << name.found() << std::endl;
|
std::cout << "\tName: " << name.found << std::endl;
|
||||||
std::cout << "\tAge: " << age.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;
|
std::cout << std::endl << "Values:" << std::endl;
|
||||||
if (first.found())
|
std::cout << "\tFirst: " << first.value << std::endl;
|
||||||
std::cout << "\tFirst: " << (char*) first.data << std::endl;
|
std::cout << "\tSecond: " << second.value << std::endl;
|
||||||
if (second.found())
|
std::cout << "\tName: " << name.value << std::endl;
|
||||||
std::cout << "\tSecond: " << *((int*) second.data) << std::endl;
|
std::cout << "\tAge: " << age.value << std::endl;
|
||||||
if (name.found())
|
|
||||||
std::cout << "\tName: " << (char*) name.data << std::endl;
|
|
||||||
if (age.found())
|
|
||||||
std::cout << "\tAge: " << *((int*) age.data) << std::endl;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user