#17 Add own simple argument parser

pull/32/head
Geoffrey Merck 4 years ago
parent 5c76994689
commit bd0fca50f4

@ -0,0 +1,60 @@
/*
* Copyright (c) 2022 by Geoffrey Merck F4FXL / KC3FRA
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <cassert>
#include <boost/algorithm/string.hpp>
#include "ProgramArgs.h"
void CProgramArgs::eatArguments(int argc, const char *argv[], std::unordered_map<std::string, std::string>& namedArgs, std::vector<std::string>& positionalArgs)
{
assert(argv != nullptr);
namedArgs.clear();
positionalArgs.clear();
std::vector<std::string> programArgs;
// Copy to a vector for easier handling, also skip program name
for(int i = 1;i < argc; i++) {
if(argv[i] != nullptr) {
programArgs.push_back(std::string(argv[i]));
}
}
// Consume Named args first
for(auto it = programArgs.begin(); it != programArgs.end();) {
if(boost::starts_with(*it, "-")) {
std::string argName = boost::trim_left_copy_if(*it, [] (char c) { return c == '-'; });
if(!argName.empty()) {
namedArgs[argName] = "";
it = programArgs.erase(it);
if(it != programArgs.end()) {
namedArgs[argName] = *it;
it = programArgs.erase(it);
}
}
}
else {
it++;
}
}
//ProgramArgs now only contains pôsitional Args
positionalArgs.assign(programArgs.begin(), programArgs.end());
}

@ -0,0 +1,29 @@
/*
* Copyright (c) 2022 by Geoffrey Merck F4FXL / KC3FRA
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#pragma once
#include <string>
#include <vector>
#include <unordered_map>
class CProgramArgs
{
public:
static void eatArguments(int argc, const char *argv[], std::unordered_map<std::string, std::string>& namedArgs, std::vector<std::string>& positionalArgs);
};

@ -1,699 +0,0 @@
//-----------------------------------------------------------------------------
// optionparser.h -- A Header-Only commandline argument parser
// Author: Luke de Oliveira <lukedeo@ldo.io>
// License: MIT
//-----------------------------------------------------------------------------
#ifndef OPTIONPARSER_H_
#define OPTIONPARSER_H_
#include <iostream>
#include <map>
#include <numeric>
#include <sstream>
#include <utility>
#include <vector>
#include <algorithm>
namespace optionparser {
// The utils::* namespace contains general utilities not necessarily useful
// outside the main scope of the library
namespace utils {
std::vector<std::string> split_str(std::string s,
const std::string &delimiter = " ") {
size_t pos = 0;
size_t delimiter_length = delimiter.length();
std::vector<std::string> vals;
while ((pos = s.find(delimiter)) != std::string::npos) {
vals.push_back(s.substr(0, pos));
s.erase(0, pos + delimiter_length);
}
vals.push_back(s);
return vals;
}
std::string stitch_str(const std::vector<std::string> &text,
unsigned max_per_line = 80,
const std::string &leading_str = "") {
std::vector<std::string> result;
std::string line_value;
for (const auto &token : text) {
if (line_value.empty()) {
line_value = (leading_str + token);
continue;
}
auto hypothetical_line = line_value;
hypothetical_line.append(" " + token);
if (hypothetical_line.size() > max_per_line) {
// In this case, we were better off before
result.emplace_back(line_value);
line_value = (leading_str + token);
} else {
line_value = hypothetical_line;
}
}
// Collect the last line since we don't track indices in the loop proper.
result.emplace_back(line_value);
return std::accumulate(
result.begin() + 1, result.end(), result.at(0),
[](std::string &s, const std::string &piece) -> std::string {
return s + "\n" + piece;
});
}
} // end namespace utils
// Define a thin error for any sort of parser error that arises
class ParserError : public std::runtime_error {
using std::runtime_error::runtime_error;
};
// Enums for Option config
enum StorageMode { STORE_TRUE = 0, STORE_VALUE, STORE_MULT_VALUES };
enum OptionType { LONG_OPT = 0, SHORT_OPT, POSITIONAL_OPT, EMPTY_OPT };
// Option class definition
class Option {
public:
Option() = default;
std::string help_doc();
std::string &short_flag() { return m_short_flag; }
std::string &long_flag() { return m_long_flag; }
std::string &pos_flag() { return m_pos_flag; }
bool found() { return m_found; }
Option &found(bool found) {
m_found = found;
return *this;
}
StorageMode mode() { return m_mode; }
Option &mode(const StorageMode &mode) {
m_mode = mode;
return *this;
}
bool required() { return m_required; }
Option &required(bool req) {
m_required = req;
return *this;
}
std::string metavar() {
std::string formatted_metavar;
if (!m_metavar.empty()) {
if (m_mode == STORE_TRUE) {
return "";
}
formatted_metavar = m_metavar;
} else {
for (const auto &cand :
{m_dest, m_pos_flag, m_long_flag, std::string("ARG")}) {
if (!cand.empty()) {
formatted_metavar = cand.substr(cand.find_first_not_of('-'));
std::transform(formatted_metavar.begin(), formatted_metavar.end(),
formatted_metavar.begin(), ::toupper);
break;
}
}
}
if (m_mode == STORE_MULT_VALUES) {
formatted_metavar = (formatted_metavar + "1 [" + formatted_metavar +
"2, " + formatted_metavar + "3, ...]");
}
return formatted_metavar;
}
Option &metavar(const std::string &mvar) {
m_metavar = mvar;
return *this;
}
std::string help() { return m_help; }
Option &help(const std::string &help) {
m_help = help;
return *this;
}
std::string dest() { return m_dest; }
Option &dest(const std::string &dest) {
m_dest = dest;
return *this;
}
std::string default_value() { return m_default_value; }
Option &default_value(const std::string &default_value) {
m_default_value = default_value;
return *this;
}
Option &default_value(const char *default_value) {
m_default_value = std::string(default_value);
return *this;
}
template <typename T> Option &default_value(const T &default_value) {
m_default_value = std::to_string(default_value);
return *this;
}
static OptionType get_type(std::string opt);
static std::string get_destination(const std::string &first_option,
const std::string &second_option);
static void validate_option_types(const OptionType &first_option_type,
const OptionType &second_option_type);
private:
bool m_found = false;
bool m_required = false;
StorageMode m_mode = STORE_TRUE;
std::string m_help = "";
std::string m_dest = "";
std::string m_default_value = "";
std::string m_metavar = "";
std::string m_short_flag = "";
std::string m_long_flag = "";
std::string m_pos_flag = "";
};
// Non-inline definitions for Option methods
std::string Option::help_doc() {
std::string h = " ";
if (!m_long_flag.empty()) {
h += m_long_flag;
if (!m_short_flag.empty()) {
h += ", ";
}
}
if (!m_short_flag.empty()) {
h += m_short_flag;
}
if (!m_pos_flag.empty()) {
h += m_pos_flag;
}
auto arg_buf = std::max(h.length() + 1, static_cast<unsigned long>(25));
auto help_str = utils::stitch_str(utils::split_str(m_help), arg_buf + 50,
std::string(arg_buf, ' '));
char char_buf[h.length() + help_str.length() + 100];
sprintf(char_buf, ("%-" + std::to_string(arg_buf) + "s%s\n").c_str(),
h.c_str(), help_str.substr(arg_buf).c_str());
return std::string(char_buf);
}
OptionType Option::get_type(std::string opt) {
if (opt.empty()) {
return OptionType::EMPTY_OPT;
}
if (opt.size() == 2) {
if (opt[0] == '-') {
return OptionType::SHORT_OPT;
}
}
if (opt.size() > 2) {
if (opt[0] == '-' && opt[1] == '-') {
return OptionType::LONG_OPT;
}
}
return OptionType::POSITIONAL_OPT;
}
void Option::validate_option_types(const OptionType &first_option_type,
const OptionType &second_option_type) {
auto err = [](const std::string &msg) {
throw std::runtime_error("Parser inconsistency: " + msg);
};
if (first_option_type == OptionType::EMPTY_OPT) {
err("Cannot have first option be empty.");
}
if (first_option_type == OptionType::POSITIONAL_OPT &&
second_option_type != OptionType::EMPTY_OPT) {
err("Positional arguments can only have one option, found non-empty second "
"option.");
}
if (second_option_type == OptionType::POSITIONAL_OPT) {
err("Cannot have second option be a positional option.");
}
}
std::string Option::get_destination(const std::string &first_option,
const std::string &second_option) {
std::string dest;
auto first_opt_type = Option::get_type(first_option);
auto second_opt_type = Option::get_type(second_option);
validate_option_types(first_opt_type, second_opt_type);
if (first_opt_type == OptionType::LONG_OPT) {
dest = first_option.substr(2);
} else if (second_opt_type == OptionType::LONG_OPT) {
dest = second_option.substr(2);
} else {
if (first_opt_type == OptionType::SHORT_OPT) {
dest = first_option.substr(1) + "_option";
} else if (second_opt_type == OptionType::SHORT_OPT) {
dest = second_option.substr(1) + "_option";
} else {
if (first_opt_type == OptionType::POSITIONAL_OPT &&
second_opt_type == OptionType::EMPTY_OPT) {
dest = first_option;
} else {
std::string msg = "Parser inconsistency error.";
throw std::runtime_error(msg);
}
}
}
return dest;
}
// OptionParser class definition
class OptionParser {
public:
explicit OptionParser(std::string description = "", bool create_help = true)
: m_options(0), m_description(std::move(description)),
m_pos_args_count(1), m_exit_on_failure(true) {
if (create_help) {
add_option("--help", "-h").help("Display this help message and exit.");
}
}
~OptionParser() = default;
void eat_arguments(unsigned int argc, char const *argv[]);
Option &add_option(const std::string &first_option,
const std::string &second_option = "");
// We template-specialize these later
template <class T = bool> T get_value(const std::string &key);
void help();
OptionParser &exit_on_failure(bool exit = true);
OptionParser &throw_on_failure(bool throw_ = true);
private:
Option &add_option_internal(const std::string &first_option,
const std::string &second_option);
void try_to_exit_with_message(const std::string &e);
ParserError fail_for_missing_key(const std::string &key);
ParserError fail_unrecognized_argument(const std::string &arg);
ParserError
fail_for_missing_arguments(const std::vector<std::string> &missing_flags);
bool get_value_arg(std::vector<std::string> &arguments, unsigned int &arg,
Option &opt, std::string &flag);
bool try_to_get_opt(std::vector<std::string> &arguments, unsigned int &arg,
Option &option, std::string &flag);
void check_for_missing_args();
std::vector<Option> m_options;
std::string m_prog_name, m_description;
int m_pos_args_count;
bool m_exit_on_failure;
std::map<std::string, std::vector<std::string>> m_values;
std::vector<std::string> m_positional_options_names;
std::map<std::string, unsigned int> m_option_idx;
};
// Define methods non-inline
Option &OptionParser::add_option(const std::string &first_option,
const std::string &second_option) {
return add_option_internal(first_option, second_option);
}
Option &OptionParser::add_option_internal(const std::string &first_option,
const std::string &second_option) {
m_options.resize(m_options.size() + 1);
Option &opt = m_options.back();
OptionType first_option_type = Option::get_type(first_option);
OptionType second_option_type = Option::get_type(second_option);
try {
opt.dest(Option::get_destination(first_option, second_option));
} catch (const std::runtime_error &err) {
try_to_exit_with_message(err.what());
throw err;
}
if (first_option_type == OptionType::LONG_OPT) {
opt.long_flag() = first_option;
} else if (second_option_type == OptionType::LONG_OPT) {
opt.long_flag() = second_option;
}
if (first_option_type == OptionType::SHORT_OPT) {
opt.short_flag() = first_option;
} else if (second_option_type == OptionType::SHORT_OPT) {
opt.short_flag() = second_option;
}
if (first_option_type == OptionType::POSITIONAL_OPT) {
opt.pos_flag() = first_option;
m_pos_args_count += 1;
m_positional_options_names.push_back(first_option);
}
return opt;
}
bool OptionParser::get_value_arg(std::vector<std::string> &arguments,
unsigned int &arg, Option &opt,
std::string &flag) {
std::string val;
m_values[opt.dest()].clear();
if (arguments[arg].size() > flag.size()) {
auto search_pt = arguments[arg].find_first_of('=');
if (search_pt == std::string::npos) {
search_pt = arguments[arg].find_first_of(' ');
if (search_pt == std::string::npos) {
try_to_exit_with_message("Error, long options (" + flag +
") require a '=' or space before a value.");
return false;
}
auto vals = utils::split_str(arguments[arg].substr(search_pt + 1));
for (const auto &v : vals) {
m_values[opt.dest()].push_back(v);
}
}
} else {
if (arg + 1 >= arguments.size()) {
if (opt.default_value().empty()) {
try_to_exit_with_message("error, flag '" + flag +
"' requires an argument.");
return false;
}
if (m_values[opt.dest()].empty()) {
val = opt.default_value();
}
} else {
if (arguments[arg + 1][0] == '-') {
if (opt.default_value().empty()) {
try_to_exit_with_message("error, flag '" + flag +
"' requires an argument.");
return false;
}
if (m_values[opt.dest()].empty()) {
val = opt.default_value();
}
}
}
}
if (!val.empty()) {
m_values[opt.dest()].push_back(val);
return true;
}
int arg_distance = 0;
while (arguments[arg + 1][0] != '-') {
arg++;
if (arg_distance && (opt.mode() != StorageMode::STORE_MULT_VALUES)) {
break;
}
arg_distance++;
m_values[opt.dest()].push_back(arguments[arg]);
if (arg + 1 >= arguments.size()) {
break;
}
}
return true;
}
bool OptionParser::try_to_get_opt(std::vector<std::string> &arguments,
unsigned int &arg, Option &option,
std::string &flag) {
if (flag.empty()) {
return false;
}
if (arguments[arg] != flag) {
return false;
}
if (!option.pos_flag().empty()) {
m_values[option.dest()].push_back(option.pos_flag());
option.found(true);
return true;
}
if (option.mode() == STORE_TRUE) {
option.found(true);
return true;
}
if (((option.mode() == STORE_VALUE) ||
(option.mode() == STORE_MULT_VALUES)) &&
!option.found()) {
if (get_value_arg(arguments, arg, option, flag)) {
option.found(true);
return true;
}
}
return false;
}
void OptionParser::check_for_missing_args() {
std::vector<std::string> missing;
for (auto &opt : m_options) {
if ((opt.required()) && (!opt.found())) {
missing.push_back(opt.dest());
} else if ((!opt.default_value().empty()) && (!opt.found())) {
m_values[opt.dest()].push_back(opt.default_value());
opt.found(true);
}
}
if (!missing.empty()) {
throw fail_for_missing_arguments(missing);
}
}
void OptionParser::eat_arguments(unsigned int argc, char const *argv[]) {
unsigned int idx_ctr = 0;
for (auto &opt : m_options) {
m_option_idx[opt.dest()] = idx_ctr;
idx_ctr++;
}
const std::string args_end = "- ";
m_prog_name = argv[0];
std::vector<std::string> arguments(argv + 1, argv + argc);
// dummy way to solve problem with last arg of
arguments.emplace_back(args_end);
// for each argument cluster
int pos_args = 1;
for (unsigned int arg = 0; arg < arguments.size(); ++arg) {
bool match_found = false;
// for each option sets
for (auto &option : m_options) {
match_found = try_to_get_opt(arguments, arg, option, option.long_flag());
if (match_found) {
break;
}
match_found = try_to_get_opt(arguments, arg, option, option.short_flag());
if (match_found) {
break;
}
}
if (!match_found) {
if (arguments[arg] != args_end) {
if (m_pos_args_count > pos_args) {
m_options[m_option_idx.at(m_positional_options_names[pos_args - 1])]
.found(true);
m_values[m_positional_options_names[pos_args - 1]].push_back(
arguments[arg]);
pos_args++;
} else
throw fail_unrecognized_argument(arguments[arg]);
}
}
}
if (get_value("help")) {
help();
}
check_for_missing_args();
}
void OptionParser::try_to_exit_with_message(const std::string &e) {
if (m_exit_on_failure) {
std::cerr << "In excecutable \'";
std::cerr << m_prog_name << "\':\n" << e << std::endl;
exit(1);
}
}
ParserError OptionParser::fail_for_missing_key(const std::string &key) {
auto msg = "Tried to access value for field '" + key +
"' which is not a valid field.";
try_to_exit_with_message(msg);
return ParserError(msg);
}
ParserError OptionParser::fail_unrecognized_argument(const std::string &arg) {
auto msg = "Unrecognized flag/option '" + arg + "'";
try_to_exit_with_message(msg);
return ParserError(msg);
}
ParserError OptionParser::fail_for_missing_arguments(
const std::vector<std::string> &missing_flags) {
auto msg =
"Missing required flags: " +
std::accumulate(
missing_flags.begin() + 1, missing_flags.end(), missing_flags.at(0),
[](std::string &s, const std::string &piece) -> std::string {
return s + ", " + piece;
}) +
".";
try_to_exit_with_message(msg);
return ParserError(msg);
}
void OptionParser::help() {
auto split = m_prog_name.find_last_of('/');
std::stringstream leading;
leading << "usage: " << m_prog_name.substr(split + 1) << " ";
std::string usage_str = leading.str();
std::cout << usage_str;
std::vector<std::string> option_usage;
for (auto &option : m_options) {
std::stringstream optss;
optss << (option.required() ? "" : "[");
if (!option.short_flag().empty()) {
optss << option.short_flag();
} else if (!option.long_flag().empty()) {
optss << option.long_flag();
}
if (option.mode() != StorageMode::STORE_TRUE) {
optss << (option.pos_flag().empty() ? " " : "") << option.metavar();
}
optss << (option.required() ? " " : "] ");
option_usage.emplace_back(optss.str());
}
std::cout << utils::stitch_str(option_usage, 80,
std::string(usage_str.size(), ' '))
.substr(usage_str.size())
<< std::endl;
if (!m_description.empty()) {
std::cout << "\n" << m_description << "\n" << std::endl;
}
std::vector<Option> pos_opts;
std::vector<Option> reg_opts;
std::copy_if(m_options.begin(), m_options.end(), std::back_inserter(pos_opts),
[](Option &o) { return !o.pos_flag().empty(); });
std::copy_if(m_options.begin(), m_options.end(), std::back_inserter(reg_opts),
[](Option &o) { return o.pos_flag().empty(); });
if (!pos_opts.empty()) {
std::cout << "\nPositional Arguments:" << std::endl;
std::for_each(pos_opts.begin(), pos_opts.end(),
[](Option &o) { std::cout << o.help_doc(); });
}
std::cout << "\nOptions:" << std::endl;
std::for_each(reg_opts.begin(), reg_opts.end(),
[](Option &o) { std::cout << o.help_doc(); });
exit(0);
}
OptionParser &OptionParser::exit_on_failure(bool exit) {
m_exit_on_failure = exit;
return *this;
}
OptionParser &OptionParser::throw_on_failure(bool throw_) {
m_exit_on_failure = !throw_;
return *this;
}
template <class T> T OptionParser::get_value(const std::string &key) {
try {
return m_options[m_option_idx.at(key)].found();
} catch (std::out_of_range &err) {
throw fail_for_missing_key(key);
}
}
// Provide all template specializations for get_value<T>(keyName)
#define GET_VALUE_SPECIALIZE(type, code) \
template <> type OptionParser::get_value<type>(const std::string &key) { \
try { \
code \
} catch (std::out_of_range & err) { \
throw fail_for_missing_key(key); \
} \
}
GET_VALUE_SPECIALIZE(std::string, { return m_values.at(key).at(0); })
GET_VALUE_SPECIALIZE(const char *, { return m_values.at(key).at(0).c_str(); })
GET_VALUE_SPECIALIZE(double, { return std::stod(m_values.at(key).at(0)); })
GET_VALUE_SPECIALIZE(float, { return std::stof(m_values.at(key).at(0)); })
GET_VALUE_SPECIALIZE(int, { return std::stoi(m_values.at(key).at(0)); })
GET_VALUE_SPECIALIZE(unsigned int,
{ return std::stoul(m_values.at(key).at(0)); })
GET_VALUE_SPECIALIZE(std::vector<std::string>, { return m_values.at(key); })
#define GET_VALUE_SPECIALIZE_VECTOR(type, converter) \
GET_VALUE_SPECIALIZE(std::vector<type>, { \
std::vector<type> v; \
for (auto &entry : m_values.at(key)) { \
v.push_back(converter(entry)); \
} \
return /*std::move(v);*/v; \
})
GET_VALUE_SPECIALIZE_VECTOR(const char *,
[](const std::string &s) { return s.c_str(); })
GET_VALUE_SPECIALIZE_VECTOR(int, std::stoi)
GET_VALUE_SPECIALIZE_VECTOR(unsigned int, std::stoul)
GET_VALUE_SPECIALIZE_VECTOR(float, std::stof)
GET_VALUE_SPECIALIZE_VECTOR(double, std::stod)
} // end namespace optionparser
#endif

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2020 Luke de Oliveira
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

@ -19,9 +19,11 @@
#include <string>
#include <cstdio>
#include <unordered_map>
#include <vector>
#include "optionparser.h"
#include "Version.h"
#include "ProgramArgs.h"
const std::string NAME_OPTION("name");
const std::string REPEATER_PARAM("Callsign");
@ -31,46 +33,15 @@ const std::string REFLECTOR_PARAM("Param2");
int main(int argc, const char* argv[])
{
unsigned int paramsCount = 0U;
optionparser::OptionParser p;
p.exit_on_failure(false);
p.throw_on_failure(false);
std::string name;
p.add_option("--" + NAME_OPTION).default_value("").help("Gateway Name");
p.add_option(REPEATER_PARAM);
p.add_option(ACTION_PARAM);
p.add_option(RECONNECT_PARAM);
p.add_option(REFLECTOR_PARAM);
std::unordered_map<std::string, std::string> namedArgs;
std::vector<std::string> positionalArgs;
p.eat_arguments(argc, argv);
CProgramArgs::eatArguments(argc, argv, namedArgs, positionalArgs);
if(p.get_value(NAME_OPTION)) {
std::cout << p.get_value<std::string>(NAME_OPTION) << std::endl;
paramsCount++;
}
if(p.get_value(REPEATER_PARAM)) {
std::cout << p.get_value<std::string>(REPEATER_PARAM) << std::endl;
paramsCount++;
}
if(p.get_value(ACTION_PARAM)) {
std::cout << p.get_value<std::string>(ACTION_PARAM) << std::endl;
paramsCount++;
}
if(p.get_value(RECONNECT_PARAM)) {
std::cout << p.get_value<std::string>(RECONNECT_PARAM) << std::endl;
paramsCount++;
}
if(p.get_value(REFLECTOR_PARAM)) {
std::cout << p.get_value<std::string>(REFLECTOR_PARAM) << std::endl;
paramsCount++;
}
if(paramsCount < 2U) {
::fprintf(stderr, "\ndgwremotecontrol %s : invalid command line usage:\n\n", LONG_VERSION.c_str());
if(namedArgs.size() == 0U && positionalArgs.size() == 0U) {
::fprintf(stderr, "\ndgwremotecontrol v%s : invalid command line usage:\n\n", LONG_VERSION.c_str());
::fprintf(stderr, "\t\tdgwremotecontrol [--name <name>] <repeater> link <reconnect> <reflector>\n");
::fprintf(stderr, "\t\tdgwremotecontrol [--name <name>] <repeater> unlink\n");
::fprintf(stderr, "\t\tdgwremotecontrol [--name <name>] <starnet> drop <user>\n");
@ -78,5 +49,6 @@ int main(int argc, const char* argv[])
return 1;
}
return 0;
}

@ -0,0 +1,91 @@
/*
* Copyright (C) 2022 by Geoffrey Merck F4FXL / KC3FRA
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <gtest/gtest.h>
#include <unordered_map>
#include <vector>
#include <string>
#include "ProgramArgs.h"
namespace ProgramArgsTests
{
class ProgramArgs_eatArguments : public ::testing::Test {
};
TEST_F(ProgramArgs_eatArguments, OnlyExecutable)
{
const char *argv[] { "ACME" };
std::unordered_map<std::string, std::string> namedArgs;
std::vector<std::string> positionalArgs;
CProgramArgs::eatArguments(1, argv, namedArgs, positionalArgs);
EXPECT_EQ(namedArgs.size(), 0U) << "Named Args shall be empty because we did not pass any args";
EXPECT_EQ(positionalArgs.size(), 0U) << "Positional Args shall be empty because we did not pass any args";
}
TEST_F(ProgramArgs_eatArguments, OneNamedArgsWithValue)
{
const char *argv[] { "ACME", "-name", "giovanni" };
std::unordered_map<std::string, std::string> namedArgs;
std::vector<std::string> positionalArgs;
CProgramArgs::eatArguments(3, argv, namedArgs, positionalArgs);
EXPECT_EQ(namedArgs.size(), 1U) << "Named args shall have one argument";
EXPECT_EQ(namedArgs.count("name"), 1U) << "Named args shall contained name";
EXPECT_STREQ(namedArgs["name"].c_str(), "giovanni") << "Name shall be giovanni";
EXPECT_EQ(positionalArgs.size(), 0U) << "Positional Args shall be empty because we did not pass any positional args";
}
TEST_F(ProgramArgs_eatArguments, OneNamedArgsWithoutValue)
{
const char *argv[] { "ACME", "-name" };
std::unordered_map<std::string, std::string> namedArgs;
std::vector<std::string> positionalArgs;
CProgramArgs::eatArguments(2, argv, namedArgs, positionalArgs);
EXPECT_EQ(namedArgs.size(), 1U) << "Named args shall have one argument";
EXPECT_EQ(namedArgs.count("name"), 1U) << "Named args shall contained name";
EXPECT_STREQ(namedArgs["name"].c_str(), "") << "Name shall be empty";
EXPECT_EQ(positionalArgs.size(), 0U) << "Positional Args shall be empty because we did not pass any positional args";
}
TEST_F(ProgramArgs_eatArguments, OneNamedArgsWithValueSeveralPositionalArgs)
{
const char *argv[] { "ACME", "my", "-name", "is", "giovanni", "giorgio" };
std::unordered_map<std::string, std::string> namedArgs;
std::vector<std::string> positionalArgs;
CProgramArgs::eatArguments(6, argv, namedArgs, positionalArgs);
EXPECT_EQ(namedArgs.size(), 1U) << "Named args shall have one argument";
EXPECT_EQ(namedArgs.count("name"), 1U) << "Named args shall contained name";
EXPECT_STREQ(namedArgs["name"].c_str(), "is") << "Name shall be giovanni";
EXPECT_EQ(positionalArgs.size(), 3U) << "Positional Args shall contain 3 values";
EXPECT_STREQ(positionalArgs[0].c_str(), "my");
EXPECT_STREQ(positionalArgs[1].c_str(), "giovanni");
EXPECT_STREQ(positionalArgs[2].c_str(), "giorgio");
}
}
Loading…
Cancel
Save

Powered by TurnKey Linux.