Squashed 'external/cflags/' content from commit cc3ea1b95
git-subtree-dir: external/cflags git-subtree-split: cc3ea1b95d49fb3183cc26b60a7caa978e80500b
This commit is contained in:
505
include/cflags.h
Normal file
505
include/cflags.h
Normal file
@@ -0,0 +1,505 @@
|
||||
//
|
||||
// cflags version 3.0.3
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Stephen Lane-Walsh
|
||||
//
|
||||
// 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 CFLAGS_H
|
||||
#define CFLAGS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
#define CFLAGS_ERROR_OOM "cflags: out of memory"
|
||||
|
||||
enum cflags_type
|
||||
{
|
||||
CFLAGS_TYPE_UNDEFINED = -1,
|
||||
CFLAGS_TYPE_STRING,
|
||||
CFLAGS_TYPE_BOOL,
|
||||
CFLAGS_TYPE_INT,
|
||||
CFLAGS_TYPE_FLOAT,
|
||||
CFLAGS_TYPE_STRING_CALLBACK,
|
||||
CFLAGS_TYPE_BOOL_CALLBACK,
|
||||
CFLAGS_TYPE_INT_CALLBACK,
|
||||
CFLAGS_TYPE_FLOAT_CALLBACK,
|
||||
};
|
||||
|
||||
typedef enum cflags_type cflags_type_t;
|
||||
|
||||
struct cflags_flag
|
||||
{
|
||||
char short_name;
|
||||
const char * long_name;
|
||||
const char * description;
|
||||
|
||||
cflags_type_t type;
|
||||
unsigned count;
|
||||
|
||||
struct cflags_flag * next;
|
||||
|
||||
union {
|
||||
const char ** string_ptr;
|
||||
bool * bool_ptr;
|
||||
int * int_ptr;
|
||||
float * float_ptr;
|
||||
};
|
||||
|
||||
void (*string_callback)(const char *);
|
||||
void (*bool_callback)(bool);
|
||||
void (*int_callback)(int);
|
||||
void (*float_callback)(float);
|
||||
};
|
||||
|
||||
typedef struct cflags_flag cflags_flag_t;
|
||||
|
||||
struct cflags
|
||||
{
|
||||
const char * program;
|
||||
|
||||
int argc;
|
||||
char ** argv;
|
||||
|
||||
cflags_flag_t * first_flag;
|
||||
};
|
||||
|
||||
typedef struct cflags cflags_t;
|
||||
|
||||
static cflags_t * cflags_init()
|
||||
{
|
||||
cflags_t * flags = (cflags_t *)malloc(sizeof(cflags_t));
|
||||
if (!flags) {
|
||||
fprintf(stderr, CFLAGS_ERROR_OOM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flags->program = NULL;
|
||||
flags->argc = 0;
|
||||
flags->argv = NULL;
|
||||
flags->first_flag = NULL;
|
||||
return flags;
|
||||
}
|
||||
|
||||
cflags_flag_t * _cflags_add_flag(cflags_t * flags)
|
||||
{
|
||||
cflags_flag_t ** next_flag = &flags->first_flag;
|
||||
|
||||
while (*next_flag) {
|
||||
next_flag = &(*next_flag)->next;
|
||||
}
|
||||
|
||||
*next_flag = (cflags_flag_t *)malloc(sizeof(cflags_flag_t));
|
||||
if (!*next_flag) {
|
||||
fprintf(stderr, CFLAGS_ERROR_OOM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(*next_flag)->short_name = '\0';
|
||||
(*next_flag)->long_name = NULL;
|
||||
(*next_flag)->type = CFLAGS_TYPE_UNDEFINED;
|
||||
(*next_flag)->count = 0;
|
||||
(*next_flag)->description = NULL;
|
||||
(*next_flag)->next = NULL;
|
||||
|
||||
return *next_flag;
|
||||
}
|
||||
|
||||
static cflags_flag_t * cflags_add_string(cflags_t * flags, char short_name, const char * long_name, const char ** value, const char * description)
|
||||
{
|
||||
cflags_flag_t * flag = _cflags_add_flag(flags);
|
||||
if (!flag) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flag->short_name = short_name;
|
||||
flag->long_name = long_name;
|
||||
flag->type = CFLAGS_TYPE_STRING;
|
||||
flag->string_ptr = value;
|
||||
flag->description = description;
|
||||
return flag;
|
||||
}
|
||||
|
||||
static cflags_flag_t * cflags_add_bool(cflags_t * flags, char short_name, const char * long_name, bool * value, const char * description)
|
||||
{
|
||||
cflags_flag_t * flag = _cflags_add_flag(flags);
|
||||
if (!flag) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flag->short_name = short_name;
|
||||
flag->long_name = long_name;
|
||||
flag->type = CFLAGS_TYPE_BOOL;
|
||||
flag->bool_ptr = value;
|
||||
flag->description = description;
|
||||
return flag;
|
||||
}
|
||||
|
||||
static cflags_flag_t * cflags_add_int(cflags_t * flags, char short_name, const char * long_name, int * value, const char * description)
|
||||
{
|
||||
cflags_flag_t * flag = _cflags_add_flag(flags);
|
||||
if (!flag) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flag->short_name = short_name;
|
||||
flag->long_name = long_name;
|
||||
flag->type = CFLAGS_TYPE_INT;
|
||||
flag->int_ptr = value;
|
||||
flag->description = description;
|
||||
return flag;
|
||||
}
|
||||
|
||||
static cflags_flag_t * cflags_add_float(cflags_t * flags, char short_name, const char * long_name, float * value, const char * description)
|
||||
{
|
||||
cflags_flag_t * flag = _cflags_add_flag(flags);
|
||||
if (!flag) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flag->short_name = short_name;
|
||||
flag->long_name = long_name;
|
||||
flag->type = CFLAGS_TYPE_FLOAT;
|
||||
flag->float_ptr = value;
|
||||
flag->description = description;
|
||||
return flag;
|
||||
}
|
||||
|
||||
static cflags_flag_t * cflags_add_string_callback(cflags_t * flags, char short_name, const char * long_name, void (*func)(const char *), const char * description)
|
||||
{
|
||||
cflags_flag_t * flag = _cflags_add_flag(flags);
|
||||
if (!flag) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flag->short_name = short_name;
|
||||
flag->long_name = long_name;
|
||||
flag->type = CFLAGS_TYPE_STRING_CALLBACK;
|
||||
flag->string_callback = func;
|
||||
flag->description = description;
|
||||
return flag;
|
||||
}
|
||||
|
||||
static cflags_flag_t * cflags_add_bool_callback(cflags_t * flags, char short_name, const char * long_name, void (*func)(bool), const char * description)
|
||||
{
|
||||
cflags_flag_t * flag = _cflags_add_flag(flags);
|
||||
if (!flag) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flag->short_name = short_name;
|
||||
flag->long_name = long_name;
|
||||
flag->type = CFLAGS_TYPE_BOOL_CALLBACK;
|
||||
flag->bool_callback = func;
|
||||
flag->description = description;
|
||||
return flag;
|
||||
}
|
||||
|
||||
static cflags_flag_t * cflags_add_int_callback(cflags_t * flags, char short_name, const char * long_name, void (*func)(int), const char * description)
|
||||
{
|
||||
cflags_flag_t * flag = _cflags_add_flag(flags);
|
||||
if (!flag) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flag->short_name = short_name;
|
||||
flag->long_name = long_name;
|
||||
flag->type = CFLAGS_TYPE_INT_CALLBACK;
|
||||
flag->int_callback = func;
|
||||
flag->description = description;
|
||||
return flag;
|
||||
}
|
||||
|
||||
static cflags_flag_t * cflags_add_float_callback(cflags_t * flags, char short_name, const char * long_name, void (*func)(float), const char * description)
|
||||
{
|
||||
cflags_flag_t * flag = _cflags_add_flag(flags);
|
||||
if (!flag) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flag->short_name = short_name;
|
||||
flag->long_name = long_name;
|
||||
flag->type = CFLAGS_TYPE_FLOAT_CALLBACK;
|
||||
flag->float_callback = func;
|
||||
flag->description = description;
|
||||
return flag;
|
||||
}
|
||||
|
||||
static bool _cflags_parse_bool(const char * str)
|
||||
{
|
||||
return !(strcmp(str, "false") == 0 ||
|
||||
strcmp(str, "FALSE") == 0 ||
|
||||
strcmp(str, "0") == 0);
|
||||
}
|
||||
|
||||
static void _cflags_process_flag(cflags_flag_t * flag, const char * value)
|
||||
{
|
||||
++flag->count;
|
||||
|
||||
switch (flag->type) {
|
||||
case CFLAGS_TYPE_STRING:
|
||||
if (flag->string_ptr) {
|
||||
*flag->string_ptr = value;
|
||||
}
|
||||
break;
|
||||
case CFLAGS_TYPE_STRING_CALLBACK:
|
||||
if (flag->string_callback) {
|
||||
flag->string_callback(value);
|
||||
}
|
||||
break;
|
||||
case CFLAGS_TYPE_BOOL:
|
||||
if (flag->bool_ptr) {
|
||||
if (value) {
|
||||
*flag->bool_ptr = _cflags_parse_bool(value);
|
||||
}
|
||||
else {
|
||||
*flag->bool_ptr = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CFLAGS_TYPE_BOOL_CALLBACK:
|
||||
if (flag->bool_callback) {
|
||||
if (value) {
|
||||
flag->bool_callback(_cflags_parse_bool(value));
|
||||
}
|
||||
else {
|
||||
flag->bool_callback(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CFLAGS_TYPE_INT:
|
||||
if (flag->int_ptr) {
|
||||
if (value) {
|
||||
*flag->int_ptr = strtol(value, NULL, 10);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CFLAGS_TYPE_INT_CALLBACK:
|
||||
if (flag->int_callback) {
|
||||
if (value) {
|
||||
flag->int_callback(strtol(value, NULL, 10));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CFLAGS_TYPE_FLOAT:
|
||||
if (flag->float_ptr) {
|
||||
if (value) {
|
||||
*flag->float_ptr = strtof(value, NULL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CFLAGS_TYPE_FLOAT_CALLBACK:
|
||||
if (flag->float_callback) {
|
||||
if (value) {
|
||||
flag->float_callback(strtof(value, NULL));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
|
||||
static bool cflags_parse(cflags_t * flags, int argc, char ** argv)
|
||||
{
|
||||
flags->argc = 1;
|
||||
flags->argv = (char **)malloc(flags->argc * sizeof(char *));
|
||||
if (!flags->argv) {
|
||||
fprintf(stderr, CFLAGS_ERROR_OOM);
|
||||
return false;
|
||||
}
|
||||
flags->argv[0] = argv[0];
|
||||
flags->program = flags->argv[0];
|
||||
|
||||
bool passthrough = false;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
char * pch = argv[i];
|
||||
if (!passthrough && *pch == '-') {
|
||||
++pch;
|
||||
if (*pch == '-') {
|
||||
++pch;
|
||||
|
||||
if (*pch == '\0') {
|
||||
// All following flags are not to be processed
|
||||
passthrough = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Long
|
||||
char * key = pch;
|
||||
char * value = NULL;
|
||||
|
||||
char * divider = strchr(pch, '=');
|
||||
if (divider) {
|
||||
*divider = '\0';
|
||||
value = divider + 1;
|
||||
}
|
||||
|
||||
bool next_arg_is_value = (i + 1 < argc && argv[i + 1][0] != '-');
|
||||
|
||||
bool found = false;
|
||||
cflags_flag_t * flag = flags->first_flag;
|
||||
while (flag) {
|
||||
if (strcmp(flag->long_name, key) == 0) {
|
||||
found = true;
|
||||
|
||||
if (value) {
|
||||
_cflags_process_flag(flag, value);
|
||||
}
|
||||
else if (next_arg_is_value) {
|
||||
_cflags_process_flag(flag, argv[i + 1]);
|
||||
++i;
|
||||
}
|
||||
else if (flag->type == CFLAGS_TYPE_BOOL || flag->type == CFLAGS_TYPE_BOOL_CALLBACK) {
|
||||
_cflags_process_flag(flag, NULL);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "%s: option '--%s' requires an value\n", flags->program, key);
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
flag = flag->next;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
fprintf(stderr, "%s: unrecognized option '--%s'\n", flags->program, key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Short
|
||||
while (*pch) {
|
||||
bool is_last_short_flag = (*(pch + 1) == '\0');
|
||||
bool next_arg_is_value = (i + 1 < argc && argv[i + 1][0] != '-');
|
||||
|
||||
bool found = false;
|
||||
cflags_flag_t * flag = flags->first_flag;
|
||||
while (flag) {
|
||||
if (flag->short_name == *pch) {
|
||||
found = true;
|
||||
|
||||
if (is_last_short_flag && next_arg_is_value) {
|
||||
_cflags_process_flag(flag, argv[i + 1]);
|
||||
++i;
|
||||
}
|
||||
else if (flag->type == CFLAGS_TYPE_BOOL || flag->type == CFLAGS_TYPE_BOOL_CALLBACK) {
|
||||
_cflags_process_flag(flag, NULL);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "%s: option '-%c' requires an value\n", flags->program, *pch);
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
flag = flag->next;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
fprintf(stderr, "%s: unrecognized option '-%c'\n", flags->program, *pch);
|
||||
return false;
|
||||
}
|
||||
|
||||
++pch;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
++flags->argc;
|
||||
char ** tmp = (char **)realloc(flags->argv, flags->argc * sizeof(char *));
|
||||
if (!tmp) {
|
||||
fprintf(stderr, CFLAGS_ERROR_OOM);
|
||||
return false;
|
||||
}
|
||||
flags->argv = tmp;
|
||||
flags->argv[flags->argc - 1] = pch;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void cflags_free(cflags_t * flags)
|
||||
{
|
||||
free(flags->argv);
|
||||
flags->argv = NULL;
|
||||
|
||||
cflags_flag_t * tmp = NULL;
|
||||
cflags_flag_t * flag = flags->first_flag;
|
||||
while (flag) {
|
||||
tmp = flag;
|
||||
flag = flag->next;
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
free(flags);
|
||||
flags = NULL;
|
||||
}
|
||||
|
||||
static void cflags_print_usage(cflags_t * flags, const char * usage, const char * above, const char * below)
|
||||
{
|
||||
printf("%s %s\n", flags->program, usage);
|
||||
printf("%s\n\n", above);
|
||||
|
||||
cflags_flag_t * flag = flags->first_flag;
|
||||
while (flag) {
|
||||
printf(" ");
|
||||
if (flag->short_name != '\0') {
|
||||
printf("-%c, ", flag->short_name);
|
||||
}
|
||||
else {
|
||||
printf(" ");
|
||||
}
|
||||
size_t long_name_len = 0;
|
||||
if (flag->long_name) {
|
||||
printf("--%s", flag->long_name);
|
||||
long_name_len = strlen(flag->long_name);
|
||||
}
|
||||
if (long_name_len > 20) {
|
||||
printf("\n ");
|
||||
}
|
||||
else {
|
||||
for (size_t i = 0; i < 20 - long_name_len; ++i) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
printf("%s\n", flag->description);
|
||||
flag = flag->next;
|
||||
}
|
||||
|
||||
printf("\n%s\n", below);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // CFLAGS_H
|
||||
484
include/cflags.hpp
Normal file
484
include/cflags.hpp
Normal file
@@ -0,0 +1,484 @@
|
||||
//
|
||||
// cflags version 3.0.3
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Stephen Lane-Walsh
|
||||
//
|
||||
// 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 CFLAGS_HPP
|
||||
#define CFLAGS_HPP
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace cflags {
|
||||
|
||||
using std::string;
|
||||
using std::string_view;
|
||||
using std::vector;
|
||||
using std::function;
|
||||
|
||||
struct flag
|
||||
{
|
||||
public:
|
||||
|
||||
enum class type
|
||||
{
|
||||
Undefined = -1,
|
||||
String,
|
||||
CString,
|
||||
Bool,
|
||||
Int,
|
||||
Float,
|
||||
StringCallback,
|
||||
CStringCallback,
|
||||
BoolCallback,
|
||||
IntCallback,
|
||||
FloatCallback,
|
||||
};
|
||||
|
||||
char short_name;
|
||||
string long_name;
|
||||
string description;
|
||||
|
||||
type type;
|
||||
unsigned count;
|
||||
|
||||
union {
|
||||
string * string_ptr;
|
||||
const char ** cstring_ptr;
|
||||
bool * bool_ptr;
|
||||
int * int_ptr;
|
||||
float * float_ptr;
|
||||
};
|
||||
|
||||
function<void(string)> string_callback;
|
||||
function<void(const char *)> cstring_callback;
|
||||
function<void(bool)> bool_callback;
|
||||
function<void(int)> int_callback;
|
||||
function<void(float)> float_callback;
|
||||
|
||||
flag()
|
||||
: short_name('\0')
|
||||
, type(type::Undefined)
|
||||
, count(0)
|
||||
, string_ptr(nullptr) // This will set all of the *_ptr members
|
||||
{ }
|
||||
|
||||
inline void process(const char * value)
|
||||
{
|
||||
auto parse_bool = [](string_view str) {
|
||||
return !(
|
||||
str == "false" ||
|
||||
str == "FALSE" ||
|
||||
str == "0"
|
||||
);
|
||||
};
|
||||
|
||||
++count;
|
||||
|
||||
switch (type) {
|
||||
case type::String:
|
||||
if (string_ptr) {
|
||||
*string_ptr = value;
|
||||
}
|
||||
break;
|
||||
case type::StringCallback:
|
||||
if (string_callback) {
|
||||
if (value) {
|
||||
string_callback(value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case type::CString:
|
||||
if (cstring_ptr) {
|
||||
*cstring_ptr = value;
|
||||
}
|
||||
break;
|
||||
case type::CStringCallback:
|
||||
if (cstring_callback) {
|
||||
cstring_callback(value);
|
||||
}
|
||||
break;
|
||||
case type::Bool:
|
||||
if (bool_ptr) {
|
||||
if (value) {
|
||||
*bool_ptr = parse_bool(value);
|
||||
}
|
||||
else {
|
||||
*bool_ptr = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case type::BoolCallback:
|
||||
if (bool_callback) {
|
||||
if (value) {
|
||||
bool_callback(parse_bool(value));
|
||||
}
|
||||
else {
|
||||
bool_callback(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case type::Int:
|
||||
if (int_ptr) {
|
||||
if (value) {
|
||||
*int_ptr = strtol(value, nullptr, 10);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case type::IntCallback:
|
||||
if (int_callback) {
|
||||
if (value) {
|
||||
int_callback(strtol(value, nullptr, 10));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case type::Float:
|
||||
if (float_ptr) {
|
||||
if (value) {
|
||||
*float_ptr = strtof(value, nullptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case type::FloatCallback:
|
||||
if (float_callback) {
|
||||
if (value) {
|
||||
float_callback(strtof(value, nullptr));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class cflags
|
||||
{
|
||||
public:
|
||||
|
||||
string program;
|
||||
vector<string_view> args;
|
||||
|
||||
int argc;
|
||||
char ** argv;
|
||||
|
||||
cflags()
|
||||
: argc(0)
|
||||
, argv(nullptr)
|
||||
{ }
|
||||
|
||||
flag * add_flag(flag && flag)
|
||||
{
|
||||
_flags.push_back(flag);
|
||||
return &_flags.back();
|
||||
}
|
||||
|
||||
flag * add_string(char short_name, string long_name, string * value_ptr, string description)
|
||||
{
|
||||
flag flag;
|
||||
flag.short_name = short_name;
|
||||
flag.long_name = long_name;
|
||||
flag.type = flag::type::String;
|
||||
flag.string_ptr = value_ptr;
|
||||
flag.description = description;
|
||||
|
||||
_flags.push_back(flag);
|
||||
return &_flags.back();
|
||||
}
|
||||
|
||||
flag * add_cstring(char short_name, string long_name, const char ** value_ptr, string description)
|
||||
{
|
||||
flag flag;
|
||||
flag.short_name = short_name;
|
||||
flag.long_name = long_name;
|
||||
flag.type = flag::type::CString;
|
||||
flag.cstring_ptr = value_ptr;
|
||||
flag.description = description;
|
||||
|
||||
_flags.push_back(flag);
|
||||
return &_flags.back();
|
||||
}
|
||||
|
||||
flag * add_bool(char short_name, string long_name, bool * value_ptr, string description)
|
||||
{
|
||||
flag flag;
|
||||
flag.short_name = short_name;
|
||||
flag.long_name = long_name;
|
||||
flag.type = flag::type::Bool;
|
||||
flag.bool_ptr = value_ptr;
|
||||
flag.description = description;
|
||||
|
||||
_flags.push_back(flag);
|
||||
return &_flags.back();
|
||||
}
|
||||
|
||||
flag * add_int(char short_name, string long_name, int * value_ptr, string description)
|
||||
{
|
||||
flag flag;
|
||||
flag.short_name = short_name;
|
||||
flag.long_name = long_name;
|
||||
flag.type = flag::type::Int;
|
||||
flag.int_ptr = value_ptr;
|
||||
flag.description = description;
|
||||
|
||||
_flags.push_back(flag);
|
||||
return &_flags.back();
|
||||
}
|
||||
|
||||
flag * add_float(char short_name, string long_name, float * value_ptr, string description)
|
||||
{
|
||||
flag flag;
|
||||
flag.short_name = short_name;
|
||||
flag.long_name = long_name;
|
||||
flag.type = flag::type::Float;
|
||||
flag.float_ptr = value_ptr;
|
||||
flag.description = description;
|
||||
|
||||
_flags.push_back(flag);
|
||||
return &_flags.back();
|
||||
}
|
||||
|
||||
flag * add_string_callback(char short_name, string long_name, function<void(string)> callback, string description)
|
||||
{
|
||||
flag flag;
|
||||
flag.short_name = short_name;
|
||||
flag.long_name = long_name;
|
||||
flag.type = flag::type::StringCallback;
|
||||
flag.string_callback = callback;
|
||||
flag.description = description;
|
||||
|
||||
_flags.push_back(flag);
|
||||
return &_flags.back();
|
||||
}
|
||||
|
||||
flag * add_cstring_callback(char short_name, string long_name, function<void(const char *)> callback, string description)
|
||||
{
|
||||
flag flag;
|
||||
flag.short_name = short_name;
|
||||
flag.long_name = long_name;
|
||||
flag.type = flag::type::CStringCallback;
|
||||
flag.cstring_callback = callback;
|
||||
flag.description = description;
|
||||
|
||||
_flags.push_back(flag);
|
||||
return &_flags.back();
|
||||
}
|
||||
|
||||
flag * add_bool_callback(char short_name, string long_name, function<void(bool)> callback, string description)
|
||||
{
|
||||
flag flag;
|
||||
flag.short_name = short_name;
|
||||
flag.long_name = long_name;
|
||||
flag.type = flag::type::BoolCallback;
|
||||
flag.bool_callback = callback;
|
||||
flag.description = description;
|
||||
|
||||
_flags.push_back(flag);
|
||||
return &_flags.back();
|
||||
}
|
||||
|
||||
flag * add_int_callback(char short_name, string long_name, function<void(int)> callback, string description)
|
||||
{
|
||||
flag flag;
|
||||
flag.short_name = short_name;
|
||||
flag.long_name = long_name;
|
||||
flag.type = flag::type::IntCallback;
|
||||
flag.int_callback = callback;
|
||||
flag.description = description;
|
||||
|
||||
_flags.push_back(flag);
|
||||
return &_flags.back();
|
||||
}
|
||||
|
||||
flag * add_float_callback(char short_name, string long_name, function<void(float)> callback, string description)
|
||||
{
|
||||
flag flag;
|
||||
flag.short_name = short_name;
|
||||
flag.long_name = long_name;
|
||||
flag.type = flag::type::FloatCallback;
|
||||
flag.float_callback = callback;
|
||||
flag.description = description;
|
||||
|
||||
_flags.push_back(flag);
|
||||
return &_flags.back();
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
inline bool parse(int main_argc, char * main_argv[])
|
||||
{
|
||||
argc = main_argc;
|
||||
argv = main_argv;
|
||||
|
||||
program = argv[0];
|
||||
|
||||
bool passthrough = false;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
char * pch = argv[i];
|
||||
if (!passthrough && *pch == '-') {
|
||||
++pch;
|
||||
if (*pch == '-') {
|
||||
++pch;
|
||||
|
||||
if (*pch == '\0') {
|
||||
// All following flags are not to be processed
|
||||
passthrough = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Long
|
||||
char * key = pch;
|
||||
char * value = NULL;
|
||||
|
||||
char * divider = strchr(pch, '=');
|
||||
if (divider) {
|
||||
*divider = '\0';
|
||||
value = divider + 1;
|
||||
}
|
||||
|
||||
bool next_arg_is_value = (i + 1 < argc && argv[i + 1][0] != '-');
|
||||
|
||||
bool found = false;
|
||||
for (auto& flag : _flags) {
|
||||
if (flag.long_name == key) {
|
||||
found = true;
|
||||
|
||||
if (value) {
|
||||
flag.process(value);
|
||||
}
|
||||
else if (next_arg_is_value) {
|
||||
flag.process(argv[i + 1]);
|
||||
++i;
|
||||
}
|
||||
else if (flag.type == flag::type::Bool || flag.type == flag::type::BoolCallback) {
|
||||
flag.process(nullptr);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "%s: option '--%s' requires an value\n", program.c_str(), key);
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
fprintf(stderr, "%s: unrecognized option '--%s'\n", program.c_str(), key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Short
|
||||
while (*pch) {
|
||||
bool is_last_short_flag = (*(pch + 1) == '\0');
|
||||
bool next_arg_is_value = (i + 1 < argc && argv[i + 1][0] != '-');
|
||||
|
||||
bool found = false;
|
||||
for (flag& flag : _flags) {
|
||||
if (flag.short_name == *pch) {
|
||||
found = true;
|
||||
|
||||
if (is_last_short_flag && next_arg_is_value) {
|
||||
flag.process(argv[i + 1]);
|
||||
++i;
|
||||
}
|
||||
else if (flag.type == flag::type::Bool || flag.type == flag::type::BoolCallback) {
|
||||
flag.process(nullptr);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "%s: option '-%c' requires an value\n", program.c_str(), *pch);
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
fprintf(stderr, "%s: unrecognized option '-%c'\n", program.c_str(), *pch);
|
||||
return false;
|
||||
}
|
||||
|
||||
++pch;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
args.push_back(pch);
|
||||
_argv.push_back(pch);
|
||||
}
|
||||
}
|
||||
|
||||
argc = static_cast<int>(_argv.size());
|
||||
argv = _argv.data();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void print_usage(const string& usage, const string& above, const string& below)
|
||||
{
|
||||
printf("Usage: %s %s\n", program.c_str(), usage.c_str());
|
||||
printf("%s\n\n", above.c_str());
|
||||
|
||||
for (auto& flag : _flags) {
|
||||
printf(" ");
|
||||
if (flag.short_name != '\0') {
|
||||
printf("-%c, ", flag.short_name);
|
||||
}
|
||||
else {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
if (!flag.long_name.empty()) {
|
||||
printf("--%s", flag.long_name.c_str());
|
||||
}
|
||||
if (flag.long_name.size() > 20) {
|
||||
printf("\n ");
|
||||
}
|
||||
else {
|
||||
for (size_t i = 0; i < 20 - flag.long_name.size(); ++i) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
printf("%s\n", flag.description.c_str());
|
||||
}
|
||||
|
||||
printf("\n%s\n", below.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
vector<char *> _argv;
|
||||
|
||||
vector<flag> _flags;
|
||||
|
||||
};
|
||||
|
||||
} // namespace cflags
|
||||
|
||||
#endif // CFLAGS_HPP
|
||||
Reference in New Issue
Block a user