Commit 94615634 authored by Gauthier Quesnel's avatar Gauthier Quesnel
Browse files

getopt: first commit

parent 9e24780c
/* Copyright (C) 2018 INRA
*
* 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.
*/
#ifndef ORG_VLEPROJECT_BITS_GETOPT_HPP
#define ORG_VLEPROJECT_BITS_GETOPT_HPP
#include <algorithm>
#include <functional>
#include <limits>
#include <sstream>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include <climits>
#include <cmath>
#include <cstring>
namespace bits {
template<class T, class U>
struct return_
{
static T& perform(U& /*u*/)
{
throw std::runtime_error("return type mismatch");
}
};
template<class T>
struct return_<T, T>
{
static T& perform(T& t)
{
return t;
}
};
template<class Ret, class Tuple, unsigned S>
struct get_by_idx_
{
static Ret& perform(Tuple& t, int idx)
{
constexpr unsigned C = S - 1;
if (idx == C) {
typedef typename std::tuple_element<C, Tuple>::type elem_type;
return return_<Ret, elem_type>::perform(std::get<C>(t));
} else {
return get_by_idx_<Ret, Tuple, C>::perform(t, idx);
}
}
};
template<class Ret, class Tuple>
struct get_by_idx_<Ret, Tuple, 0>
{
static Ret& perform(Tuple&, int)
{
throw std::runtime_error("bad field index");
}
};
template<class Ret, class Tuple>
Ret&
runtime_get(Tuple& t, int idx)
{
constexpr unsigned S = std::tuple_size<Tuple>::value;
return get_by_idx_<Ret, Tuple, S>::perform(t, idx);
}
enum class argument_opt
{
success_arg_consumed,
success_arg_not_consumed,
failure,
};
template<typename Tuple>
struct exist_parameter
{
argument_opt operator()(const char* /*str*/, Tuple& t, std::size_t idx)
{
runtime_get<bool>(t, idx) = true;
return argument_opt::success_arg_not_consumed;
}
};
template<typename Tuple>
struct toggle_parameter
{
argument_opt operator()(const char* /*str*/, Tuple& t, std::size_t idx)
{
runtime_get<bool>(t, idx) = !runtime_get<bool>(t, idx);
return argument_opt::success_arg_not_consumed;
}
};
template<typename Tuple>
struct string_parameter
{
argument_opt operator()(const char* str, Tuple& t, std::size_t idx)
{
if (not str)
return argument_opt::failure;
runtime_get<std::string>(t, idx) = str;
return argument_opt::success_arg_consumed;
}
};
template<typename Tuple>
struct string_optional_parameter
{
argument_opt operator()(const char* str, Tuple& t, std::size_t idx)
{
if (not str)
return argument_opt::success_arg_not_consumed;
runtime_get<std::string>(t, idx) = str;
return argument_opt::success_arg_consumed;
}
};
template<typename Tuple, typename Integer, Integer minimal, Integer maximal>
struct arithmetic_parameter
{
static_assert(std::is_arithmetic<Integer>::value,
"Only integral or real type.");
argument_opt operator()(const char* str, Tuple& t, std::size_t idx)
{
if (str == nullptr)
return argument_opt::success_arg_not_consumed;
std::istringstream iss(str);
Integer ret;
if (not(iss >> ret))
return argument_opt::failure;
if (ret < minimal)
runtime_get<Integer>(t, idx) = minimal;
else if (ret > maximal)
runtime_get<Integer>(t, idx) = maximal;
else
runtime_get<Integer>(t, idx) = ret;
return argument_opt::success_arg_consumed;
}
};
template<typename Tuple, typename Integer, Integer minimal, Integer maximal>
struct optional_arithmetic_parameter
{
static_assert(std::is_arithmetic<Integer>::value,
"Only integral or real type.");
argument_opt operator()(const char* str, Tuple& t, std::size_t idx)
{
if (str == nullptr)
return argument_opt::success_arg_not_consumed;
std::istringstream iss(str);
Integer ret;
if (not(iss >> ret))
return argument_opt::success_arg_not_consumed;
if (ret < minimal)
runtime_get<Integer>(t, idx) = minimal;
else if (ret > maximal)
runtime_get<Integer>(t, idx) = maximal;
else
runtime_get<Integer>(t, idx) = ret;
return argument_opt::success_arg_consumed;
}
};
template<typename Tuple>
struct option
{
const char* descr;
const char* long_arg;
const char* short_arg;
std::function<argument_opt(const char*, Tuple&, std::size_t)> fn;
option(
const char* description_,
const char* long_arg_,
const char* short_arg_,
std::function<argument_opt(const char*, Tuple&, std::size_t)> callback_)
: descr(description_)
, long_arg(long_arg_)
, short_arg(short_arg_)
, fn(callback_)
{}
constexpr const char* description() const
{
return descr;
}
constexpr const char* long_argument() const
{
return long_arg;
}
constexpr const char* short_argument() const
{
return short_arg;
}
constexpr argument_opt callback(const char* str,
Tuple& t,
std::size_t idx) const
{
return fn(str, t, idx);
}
};
enum getopt_status : unsigned
{
done = 0,
unknown_parameter = 1,
argument_error = 2,
};
template<typename Tuple>
unsigned
getopt(int argc,
const char* argv[],
const std::vector<option<Tuple>>& opts,
Tuple& t,
int& optind)
{
unsigned ret = 0;
int i = 1;
while (i != argc) {
auto length = std::strlen(argv[i]);
auto it = opts.end();
bool next_arg = false;
size_t n = length;
auto* argument = std::strchr(argv[i], ':');
if (not argument)
argument = std::strchr(argv[i], '=');
if (not argument) {
if (i + 1 != argc) {
auto argument_lenght = std::strlen(argv[i + 1]);
if (argument_lenght > 1) {
if (argv[i + 1][0] == '-') {
argument = argv[i + 1];
next_arg = true;
}
} else {
argument = argv[i + 1];
next_arg = true;
}
}
} else {
n = length - std::strlen(argument) - 1;
++argument;
}
if (length > 1) {
if (argv[i][0] == '-') {
if (argv[i][1] == '-') {
const char* arg = &(argv[i][2]);
it = std::find_if(opts.begin(),
opts.end(),
[arg, n](const option<Tuple>& elem) {
if (not elem.long_argument())
return false;
return !std::strncmp(
elem.long_argument(), arg, n);
});
} else {
const char* arg = &(argv[i][1]);
it = std::find_if(opts.begin(),
opts.end(),
[arg, n](const option<Tuple>& elem) {
if (not elem.short_argument())
return false;
return !std::strncmp(
elem.short_argument(), arg, n);
});
}
if (it == opts.end()) {
fprintf(stderr, "Unknown argument `%s'\n", argv[i]);
ret |=
static_cast<unsigned>(getopt_status::unknown_parameter);
++i;
} else {
auto eat_argument = it->callback(
argument,
t,
static_cast<unsigned>(std::distance(opts.begin(), it)));
if (eat_argument == argument_opt::failure) {
ret |=
static_cast<unsigned>(getopt_status::argument_error);
fprintf(stderr,
"Can not convert argument `%s' for option "
"`%s' (%d)\n",
(it->long_argument() ? it->long_argument()
: it->short_argument()),
argument,
static_cast<unsigned>(
std::distance(opts.begin(), it)));
} else if (eat_argument ==
argument_opt::success_arg_consumed and
next_arg)
++i;
++i;
}
} else {
optind = i;
i = argc;
}
}
}
return ret;
}
} // namespace bits
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment