2022-07-26 02:57:57 +00:00
|
|
|
#include "argparser.h"
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
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';
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace Cam
|
|
|
|
{
|
|
|
|
namespace Arguments
|
|
|
|
{
|
|
|
|
|
|
|
|
Option::Option(const char* name, const char* description, OPTTYPE type)
|
2022-07-26 21:14:58 +00:00
|
|
|
: m_name(name), m_short_name(0), m_description(description), m_type(type), m_found(false) {}
|
2022-07-26 02:57:57 +00:00
|
|
|
|
2022-07-26 21:14:58 +00:00
|
|
|
Option::Option(const char* name, const char short_name, const char* description, OPTTYPE type)
|
2022-07-26 02:57:57 +00:00
|
|
|
: m_name(name), m_short_name(short_name), m_description(description), m_type(type), m_found(false) {}
|
|
|
|
|
2022-07-26 21:14:58 +00:00
|
|
|
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) {}
|
|
|
|
|
2022-07-26 02:57:57 +00:00
|
|
|
bool Option::found()
|
|
|
|
{
|
|
|
|
return m_found;
|
|
|
|
}
|
|
|
|
|
|
|
|
PositionalArgument::PositionalArgument(const char* name, bool required, OPTTYPE type /* = STRING */)
|
2022-08-20 22:54:48 +00:00
|
|
|
: m_name(name), m_type(type), m_req(required), m_found(false)
|
2022-07-26 02:57:57 +00:00
|
|
|
{
|
|
|
|
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)
|
2022-08-20 22:54:48 +00:00
|
|
|
: m_program_name(program_name), m_description(nullptr), m_options(), m_positional_args() {}
|
2022-07-26 02:57:57 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-08-20 22:54:48 +00:00
|
|
|
ERROR Parser::parse(int argc, char** argv)
|
2022-07-26 02:57:57 +00:00
|
|
|
{
|
2022-08-20 22:54:48 +00:00
|
|
|
m_pos_index = 0;
|
2022-07-26 21:14:58 +00:00
|
|
|
for (m_opt_index = 1; m_opt_index < argc; m_opt_index++)
|
2022-07-26 02:57:57 +00:00
|
|
|
{
|
2022-07-26 21:14:58 +00:00
|
|
|
if (argv[m_opt_index][0] == '-' && argv[m_opt_index][1] == '-') {
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
ERROR ret = handle_long_option(argv[m_opt_index] + 2, m_opt_index + 1 < argc ? argv[m_opt_index + 1] : nullptr);
|
2022-07-26 21:14:58 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
} else if (argv[m_opt_index][0] == '-' && argv[m_opt_index][1] != '-') {
|
2022-07-26 02:57:57 +00:00
|
|
|
|
2022-07-26 21:14:58 +00:00
|
|
|
for (uint8_t i = 1; argv[m_opt_index][i] != 0; i++) {
|
|
|
|
bool last = argv[m_opt_index][i + 1] == 0;
|
2022-07-27 03:26:02 +00:00
|
|
|
ERROR ret = handle_short_option(argv[m_opt_index][i], m_opt_index + 1 < argc && last ? argv[m_opt_index + 1] : nullptr);
|
2022-07-26 21:14:58 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
if (last)
|
2022-07-26 02:57:57 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
ERROR ret = handle_positional_argument(argv[m_opt_index]);
|
2022-07-26 21:14:58 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2022-07-26 02:57:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-26 21:14:58 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2022-07-26 02:57:57 +00:00
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
return NO_ERROR;
|
2022-07-26 21:14:58 +00:00
|
|
|
}
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
ERROR Parser::handle_long_option(const char* option, const char* next_value)
|
2022-07-26 21:14:58 +00:00
|
|
|
{
|
|
|
|
Option* matched_option = nullptr;
|
|
|
|
|
|
|
|
for (Option* opt: m_options) {
|
2022-07-27 03:26:02 +00:00
|
|
|
if (opt->m_name == nullptr)
|
|
|
|
continue;
|
2022-07-26 21:14:58 +00:00
|
|
|
if (strcmp(opt->m_name, option) == 0) {
|
|
|
|
matched_option = opt;
|
|
|
|
break;
|
2022-07-26 02:57:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-26 21:14:58 +00:00
|
|
|
if (matched_option == nullptr) {
|
|
|
|
|
|
|
|
if (strcmp("help", option) == 0)
|
|
|
|
return print_help_message();
|
|
|
|
|
|
|
|
return unknown_option(option);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
matched_option->m_found = true;
|
2022-07-27 03:26:02 +00:00
|
|
|
return get_option_data(matched_option, matched_option->m_name, next_value);
|
2022-07-26 21:14:58 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << "Congratulations! You have reached an impossible state" << std::endl << std::endl;
|
|
|
|
std::cout << "Reality is broken :)" << std::endl;
|
2022-07-27 03:26:02 +00:00
|
|
|
return IMPOSSIBLE;
|
2022-07-26 21:14:58 +00:00
|
|
|
}
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
ERROR Parser::handle_short_option(const char option, const char* next_value)
|
2022-07-26 21:14:58 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
matched_option->m_found = true;
|
2022-07-27 03:26:02 +00:00
|
|
|
char* name = new char[2];
|
|
|
|
name[0] = matched_option->m_short_name;
|
|
|
|
name[1] = 0;
|
|
|
|
return get_option_data(matched_option, name, next_value);
|
|
|
|
delete[] name;
|
2022-07-26 21:14:58 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << "Congratulations! You have reached impossible state #2" << std::endl << std::endl;
|
|
|
|
std::cout << "Reality is broken :)" << std::endl;
|
2022-07-27 03:26:02 +00:00
|
|
|
return IMPOSSIBLE;
|
2022-07-26 21:14:58 +00:00
|
|
|
}
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
ERROR Parser::handle_positional_argument(const char* arg)
|
2022-07-26 21:14:58 +00:00
|
|
|
{
|
|
|
|
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 {
|
|
|
|
pos_arg->data = (void*) arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos_arg->m_found = true;
|
|
|
|
m_pos_index++;
|
2022-07-27 03:26:02 +00:00
|
|
|
return NO_ERROR;
|
2022-07-26 21:14:58 +00:00
|
|
|
}
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
ERROR Parser::get_option_data(Option* opt, const char* option_name, const char* data_str)
|
2022-07-26 21:14:58 +00:00
|
|
|
{
|
|
|
|
if (opt->m_type == FLAG)
|
2022-07-27 03:26:02 +00:00
|
|
|
return NO_ERROR;
|
2022-07-26 21:14:58 +00:00
|
|
|
|
|
|
|
if (data_str == nullptr || data_str[0] == '-')
|
2022-07-27 03:26:02 +00:00
|
|
|
return missing_argument(option_name);
|
2022-07-26 21:14:58 +00:00
|
|
|
|
|
|
|
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 {
|
|
|
|
opt->data = (void*) data_str;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_opt_index++;
|
2022-07-27 03:26:02 +00:00
|
|
|
return NO_ERROR;
|
2022-07-26 02:57:57 +00:00
|
|
|
}
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
ERROR Parser::unknown_option(const char* option)
|
2022-07-26 21:14:58 +00:00
|
|
|
{
|
|
|
|
std::cout << m_program_name << ": invalid option '" << option << '\'' << std::endl << std::endl;
|
|
|
|
std::cout << "See " << m_program_name << " --help" << std::endl;
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
return ERROR_UNKNOWN_OPTION;
|
2022-07-26 21:14:58 +00:00
|
|
|
}
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
ERROR Parser::unknown_option(const char option)
|
2022-07-26 21:14:58 +00:00
|
|
|
{
|
|
|
|
std::cout << m_program_name << ": invalid option: " << option << std::endl << std::endl;
|
|
|
|
std::cout << "See " << m_program_name << " --help" << std::endl;
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
return ERROR_UNKNOWN_OPTION;
|
2022-07-26 21:14:58 +00:00
|
|
|
}
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
ERROR Parser::incorrect_type(const char* option, const char* got)
|
2022-07-26 21:14:58 +00:00
|
|
|
{
|
|
|
|
std::cout << m_program_name << ": argument '" << option << "' expects an integer as an argument, got " << got << " instead" << std::endl;
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
return ERROR_INCORRECT_TYPE;
|
2022-07-26 21:14:58 +00:00
|
|
|
}
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
ERROR Parser::missing_argument(const char* option)
|
2022-07-26 21:14:58 +00:00
|
|
|
{
|
|
|
|
std::cout << m_program_name << ": Missing argument for option '" << option << '\'' << std::endl;
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
return ERROR_MISSING_ARGUMENT;
|
2022-07-26 21:14:58 +00:00
|
|
|
}
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
ERROR Parser::missing_positional_argument(const char* arg)
|
2022-07-26 21:14:58 +00:00
|
|
|
{
|
|
|
|
std::cout << m_program_name << ": Missing required positional argument '" << arg << "'" << std::endl << std::endl;
|
|
|
|
std::cout << "See " << m_program_name << " --help" << std::endl;
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
return ERROR_MISSING_POSITIONAL_ARGUMENT;
|
2022-07-26 21:14:58 +00:00
|
|
|
}
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
ERROR Parser::print_help_message()
|
2022-07-26 02:57:57 +00:00
|
|
|
{
|
|
|
|
std::cout << "Usage: " << m_program_name << (m_options.size() > 0 ? " [OPTIONS]" : "");
|
|
|
|
for (PositionalArgument* arg: m_positional_args)
|
|
|
|
{
|
|
|
|
std::cout << " " << arg->m_name;
|
|
|
|
}
|
2022-07-26 21:14:58 +00:00
|
|
|
std::cout << std::endl;
|
2022-07-26 02:57:57 +00:00
|
|
|
|
|
|
|
|
2022-07-26 21:14:58 +00:00
|
|
|
if (m_options.size() > 0)
|
|
|
|
std::cout << std::endl << std::endl << "Options:" << std::endl;
|
2022-07-26 02:57:57 +00:00
|
|
|
for (Option* opt: m_options)
|
|
|
|
{
|
2022-07-26 21:14:58 +00:00
|
|
|
std::cout << " ";
|
|
|
|
if (opt->m_short_name != 0)
|
|
|
|
std::cout << '-' << opt->m_short_name;
|
2022-07-26 02:57:57 +00:00
|
|
|
|
2022-07-26 21:14:58 +00:00
|
|
|
if (opt->m_short_name != 0 && opt->m_name != nullptr)
|
|
|
|
std::cout << ", ";
|
2022-07-26 02:57:57 +00:00
|
|
|
|
2022-07-26 21:14:58 +00:00
|
|
|
if (opt->m_name != nullptr)
|
|
|
|
std::cout << "--" << opt->m_name;
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
if (opt->m_name != nullptr) {
|
|
|
|
for (uint8_t i = 0; i < 20 - strlen(opt->m_name) - ((opt->m_short_name != 0) * 4); i++)
|
2022-07-26 02:57:57 +00:00
|
|
|
std::cout << ' ';
|
|
|
|
} else {
|
2022-07-27 03:26:02 +00:00
|
|
|
for (uint8_t i = 0; i < 24 - ((opt->m_short_name != 0) * 4); i++)
|
2022-07-26 02:57:57 +00:00
|
|
|
std::cout << ' ';
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << opt->m_description << std::endl;
|
|
|
|
}
|
|
|
|
|
2022-07-26 21:14:58 +00:00
|
|
|
if (m_description)
|
|
|
|
std::cout << std::endl << std::endl << m_description << std::endl;
|
|
|
|
|
2022-07-27 03:26:02 +00:00
|
|
|
return SPECIAL_CASE_HELP;
|
2022-07-26 02:57:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Arguments
|
|
|
|
} // namespace Cam
|